add generic templates

pull/5/head
ChristopheSeux 2024-04-16 14:28:02 +02:00
parent 2960bab63d
commit 0055e0f39e
11 changed files with 343 additions and 116 deletions

View File

@ -164,19 +164,24 @@ def parse(template, string):
if result := re.match(reg, string):
return result.groupdict()
def find_last(template, **kargs):
#print(find_last, template, kargs)
pattern = Path(template).as_posix()
pattern = expandvars(pattern)
def expand(template, **kargs):
template = Path(template).as_posix()
template = expandvars(template)
for key, value in kargs.items():
pattern = re.sub(r'\{%s\}'%key, value, pattern)
template = re.sub(r'\{%s\}'%key, value, template)
return template
def find_last(template, **kargs):
pattern = expand(template, **kargs)
pattern = re.sub(r'{\w+:(\d{2})d}', lambda x : '?' * int(x.groups()[0]), pattern)
pattern = re.sub(r'{.*?}', '*', pattern)
print(pattern)
filepaths = glob.glob(pattern)
if filepaths:
return Path(max(filepaths))
return Path(max(filepaths))
else:
print(f'No preview found for template {pattern}')

View File

@ -5,6 +5,7 @@ import os
import bpy
from bpy.types import Operator
from bpy.props import IntProperty
import vse_toolbox
@ -37,6 +38,21 @@ class VSETB_OT_load_settings(Operator):
bl_label = 'Load Settings'
bl_description = 'Load VSE ToolBox settings from config file'
def set_config(self, data, values):
for k, v in values.items():
if not hasattr(data, k):
print(f'{repr(data)} has no attribute {k}')
continue
if isinstance(v, list):
data[k] = v
return
try:
setattr(data, k, v)
except Exception:
print(f'Could not set property {k} with value {v} to {repr(data)}')
def execute(self, context):
prefs = get_addon_prefs()
settings = get_scene_settings()
@ -48,6 +64,10 @@ class VSETB_OT_load_settings(Operator):
if not addon_config:
return {'CANCELLED'}
# Environ set
for k, v in addon_config.get('environ', {}).items():
os.environ[k] = str(v)
addon_config['trackers'] = addon_config.get('trackers')
trackers_config = addon_config.pop('trackers')
@ -61,11 +81,10 @@ class VSETB_OT_load_settings(Operator):
for k, v in tracker_config.items():
setattr(tracker, k, v)
addon_config['spreadsheet_export'] = addon_config.get('spreadsheet_export', {})
spreadsheet_export_config = addon_config.pop('spreadsheet_export')
addon_config['spreadsheet_import'] = addon_config.get('spreadsheet_import', {})
spreadsheet_import_config = addon_config.pop('spreadsheet_import')
spreadsheet_export_config = addon_config.pop('spreadsheet_export', {})
spreadsheet_import_config = addon_config.pop('spreadsheet_import', {})
upload_to_tracker_config = addon_config.pop('upload_to_tracker', {})
import_shots_config = addon_config.pop('import_shots', {})
project_name = addon_config.get('project_name')
if project_name not in settings.projects:
@ -77,12 +96,7 @@ class VSETB_OT_load_settings(Operator):
# Project Properties
project = settings.active_project
for k, v in addon_config.items():
try:
setattr(project, k, v)
except Exception:
print(f'Could not set property {k} with value {v} to project {settings.project_name}')
self.set_config(project, addon_config)
export_cells = project.spreadsheet_export.cells
for k, v in spreadsheet_export_config.items():
@ -148,13 +162,53 @@ class VSETB_OT_load_settings(Operator):
except Exception:
print(f'Could not set option {k} with value {v} to spreadsheet')
self.report({"INFO"}, 'Settings loaded with sucess')
# Upload to Tracker
self.set_config(project.upload_to_tracker, upload_to_tracker_config)
# Import Shots
self.set_config(project.import_shots, import_shots_config)
self.report({"INFO"}, 'Settings loaded with success')
return {'FINISHED'}
class VSETB_OT_add_template(Operator):
bl_idname = "vse_toolbox.add_template"
bl_label = "Add Template"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
scn = context.scene
settings = get_scene_settings()
project = settings.active_project
project.templates.add()
return {'FINISHED'}
class VSETB_OT_remove_template(Operator):
bl_idname = "vse_toolbox.remove_template"
bl_label = "Remove Template"
bl_options = {"REGISTER", "UNDO"}
index : IntProperty()
def execute(self, context):
scn = context.scene
settings = get_scene_settings()
project = settings.active_project
project.templates.remove(self.index)
return {'FINISHED'}
classes = (
VSETB_OT_reload_addon,
VSETB_OT_load_settings
VSETB_OT_load_settings,
VSETB_OT_add_template,
VSETB_OT_remove_template
)
def register():

