diff --git a/__init__.py b/__init__.py index 2032d7b..a68d222 100644 --- a/__init__.py +++ b/__init__.py @@ -22,10 +22,10 @@ from vse_toolbox import preferences from vse_toolbox import properties from vse_toolbox import operators from vse_toolbox.constants import ASSET_PREVIEWS -from vse_toolbox.sequencer_utils import get_active_strip +from vse_toolbox.sequencer_utils import set_active_strip, update_text_strips -mods = ( +modules = ( properties, preferences, operators, @@ -34,8 +34,8 @@ mods = ( if 'bpy' in locals(): import importlib - for mod in mods: - importlib.reload(mod) + for module in modules: + importlib.reload(module) import bpy @@ -43,10 +43,11 @@ def register(): if bpy.app.background: return - for mod in mods: - mod.register() + for module in modules: + module.register() - bpy.app.handlers.frame_change_post.append(get_active_strip) + bpy.app.handlers.frame_change_post.append(set_active_strip) + bpy.app.handlers.frame_change_post.append(update_text_strips) def unregister(): @@ -60,9 +61,11 @@ def unregister(): if bpy.app.background: return - bpy.app.handlers.frame_change_post.remove(get_active_strip) - for mod in reversed(mods): - mod.unregister() + bpy.app.handlers.frame_change_post.remove(set_active_strip) + bpy.app.handlers.frame_change_post.remove(set_active_strip) + + for module in reversed(modules): + module.unregister() if __name__ == "__main__": register() diff --git a/constants.py b/constants.py index 7c842c8..0f70754 100644 --- a/constants.py +++ b/constants.py @@ -1,4 +1,4 @@ -import appdirs +from vse_toolbox import appdirs import bpy import bpy.utils.previews import os diff --git a/operators/operators.py b/operators/operators.py index 4ee9274..da298bf 100644 --- a/operators/operators.py +++ b/operators/operators.py @@ -32,19 +32,21 @@ from vse_toolbox.constants import ( ) from vse_toolbox.sequencer_utils import ( clean_sequencer, - get_shot_sequence, get_strips, - get_active_strip, + set_active_strip, import_edit, import_movie, import_sound, rename_strips, render_strips, set_channels, + get_channel_index, + new_text_strip ) from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings, get_strip_settings from vse_toolbox.file_utils import install_module, norm_name, norm_str + class VSETB_OT_tracker_connect(Operator): bl_idname = "vse_toolbox.tracker_connect" bl_label = "Connect to Tracker" @@ -71,7 +73,7 @@ class VSETB_OT_tracker_connect(Operator): class VSETB_OT_export_csv(Operator): - bl_idname = "sequencer.export_csv" + bl_idname = "vse_toolbox.export_csv" bl_label = "Set Scene" bl_description = "Set Scene for Breakdown" bl_options = {"REGISTER", "UNDO"} @@ -132,7 +134,7 @@ class VSETB_OT_auto_select_files(Operator): class VSETB_OT_import_files(Operator): - bl_idname = "sequencer.import_files" + bl_idname = "vse_toolbox.import_files" bl_label = "Import" bl_description = "Import Edit" bl_options = {"REGISTER", "UNDO"} @@ -316,24 +318,30 @@ class VSETB_OT_load_projects(Operator): for project_data in tracker.get_projects(): project = settings.projects.add() + project.type = project_data['production_type'] project.name = project_data['name'] project.id = project_data['id'] - for episode_data in tracker.get_episodes(project_data): - episode = project.episodes.add() - episode.name = episode_data['name'] - episode.id = episode_data['id'] + if project.type == 'TV Show': + for episode_data in tracker.get_episodes(project_data): + episode = project.episodes.add() + episode.name = episode_data['name'] + episode.id = episode_data['id'] for metadata_data in tracker.get_shots_metadata(project_data): + #print(metadata_data) metadata_type = project.metadata_types.add() - metadata_type.name = metadata_data + metadata_type.name = metadata_data['field_name'] + metadata_type['choices'] = metadata_data['choices'] for asset_type_data in tracker.get_asset_types(project_data): - asset_type = project.asset_types.add() - asset_type.name = asset_type_data['name'] + asset_type = project.asset_types.add() + asset_type.name = asset_type_data['name'] - # settings.load_metadata_types() + project.set_strip_metadata() + bpy.ops.vse_toolbox.load_assets() + return {'FINISHED'} @@ -395,7 +403,7 @@ class VSETB_OT_reload_addon(Operator): class VSETB_OT_rename(Operator): - bl_idname = "sequencer.strips_rename" + bl_idname = "vse_toolbox.strips_rename" bl_label = "Rename Strips" bl_description = "Rename Strips" bl_options = {"REGISTER", "UNDO"} @@ -403,7 +411,7 @@ class VSETB_OT_rename(Operator): template : StringProperty(name="Strip Template Name", default="") increment : IntProperty(name="Name Increment", default=0) channel_name : StringProperty(name="Channel Name", default="") - selected_only : BoolProperty(name="Selected Only", default=False) + #selected_only : BoolProperty(name="Selected Only", default=False) start_number : IntProperty(name="Start Number", default=0, min=0) by_sequence : BoolProperty( name="Reset By Sequence", @@ -432,11 +440,12 @@ class VSETB_OT_rename(Operator): col.prop(self, 'start_number') if self.channel_name == 'Shots': col.prop(self, 'by_sequence') - col.prop(self, 'selected_only') + #col.prop(self, 'selected_only') def execute(self, context): scn = context.scene - strips = get_strips(channel=self.channel_name, selected_only=self.selected_only) + #strips = get_strips(channel=self.channel_name, selected_only=self.selected_only) + strips = get_strips(channel=self.channel_name, selected_only=True) rename_strips( strips, self.template, @@ -448,7 +457,7 @@ class VSETB_OT_rename(Operator): class VSETB_OT_render(Operator): - bl_idname = "sequencer.strips_render" + bl_idname = "vse_toolbox.strips_render" bl_label = "Render Shots Strips" bl_description = "Render Shots Strips" bl_options = {"REGISTER", "UNDO"} @@ -486,10 +495,10 @@ class VSETB_OT_render(Operator): return {"FINISHED"} -class VSETB_OT_set_scene(Operator): - bl_idname = "vse_toolbox.set_scene" - bl_label = "Set Scene" - bl_description = "Set Scene for Breakdown" +class VSETB_OT_set_sequencer(Operator): + bl_idname = "vse_toolbox.set_sequencer" + bl_label = "Set Sequencer" + bl_description = "Set resolution, frame end and channel names" bl_options = {"REGISTER", "UNDO"} @classmethod @@ -519,6 +528,7 @@ def get_asset_items(self, context): return [(a.id, a.label, '', i) for i, a in enumerate(project.assets)] + class VSETB_OT_casting_add(Operator): bl_idname = "vse_toolbox.casting_add" bl_label = "Casting Add" @@ -557,6 +567,8 @@ class VSETB_OT_casting_add(Operator): item.name = asset.name item.id = project.assets[self.asset_name].id + item['_name'] = asset.label + strip_settings.casting.update() return {"FINISHED"} @@ -586,7 +598,8 @@ class VSETB_OT_casting_remove(Operator): pass else: item = strip_settings.casting[strip_settings.casting_index] - info = f"Item {item.asset.label} removed from casting" + label = item.asset.label if item.asset.label else 'Empty' + info = f"Item {label} removed from casting" strip_settings.casting.remove(idx) if strip_settings.casting_index == 0: @@ -648,7 +661,7 @@ class VSETB_OT_casting_move(Operator): class VSETB_OT_copy_casting(Operator): - bl_idname = "sequencer.copy_casting" + bl_idname = "vse_toolbox.copy_casting" bl_label = "Casting Actions" bl_description = "Copy Casting from active strip" bl_options = {"REGISTER", "UNDO"} @@ -674,7 +687,7 @@ class VSETB_OT_copy_casting(Operator): class VSETB_OT_paste_casting(Operator): - bl_idname = "sequencer.paste_casting" + bl_idname = "vse_toolbox.paste_casting" bl_label = "Casting Actions" bl_description = "Copy Casting from active strip" bl_options = {"REGISTER", "UNDO"} @@ -709,7 +722,71 @@ class VSETB_OT_paste_casting(Operator): return {"FINISHED"} -classes=( + +class VSETB_OT_set_stamps(Operator): + bl_idname = "vse_toolbox.set_stamps" + bl_label = "Set Stamps" + bl_description = "Set Stamps on Video" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + scn = context.scene + settings = get_scene_settings() + #strip_settings = get_strip_settings() + channel_index = get_channel_index('Stamps') + + for strip in get_strips('Stamps'): + if strip.type == 'META': + scn.sequence_editor.sequences.remove(strip) + + bpy.ops.sequencer.select_all(action='DESELECT') + + # Project Name + project_strip_stamp = new_text_strip( + 'project_name_stamp', channel=1, start=scn.frame_start, end=scn.frame_end, + text=settings.active_project.name, font_size=24, + x=0.01, y=0.01, align_x='LEFT', align_y='BOTTOM', select=True, + box_color=(0, 0, 0, 0.5), box_margin=0.005, + ) + + # Shot Name + shot_strip_stamp = new_text_strip( + f'shot_name_stamp', channel=2, start=scn.frame_start, end=scn.frame_end, + text='{active_shot_name}', font_size=24, + y=0.01, align_y='BOTTOM', select=True, + box_color=(0, 0, 0, 0.5), box_margin=0.005, + ) + + # Frame Range + frame_strip_stamp = new_text_strip( + 'frame_range_stamp', channel=3, start=scn.frame_start, end=scn.frame_end, + text='{active_shot_frame} / {active_shot_duration}', font_size=24, + x=0.99, y=0.01, align_x='RIGHT', align_y='BOTTOM', select=True, + box_color=(0, 0, 0, 0.5), box_margin=0.005, + ) + + # for shot_strip in get_strips('Shots'): + # # Shot Name + # shot_strip_stamp = new_text_strip( + # f'{shot_strip.name}_stamp', channel=3, start=shot_strip.frame_final_start, end=shot_strip.frame_final_end, + # text=shot_strip.name, font_size=24, + # y=0.01, align_y='BOTTOM', select=True, + # box_color=(0, 0, 0, 0.35), box_margin=0.005, + # ) + + bpy.ops.sequencer.meta_make() + stamps_strip = context.active_sequence_strip + stamps_strip.name = 'Stamps' + stamps_strip.channel = channel_index + + #stamps_strip = scn.sequence_editor.sequences.new_meta('Stamps', scn.frame_start, scn.frame_end) + #stamps_strip.channel = get_channel_index('Stamps') + scn.frame_set(scn.frame_current) # For update stamps + + return {"FINISHED"} + + +classes = ( VSETB_OT_auto_select_files, VSETB_OT_casting_add, VSETB_OT_casting_remove, @@ -724,8 +801,9 @@ classes=( VSETB_OT_reload_addon, VSETB_OT_rename, VSETB_OT_render, - VSETB_OT_set_scene, + VSETB_OT_set_sequencer, VSETB_OT_tracker_connect, + VSETB_OT_set_stamps ) def register(): diff --git a/panels.py b/panels.py index ca2b10a..1f4a808 100644 --- a/panels.py +++ b/panels.py @@ -3,9 +3,10 @@ import bpy from bpy.types import Panel from pathlib import Path -from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings, get_strip_settings +from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings, get_strip_settings) from vse_toolbox.constants import ASSET_PREVIEWS -from vse_toolbox.sequencer_utils import get_active_strip +from vse_toolbox.sequencer_utils import (set_active_strip, get_channel_name) + class VSETB_main: bl_space_type = "SEQUENCE_EDITOR" @@ -16,10 +17,8 @@ class VSETB_main: class VSETB_PT_main(VSETB_main, Panel): - def draw_header(self, context): - layout = self.layout - row = layout.row() - row.operator('vse_toolbox.load_projects', icon='FILE_REFRESH', text='') + def draw_header_preset(self, context): + self.layout.operator('vse_toolbox.load_projects', icon='FILE_REFRESH', text='', emboss=False) def draw(self, context): wm = context.window_manager @@ -30,13 +29,22 @@ class VSETB_PT_main(VSETB_main, Panel): project = settings.active_project - layout = self.layout + layout = self.layout col = layout.column() - row = col.row(align=True) - row.operator('vse_toolbox.set_scene', text='Set Scene', icon='SCENE_DATA') - row.prop(settings, 'toogle_prefs', text='', icon='PREFERENCES', toggle=True) + col.prop(settings, 'project_name', text='Project') + + if project: + if project.type == 'TV Shows': + col.prop(project, 'episode_name', text='Episodes') + + #col.separator() + + #row = col.row(align=True) + #row.prop(settings, 'toogle_prefs', text='', icon='PREFERENCES', toggle=True) + + ''' if settings.toogle_prefs: box = col.box() col = box.column(align=True) @@ -46,67 +54,125 @@ class VSETB_PT_main(VSETB_main, Panel): col.prop(settings, 'project_name', text='Projects') if project: - if project.episodes: + if project.type == 'TV Shows': col.prop(project, 'episode_name', text='Episodes') - col.prop(project, 'shot_template') + #col.prop(project, 'sequence_template') + #col.prop(project, 'shot_template') # col.separator() # col.operator('vse_toolbox.new_episode', text='Add Episode', icon='IMPORT') + ''' - col = layout.column() - row = col.row(align=True) - row.operator('sequencer.import_files', text='Import', icon='IMPORT') - # TODO FAIRE DES VRAIS OPS - row.operator('sequencer.export_csv', text='Export', icon='EXPORT') - op = col.operator('sequencer.strips_render', text='Render Strips', icon='RENDER_ANIMATION') + # Rename -class VSETB_PT_rename(VSETB_main, Panel): - bl_label = "Rename Strips" +class VSETB_PT_sequencer(VSETB_main, Panel): + bl_label = "Sequencer" bl_parent_id = "VSETB_PT_main" + def draw_header_preset(self, context): + settings = get_scene_settings() + ico = ("RESTRICT_SELECT_OFF" if settings.auto_select_strip else "RESTRICT_SELECT_ON") + self.layout.prop(settings, "auto_select_strip", text="", icon=ico) + def draw(self, context): prefs = get_addon_prefs() + layout = self.layout settings = get_scene_settings() project = settings.active_project - - if not project: - return - episode = project.episode_name - - layout = self.layout col = layout.column() - col.use_property_split = True + col.operator('vse_toolbox.set_sequencer', text='Set-Up Sequencer', icon='SEQ_SEQUENCER') - row = col.row() - shot_increment = project.shot_increment - shot_template = project.shot_template.format( - episode=episode, index=0 * shot_increment) + #row = col.row() + episode = project.episode_name + sequence_label = project.sequence_template.format(episode=episode, index=project.sequence_start_number) + shot_label = project.shot_template.format(episode=episode, sequence=sequence_label, index=project.shot_start_number) - row.separator() + #row.separator() + + strip = context.active_sequence_strip + channel_name = get_channel_name(strip) or '' + if channel_name == 'Shots': + label = shot_label + elif channel_name == 'Sequences': + label = sequence_label + else: + label = 'Not Supported' + + row = col.row(align=True) + if label == 'Not Supported': + row.enabled = False + + op = row.operator('vse_toolbox.strips_rename', text=f'Rename {channel_name} ( {label} )', icon='SORTALPHA') + op.channel_name = channel_name + if channel_name == 'Shots': + op.start_number = project.shot_start_number + op.template = project.shot_template + op.increment = project.shot_increment + else: + op.start_number = project.sequence_start_number + op.template = project.sequence_template + op.increment =project.sequence_increment - op = row.operator('sequencer.strips_rename', text='Rename Shots', icon='SORTALPHA') - op.channel_name = 'Shots' - op.template = project.shot_template - op.increment = project.shot_increment - op.start_number = 10 - row.label(text=f'{shot_template}') + col.operator('vse_toolbox.set_stamps', text='Set Stamps', icon='COLOR') + + +class VSETB_PT_imports(VSETB_main, Panel): + bl_label = "Imports" + bl_parent_id = "VSETB_PT_main" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header_preset(self, context): + self.layout.operator('vse_toolbox.import_files', icon='IMPORT', text='', emboss=False) + + def draw(self, context): + prefs = get_addon_prefs() + layout = self.layout + settings = get_scene_settings() + project = settings.active_project + + col = layout.column() + #row = col.row(align=True) + col.operator('vse_toolbox.import_files', text='Import', icon='IMPORT') + + +class VSETB_PT_exports(VSETB_main, Panel): + bl_label = "Exports" + bl_parent_id = "VSETB_PT_main" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header_preset(self, context): + self.layout.operator('vse_toolbox.export_csv', icon='EXPORT', text='', emboss=False) + + def draw(self, context): + prefs = get_addon_prefs() + layout = self.layout + settings = get_scene_settings() + project = settings.active_project + + # TODO FAIRE DES VRAIS OPS + layout.operator('vse_toolbox.strips_render', text='Render Strips', icon='SEQUENCE') + layout.operator('vse_toolbox.export_csv', text='Export', icon='EXPORT') class VSETB_PT_casting(VSETB_main, Panel): - bl_label = "Shot Casting" + bl_label = "Casting" bl_parent_id = "VSETB_PT_main" + def draw_header_preset(self, context): + active_strip = context.scene.sequence_editor.active_strip + self.layout.label(text=active_strip.name) + + @classmethod + def poll(cls, context): + strip = context.scene.sequence_editor.active_strip + return strip and get_channel_name(strip) == 'Shots' + def draw(self, context): layout = self.layout - scn = context.scene - active_strip = scn.sequence_editor.active_strip - if not active_strip: - return - settings = get_scene_settings() strip_settings = get_strip_settings() @@ -117,47 +183,37 @@ class VSETB_PT_casting(VSETB_main, Panel): if not project.assets: row = layout.row(align=True) - row.label(text='No Assets found. Load Assets first.') + row.label(text='No Assets in this Project') else: - row = layout.row(align=True) - row.label(text=f'Assets for {project.name} successfully loaded.') - row.operator('vse_toolbox.load_assets', icon='FILE_REFRESH', text='') - - row = layout.row() - ico = ("RESTRICT_SELECT_OFF" if settings.auto_select_strip else "RESTRICT_SELECT_ON") - row.prop(settings, "auto_select_strip", icon=ico) - - row = layout.row() - row.label(text=f"Current Shot: {Path(active_strip.name).stem}") - row = layout.row() - col = row.column() - col.template_list("VSETB_UL_casting", "shot_casting", strip_settings, "casting", strip_settings, "casting_index", rows=6) - - if strip_settings.casting: - casting_item = strip_settings.casting[strip_settings.casting_index] - asset = casting_item.asset - if asset: - ico = ASSET_PREVIEWS.get(asset.preview) - if ico: - box = col.box() - box.template_icon(icon_value=ico.icon_id, scale=7.5) - - col_tool = row.column(align=True) - col_tool.operator('vse_toolbox.casting_add', icon='ADD', text="") - col_tool.operator('vse_toolbox.casting_remove', icon='REMOVE', text="") - col_tool.separator() - col_tool.operator('vse_toolbox.casting_move', icon='TRIA_UP', text="").action = 'UP' - col_tool.operator('vse_toolbox.casting_move', icon='TRIA_DOWN', text="").action = 'DOWN' - col_tool.separator() - col_tool.operator('sequencer.copy_casting', icon='COPYDOWN', text="") - col_tool.operator('sequencer.paste_casting', icon='PASTEDOWN', text="") + row = layout.row() + col = row.column() + col.template_list("VSETB_UL_casting", "shot_casting", strip_settings, "casting", strip_settings, "casting_index", rows=6) + + if strip_settings.casting: + casting_item = strip_settings.casting[strip_settings.casting_index] + asset = casting_item.asset + if asset: + ico = ASSET_PREVIEWS.get(asset.preview) + if ico: + box = col.box() + box.template_icon(icon_value=ico.icon_id, scale=7.5) + + col_tool = row.column(align=True) + col_tool.operator('vse_toolbox.casting_add', icon='ADD', text="") + col_tool.operator('vse_toolbox.casting_remove', icon='REMOVE', text="") + col_tool.separator() + col_tool.operator('vse_toolbox.casting_move', icon='TRIA_UP', text="").action = 'UP' + col_tool.operator('vse_toolbox.casting_move', icon='TRIA_DOWN', text="").action = 'DOWN' + col_tool.separator() + col_tool.operator('vse_toolbox.copy_casting', icon='COPYDOWN', text="") + col_tool.operator('vse_toolbox.paste_casting', icon='PASTEDOWN', text="") class VSETB_PT_metadata(VSETB_main, Panel): bl_label = "Shot Metadata" bl_parent_id = "VSETB_PT_casting" - bl_options = {"DEFAULT_CLOSED"} + #bl_options = {"DEFAULT_CLOSED"} def draw(self, context): layout = self.layout @@ -175,15 +231,17 @@ class VSETB_PT_metadata(VSETB_main, Panel): if not project: return - col = layout.column() - for key in strip_settings.metadata.keys(): - col.prop(strip_settings.metadata, key) + #col = layout.column() + for key in strip_settings.metadata.__annotations__.keys(): + layout.prop(strip_settings.metadata, key, text=key.upper()) -classes=( +classes = ( VSETB_PT_main, - VSETB_PT_rename, + VSETB_PT_imports, + VSETB_PT_sequencer, VSETB_PT_casting, VSETB_PT_metadata, + VSETB_PT_exports, ) def register(): diff --git a/properties.py b/properties.py index 6764b66..98879c0 100644 --- a/properties.py +++ b/properties.py @@ -37,13 +37,18 @@ def get_project_items(self, context): return [(p, p, '', i) for i, p in enumerate(self.projects.keys())] -def update_episodes(self, context): +def on_project_updated(self, context): settings = get_scene_settings() settings['episodes'] = 0 + + print('Update active Project') + + bpy.ops.vse_toolbox.load_assets() def get_tracker_items(self, context): return [(norm_str(a.name, format=str.upper), a.name, "", i) for i, a in enumerate(TRACKERS)] + class Asset(PropertyGroup): name : StringProperty(default='') id : StringProperty(default='') @@ -79,25 +84,19 @@ class AssetCasting(PropertyGroup): def to_dict(self): return {k: v for k,v in self.items()} -class AssetTypes(PropertyGroup): - choices : EnumProperty(items=[('NONE', 'None', '', 0)]) + +class AssetType(PropertyGroup): + __annotations__ = {} -class MetaDataTypes(PropertyGroup): - choices : EnumProperty(items=[('NONE', 'None', '', 0)]) +class MetadataType(PropertyGroup): + choices = [] + choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']]) -class MetaData(PropertyGroup): - - notes : StringProperty() - ambiance : StringProperty() - fx_compo : StringProperty() +class Metadata(PropertyGroup): + __annotations__ = {} - def __iter__(self): - return (getattr(self, k) for k in self.keys()) - - def keys(self): - return (p for p in self.bl_rna.properties.keys() if p not in ('rna_type', 'name')) class Episode(PropertyGroup): id : StringProperty(default='') @@ -111,6 +110,9 @@ class Episode(PropertyGroup): class Project(PropertyGroup): id : StringProperty(default='') + shot_start_number : IntProperty(name="Shot Start Number", default=10, min=0) + sequence_start_number : IntProperty(name="Sequence Start Number", default=10, min=0) + sequence_increment : IntProperty( name="Sequence Increment", default=10, min=0, step=10) @@ -121,16 +123,28 @@ class Project(PropertyGroup): name="Sequence Name", default="sq{index:03d}") episode_template : StringProperty( - name="Episode Name", default="e{index:03d}") + name="Episode Name", default="ep{index:03d}") shot_template : StringProperty( - name="Shot Name", default="{episode}s{index:04d}") + name="Shot Name", default="{sequence}_sh{index:04d}") episode_name : EnumProperty(items=get_episodes_items) episodes : CollectionProperty(type=Episode) assets : CollectionProperty(type=Asset) - asset_types : CollectionProperty(type=AssetTypes) - metadata_types : CollectionProperty(type=MetaDataTypes) + asset_types : CollectionProperty(type=AssetType) + metadata_types : CollectionProperty(type=MetadataType) + type : StringProperty() + + def set_strip_metadata(self): + for metadata_type in self.metadata_types: + prop_name = metadata_type.name + if metadata_type['choices']: + prop = bpy.props.EnumProperty(items=[(c, c.replace(' ', '_').upper(), '') for c in metadata_type['choices']]) + else: + prop = bpy.props.StringProperty() + + Metadata.__annotations__[prop_name] = prop + setattr(Metadata, prop_name, prop) class VSETB_UL_casting(UIList): @@ -146,7 +160,7 @@ class VSETB_UL_casting(UIList): asset = item.asset if asset is None: #TODO deal if asset was removed - layout.label(text='Load Assets') + layout.label(text=f'Asset not Found ({item.get("_name", "...")})') return icon_id = asset.icon_id @@ -199,7 +213,7 @@ class VSETB_UL_casting(UIList): class VSETB_PGT_scene_settings(PropertyGroup): projects : CollectionProperty(type=Project) - project_name : EnumProperty(items=get_project_items, update=update_episodes) + project_name : EnumProperty(items=get_project_items, update=on_project_updated) tracker_name : EnumProperty(items=get_tracker_items) toogle_prefs : BoolProperty( @@ -214,6 +228,7 @@ class VSETB_PGT_scene_settings(PropertyGroup): ('MOVIE', 'Movie', '', 1), ('SHOTS', 'Shots', '', 2), ('SEQUENCES', 'Sequences', '', 3), + ('STAMPS', 'Sequences', '', 4), ] ) @@ -233,46 +248,22 @@ class VSETB_PGT_scene_settings(PropertyGroup): project = self.active_project if project: return project.episodes.get(project.episode_name) - """ - def load_metadata_types(self): - settings = get_scene_settings() - print('settings: ', settings) - - for project in settings.projects: - print('project: ', project) - - metadata_props = {'__annotations__': {key : StringProperty() for key in project.metadata_types.keys()}} - MetadataProps = type(f"{project.name}_MetaData", (PropertyGroup,), metadata_props) - bpy.utils.register_class(MetadataProps) - - setattr(VSETB_PGT_strip_settings, 'metadata', PointerProperty(type=MetadataProps)) - - # if "__annotations__" not in MetaData.__dict__: - # setattr(MetaData, "__annotations__", {}) - - # for metadata_type in project.metadata_types: - # print('metadata_type: ', metadata_type) - # MetaData.__annotations__[metadata_type.name] = StringProperty() - - # bpy.utils.unregister_class(MetaData) - # bpy.utils.register_class(MetaData) - """ - + class VSETB_PGT_strip_settings(PropertyGroup): casting : CollectionProperty(type=AssetCasting) casting_index : IntProperty(name='Casting Index', default=0) source_name : StringProperty(name='') - metadata : PointerProperty(type=MetaData) + metadata : PointerProperty(type=Metadata) classes=( Asset, AssetCasting, - AssetTypes, + AssetType, Episode, - MetaData, - MetaDataTypes, + Metadata, + MetadataType, Project, VSETB_UL_casting, VSETB_PGT_scene_settings, @@ -280,6 +271,16 @@ classes=( ) + +from bpy.app.handlers import persistent + +@persistent +def load_handler(dummy): + settings = get_scene_settings() + if settings.active_project: + settings.active_project.set_strip_metadata() + + def register(): for cls in classes: bpy.utils.register_class(cls) @@ -287,7 +288,9 @@ def register(): bpy.types.Scene.vsetb_settings = PointerProperty(type=VSETB_PGT_scene_settings) bpy.types.Sequence.vsetb_strip_settings = PointerProperty(type=VSETB_PGT_strip_settings) - # load_metadata_types() + #load_metadata_types() + bpy.app.handlers.load_post.append(load_handler) + def unregister(): for cls in reversed(classes): @@ -295,3 +298,5 @@ def unregister(): del bpy.types.Sequence.vsetb_strip_settings del bpy.types.Scene.vsetb_settings + + bpy.app.handlers.load_post.remove(load_handler) diff --git a/resources/trackers/kitsu.py b/resources/trackers/kitsu.py index 94f91ce..753a0a1 100644 --- a/resources/trackers/kitsu.py +++ b/resources/trackers/kitsu.py @@ -12,7 +12,12 @@ from vse_toolbox.bl_utils import get_addon_prefs, get_scene_settings from vse_toolbox.file_utils import install_module, norm_str from vse_toolbox.resources.trackers.tracker import Tracker -gazu = install_module('gazu') +try: + gazu = install_module('gazu') +except Exception as e: + print('Could not install gazu') + print(e) + class Kitsu(Tracker): name = "Kitsu" @@ -51,14 +56,13 @@ class Kitsu(Tracker): return assets def get_shots_metadata(self, project): - metadata = [] - for md in gazu.project.all_metadata_descriptors(project): - asset_type = md.get('asset_type') - field_name = md.get('field_name') - if asset_type and asset_type.lower() == 'shot' and field_name: - metadata.append(field_name) + metadatas = [] - return metadata + for metadata in gazu.project.all_metadata_descriptors(project): + if metadata['entity_type'] == 'Shot' and metadata['name']: + metadatas.append(metadata) + + return metadatas def download_preview(self, preview_id, filepath): if isinstance(filepath, str): diff --git a/sequencer_utils.py b/sequencer_utils.py index 7fc6331..4fb31ea 100644 --- a/sequencer_utils.py +++ b/sequencer_utils.py @@ -8,12 +8,39 @@ from pathlib import Path from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings from vse_toolbox.constants import SOUND_SUFFIXES +def new_text_strip(name='Text', channel=0, start=0, end=50, text='Text', font_size=48, + x=0.5, y=0.5, align_x='CENTER', align_y='CENTER', select=False, + box_color=None, box_margin=0.005): + + sequences = bpy.context.scene.sequence_editor.sequences + strip = sequences.new_effect(name, 'TEXT', channel, frame_start=start, frame_end=end) + strip.select = select + strip.text = text + strip.location.x = x + strip.location.y = y + strip.align_y = align_y + strip.align_x = align_x + strip.channel = channel + strip.font_size = font_size + + if box_color: + strip.use_box = True + strip.box_color = box_color + strip.box_margin = box_margin + + return strip + +def is_strip_at(strip, frame=None): + if frame is None: + frame = bpy.context.scene.frame_current + + return (strip.frame_final_start <= frame < strip.frame_final_end) def get_strips(channel=0, selected_only=False): scn = bpy.context.scene if isinstance(channel, str): - channel = get_channel(channel) + channel = get_channel_index(channel) strips = [s for s in scn.sequence_editor.sequences_all if s.channel==channel] @@ -22,7 +49,11 @@ def get_strips(channel=0, selected_only=False): return sorted(strips, key=lambda x : x.frame_final_start) -def get_channel(name): +def get_strip_at(channel=0, frame=None): + strips = get_strips(channel=channel) + return next((s for s in strips if is_strip_at(s, frame)), None) + +def get_channel_index(name): scn = bpy.context.scene channel_id = 0 @@ -32,9 +63,19 @@ def get_channel(name): return channel_id -def get_shot_sequence(shot): - sequences = get_strips(channel='Sequences') - return next((s.name for s in sequences if s.frame_final_start<=shot.frame_final_start {name}') strip.name = name - previous_sequence = sequence + prev_sequence_name = sequence_name strip_number += 1 def set_channels(): @@ -126,10 +170,10 @@ def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False): # FIXME Exclude Audio for now if any(child.name.lower().endswith(ext) for ext in SOUND_SUFFIXES): - channel = get_channel('Audio') + channel = get_channel_index('Audio') continue - channel = get_channel('Shots') + channel = get_channel_index('Shots') frame_start = otio.opentime.to_frames( child.range_in_parent().start_time) @@ -164,7 +208,7 @@ def import_movie(filepath): strip = scn.sequence_editor.sequences.new_movie( name=filepath.stem, filepath=str(filepath), - channel=get_channel('Movie'), + channel=get_channel_index('Movie'), frame_start=scn.frame_start ) @@ -187,7 +231,7 @@ def import_sound(filepath): strip = scn.sequence_editor.sequences.new_sound( name=filepath.stem, filepath=str(filepath), - channel=get_channel('Audio'), + channel=get_channel_index('Audio'), frame_start=scn.frame_start ) @@ -212,30 +256,52 @@ def clean_sequencer(edit=False, movie=False, sound=False): scn.sequence_editor.sequences.remove(sequence) @persistent -def get_active_strip(scene): - scn = bpy.context.scene +def set_active_strip(scene): + #scn = bpy.context.scene settings = get_scene_settings() - if settings.auto_select_strip == False: + if not settings.auto_select_strip: return - screen = bpy.context.screen - bpy.ops.sequencer.select_all(action="DESELECT") + #scene.sequence_editor.active_strip = None - strip = None - frame_current = scn.frame_current + shot_strip = get_strip_at('Shots') + if shot_strip: + shot_strip.select = True + scene.sequence_editor.active_strip = shot_strip - strips = bpy.context.sequences - strips = sorted(strips,key=lambda x: (x.channel, x.frame_final_start)) +@persistent +def update_text_strips(scene): + #scn = bpy.context.scene + format_data = { + 'scene': scene, + 'active_shot_name': 'None', + 'active_shot_frame': 0, + 'active_shot_duration': 0, + 'active_shot_start': 0, + 'active_shot_end': 0 + } - for strip in strips: - #FIXME Pas propre de mettre le channel name en dur - if strip.channel != get_channel('Shots') or 0: + shot_strip = get_strip_at('Shots', frame=scene.frame_current) + if shot_strip: + format_data.update({ + 'active_shot_name': shot_strip.name, + 'active_shot_duration': shot_strip.frame_final_duration, + 'active_shot_frame': scene.frame_current - shot_strip.frame_final_start + 1, + 'active_shot_start': shot_strip.frame_final_start, + 'active_shot_end': shot_strip.frame_final_end, + }) + + for strip in scene.sequence_editor.sequences_all: + if not strip.type == 'TEXT': continue - if (not strip.lock - and strip.frame_final_end >= frame_current - and strip.frame_final_start <= frame_current): - strip.select = True - scn.sequence_editor.active_strip = strip \ No newline at end of file + if not is_strip_at(strip): + continue + + if '{' in strip.text: + strip['text_pattern'] = strip.text + + if 'text_pattern' in strip.keys(): + strip.text = strip['text_pattern'].format(**format_data) \ No newline at end of file