-
Notifications
You must be signed in to change notification settings - Fork 3
/
__init__.py
254 lines (199 loc) · 10.3 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Add Rendered Strips",
"author": "tintwotin",
"version": (1, 0),
"blender": (3, 4, 0),
"location": "Add > Rendered Strips",
"description": "Render selected strips to hard disk and add the rendered files as movie strips to the original scene in the first free channel",
"warning": "",
"doc_url": "",
"category": "Sequencer",
}
import os
import bpy
from datetime import date
def find_first_empty_channel(start_frame, end_frame):
for ch in range(1, len(bpy.context.scene.sequence_editor.sequences_all) + 1):
for seq in bpy.context.scene.sequence_editor.sequences_all:
if (
seq.channel == ch
and seq.frame_final_start < end_frame
and (seq.frame_final_start + seq.frame_final_duration) > start_frame
):
break
else:
return ch
return 1
def copy_struct(source, target):
if not source or not target:
return
for name, prop in source.bl_rna.properties.items():
if name in ("rna_type", "name", "name_full", "original", "is_evaluated"):
continue
try:
setattr(target, name, getattr(source, name))
except AttributeError:
new_source = getattr(source, name)
new_target = getattr(target, name)
if hasattr(new_source, "bl_rna"):
copy_struct(new_source, new_target)
except TypeError:
pass
class RenderSelectedStripsOperator(bpy.types.Operator):
"""Render selected strips to hard disk and add the rendered files as movie strips to the original scene in the first free channel"""
bl_idname = "sequencer.render_selected_strips"
bl_label = "Rendered Strips"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context and context.scene and context.scene.sequence_editor
def execute(self, context):
# Check for the context and selected strips
if not context or not context.scene or not context.scene.sequence_editor:
self.report({"ERROR"}, "No valid context or selected strips")
return {"CANCELLED"}
# Get the current scene and sequencer
current_scene = context.scene
sequencer = current_scene.sequence_editor
current_frame_old = bpy.context.scene.frame_current
# Check if there are any selected strips
if not any(strip.select for strip in sequencer.sequences_all):
self.report({"ERROR"}, "No strips selected")
return {"CANCELLED"}
# Get the selected sequences in the sequencer
selected_sequences = bpy.context.selected_sequences
# Get the first empty channel above all strips
insert_channel_total = 1
for s in sequencer.sequences_all:
if s.channel >= insert_channel_total:
insert_channel_total = s.channel + 1
# Loop over the selected strips in the current scene
for strip in selected_sequences:
if strip.type in {"MOVIE", "IMAGE", "SOUND", "SCENE", "TEXT", "COLOR", "META", "MASK"}:
# Deselect all strips in the current scene
for s in sequencer.sequences_all:
s.select = False
# Select the current strip in the current scene
strip.select = True
# Store current frame for later
bpy.context.scene.frame_current = int(strip.frame_start)
# Copy the strip to the clipboard
bpy.ops.sequencer.copy()
# Create a new scene
#new_scene = bpy.data.scenes.new(name="New Scene")
# Create a new scene
new_scene = bpy.ops.scene.new(type='EMPTY')
# Get the newly created scene
new_scene = bpy.context.scene
# Add a sequencer to the new scene
new_scene.sequence_editor_create()
# Set the new scene as the active scene
context.window.scene = new_scene
# Copy the scene properties from the current scene to the new scene
new_scene.render.resolution_x = current_scene.render.resolution_x
new_scene.render.resolution_y = current_scene.render.resolution_y
new_scene.render.resolution_percentage = (current_scene.render.resolution_percentage)
new_scene.render.pixel_aspect_x = current_scene.render.pixel_aspect_x
new_scene.render.pixel_aspect_y = current_scene.render.pixel_aspect_y
new_scene.render.fps = current_scene.render.fps
new_scene.render.fps_base = current_scene.render.fps_base
new_scene.render.sequencer_gl_preview = (current_scene.render.sequencer_gl_preview)
new_scene.render.use_sequencer_override_scene_strip = (current_scene.render.use_sequencer_override_scene_strip)
new_scene.world = current_scene.world
area = [area for area in context.screen.areas if area.type == "SEQUENCE_EDITOR"][0]
with bpy.context.temp_override(area=area):
# Paste the strip from the clipboard to the new scene
bpy.ops.sequencer.paste()
# Get the new strip in the new scene
new_strip = (new_scene.sequence_editor.active_strip) = bpy.context.selected_sequences[0]
copy_struct(strip, new_strip)
# Set the range in the new scene to fit the pasted strip
new_scene.frame_start = int(new_strip.frame_final_start)
new_scene.frame_end = (int(new_strip.frame_final_start + new_strip.frame_final_duration)-1)
# Set the name of the file
src_name = strip.name
src_dir = ""
src_ext = ".mp4"
# Set the path to the blend file
rendered_dir = blend_path = bpy.utils.user_resource("DATAFILES") + "/Rendered_Strips_" + str(date.today()) #bpy.data.filepath
# Set the render settings for rendering animation with FFmpeg and MP4 with sound
bpy.context.scene.render.image_settings.file_format = "FFMPEG"
bpy.context.scene.render.ffmpeg.format = "MPEG4"
bpy.context.scene.render.ffmpeg.audio_codec = "AAC"
# Create a new folder for the rendered files
if not os.path.exists(rendered_dir):
os.makedirs(rendered_dir)
# Set the output path for the rendering
output_path = os.path.join(
rendered_dir, src_name + "_rendered" + src_ext
)
new_scene.render.filepath = output_path
# Render the strip to hard disk
bpy.ops.render.opengl(animation=True, sequencer=True)
# Delete the new scene
bpy.data.scenes.remove(new_scene, do_unlink=True)
# Set the original scene as the active scene
context.window.scene = current_scene
# Reset to total top channel
insert_channel = insert_channel_total
area = [area for area in context.screen.areas if area.type == "SEQUENCE_EDITOR"][0]
with bpy.context.temp_override(area=area):
insert_channel = find_first_empty_channel(strip.frame_final_start, strip.frame_final_start+strip.frame_final_duration)
if strip.type == "SOUND":
# Insert the rendered file as a sound strip in the original scene without video.
bpy.ops.sequencer.sound_strip_add(
channel=insert_channel,
filepath=output_path,
frame_start=int(strip.frame_final_start),
overlap=0,
)
elif strip.type == "SCENE":
# Insert the rendered file as a movie strip and sound strip in the original scene.
bpy.ops.sequencer.movie_strip_add(
channel=insert_channel,
filepath=output_path,
frame_start=int(strip.frame_final_start),
overlap=0,
)
else:
# Insert the rendered file as a movie strip in the original scene without sound.
bpy.ops.sequencer.movie_strip_add(
channel=insert_channel,
filepath=output_path,
frame_start=int(strip.frame_final_start),
overlap=0,
sound=False,
)
# Redraw UI to display the new strip. Remove this if Blender crashes: https://docs.blender.org/api/current/info_gotcha.html#can-i-redraw-during-script-execution
bpy.ops.wm.redraw_timer(type="DRAW_WIN_SWAP", iterations=1)
# Reset current frame
bpy.context.scene.frame_current = current_frame_old
return {"FINISHED"}
def menu_func(self, context):
self.layout.separator()
self.layout.operator(RenderSelectedStripsOperator.bl_idname, icon="SEQ_STRIP_DUPLICATE")
def register():
bpy.utils.register_class(RenderSelectedStripsOperator)
bpy.types.SEQUENCER_MT_add.append(menu_func)
def unregister():
bpy.types.SEQUENCER_MT_strip.remove(menu_func)
bpy.utils.unregister_class(RenderSelectedStripsOperator)
if __name__ == "__main__":
register()