View File

@ -8,9 +8,10 @@ import bpy
from bpy.types import Operator
from bpy.props import BoolProperty
from vse_toolbox.sequencer_utils import (get_strips, render_strip, render_sound, render_scene)
from vse_toolbox.sequencer_utils import (get_strips, render_strip, render_sound,
render_scene, get_render_attributes)
from vse_toolbox.bl_utils import (get_scene_settings, background_render)
from vse_toolbox.file_utils import install_module
from vse_toolbox.file_utils import install_module, expand
@ -116,53 +117,67 @@ class VSETB_OT_render(Operator):
settings = get_scene_settings()
project = settings.active_project
project_templates = {t.name: t.value for t in project.templates}
format_data = {**settings.format_data, **project.format_data}
single_file = False
sequence_strips = []
shot_strips = []
render_attrs = get_render_attributes()
start_time = time.perf_counter()
if project.render_single_file:
single_file = True
if project.render_video:
video_template = os.path.expandvars(project.render_video_template)
video_template = expand(project.render_video_template, **project_templates)
video_path = video_template.format(**format_data)
render_scene(video_path)
render_scene(video_path, attributes=render_attrs)
#background_render(output=video_path)
if project.render_audio:
audio_template = os.path.expandvars(project.render_audio_template)
audio_template = expand(project.render_audio_template, **project_templates)
audio_path = audio_template.format(**format_data)
bpy.ops.sound.mixdown(filepath=audio_path)
if project.render_sequence:
print('Render Sequences...')
for strip in get_strips(channel='Sequences', selected_only=project.render_selected_only):
sequence_strips = get_strips(channel='Sequences', selected_only=project.render_selected_only)
for strip in sequence_strips:
#print(strip.name)
strip_settings = strip.vsetb_strip_settings
strip_data = {**format_data, **strip_settings.format_data}
if project.render_sequence and project.render_video_per_sequence:
video_sequence_template = os.path.expandvars(project.render_video_sequence_template)
video_sequence_template = expand(project.render_video_sequence_template, **project_templates)
sequence_render_path = video_sequence_template.format(**strip_data)
render_strip(strip, sequence_render_path)
render_strip(strip, sequence_render_path, attributes=render_attrs)
if project.render_shot and project.render_audio_per_sequence:
audio_sequence_template = os.path.expandvars(project.render_audio_sequence_template)
audio_sequence_template = expand(project.render_audio_sequence_template, **project_templates)
audio_render_path = audio_sequence_template.format(**strip_data)
render_sound(strip, audio_render_path)
if project.render_shot:
for strip in get_strips(channel='Shots', selected_only=project.render_selected_only):
shot_strips = get_strips(channel='Shots', selected_only=project.render_selected_only)
for strip in shot_strips:
strip_settings = strip.vsetb_strip_settings
strip_data = {**format_data, **strip_settings.format_data}
if project.render_video_per_strip:
video_strip_template = os.path.expandvars(project.render_video_strip_template)
video_strip_template = expand(project.render_video_strip_template, **project_templates)
strip_render_path = video_strip_template.format(**strip_data)
render_strip(strip, strip_render_path)
render_strip(strip, strip_render_path, attributes=render_attrs)
if project.render_audio_per_strip:
audio_strip_template = os.path.expandvars(project.render_audio_strip_template)
audio_strip_template = expand(project.render_audio_strip_template, **project_templates)
audio_render_path = audio_strip_template.format(**strip_data)
render_sound(strip, audio_render_path)
if not single_file and not sequence_strips and not shot_strips:
self.report({"ERROR"}, f'No strips rendered, select sequence or shot strips')
return {"CANCELLED"}
self.report({"INFO"}, f'Strips rendered in {time.perf_counter()-start_time} seconds')
return {"FINISHED"}

View File

