diff --git a/bl_utils.py b/bl_utils.py index d16b87b..7612902 100644 --- a/bl_utils.py +++ b/bl_utils.py @@ -52,3 +52,4 @@ def get_bl_cmd(blender=None, background=False, focus=True, blendfile=None, scrip def get_addon_prefs(): addon_name = __package__.split('.')[0] return bpy.context.preferences.addons[addon_name].preferences + diff --git a/operators/addon.py b/operators/addon.py index 4cac142..292e2b6 100644 --- a/operators/addon.py +++ b/operators/addon.py @@ -10,7 +10,7 @@ import vse_toolbox from vse_toolbox.bl_utils import (get_addon_prefs, get_scene_settings) -from vse_toolbox.file_utils import (read_file, ) +from vse_toolbox.file_utils import (read_file, norm_str) @@ -50,9 +50,12 @@ class VSETB_OT_load_settings(Operator): addon_config['trackers'] = addon_config.get('trackers') trackers = addon_config.pop('trackers') - addon_config['spreadsheet_export'] = addon_config.get('spreadsheet_export') + 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') + project_name = addon_config.get('project_name') if project_name: settings.project_name = project_name @@ -93,6 +96,41 @@ class VSETB_OT_load_settings(Operator): except Exception: print(f'Could not set option {k} with value {v} to spreadsheet') + + import_cells = project.spreadsheet_import.cells + if spreadsheet_import_config: + import_cells.clear() + + cell_names = project.get_cell_names() + for k, v in spreadsheet_import_config.items(): + if k == 'cells': + for i, cell_data in enumerate(v): + if not 'name' in cell_data: + print(f'cell_data {cell_data} need to have a attribute name') + continue + + cell = import_cells.add() + cell.name = cell_data['name'] + + norm_import_name = norm_str(cell_data.get('import_name', cell_data['name'])) + import_name = next((c for c in cell_names if norm_str(c) == norm_import_name), None) + if import_name is None: + print(f'import_name {norm_import_name} not in {cell_names}') + else: + cell.import_name = import_name + + if 'enabled' in cell_data: + try: + cell.enabled = cell_data['enabled'] + except TypeError as e: + print(e) + + else: + try: + setattr(project.spreadsheet_import, k, v) + except Exception: + print(f'Could not set option {k} with value {v} to spreadsheet') + return {'FINISHED'} diff --git a/operators/casting.py b/operators/casting.py index 892eb06..d769a9c 100644 --- a/operators/casting.py +++ b/operators/casting.py @@ -258,8 +258,6 @@ class VSETB_OT_paste_casting(Operator): casting_datas = json.loads(CASTING_BUFFER.read_text()) - - for strip in context.selected_sequences: strip_settings = strip.vsetb_strip_settings strip.vsetb_strip_settings.casting.clear() diff --git a/operators/spreadsheet.py b/operators/spreadsheet.py index 2c9871d..c306f35 100644 --- a/operators/spreadsheet.py +++ b/operators/spreadsheet.py @@ -92,6 +92,165 @@ class VSETB_OT_spreadsheet_cell_move(Operator): return {"FINISHED"} +class VSETB_OT_spreadsheet_from_file(Operator): + bl_idname = "vse_toolbox.spreadsheet_from_file" + bl_label = "Read Spreadsheet Column" + bl_description = "Read Spreadsheet Column" + bl_options = {"REGISTER", "UNDO"} + + filepath : StringProperty() + + def execute(self, context): + scn = context.scene + project = get_scene_settings().active_project + + if not self.filepath: + self.report({'ERROR'}, 'No filepath provided') + return {'CANCELLED'} + + if not Path(self.filepath).exists(): + self.report({'ERROR'}, f'Filepath {self.filepath} not exists') + return {'CANCELLED'} + + filepath = Path(self.filepath) + + if filepath.suffix.lower() == '.csv': + with open(filepath, newline='') as csvfile: + reader = csv.reader(csvfile) + next(reader) + data = dict(reader) + + + elif filepath.suffix.lower() == '.xlsx': + raise Exception('Not Supported') + workbook = openpyxl.load_workbook(filepath, read_only=True) + sheet = next((ws for ws in workbook.worksheets if ws.title.lower() == 'export'), None) + + + else: + self.report({'ERROR'}, f'File extension {filepath.suffix} should be in [.csv, .xlsx]') + return {'CANCELLED'} + + return {"FINISHED"} + + +class VSETB_OT_spreadsheet_from_clipboard(Operator): + bl_idname = "vse_toolbox.spreadsheet_from_clipboard" + bl_label = "Read Spreadsheet from clipboard" + bl_description = "Read Spreadsheet from clipboard" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + scn = context.scene + project = get_scene_settings().active_project + + spreadsheet = context.window_manager.clipboard + + reader = list(csv.reader(spreadsheet.splitlines(), delimiter='\t')) + header = reader[0] + print(header) + + return {"FINISHED"} + +class VSETB_OT_import_spreadsheet(Operator): + bl_idname = "vse_toolbox.import_spreadsheet" + bl_label = "Import Spreadsheet" + bl_description = "Create strips from nb frames with casting and custom data" + bl_options = {"REGISTER", "UNDO"} + + directory : StringProperty(subtype='DIR_PATH') + filepath: StringProperty( + name="File Path", + description="Filepath used for importing the file", + maxlen=1024, + subtype='FILE_PATH', + ) + files : CollectionProperty(type=OperatorFileListElement) + + @classmethod + def poll(cls, context): + settings = get_scene_settings() + return settings.active_project + + def invoke(self, context, event): + settings = get_scene_settings() + project = settings.active_project + + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def draw(self, context): + scn = context.scene + settings = get_scene_settings() + project = settings.active_project + spreadsheet = project.spreadsheet_import + #options = project.spreadsheet_options + + layout = self.layout + + row = layout.row(align=True) + row.label(text='Source') + #row.alignment='RIGHT' + row.operator("vse_toolbox.spreadsheet_from_clipboard", text='Clipboard', icon='PASTEDOWN') + row.operator("vse_toolbox.spreadsheet_from_file", text='File', icon='FILE') + + + row = layout.row(align=True) + row.prop(spreadsheet, 'use_custom_cells', text='Custom Cells') + + col = layout.column(align=False) + col.enabled = spreadsheet.use_custom_cells + + row = col.row(align=True, heading='Custom Asset Name') + row.use_property_split = True + row.use_property_decorate = False + row.prop(spreadsheet, 'use_custom_name', text='') + sub = row.row(align=False) + sub.enabled = spreadsheet.use_custom_name + sub.prop(spreadsheet, 'custom_name', text='') + sub.label(icon='BLANK1') + + row = layout.row() + row.enabled = spreadsheet.use_custom_cells + row.template_list("VSETB_UL_spreadsheet_import", "spreadsheet_import", spreadsheet, "cells", spreadsheet, "cell_index", rows=8) + + col_tool = row.column(align=True) + + #bpy.types.VSETB_PT_presets.draw_panel_header(col_tool) + #col_tool.operator('wm.call_menu', icon="PRESET").name = 'VSETB_MT_spreadsheet_presets' + #col_tool.operator('vse_toolbox.load_spreadsheet_preset', icon='PRESET', text="") + op = col_tool.operator('wm.call_panel', icon="PRESET", emboss=False, text='') + op.name = 'VSETB_PT_presets' + op.keep_open = False + + #col_tool.separator() + #col_tool.operator('vse_toolbox.spreadsheet_move', icon='TRIA_UP', text="").direction = 'UP' + #col_tool.operator('vse_toolbox.spreadsheet_move', icon='TRIA_DOWN', text="").direction = 'DOWN' + + col = layout.column() + col.use_property_split = True + col.use_property_decorate = False + + #col.separator() + + row = col.row(align=False) + + col.prop(spreadsheet, "separator", expand=True, text='Separator') + if self.filepath.endswith('.csv'): + col.prop(spreadsheet, "delimiter", expand=True, text='Delimiter') + + + col.prop(spreadsheet, 'import_casting', text='Import Casting') + col.prop(spreadsheet, 'import_custom_data', text='Import Custom Data') + col.prop(spreadsheet, 'update_edit', text='Update Edit') + + col.separator() + + def execute(self, context): + + return {"FINISHED"} + + class VSETB_OT_export_spreadsheet(Operator): bl_idname = "vse_toolbox.export_spreadsheet" bl_label = "Export Spreadsheet" @@ -133,7 +292,7 @@ class VSETB_OT_export_spreadsheet(Operator): sub.label(icon='BLANK1') row = col.row() - row.template_list("VSETB_UL_spreadsheet", "spreadsheet_export", spreadsheet, "cells", spreadsheet, "cell_index", rows=8) + row.template_list("VSETB_UL_spreadsheet_export", "spreadsheet_export", spreadsheet, "cells", spreadsheet, "cell_index", rows=8) col_tool = row.column(align=True) @@ -276,98 +435,13 @@ class VSETB_OT_export_spreadsheet(Operator): open_file(export_path, select=True) return {"FINISHED"} - - - -class VSETB_OT_import_spreadsheet(Operator): - bl_idname = "vse_toolbox.import_spreadsheet" - bl_label = "Import Spreadsheet" - bl_description = "Create strips from nb frames with casting and custom data" - bl_options = {"REGISTER", "UNDO"} - - directory : StringProperty(subtype='DIR_PATH') - filepath: StringProperty( - name="File Path", - description="Filepath used for importing the file", - maxlen=1024, - subtype='FILE_PATH', - ) - files : CollectionProperty(type=OperatorFileListElement) - - @classmethod - def poll(cls, context): - settings = get_scene_settings() - return settings.active_project - - def invoke(self, context, event): - settings = get_scene_settings() - project = settings.active_project - - context.window_manager.fileselect_add(self) - return {'RUNNING_MODAL'} - - def draw(self, context): - scn = context.scene - settings = get_scene_settings() - project = settings.active_project - spreadsheet = project.spreadsheet_import - #options = project.spreadsheet_options - - layout = self.layout - row = layout.row(align=True) - row.prop(spreadsheet, 'use_custom_cells', text='Custom Cells') - - col = layout.column(align=False) - col.enabled = spreadsheet.use_custom_cells - - row = col.row(align=True, heading='Custom Asset Name') - row.use_property_split = True - row.use_property_decorate = False - row.prop(spreadsheet, 'use_custom_name', text='') - sub = row.row(align=False) - sub.enabled = spreadsheet.use_custom_name - sub.prop(spreadsheet, 'custom_name', text='') - sub.label(icon='BLANK1') - - row = layout.row() - row.enabled = spreadsheet.use_custom_cells - row.template_list("VSETB_UL_spreadsheet", "spreadsheet_import", spreadsheet, "cells", spreadsheet, "cell_index", rows=8) - - col_tool = row.column(align=True) - - #bpy.types.VSETB_PT_presets.draw_panel_header(col_tool) - #col_tool.operator('wm.call_menu', icon="PRESET").name = 'VSETB_MT_spreadsheet_presets' - #col_tool.operator('vse_toolbox.load_spreadsheet_preset', icon='PRESET', text="") - op = col_tool.operator('wm.call_panel', icon="PRESET", emboss=False, text='') - op.name = 'VSETB_PT_presets' - op.keep_open = False - - col_tool.separator() - col_tool.operator('vse_toolbox.spreadsheet_move', icon='TRIA_UP', text="").direction = 'UP' - col_tool.operator('vse_toolbox.spreadsheet_move', icon='TRIA_DOWN', text="").direction = 'DOWN' - - col = layout.column() - col.use_property_split = True - row.use_property_decorate = False - - #col.separator() - - row = col.row(align=False) - - col.prop(spreadsheet, "separator", expand=True, text='Separator') - if self.filepath.endwith('.csv'): - col.prop(spreadsheet, "delimiter", expand=True, text='Delimiter') - - col.separator() - - def execute(self, context): - - return {"FINISHED"} classes = ( VSETB_OT_add_export_spreadsheet_preset, VSETB_MT_export_spreadsheet_presets, + VSETB_OT_spreadsheet_from_file, + VSETB_OT_spreadsheet_from_clipboard, VSETB_OT_spreadsheet_cell_move, VSETB_OT_export_spreadsheet, VSETB_OT_import_spreadsheet diff --git a/ui/properties.py b/ui/properties.py index 1d1f3fc..03cf9ec 100644 --- a/ui/properties.py +++ b/ui/properties.py @@ -159,7 +159,7 @@ class Episode(PropertyGroup): return self.get(settings.project_name) -class SpreadsheetCell(PropertyGroup): +class SpreadsheetExportCell(PropertyGroup): export_name : StringProperty() enabled : BoolProperty(default=True) field_name : StringProperty() @@ -167,6 +167,17 @@ class SpreadsheetCell(PropertyGroup): #sort : BoolProperty(default=True) +def get_cell_items(self, context): + settings = get_scene_settings() + project = settings.active_project + + return [(cell, cell, '') for cell in project.get_cell_names()] + +class SpreadsheetImportCell(PropertyGroup): + enabled : BoolProperty(default=True) + import_name : EnumProperty(items=get_cell_items) + + def get_custom_name_items(self, context): settings = get_scene_settings() project = settings.active_project @@ -184,7 +195,7 @@ class SpreadsheetExport(PropertyGroup): open_folder : BoolProperty(default=False) show_settings : BoolProperty(default=False) - cells: CollectionProperty(type=SpreadsheetCell) + cells: CollectionProperty(type=SpreadsheetExportCell) cell_index : IntProperty(name='Spreadsheet Index', default=0) @@ -196,9 +207,13 @@ class SpreadsheetImport(PropertyGroup): custom_name : EnumProperty(items=get_custom_name_items, description='Use a custom name for asset using a metadata value') - cells: CollectionProperty(type=SpreadsheetCell) + cells: CollectionProperty(type=SpreadsheetImportCell) cell_index : IntProperty(name='Spreadsheet Index', default=0) + import_casting: BoolProperty(default=True) + import_custom_data: BoolProperty(default=True) + update_edit: BoolProperty(default=True) + class Project(PropertyGroup): id : StringProperty(default='') @@ -243,13 +258,32 @@ class Project(PropertyGroup): type : StringProperty() + def get_cell_names(self): + settings = get_scene_settings() + project = settings.active_project + + cell_names = ['Sequence', 'Shot', 'Frames', 'Description'] + + if project.type == 'TVSHOW': + cell_names.insert(0, 'Episode') + + for metadata_type in project.metadata_types: + if metadata_type['entity_type'] == "SHOT": + cell_names += [metadata_type.name] + + for asset_type in project.asset_types: + cell_names += [asset_type.name] + + return cell_names + def set_spreadsheet(self): - spreadsheet = self.spreadsheet_export cell_names = ['Sequence', 'Shot', 'Frames', 'Description'] if self.type == 'TVSHOW': cell_names.insert(0, 'Episode') + # Export SpreadSheet + spreadsheet = self.spreadsheet_export for cell_name in cell_names: cell = spreadsheet.cells.add() cell.name = cell_name @@ -273,7 +307,6 @@ class Project(PropertyGroup): cell.field_name = asset_type.name.upper() cell.type = "ASSET_TYPE" - def set_strip_metadata(self): # Clear Metadatas @@ -363,7 +396,25 @@ class VSETB_UL_casting(UIList): return filtered, ordered -class VSETB_UL_spreadsheet(UIList): +class VSETB_UL_spreadsheet_import(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, + active_propname, index): + + settings = get_scene_settings() + project = settings.active_project + + layout.use_property_split = True + layout.use_property_decorate = False + + row = layout.row(align=True) + row.alignment = 'LEFT' + + row.prop(item, 'enabled', text='') + layout.label(text=item.name) + layout.prop(item, 'import_name', text='') + + +class VSETB_UL_spreadsheet_export(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -380,16 +431,6 @@ class VSETB_UL_spreadsheet(UIList): layout.label(text=item.name) layout.prop(item, 'export_name', text='') - def draw_filter(self, context, layout): - row = layout.row() - - subrow = row.row(align=True) - subrow.prop(self, "filter_name", text="") - subrow.prop(self, "use_filter_invert", text="", icon='ARROW_LEFTRIGHT') - - subrow.separator() - subrow.prop(self, "order_by_type", text="Order by Type", icon='MESH_DATA') - class VSETB_PGT_scene_settings(PropertyGroup): @@ -442,7 +483,8 @@ class VSETB_PGT_strip_settings(PropertyGroup): classes = ( Asset, AssetCasting, - SpreadsheetCell, + SpreadsheetImportCell, + SpreadsheetExportCell, AssetType, TaskStatus, Episode, @@ -452,7 +494,8 @@ classes = ( SpreadsheetImport, SpreadsheetExport, Project, - VSETB_UL_spreadsheet, + VSETB_UL_spreadsheet_import, + VSETB_UL_spreadsheet_export, VSETB_UL_casting, VSETB_PGT_scene_settings, VSETB_PGT_strip_settings,