From 02785f9ec08856442e4266cccc4c37a3d8b94817 Mon Sep 17 00:00:00 2001 From: Christophe SEUX Date: Fri, 21 Apr 2023 21:44:05 +0200 Subject: [PATCH] Export Csv --- operators/operators.py | 298 +++++++++++++++++++++++++----------- panels.py | 10 +- properties.py | 112 ++++++++++++-- resources/trackers/kitsu.py | 46 ++++++ sequencer_utils.py | 10 +- 5 files changed, 363 insertions(+), 113 deletions(-) diff --git a/operators/operators.py b/operators/operators.py index 64aece5..ba0e216 100644 --- a/operators/operators.py +++ b/operators/operators.py @@ -5,6 +5,12 @@ import importlib import json import re import time +from pprint import pprint +import csv +from datetime import datetime +import os + + import vse_toolbox from bpy_extras.io_utils import ImportHelper @@ -77,20 +83,122 @@ class VSETB_OT_tracker_connect(Operator): return {"CANCELLED"} -class VSETB_OT_export_csv(Operator): - bl_idname = "vse_toolbox.export_csv" - bl_label = "Set Scene" - bl_description = "Set Scene for Breakdown" +class VSETB_OT_export_spreadsheet(Operator): + bl_idname = "vse_toolbox.export_spreadsheet" + bl_label = "Export Spreadsheet" + bl_description = "Export Shot data in a table as a csv or an xlsl" bl_options = {"REGISTER", "UNDO"} + format : EnumProperty(items=[(i, i, '') for i in ('CSV', 'XLSL')]) + separator : StringProperty(default='\\n') + delimiter : StringProperty(default=';') + export_path : StringProperty(default='//export') + @classmethod def poll(cls, context): - return True + settings = get_scene_settings() + return settings.active_project + + def invoke(self, context, event): + settings = get_scene_settings() + project = settings.active_project + + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + scn = context.scene + settings = get_scene_settings() + project = settings.active_project + + layout = self.layout + + row = layout.row() + row.template_list("VSETB_UL_spreadsheet", "spreadsheet", project, "spreadsheet", project, "spreadsheet_index", rows=8) + + col_tool = row.column(align=True) + 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.prop(self, "separator", expand=True, text='Separator') + row = col.row(align=True) + row.prop(self, "format", expand=True, text='Format') + if self.format == 'CSV': + col.prop(self, "delimiter", expand=True, text='Delimiter') + + col.prop(self, 'export_path') def execute(self, context): - self.report({'ERROR'}, f'Export not implemented yet.') + #self.report({'ERROR'}, f'Export not implemented yet.') + prefs = get_addon_prefs() + settings = get_scene_settings() + project = settings.active_project + episode = settings.active_episode - return {"CANCELLED"} + cells = [cell for cell in project.spreadsheet if cell.enabled] + rows = [] + + # Header + rows.append([cell.export_name for cell in cells]) + + for strip in get_strips('Shots'): + row = [] + for cell in cells: + if cell.is_metadata: + row += [getattr(strip.vsetb_strip_settings.metadata, cell.field_name)] + elif cell.field_name == 'EPISODE': + row += [settings.active_episode.name] + elif cell.field_name == 'SEQUENCE': + row += [get_strip_sequence_name(strip)] + elif cell.field_name == 'SHOT': + row += [strip.name] + elif cell.field_name == 'DESCRIPTION': + row += [strip.vsetb_strip_settings.description] + elif cell.field_name == 'FRAMES': + row += [strip.frame_final_duration] + + rows.append(row) + + #print(rows) + + export_path = Path(os.path.abspath(bpy.path.abspath(self.export_path))) + export_name = export_path.name + + if export_path.suffix or export_name.endswith('{ext}'): + export_path = export_path.parent + + else: # It's a directory + if project.type == 'TVSHOW': + export_name = '{date}_{project}_{episode}_{tracker}_shots.{ext}' + else: + export_name = '{date}_{project}_{tracker}_shots.{ext}' + + date = datetime.now().strftime('%Y_%m_%d') + project_name = project.name.replace(' ', '_').lower() + episode_name = episode.name.replace(' ', '_').lower() if episode else 'episode' + ext = self.format.lower() + + export_name = export_name.format(date=date, project=project_name, + episode=episode_name, tracker=settings.tracker_name.lower(), ext=ext) + + export_path = export_path / export_name + + #2023_04_11_kitsu_boris_ep01_shots + export_path.parent.mkdir(parents=True, exist_ok=True) + + print('Writing .csv file to', export_path) + with open(str(export_path), 'w', newline='\n', encoding='utf-8') as f: + writer = csv.writer(f) + for row in rows: + writer.writerow(row) + + #if show_in_explorer: + # open_file(filepath, select=True) + + + return {"FINISHED"} def get_task_status_items(self, context): @@ -118,11 +226,12 @@ class VSETB_OT_upload_to_tracker(Operator): bl_description = "Upload selected strip to tracker" bl_options = {"REGISTER", "UNDO"} - task: EnumProperty(items=get_task_type_items) - status: EnumProperty(items=get_task_status_items) + task : EnumProperty(items=get_task_type_items) + status : EnumProperty(items=get_task_status_items) comment : StringProperty() - add_preview : BoolProperty(default=False) + add_preview : BoolProperty(default=True) preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '') for m in ('ONLY_NEW', 'REPLACE', 'ADD')]) + set_main_preview : BoolProperty(default=True) casting : BoolProperty(default=True) custom_data : BoolProperty(default=True) @@ -174,10 +283,8 @@ class VSETB_OT_upload_to_tracker(Operator): sequence_name = get_strip_sequence_name(strip) shot_name = strip.name sequence = tracker.get_sequence(sequence_name) - - metadata = strip.vsetb_strip_settings.metadata.to_dict() - print(metadata) + #print(metadata) if not sequence: self.report({"INFO"}, f'Create sequence {sequence_name} in Kitsu') @@ -198,49 +305,33 @@ class VSETB_OT_upload_to_tracker(Operator): preview = None if self.add_preview: preview = Path(get_strip_render_path(strip, project.render_template)) + #print(preview) if not preview.exists(): preview = None - elif task['last_comment'].get('previews'): + elif task.get('last_comment') and task['last_comment']['previews']: if self.preview_mode == 'REPLACE': tracker.remove_comment(task['last_comment']) elif self.preview_mode == 'ONLY_NEW': preview = None - if status != 'CURRENT' or preview: - tracker.new_comment(task, comment=self.comment, status=status, preview=preview) + #print(f'{preview=}') + #print(f'{status=}') + if status or preview: + tracker.new_comment(task, comment=self.comment, status=status, preview=preview, set_main_preview=self.set_main_preview) if self.custom_data: - tracker.update_data(shot, strip.vsetb_strip_settings.metadata.to_dict()) + metadata = strip.vsetb_strip_settings.metadata.to_dict() + description = strip.vsetb_strip_settings.description + tracker.update_data(shot, metadata, frames=strip.frame_final_duration, description=description) + + if self.casting: + casting = [{'asset_id': a.id, 'nb_occurences': a.instance} for a in strip.vsetb_strip_settings.casting] + tracker.update_casting(shot, casting) return {"FINISHED"} - # if add_to_kitsu: - # shot_name = T['shot_tracker_name'].fields['shot'].format(data['shot']) - # seq_name = T['sequence_tracker_name'].fields['sequence'].format(data['sequence']) - # sequence = p.sequences.add(seq_name) - # shot = sequence.shots.add(shot_name) - # animatic_task = shot.tasks.add('animatic') - - # animatic_task.comments.new( - # comment=f'Reel edit v{version:03d}', - # status='done', - # preview=movie_output, - # set_main_preview=True - # ) - - # shot_data = { - # 'fps': int(FPS), - # 'frame_in': strip.frame_final_start, - # 'frame_out': strip.frame_final_end-1, - # 'nb_frames': strip.frame_final_duration - # } - - # shot.update_data(shot_data) - - # return {"CANCELLED"} - class VSETB_OT_auto_select_files(Operator): bl_idname = "import.auto_select_files" @@ -485,9 +576,10 @@ class VSETB_OT_load_projects(Operator): episode.id = episode_data['id'] for metadata_data in tracker.get_shots_metadata(project_data): - #print(metadata_data) + pprint(metadata_data) metadata_type = project.metadata_types.add() - metadata_type.name = metadata_data['field_name'] + metadata_type.name = metadata_data['name'] + metadata_type.field_name = metadata_data['field_name'] metadata_type['choices'] = metadata_data['choices'] for status_data in tracker.get_task_statuses(project_data): @@ -502,12 +594,14 @@ class VSETB_OT_load_projects(Operator): for asset_type_data in tracker.get_asset_types(project_data): asset_type = project.asset_types.add() asset_type.name = asset_type_data['name'] + + project.set_spreadsheet() for project in settings.projects: - print(project.name, project.name.replace(' ', '_').upper(), old_project_name) + #print(project.name, project.name.replace(' ', '_').upper(), old_project_name) if project.name.replace(' ', '_').upper() == old_project_name: - print('Restore Project Name') + #print('Restore Project Name') settings.project_name = project.name #else: # print('Could Not restore Project Name') @@ -545,8 +639,8 @@ class VSETB_OT_new_episode(Operator): episode_name = settings.episode_template.format(index=int(self.episode_name)) - print(self.episode_name) - print('episode_name: ', episode_name) + #print(self.episode_name) + #print('episode_name: ', episode_name) episode = tracker.get_episode(episode_name) if episode: @@ -805,11 +899,11 @@ class VSETB_OT_casting_remove(Operator): class VSETB_OT_casting_move(Operator): bl_idname = "vse_toolbox.casting_move" - bl_label = "Casting Actions" - bl_description = "Actions to Add, Remove, Move casting items" + bl_label = "Move Casting items" + bl_description = "Move Casting items" bl_options = {"REGISTER", "UNDO"} - action: EnumProperty( + direction: EnumProperty( items=( ('UP', "Up", ""), ('DOWN', "Down", ""), @@ -824,7 +918,7 @@ class VSETB_OT_casting_move(Operator): if active_strip: return True - def invoke(self, context, event): + def execute(self, context): scn = context.scene strip_settings = get_strip_settings() @@ -835,12 +929,12 @@ class VSETB_OT_casting_move(Operator): except IndexError: pass else: - if self.action == 'DOWN' and idx < len(strip_settings.casting) - 1: + if self.direction == 'DOWN' and idx < len(strip_settings.casting) - 1: item_next = strip_settings.casting[idx+1].name strip_settings.casting.move(idx, idx+1) strip_settings.casting_index += 1 - elif self.action == 'UP' and idx >= 1: + elif self.direction == 'UP' and idx >= 1: item_prev = strip_settings.casting[idx-1].name strip_settings.casting.move(idx, idx-1) strip_settings.casting_index -= 1 @@ -851,9 +945,45 @@ class VSETB_OT_casting_move(Operator): return {"FINISHED"} +class VSETB_OT_spreadsheet_move(Operator): + bl_idname = "vse_toolbox.spreadsheet_move" + bl_label = "Move Spreadsheet items" + bl_description = "Move Spreadsheet items" + bl_options = {"REGISTER", "UNDO"} + + direction: EnumProperty( + items=( + ('UP', "Up", ""), + ('DOWN', "Down", ""), + ) + ) + + def execute(self, context): + scn = context.scene + project = get_scene_settings().active_project + + idx = project.spreadsheet_index + + try: + item = project.spreadsheet[idx] + except IndexError: + pass + else: + if self.direction == 'DOWN' and idx < len(project.spreadsheet) - 1: + item_next = project.spreadsheet[idx+1].name + project.spreadsheet.move(idx, idx+1) + project.spreadsheet_index += 1 + + elif self.direction == 'UP' and idx >= 1: + item_prev = project.spreadsheet[idx-1].name + project.spreadsheet.move(idx, idx-1) + project.spreadsheet_index -= 1 + + return {"FINISHED"} + class VSETB_OT_copy_casting(Operator): bl_idname = "vse_toolbox.copy_casting" - bl_label = "Casting Actions" + bl_label = "Copy Casting" bl_description = "Copy Casting from active strip" bl_options = {"REGISTER", "UNDO"} @@ -879,8 +1009,8 @@ class VSETB_OT_copy_casting(Operator): class VSETB_OT_paste_casting(Operator): bl_idname = "vse_toolbox.paste_casting" - bl_label = "Casting Actions" - bl_description = "Copy Casting from active strip" + bl_label = "Paste Casting" + bl_description = "Paste Casting to active strip" bl_options = {"REGISTER", "UNDO"} @classmethod @@ -932,50 +1062,37 @@ class VSETB_OT_set_stamps(Operator): bpy.ops.sequencer.select_all(action='DESELECT') - crop_x = int(scn.render.resolution_x * 0.33) - crop_y = int(scn.render.resolution_y * 0.95) + crop_x = int(scn.render.resolution_x * 0.4) + crop_max_y = int(scn.render.resolution_y * 0.96) + crop_min_y = int(scn.render.resolution_y * 0.01) + + stamp_params = dict(start=scn.frame_start, end=scn.frame_end, + font_size=22, y=0.015, box_margin=0.005, select=True, box_color=(0, 0, 0, 0.5)) # 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, - ) + project_strip_stamp = new_text_strip('project_name_stamp', channel=1, **stamp_params, + text=settings.active_project.name, x=0.01, align_x='LEFT', align_y='BOTTOM') project_strip_stamp.crop.max_x = crop_x * 2 - project_strip_stamp.crop.max_y = crop_y + project_strip_stamp.crop.max_y = crop_max_y + project_strip_stamp.crop.min_y = crop_min_y # 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, - ) + shot_strip_stamp = new_text_strip('shot_name_stamp', channel=2, **stamp_params, + text='{active_shot_name}', align_y='BOTTOM') shot_strip_stamp.crop.min_x = crop_x shot_strip_stamp.crop.max_x = crop_x - shot_strip_stamp.crop.max_y = crop_y + shot_strip_stamp.crop.max_y = crop_max_y + shot_strip_stamp.crop.min_y = crop_min_y # 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, - ) + frame_strip_stamp = new_text_strip('frame_range_stamp', channel=3, **stamp_params, + text='{active_shot_frame} / {active_shot_duration}', x=0.99, align_x='RIGHT', align_y='BOTTOM') - frame_strip_stamp.crop.min_x = int(scn.render.resolution_x * 0.66) - frame_strip_stamp.crop.max_y = crop_y - # 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, - # ) + frame_strip_stamp.crop.min_x = crop_x *2 + frame_strip_stamp.crop.max_y = crop_max_y + frame_strip_stamp.crop.min_y = crop_min_y bpy.ops.sequencer.meta_make() stamps_strip = context.active_sequence_strip @@ -994,9 +1111,10 @@ classes = ( VSETB_OT_casting_add, VSETB_OT_casting_remove, VSETB_OT_casting_move, + VSETB_OT_spreadsheet_move, VSETB_OT_copy_casting, VSETB_OT_paste_casting, - VSETB_OT_export_csv, + VSETB_OT_export_spreadsheet, VSETB_OT_import_files, VSETB_OT_load_assets, VSETB_OT_load_projects, diff --git a/panels.py b/panels.py index abb5f7f..2935b78 100644 --- a/panels.py +++ b/panels.py @@ -170,7 +170,7 @@ class VSETB_PT_exports(VSETB_main, Panel): bl_options = {'DEFAULT_CLOSED'} def draw_header_preset(self, context): - self.layout.operator('vse_toolbox.export_csv', icon='EXPORT', text='', emboss=False) + self.layout.operator('vse_toolbox.export_spreadsheet', icon='EXPORT', text='', emboss=False) def draw(self, context): prefs = get_addon_prefs() @@ -183,7 +183,7 @@ class VSETB_PT_exports(VSETB_main, Panel): tracker_label = settings.tracker_name.title().replace('_', ' ') layout.operator('vse_toolbox.upload_to_tracker', text=f'Upload to {tracker_label}', icon='EXPORT') - layout.operator('vse_toolbox.export_csv', text='Export csv', icon='SPREADSHEET') + layout.operator('vse_toolbox.export_spreadsheet', text='Export Spreadsheet', icon='SPREADSHEET') class VSETB_PT_casting(VSETB_main, Panel): @@ -232,8 +232,8 @@ class VSETB_PT_casting(VSETB_main, Panel): 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.operator('vse_toolbox.casting_move', icon='TRIA_UP', text="").direction = 'UP' + col_tool.operator('vse_toolbox.casting_move', icon='TRIA_DOWN', text="").direction = 'DOWN' col_tool.separator() col_tool.operator('vse_toolbox.copy_casting', icon='COPYDOWN', text="") col_tool.operator('vse_toolbox.paste_casting', icon='PASTEDOWN', text="") @@ -259,6 +259,8 @@ class VSETB_PT_metadata(VSETB_main, Panel): if not project: return + + layout.prop(strip_settings, 'description', text='DESCRIPTION') #col = layout.column() for key in strip_settings.metadata.__annotations__.keys(): diff --git a/properties.py b/properties.py index 469b078..fff1891 100644 --- a/properties.py +++ b/properties.py @@ -57,6 +57,28 @@ def get_tracker_items(self, context): return [(norm_str(a.__name__, format=str.upper), a.__name__, "", i) for i, a in enumerate(TRACKERS)] +class CollectionPropertyGroup(PropertyGroup): + def __iter__(self): + return (v for v in self.values()) + def props(self): + return [p for p in self.bl_rna.properties if p.identifier not in ('rna_type', 'name')] + + def keys(self): + return [k for k in self.bl_rna.properties.keys() if k not in ('rna_type', 'name')] + + def values(self): + return [getattr(self, k) for k in self.keys()] + + def items(self): + return self.to_dict().items() + + def to_dict(self, use_name=True): + if use_name: + return {p.name: getattr(self, p.identifier) for p in self.props()} + else: + return {k: getattr(self, k) for k in self.keys()} + + class Asset(PropertyGroup): name : StringProperty(default='') id : StringProperty(default='') @@ -79,9 +101,6 @@ class Asset(PropertyGroup): class AssetCasting(PropertyGroup): id : StringProperty(default='') instance : IntProperty(default=1) - - def __iter__(self): - return (getattr(self, p) for p in self.bl_rna.properties.keys() if p not in ('rna_type', 'name')) @property def asset(self): @@ -89,8 +108,13 @@ class AssetCasting(PropertyGroup): project = settings.active_project return project.assets.get(self.id) - def to_dict(self): - return {k: v for k,v in self.items()} + +class SpreadsheetCell(PropertyGroup): + export_name : StringProperty() + enabled : BoolProperty(default=True) + is_metadata : BoolProperty(default=False) + field_name : StringProperty() + #type : EnumProperty(items=[(t, t, "")] for t in ('METADATA', 'STRIP_DATA')) class AssetType(PropertyGroup): @@ -100,6 +124,7 @@ class AssetType(PropertyGroup): class MetadataType(PropertyGroup): choices = [] choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']]) + field_name : bpy.props.StringProperty() class TaskType(PropertyGroup): @@ -110,11 +135,9 @@ class TaskStatus(PropertyGroup): __annotations__ = {} -class Metadata(PropertyGroup): +class Metadata(CollectionPropertyGroup): __annotations__ = {} - def to_dict(self): - return {p.name: getattr(self, p.identifier) for p in self.bl_rna.properties.keys() if p.name != 'RNA'} class Episode(PropertyGroup): id : StringProperty(default='') @@ -157,8 +180,30 @@ class Project(PropertyGroup): task_types : CollectionProperty(type=TaskType) task_statuses : CollectionProperty(type=TaskStatus) + spreadsheet : CollectionProperty(type=SpreadsheetCell) + spreadsheet_index : IntProperty(name='Spreadsheet Index', default=0) + type : StringProperty() + def set_spreadsheet(self): + cell_names = ['Sequence', 'Shot', 'Frames', 'Description'] + if self.type == 'TVSHOW': + cell_names.insert(0, 'Episode') + + for cell_name in cell_names: + cell = self.spreadsheet.add() + cell.name = cell_name + cell.export_name = cell_name + cell.field_name = cell_name.upper() + + for metadata_type in self.metadata_types: + cell = self.spreadsheet.add() + cell.name = metadata_type.name + cell.export_name = metadata_type.name + cell.field_name = metadata_type.field_name + cell.is_metadata = True + + def set_strip_metadata(self): # Clear Metadatas @@ -167,16 +212,17 @@ class Project(PropertyGroup): delattr(Metadata, attr) del Metadata.__annotations__[attr] - for metadata_type in self.metadata_types: - prop_name = metadata_type.name - if metadata_type.get('choices'): - prop = bpy.props.EnumProperty(items=[(c, c.replace(' ', '_').upper(), '') for c in ['/'] + metadata_type['choices']]) - else: - prop = bpy.props.StringProperty() + field_name = metadata_type.field_name + name = metadata_type.name - Metadata.__annotations__[prop_name] = prop - setattr(Metadata, prop_name, prop) + if metadata_type.get('choices'): + prop = bpy.props.EnumProperty(items=[(c, c, '') for c in ['/'] + metadata_type['choices']], name=name) + else: + prop = bpy.props.StringProperty(name=name) + + Metadata.__annotations__[field_name] = prop + setattr(Metadata, field_name, prop) class VSETB_UL_casting(UIList): @@ -240,7 +286,35 @@ class VSETB_UL_casting(UIList): ordered = sort_items(_sort, lambda x: x[1].asset.label) return filtered, ordered - + + +class VSETB_UL_spreadsheet(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, '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): @@ -287,11 +361,13 @@ class VSETB_PGT_strip_settings(PropertyGroup): casting_index : IntProperty(name='Casting Index', default=0) source_name : StringProperty(name='') metadata : PointerProperty(type=Metadata) + description : StringProperty() classes=( Asset, AssetCasting, + SpreadsheetCell, AssetType, TaskStatus, Episode, @@ -299,6 +375,7 @@ classes=( MetadataType, TaskType, Project, + VSETB_UL_spreadsheet, VSETB_UL_casting, VSETB_PGT_scene_settings, VSETB_PGT_strip_settings, @@ -313,6 +390,7 @@ def load_handler(dummy): settings = get_scene_settings() if settings.active_project: settings.active_project.set_strip_metadata() + #settings.active_project.set_spreadsheet() os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id diff --git a/resources/trackers/kitsu.py b/resources/trackers/kitsu.py index 9992519..200977f 100644 --- a/resources/trackers/kitsu.py +++ b/resources/trackers/kitsu.py @@ -6,6 +6,7 @@ import urllib3 import traceback import time import uuid +from pprint import pprint from bpy.props import PointerProperty, StringProperty from pathlib import Path @@ -176,6 +177,20 @@ class Kitsu(Tracker): return gazu.shot.get_shot_by_name(sequence, shot) + def get_asset(self, asset, asset_type=None, project=None): + #print('get_asset', "name", name, 'asset_type', asset_type) + + asset_id = self.get_id(asset) + if asset_id: + return asset_id + + project = self.get_project(project) + asset_type = self.get_id(asset_type) + + asset = gazu.asset.get_asset_by_name(project, asset, asset_type) + + return asset + def get_assets(self, project=None): project = self.get_project(project) assets = gazu.asset.all_assets_for_project(project) @@ -327,10 +342,41 @@ class Kitsu(Tracker): else: entity['data'].update(data) + #pprint(entity) entity_data = gazu.client.put(f"data/entities/{entity_id}", entity) + return entity_data['data'] + def get_casting(self, shot, project=None): + project = self.get_project(project) + project_id = self.get_id(project) + return gazu.casting.get_shot_casting({'id': self.get_id(shot), 'project_id': project_id}) + + def update_casting(self, shot, casting, clear=True, project=None): + project = self.get_project(project) + shot_id = self.get_id(shot) + + norm_casting = [] + if clear is False: + norm_casting += self.get_casting(shot, project) + + for asset in casting: + if isinstance(asset, dict) and 'asset_id' in asset: # It's an asset instance + asset_id = asset['asset_id'] + nb_occurences = asset['nb_occurences'] + else: # It's an asset + asset = self.get_asset(asset) + nb_occurences = 1 + + cast = next((c for c in norm_casting if c['asset_id'] == asset_id), None) + if cast: + cast['nb_occurences'] += 1 + else: + norm_casting.append({'asset_id': asset_id, 'nb_occurences': nb_occurences}) + + return gazu.casting.update_shot_casting(project, shot_id, norm_casting) + def draw_prefs(self, layout): layout.prop(self, 'url', text='Url') layout.prop(self, 'login', text='Login') diff --git a/sequencer_utils.py b/sequencer_utils.py index f25a627..51f09c1 100644 --- a/sequencer_utils.py +++ b/sequencer_utils.py @@ -2,6 +2,7 @@ import re from pathlib import Path +import os import bpy from bpy.app.handlers import persistent @@ -125,12 +126,15 @@ def set_channels(): def get_strip_render_path(strip, template): scn = bpy.context.scene - return template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix) + suffix = Path(scn.render.frame_path()).suffix + render_path = template.format(strip_name=strip.name, ext=suffix[1:]) + return Path(os.path.abspath(bpy.path.abspath(render_path))) def render_strips(strips, template): scn = bpy.context.scene scene_start = scn.frame_start scene_end = scn.frame_end + render_path = scn.render.filepath for strip in strips: #print(render_template, strip.name, path) @@ -140,7 +144,8 @@ def render_strips(strips, template): ## render animatic #strip_render_path = render_template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix) - scn.render.filepath = get_strip_render_path(strip, template) + + scn.render.filepath = str(get_strip_render_path(strip, template)) #print(scn.render.filepath) print(f'Render Strip to {scn.render.filepath}') #bpy.ops.render.render(animation=True) @@ -149,6 +154,7 @@ def render_strips(strips, template): scn.frame_start = scene_start scn.frame_end = scene_end + scn.render.filepath = render_path def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False): import opentimelineio as otio