@ -7,7 +7,7 @@ from tempfile import gettempdir
import bpy
from bpy.types import Operator, UIList
from bpy.props import (CollectionProperty, BoolProperty, EnumProperty, StringProperty)
from bpy.props import (CollectionProperty, BoolProperty, EnumProperty, StringProperty, IntProperty)
from vse_toolbox.constants import (EDITS, EDIT_SUFFIXES, MOVIES, MOVIE_SUFFIXES,
SOUNDS, SOUND_SUFFIXES)
@ -16,8 +16,8 @@ from vse_toolbox.sequencer_utils import (clean_sequencer, import_edit, import_mo
import_sound, get_strips, get_channel_index, get_empty_channel, scale_clip_to_fit)
from vse_toolbox.bl_utils import (get_scene_settings, get_addon_prefs, get_scene_settings)
from vse_toolbox.file_utils import install_module, parse, find_last
#from vse_toolbox.template import Template
from vse_toolbox.file_utils import install_module, parse, find_last, expand
class VSETB_OT_auto_select_files(Operator):
bl_idname = "vse_toolbox.auto_select_files"
@ -254,6 +254,38 @@ class VSETB_OT_unselect_task(Operator):
return {'FINISHED'}
class VSETB_OT_add_import_template(Operator):
bl_idname = "vse_toolbox.add_import_template"
bl_label = "Add Template"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
scn = context.scene
settings = get_scene_settings()
project = settings.active_project
project.import_shots.video_templates.add()
return {'FINISHED'}
class VSETB_OT_remove_import_template(Operator):
bl_idname = "vse_toolbox.remove_import_template"
bl_label = "Remove Template"
bl_options = {"REGISTER", "UNDO"}
index : IntProperty()
def execute(self, context):
scn = context.scene
settings = get_scene_settings()
project = settings.active_project
project.import_shots.video_templates.remove(self.index)
return {'FINISHED'}
def get_sequence_items(self, context):
settings = get_scene_settings()
project = settings.active_project
@ -351,6 +383,9 @@ class VSETB_OT_import_shots(Operator):
if not last_comment:
return
last_preview = last_comment['previews'][0]
ext = last_preview['extension']
shot_name = shot['name']
sequence_name = f"{shot['sequence_name']}_"
if shot_name.startswith(sequence_name):
@ -358,10 +393,9 @@ class VSETB_OT_import_shots(Operator):
preview_dir = self.get_preview_dir()
filepath = Path(preview_dir, f'{sequence_name}{shot_name}_{task_type.name}')
filepath = Path(preview_dir, f'{sequence_name}{shot_name}_{task_type.name}.{ext}')
filepath.parent.mkdir(parents=True, exist_ok=True)
last_preview = last_comment['previews'][0]
tracker.download_preview_file(last_preview, str(filepath))
return filepath
@ -378,17 +412,20 @@ class VSETB_OT_import_shots(Operator):
settings = get_scene_settings()
project = settings.active_project
import_shots = project.import_shots
project_templates = {t.name: t.value for t in project.templates}
pattern = Path(
import_shots.shot_folder_template,
import_shots.import_video_template)
for template in [import_shots.video_template] + list(import_shots.video_templates.keys()):
template = expand(template, **project_templates)
# Normalize name in snake_case for now
sequence = sequence.name.lower().replace(' ', '_')
shot = parse(project.shot_template, shot_name)['shot']
task = task_type.name.lower().replace(' ', '_')
format_data = project.format_data
format_data.update(parse(project.sequence_template, sequence.name))
format_data.update(parse(project.shot_template, shot_name))
# Normalize name in snake_case for now
format_data.update(task=task_type.name.lower().replace(' ', '_'))
return find_last(pattern, sequence=sequence, shot=shot, task=task)
if last_preview := find_last(template, **format_data):
return last_preview
def execute(self, context):
scn = context.scene
@ -405,6 +442,9 @@ class VSETB_OT_import_shots(Operator):
conformed = False
scn.sequence_editor_clear()
scn.sequence_editor_create()
self.set_sequencer_channels([t.name for t in task_types])
frame_index = 1
for sequence in sequences:
@ -412,6 +452,7 @@ class VSETB_OT_import_shots(Operator):
sequence_start = frame_index
for shot_data in shots_data:
frames = int(shot_data['nb_frames'])
frame_end = frame_index + frames
strip = scn.sequence_editor.sequences.new_effect(
name=shot_data['name'],
@ -425,25 +466,23 @@ class VSETB_OT_import_shots(Operator):
for task_type in task_types:
if import_shots.import_source == 'DISK':
preview = self.find_shot_preview(sequence, shot_data['name'], task_type)
#print(videos)
#return {"FINISHED"}
preview = self.find_shot_preview(sequence, shot_data['name'], task_type)
else:
preview = self.download_preview(task_type, shot_data)
if not preview:
print(f'No preview found for shot {shot_data["name"]}')
if not preview:
print(f'No preview found for shot {shot_data["name"]}')
continue
print(f'Loading Preview from {preview}')
# Load Video
channel_index = get_channel_index(f'{task_type.name} Video')
video_clip = import_movie(preview)
video_clip.frame_start = frame_index
video_clip = import_movie(preview, frame_start=frame_index, frame_end=frame_end)
video_clip.channel = channel_index
if video_clip.frame_offset_end:
video_clip.color_tag = 'COLOR_01'
if not conformed:
self.conform_render(video_clip)
conformed = True
@ -453,8 +492,10 @@ class VSETB_OT_import_shots(Operator):
# Load Audio
channel_index = get_channel_index(f'{task_type.name} Audio')
audio_clip = import_sound(preview, frame_start=frame_index,
frame_end=video_clip.frame_final_end)
frame_end=frame_end)
audio_clip.channel = channel_index
if video_clip.frame_offset_end:
audio_clip.color_tag = 'COLOR_01'
frame_index += frames
@ -471,6 +512,8 @@ class VSETB_OT_import_shots(Operator):
scn.frame_start = 1
scn.frame_end = frame_index -1
#bpy.ops.vse_toolbox.set_stamps()
return {'FINISHED'}
def draw(self, context):
@ -489,8 +532,16 @@ class VSETB_OT_import_shots(Operator):
if import_shots.import_source == 'DISK':
#col.prop(import_shots, "sequence_dir_template", text='Sequence Dir')
col.prop(import_shots, "shot_folder_template", text='Shot Folder')
col.prop(import_shots, "import_video_template", text='Video')
#col.prop(import_shots, "shot_folder_template", text='Shot Folder')
row = col.row(align=True)
row.prop(import_shots, "video_template", text='')
row.operator("vse_toolbox.add_import_template", text='', icon='ADD')
for i, template in enumerate(import_shots.video_templates):
row = col.row(align=True)
row.prop(template, "name", text='')
row.operator("vse_toolbox.remove_import_template", text='', icon='REMOVE').index=i
col.separator()
else:
col.prop(import_shots, "previews_folder", text='Previews Folder')
@ -579,7 +630,7 @@ class VSETB_OT_import_shots(Operator):
# self.report({"ERROR"}, "Save your Blender file first")
# return {"CANCELLED"}
return context.window_manager.invoke_props_dialog(self, width=400)
return context.window_manager.invoke_props_dialog(self, width=350)
def check(self, context):
return True
@ -590,6 +641,8 @@ classes = (
VSETB_OT_unselect_sequence,
VSETB_OT_unselect_task,
VSETB_OT_select_task,
VSETB_OT_add_import_template,
VSETB_OT_remove_import_template,
VSETB_UL_import_task,
VSETB_OT_auto_select_files,
VSETB_OT_import_files,

View File

@ -155,7 +155,7 @@ class VSETB_OT_set_sequencer(Operator):
scn.view_settings.view_transform = 'Standard'
scn.render.image_settings.file_format = 'FFMPEG'
scn.render.ffmpeg.gopsize = 5
scn.render.ffmpeg.gopsize = 8
scn.render.ffmpeg.constant_rate_factor = 'HIGH'
scn.render.ffmpeg.format = 'QUICKTIME'
scn.render.ffmpeg.audio_codec = 'AAC'
@ -304,8 +304,18 @@ class VSETB_OT_open_shot_folder(Operator):
@classmethod
def poll(cls, context):
settings = get_scene_settings()
project = settings.active_project
if not project.templates.get('shot_dir'):
cls.poll_message_set('No shot_dir template setted')
return
strip = context.active_sequence_strip
return strip and get_channel_name(strip) == 'Shots'
if not strip or get_channel_name(strip) != 'Shots':
cls.poll_message_set('No shot strip active')
return
return True
def execute(self, context):
strip = context.active_sequence_strip
@ -316,7 +326,7 @@ class VSETB_OT_open_shot_folder(Operator):
format_data = {**settings.format_data, **project.format_data, **strip_settings.format_data}
shot_folder_template = expandvars(project.import_shots.shot_folder_template)
shot_folder_template = expandvars(project.templates['shot_dir'].value)
shot_folder_path = shot_folder_template.format(**format_data)
bpy.ops.wm.path_open(filepath=shot_folder_path)

View File

@ -12,10 +12,11 @@ from bpy.props import (BoolProperty, EnumProperty, StringProperty)
from vse_toolbox.constants import (ASSET_PREVIEWS, PREVIEWS_DIR, ASSET_ITEMS)
from vse_toolbox.sequencer_utils import (get_strips, get_strip_render_path, get_strip_sequence_name)
from vse_toolbox.sequencer_utils import (get_strips, get_strip_render_path, get_strip_sequence_name,
render_strip, get_render_attributes)
from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings)
from vse_toolbox.file_utils import (norm_name,)
from vse_toolbox.file_utils import (norm_name, expand)
from bpy.app.handlers import persistent
@ -36,7 +37,7 @@ class VSETB_OT_tracker_connect(Operator):
settings = get_scene_settings()
try:
prefs.tracker.connect()
self.report({'INFO'}, f'Sucessfully login to {settings.tracker_name.title()}')
self.report({'INFO'}, f'successfully login to {settings.tracker_name.title()}')
return {"FINISHED"}
except Exception as e:
print('e: ', e)
@ -121,6 +122,10 @@ class VSETB_OT_load_projects(Operator):
(r,g,b), a = map(lambda component: component / 255, bytes.fromhex(color_str[-6:])), 1.0
return (r,g,b,a)
def invoke(self, context, event):
self.ctrl = event.ctrl
return self.execute(context)
def execute(self, context):
settings = get_scene_settings()
prefs = get_addon_prefs()
@ -213,14 +218,16 @@ class VSETB_OT_load_projects(Operator):
if project.name not in project_names:
settings.projects.remove(list(settings.projects).index(project))
bpy.ops.vse_toolbox.load_settings()
if self.ctrl or not settings.get('projects_loaded'):
bpy.ops.vse_toolbox.load_settings()
if prev_project_name != '/' and prev_project_name in settings.projects:
settings.project_name = prev_project_name
#if settings.active_project:
# settings.active_project.set_strip_metadata()
settings['projects_loaded'] = True
self.report({"INFO"}, 'Successfully Load Tracker Projects')
@ -251,19 +258,16 @@ class VSETB_OT_new_episode(Operator):
tracker = prefs.tracker
episode_name = settings.episode_template.format(index=int(self.episode_name))
#print(self.episode_name)
#print('episode_name: ', episode_name)
episode = tracker.get_episode(episode_name)
if episode:
self.report({'ERROR'}, f'Episode {episode_name} already exists')
return {"CANCELLED"}
tracker.new_episode(episode_name)
# tracker.get_episodes
tracker.update_project()
self.report({'INFO'}, f'Episode {episode_name} successfully created')
return {'FINISHED'}
@ -302,8 +306,6 @@ class VSETB_OT_upload_to_tracker(Operator):
col.use_property_split = False
col.prop(upload_to_tracker, 'render_strip_template', text='')
col.use_property_split = True
#else:
# col.label(text=f'Source: {self.project.render_video_strip_template}')
col.separator()
@ -329,21 +331,27 @@ class VSETB_OT_upload_to_tracker(Operator):
#self.report({'ERROR'}, f'Export not implemented yet.')
prefs = get_addon_prefs()
settings = get_scene_settings()
tracker = prefs.tracker
upload_to_tracker = self.project.upload_to_tracker
render_attrs = get_render_attributes()
project_templates = {t.name: t.value for t in self.project.templates}
episode = None
if settings.active_episode:
episode = settings.active_episode.id
format_data = {**settings.format_data, **self.project.format_data}
tracker = prefs.tracker
status = upload_to_tracker.status
if status == 'CURRENT':
status = None
shot_strips = get_strips(channel='Shots', selected_only=True)
context.window_manager.progress_begin(0, len(shot_strips))
for strip in get_strips(channel='Shots', selected_only=True):
for i, strip in enumerate(shot_strips):
context.window_manager.progress_update(i)
strip_settings = strip.vsetb_strip_settings
sequence_name = get_strip_sequence_name(strip)
shot_name = strip.name
@ -369,12 +377,17 @@ class VSETB_OT_upload_to_tracker(Operator):
strip_data = {**format_data, **strip_settings.format_data}
if upload_to_tracker.render_strips:
preview_template = expandvars(upload_to_tracker.render_strip_template)
preview_template = expand(upload_to_tracker.render_strip_template, **project_templates)
strip_data['ext'] = 'mov'
else:
preview_template = expandvars(self.project.render_video_strip_template)
preview = preview_template.format(**strip_data)
preview = Path(os.path.abspath(bpy.path.abspath(preview)))
if upload_to_tracker.render_strips:
render_strip(strip, preview, attributes=render_attrs)
#print(preview)
if not preview.exists():
print(f'The preview {preview} not exists')
@ -387,7 +400,7 @@ class VSETB_OT_upload_to_tracker(Operator):
preview = None
comment_data = None
if status or comment or preview:
if status or upload_to_tracker.comment or preview:
comment_data = tracker.new_comment(task, comment=upload_to_tracker.comment, status=status)
if preview:
print('Upload preview from', preview)
@ -423,6 +436,8 @@ class VSETB_OT_upload_to_tracker(Operator):
if task.comment and tracker_task.get('last_comment') != task.comment:
tracker.new_comment(tracker_task, comment=task.comment)
context.window_manager.progress_end()
return {"FINISHED"}
@ -448,6 +463,7 @@ class VSETB_OT_open_shot_on_tracker(Operator):
return {"FINISHED"}
@persistent
def set_asset_items(scene=None):
ASSET_ITEMS.clear()

View File

@ -78,7 +78,7 @@ class Kitsu(Tracker):
print(f'Info: Log in to kitsu as {login}')
res = gazu.log_in(login, password)
LOGIN = login
print(f'Info: Sucessfully login to Kitsu as {res["user"]["full_name"]}')
print(f'Info: successfully login to Kitsu as {res["user"]["full_name"]}')
return res['user']
except Exception as e:
print(f'Error: {traceback.format_exc()}')

View File

@ -16,6 +16,19 @@ import subprocess
from collections import defaultdict
def get_render_attributes():
scn = bpy.context.scene
return [
(scn.view_settings, "view_transform", 'Standard'),
(scn.render.image_settings, "file_format", 'FFMPEG'),
(scn.render.ffmpeg, "gopsize", 8),
(scn.render.ffmpeg, "constant_rate_factor", 'HIGH'),
(scn.render.ffmpeg, "format", 'QUICKTIME'),
(scn.render.ffmpeg, "audio_codec", 'MP3'),
(scn.render.ffmpeg, "audio_mixrate", 44100),
(scn.render.ffmpeg, "audio_bitrate", 128)
]
def frame_to_timecode(frame, fps):
total_seconds = frame / fps
@ -109,20 +122,23 @@ def rename_strips(strips, template, increment=10, start_number=0, padding=3, by_
strip_number = 0
for strip in strips:
channel = get_channel_name(strip)
sequence_name = get_strip_sequence_name(strip)
sequence_data = parse(project.sequence_template, sequence_name)
format_data = {}
if channel == 'Shots':
format_data = parse(project.sequence_template, sequence_name)
else:
format_data['sequence'] = str(strip_number*increment + start_number).zfill(padding)
if (by_sequence and prev_sequence_name and
sequence_name and sequence_name != prev_sequence_name):
strip_number = 0
format_data = dict(
format_data.update(
sequence_strip=sequence_name,
episode=episode_name,
shot=str(strip_number*increment + start_number).zfill(padding))
format_data.update(sequence_data)
name = template.format(**format_data)
existing_strip = scn.sequence_editor.sequences_all.get(name)
@ -270,26 +286,32 @@ def render_sound(strip, output):
scn.frame_end = scene_end
scn.frame_current = scene_current
def render_scene(output):
output = os.path.abspath(bpy.path.abspath(output))
def render_scene(output, attributes=None):
output = os.path.abspath(bpy.path.abspath(str(output)))
scn = bpy.context.scene
render_path = scn.render.filepath
scn.render.filepath = output
if attributes is None:
attributes = []
attributes += [
(scn.render, "filepath", output),
]
print(f'Render Strip to {scn.render.filepath}')
Path(output).parent.mkdir(exist_ok=True, parents=True)
bpy.ops.render.opengl(animation=True, sequencer=True)
scn.render.filepath = render_path
def render_strip(strip, output):
output = os.path.abspath(bpy.path.abspath(output))
def render_strip(strip, output, attributes=None):
output = os.path.abspath(bpy.path.abspath(str(output)))
scn = bpy.context.scene
attributes = [
if attributes is None:
attributes = []
attributes += [
(scn, "frame_start", strip.frame_final_start),
(scn, "frame_end", strip.frame_final_end - 1),
(scn, "frame_current"),
@ -316,7 +338,6 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
for s in strips:
s.channel = empty_channel
edl = Path(filepath)
try:
timeline = opentimelineio.adapters.read_from_file(
@ -397,11 +418,14 @@ def import_edit(filepath, adapter="cmx_3600", channel='Shots', match_by='name'):
scn.frame_end = frame_end-1
return timeline
def import_movie(filepath):
def import_movie(filepath, frame_start=None, frame_end=None):
scn = bpy.context.scene
if frame_start is None:
frame_start = scn.frame_start
res_x = scn.render.resolution_x
res_y = scn.render.resolution_y
res_y = scn.render.resolution_y
if bpy.data.is_saved:
relpath = Path(bpy.path.relpath(str(filepath)))
@ -412,7 +436,7 @@ def import_movie(filepath):
name=Path(filepath).stem,
filepath=str(filepath),
channel=get_channel_index('Movie'),
frame_start=scn.frame_start
frame_start=frame_start
)
elem = strip.strip_elem_from_frame(scn.frame_current)
@ -423,7 +447,8 @@ def import_movie(filepath):
if src_height != res_y:
strip.transform.scale_y = (res_y / src_height)
if frame_end is not None:
strip.frame_final_end = frame_end
return strip

View File

@ -39,20 +39,52 @@ class VSETB_PT_main(VSETB_main, Panel):
# row.operator('vse_toolbox.load_projects', icon='FILE_REFRESH', text='', emboss=False)
def draw(self, context):
layout = self.layout
wm = context.window_manager
scn = context.scene
settings = get_scene_settings()
prefs = get_addon_prefs()
row = self.layout.row(align=False)
row = layout.row(align=True)
row.operator('vse_toolbox.load_projects', icon='FILE_REFRESH', text='', emboss=False)
row.separator(factor=0.5)
row.prop(settings, 'project_name', text='')
project = settings.active_project
if project and project.type == 'TVSHOW':
row.separator(factor=0.5)
row.prop(project, 'episode_name', text='')
row.separator(factor=0.5)
row.prop(project, "show_settings", icon="PREFERENCES", text='')
if project.show_settings:
box = layout.box()
split = box.split(factor=0.3)
name_col = split.column()
template_col = split.column()
row = name_col.row(align=True) # Padding
row.separator(factor=0.5)
row.label(text="Name")
row = template_col.row(align=True) # Padding
row.separator(factor=0.5)
row.label(text="Template")
row.operator("vse_toolbox.add_template", text="", icon='ADD', emboss=False)
for i, template in enumerate(project.templates):
row = name_col.row()
row.prop(template, "name", text="")
row = template_col.row(align=True)
subrow = row.row()
subrow.prop(template, "value", text="")
row.separator(factor=0.25)
row.operator("vse_toolbox.remove_template", text="", icon='REMOVE', emboss=False).index = i
# settings = get_scene_settings()
# prefs = get_addon_prefs()
@ -173,7 +205,7 @@ class VSETB_PT_settings(VSETB_main, Panel):
class VSETB_PT_imports(VSETB_main, Panel):
bl_label = "Imports"
#bl_parent_id = "VSETB_PT_main"
bl_options = {'DEFAULT_CLOSED'}
#bl_options = {'DEFAULT_CLOSED'}
def draw_header_preset(self, context):
self.layout.operator('vse_toolbox.import_files', icon='IMPORT', text='', emboss=False)

View File

@ -63,6 +63,7 @@ def load_trackers():
print(f'Could not register Tracker {name}')
print(e)
def load_prefs():
prefs = get_addon_prefs()
prefs_config_file = prefs.config_path
@ -72,6 +73,9 @@ def load_prefs():
prefs_datas = read_file(os.path.expandvars(prefs_config_file))
if not prefs_datas:
return
for tracker_data in prefs_datas['trackers']:
tracker_name = norm_str(tracker_data['name'])
if not hasattr(prefs.trackers, tracker_name):

View File

@ -159,6 +159,10 @@ class Episode(PropertyGroup):
return self.get(settings.project_name)
class Template(PropertyGroup):
value : StringProperty()
class SpreadsheetExportCell(PropertyGroup):
export_name : StringProperty()
enabled : BoolProperty(default=True)
@ -241,19 +245,21 @@ def get_task_type_items(self, context):
class ImportShots(PropertyGroup):
import_source: EnumProperty(items=[(i, i.title(), '') for i in ('DISK', 'TRACKER')])
import_task: EnumProperty(items=[(i, i.title().replace('_', ' '), '')
for i in ('LAST', 'FROM_LIST', 'ALL')], default='FROM_LIST')
for i in ('FROM_LIST',)], default='FROM_LIST') #('LAST', 'FROM_LIST', 'ALL')
#sequence_dir_template: StringProperty(
# name="Sequence Template", default="$PROJECT_ROOT/sequences/{sequence}")
shot_folder_template: StringProperty(
name="Shot Template", default="$PROJECT_ROOT/sequences/sq{sequence}/sh{shot}")
#shot_folder_template: StringProperty(
# name="Shot Template", default="$PROJECT_ROOT/sequences/sq{sequence}/sh{shot}")
import_video_template : StringProperty(
name="Video Path", default="./{task}/render/{version}.{ext}")
video_template : StringProperty(
name="Video Path", default="//sources/{sequence}_{shot}_{task}.{ext}")
video_templates : CollectionProperty(type=PropertyGroup)
import_sequence: EnumProperty(items=[(i, i.title().replace('_', ' '), '')
for i in ('SELECTED_STRIPS', 'FROM_LIST', 'ALL')], default='FROM_LIST')
for i in ('FROM_LIST',)], default='FROM_LIST') #('SELECTED_STRIPS', 'FROM_LIST', 'ALL')
previews_folder: StringProperty(
name="Previews Folder", default="//sources", subtype='DIR_PATH')
@ -262,7 +268,7 @@ class ImportShots(PropertyGroup):
class UploadToTracker(PropertyGroup):
render_strips: BoolProperty(default=False)
render_strip_template : StringProperty(
name="Movie Path", default="//render/{project_basename}.{ext}")
name="Movie Path", default="//render/{strip}.{ext}")
task : EnumProperty(items=get_task_type_items)
status : EnumProperty(items=get_task_status_items)
@ -297,6 +303,9 @@ def on_episode_updated(self, context):
class Project(PropertyGroup):
id : StringProperty(default='')
show_settings : BoolProperty()
templates : CollectionProperty(type=Template)
shot_start_number : IntProperty(name="Shot Start Number", default=10, min=0)
shot_padding : IntProperty(name="Shot Padding", default=4, min=0, max=10)
@ -316,28 +325,28 @@ class Project(PropertyGroup):
name="Shot Increment", default=10, min=0, step=10)
sequence_template : StringProperty(
name="Sequence Name", default="sq{sequence}")
name="Sequence Name", default="{sequence}")
episode_template : StringProperty(
name="Episode Name", default="ep{episode}")
name="Episode Name", default="{episode}")
shot_template : StringProperty(
name="Shot Name", default="sq{sequence}_sh{shot}")
name="Shot Name", default="{sequence}_{shot}")
render_video_template : StringProperty(
name="Movie Path", default="//render/{project_basename}.{ext}")
name="Movie Path", default="//render/{project}.mov")
render_audio_template : StringProperty(
name="Audio Path", default="//render/{project_basename}.wav")
name="Audio Path", default="//render/{project}.wav")
render_video_strip_template : StringProperty(
name="Strip Path", default="//render/shots/{strip}.{ext}")
name="Strip Path", default="//render/shots/{strip}.mov")
render_audio_strip_template: StringProperty(
name="Sound Path", default="//render/sounds/{strip}.wav")
render_video_sequence_template : StringProperty(
name="Sequence Path", default="//render/sequences/{sequence}.{ext}")
name="Sequence Path", default="//render/sequences/{sequence}.mov")
render_audio_sequence_template: StringProperty(
name="Sound Path", default="//render/sounds/{sequence}.wav")
@ -359,7 +368,7 @@ class Project(PropertyGroup):
#render_sound_format: EnumProperty(items=[(e, e, '') for e in ('mp3', 'wav')])
export_edl_template : StringProperty(
name="Edl Path", default="//render/{project_basename}.edl")
name="Edl Path", default="//render/{project}.edl")
episode_name : EnumProperty(items=get_episodes_items, update=on_episode_updated)
episodes : CollectionProperty(type=Episode)
@ -394,6 +403,9 @@ class Project(PropertyGroup):
data['episode'] = norm_str(self.episode_name)
data['project_basename'] = f"{data['project']}_{data['episode']}"
#for template in self.templates:
# data[template.name] = template.value
return data
def get_cell_types(self):
@ -745,6 +757,7 @@ classes = (
AssetType,
TaskStatus,
Episode,
Template,
Metadata,
MetadataType,
Sequence,