From d2caf90ea2d6dbb522b43ef8206ed29c3192bdfd Mon Sep 17 00:00:00 2001 From: Christophe SEUX Date: Thu, 20 Apr 2023 00:12:39 +0200 Subject: [PATCH] upload --- constants.py | 2 + operators/operators.py | 188 ++++++++++++++++++-- panels.py | 55 ++++-- preferences.py | 10 +- properties.py | 28 ++- resources/trackers/kitsu.py | 324 +++++++++++++++++++++++----------- resources/trackers/tracker.py | 7 +- sequencer_utils.py | 33 +++- 8 files changed, 505 insertions(+), 142 deletions(-) diff --git a/constants.py b/constants.py index 0f70754..14b265c 100644 --- a/constants.py +++ b/constants.py @@ -23,6 +23,8 @@ SOUND_SUFFIXES = ['.mp3', '.aaf', '.flac', '.wav'] CONFIG_DIR = Path(appdirs.user_config_dir(__package__.split('.')[0])) PREVIEWS_DIR = CONFIG_DIR / 'thumbnails' +TASK_ITEMS = [] + ASSET_PREVIEWS = bpy.utils.previews.new() CASTING_BUFFER = CONFIG_DIR / 'casting.json' \ No newline at end of file diff --git a/operators/operators.py b/operators/operators.py index 8ceb80c..a47c5b3 100644 --- a/operators/operators.py +++ b/operators/operators.py @@ -4,6 +4,7 @@ import bpy import importlib import json import re +import time import vse_toolbox from bpy_extras.io_utils import ImportHelper @@ -29,6 +30,7 @@ from vse_toolbox.constants import ( PREVIEWS_DIR, SOUNDS, SOUND_SUFFIXES, + TASK_ITEMS ) from vse_toolbox.sequencer_utils import ( clean_sequencer, @@ -41,8 +43,11 @@ from vse_toolbox.sequencer_utils import ( render_strips, set_channels, get_channel_index, - new_text_strip + new_text_strip, + get_strip_render_path, + get_strip_sequence_name ) + 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 @@ -88,6 +93,133 @@ class VSETB_OT_export_csv(Operator): return {"CANCELLED"} +def get_task_status_items(self, context): + settings = get_scene_settings() + project = settings.active_project + + status_items = [('CURRENT', 'Current', '')] + status_items += [(t.name, t.name, '') for t in project.task_statuses] + return status_items + +def get_task_type_items(self, context): + settings = get_scene_settings() + project = settings.active_project + + return [(t.name, t.name, '') for t in project.task_types] + +class VSETB_OT_upload_to_tracker(Operator): + bl_idname = "vse_toolbox.upload_to_tracker" + bl_label = "Upload to tracker" + 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) + comment : StringProperty() + add_preview : BoolProperty(default=False) + preview_mode : EnumProperty(items=[(m, m.title().replace('_', ' '), '') for m in ('ONLY_NEW', 'REPLACE', 'ADD')]) + casting : BoolProperty(default=True) + custom_data : BoolProperty(default=True) + + @classmethod + def poll(cls, context): + return True + + def invoke(self, context, event): + prefs = get_addon_prefs() + settings = get_scene_settings() + project = settings.active_project + + tracker = prefs.tracker + tracker.connect() + + #self.bl_label = f"Upload to {settings.tracker_name.title()}" + + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + scn = context.scene + settings = get_scene_settings() + + layout = self.layout + col = layout.column() + col.use_property_split = True + col.prop(self, 'task', text='Task') + col.prop(self, 'status', text='Status') + col.prop(self, 'comment', text='Comment') + row = col.row(heading='Add Preview') + row.prop(self, 'add_preview', text='') + row.prop(self, 'preview_mode', text='') + col.separator() + col.prop(self, 'casting', text='Casting') + col.prop(self, 'custom_data', text='Custom Data') + + def execute(self, context): + #self.report({'ERROR'}, f'Export not implemented yet.') + prefs = get_addon_prefs() + settings = get_scene_settings() + #project = settings.active_project + tracker = prefs.tracker + + status = self.status + if status == 'CURRENT': + status = None + + for strip in get_strips(channel='Shots', selected_only=True): + sequence_name = get_strip_sequence_name(strip) + shot_name = strip.name + sequence = tracker.get_sequence(sequence_name) + + if not sequence: + self.report({"INFO"}, f'Create sequence {sequence_name} in Kitsu') + sequence = tracker.new_sequence(sequence_name) + + shot = tracker.get_shot(shot_name, sequence=sequence) + if not shot: + self.report({"INFO"}, f'Create shot {shot_name} in Kitsu') + shot = tracker.new_shot(shot_name, sequence=sequence) + + task = tracker.get_task(self.task, entity=shot) + + print(task) + tracker.new_comment(task, comment=self.comment, status=status) + #status = tracker.get_task(shot, self.task) + # if self.add_preview: + # strip_movie_path = Path(get_strip_render_path(strip, template)) + # if path.exists(): + + # else: + # self.report({"WARNING"}, f"{strip_movie_path} not exist, skipped") + + + 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" bl_label = "Auto Select" @@ -317,8 +449,6 @@ class VSETB_OT_load_projects(Operator): tracker.connect() for project_data in tracker.get_projects(): - - project = settings.projects.add() project.type = project_data['production_type'].upper().replace(' ', '') project.name = project_data['name'] @@ -335,6 +465,15 @@ class VSETB_OT_load_projects(Operator): metadata_type = project.metadata_types.add() metadata_type.name = metadata_data['field_name'] metadata_type['choices'] = metadata_data['choices'] + + for status_data in tracker.get_task_statuses(project_data): + #print(metadata_data) + task_status = project.task_statuses.add() + task_status.name = status_data['short_name'].upper() + + for task_type_data in tracker.get_shot_task_types(project_data): + task_type = project.task_types.add() + task_type.name = task_type_data['name'] for asset_type_data in tracker.get_asset_types(project_data): asset_type = project.asset_types.add() @@ -465,11 +604,12 @@ class VSETB_OT_render(Operator): bl_description = "Render Shots Strips" bl_options = {"REGISTER", "UNDO"} - selected_only : BoolProperty(name="Selected Only", default=False) + #selected_only : BoolProperty(name="Selected Only", default=False) @classmethod def poll(cls, context): - return True + settings = get_scene_settings() + return settings.active_project def invoke(self, context, event): scn = context.scene @@ -485,15 +625,20 @@ class VSETB_OT_render(Operator): col = layout.column() col.use_property_split = True col.use_property_decorate = False - col.prop(settings, 'channel', text='Channel') - col.prop(self, 'selected_only') + #col.prop(settings, 'channel', text='Channel') + #col.prop(self, 'selected_only') + + col.prop(settings.active_project, "render_template") def execute(self, context): scn = context.scene settings = get_scene_settings() - strips = get_strips(channel=settings.channel, selected_only=self.selected_only) + strips = get_strips(channel='Shots', selected_only=True) - render_strips(strips) + start_time = time.perf_counter() + render_strips(strips, settings.active_project.render_template) + + self.report({"INFO"}, f'Strips rendered in {time.perf_counter()-start_time} seconds') return {"FINISHED"} @@ -522,6 +667,16 @@ class VSETB_OT_set_sequencer(Operator): else: self.report({'INFO'}, f'Cannot set Resolution. No Movie Found.') + scn.view_settings.view_transform = 'Standard' + scn.render.image_settings.file_format = 'FFMPEG' + scn.render.ffmpeg.gopsize = 5 + scn.render.ffmpeg.constant_rate_factor = 'HIGH' + scn.render.ffmpeg.format = 'QUICKTIME' + scn.render.ffmpeg.audio_codec = 'AAC' + scn.render.ffmpeg.audio_codec = 'MP3' + scn.render.ffmpeg.audio_mixrate = 44100 + scn.render.ffmpeg.audio_bitrate = 128 + return {"FINISHED"} @@ -744,6 +899,9 @@ 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) + # Project Name project_strip_stamp = new_text_strip( 'project_name_stamp', channel=1, start=scn.frame_start, end=scn.frame_end, @@ -752,6 +910,9 @@ class VSETB_OT_set_stamps(Operator): box_color=(0, 0, 0, 0.5), box_margin=0.005, ) + project_strip_stamp.crop.max_x = crop_x * 2 + project_strip_stamp.crop.max_y = crop_y + # Shot Name shot_strip_stamp = new_text_strip( f'shot_name_stamp', channel=2, start=scn.frame_start, end=scn.frame_end, @@ -760,6 +921,10 @@ class VSETB_OT_set_stamps(Operator): box_color=(0, 0, 0, 0.5), box_margin=0.005, ) + shot_strip_stamp.crop.min_x = crop_x + shot_strip_stamp.crop.max_x = crop_x + shot_strip_stamp.crop.max_y = crop_y + # Frame Range frame_strip_stamp = new_text_strip( 'frame_range_stamp', channel=3, start=scn.frame_start, end=scn.frame_end, @@ -768,6 +933,8 @@ class VSETB_OT_set_stamps(Operator): box_color=(0, 0, 0, 0.5), box_margin=0.005, ) + 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( @@ -806,7 +973,8 @@ classes = ( VSETB_OT_render, VSETB_OT_set_sequencer, VSETB_OT_tracker_connect, - VSETB_OT_set_stamps + VSETB_OT_set_stamps, + VSETB_OT_upload_to_tracker ) def register(): diff --git a/panels.py b/panels.py index 90881c0..abb5f7f 100644 --- a/panels.py +++ b/panels.py @@ -86,9 +86,13 @@ class VSETB_PT_sequencer(VSETB_main, Panel): col.operator('vse_toolbox.set_sequencer', text='Set-Up Sequencer', icon='SEQ_SEQUENCER') #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) + shot_label = '' + sequence_label = '' + + if project: + 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() @@ -106,19 +110,41 @@ class VSETB_PT_sequencer(VSETB_main, Panel): 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 + if project: + 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 col.operator('vse_toolbox.set_stamps', text='Set Stamps', icon='COLOR') +class VSETB_PT_settings(VSETB_main, Panel): + bl_label = "Settings" + 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.prop(project, 'sequence_template') + col.prop(project, 'shot_template') + col.prop(project, 'render_template') + + class VSETB_PT_imports(VSETB_main, Panel): bl_label = "Imports" bl_parent_id = "VSETB_PT_main" @@ -154,7 +180,10 @@ class VSETB_PT_exports(VSETB_main, Panel): # 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') + + 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') class VSETB_PT_casting(VSETB_main, Panel): diff --git a/preferences.py b/preferences.py index 0de7be4..c3b30f1 100644 --- a/preferences.py +++ b/preferences.py @@ -48,16 +48,18 @@ def load_trackers(): if not Tracker in obj.__mro__: continue - if obj is Tracker or obj.name in (a.name for a in TRACKERS): + if obj is Tracker or name in (a.__name__ for a in TRACKERS): continue try: - print(f'Register Plugin {name}') + print(f'Register Tracker {name}') bpy.utils.register_class(obj) - setattr(Trackers, norm_str(obj.name), PointerProperty(type=obj)) + #obj.register() + + setattr(Trackers, norm_str(name), PointerProperty(type=obj)) TRACKERS.append(obj) except Exception as e: - print(f'Could not register library_type {name}') + print(f'Could not register Tracker {name}') print(e) def load_tracker_prefs(): diff --git a/properties.py b/properties.py index e7991f7..1679fad 100644 --- a/properties.py +++ b/properties.py @@ -41,15 +41,20 @@ def on_project_updated(self, context): settings = get_scene_settings() settings['episodes'] = 0 - print('Update active Project') + #print('Update active Project') bpy.ops.vse_toolbox.load_assets() if settings.active_project: settings.active_project.set_strip_metadata() + os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id + +def on_episode_updated(self, context): + os.environ['TRACKER_EPISODE_ID'] = settings.active_episode.id + def get_tracker_items(self, context): - return [(norm_str(a.name, format=str.upper), a.name, "", i) for i, a in enumerate(TRACKERS)] + return [(norm_str(a.__name__, format=str.upper), a.__name__, "", i) for i, a in enumerate(TRACKERS)] class Asset(PropertyGroup): @@ -97,6 +102,14 @@ class MetadataType(PropertyGroup): choice : EnumProperty(items=lambda s, c: [(c, c.replace(' ', '_').upper(), '') for c in s['choices']]) +class TaskType(PropertyGroup): + __annotations__ = {} + + +class TaskStatus(PropertyGroup): + __annotations__ = {} + + class Metadata(PropertyGroup): __annotations__ = {} @@ -131,11 +144,17 @@ class Project(PropertyGroup): shot_template : StringProperty( name="Shot Name", default="{sequence}_sh{index:04d}") - episode_name : EnumProperty(items=get_episodes_items) + render_template : StringProperty( + name="Render Name", default="//render/{strip_name}.{ext}") + + episode_name : EnumProperty(items=get_episodes_items, update=on_episode_updated) episodes : CollectionProperty(type=Episode) assets : CollectionProperty(type=Asset) asset_types : CollectionProperty(type=AssetType) metadata_types : CollectionProperty(type=MetadataType) + task_types : CollectionProperty(type=TaskType) + task_statuses : CollectionProperty(type=TaskStatus) + type : StringProperty() def set_strip_metadata(self): @@ -272,9 +291,11 @@ classes=( Asset, AssetCasting, AssetType, + TaskStatus, Episode, Metadata, MetadataType, + TaskType, Project, VSETB_UL_casting, VSETB_PGT_scene_settings, @@ -290,6 +311,7 @@ def load_handler(dummy): settings = get_scene_settings() if settings.active_project: settings.active_project.set_strip_metadata() + os.environ['TRACKER_PROJECT_ID'] = settings.active_project.id def register(): diff --git a/resources/trackers/kitsu.py b/resources/trackers/kitsu.py index 753a0a1..29caeb8 100644 --- a/resources/trackers/kitsu.py +++ b/resources/trackers/kitsu.py @@ -5,10 +5,10 @@ import re import urllib3 import traceback import time +import uuid from bpy.props import PointerProperty, StringProperty from pathlib import Path -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 @@ -18,34 +18,163 @@ except Exception as e: print('Could not install gazu') print(e) +LOGIN = None class Kitsu(Tracker): - name = "Kitsu" - + url: StringProperty() login: StringProperty() password: StringProperty(subtype='PASSWORD') - project_name = None + def connect(self, url=None, login=None, password=None): + '''Connect to kitsu api using provided url, login and password''' + + global LOGIN + + urllib3.disable_warnings() + + if url is None: + url = self.url + if login is None: + login = self.login + if password is None: + password = self.password + + if not url: + print(f'Kitsu Url: {self.url} is empty') + return + + if login == LOGIN: + return + + if not url.endswith('/api'): + url += '/api' + + print(f'Info: Setting Host for kitsu {url}') + gazu.client.set_host(url) + + if not gazu.client.host_is_up(): + print('Error: Kitsu Host is down') + + try: + 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"]}') + return res['user'] + except Exception as e: + print(f'Error: {traceback.format_exc()}') + + def get_id(self, data): + if isinstance(data, str): + if self.is_id(data): + return data + #return None + elif isinstance(data, dict): + return data['id'] + elif data: # Should be a Class + return data.id + + def is_id(self, id): + if not isinstance(id, str): + return False + try: + uuid.UUID(id) + return True + except ValueError: + return False + + def get_project(self, project=None): + if project: + project_id = self.get_id(project) + if project_id: + return project_id + return gazu.project.get_project_by_name(project) + + if os.environ.get('TRACKER_PROJECT_ID'): + return os.environ['TRACKER_PROJECT_ID'] + elif os.environ.get('TRACKER_PROJECT_NAME'): + return os.environ['TRACKER_PROJECT_NAME'] - def draw_prefs(self, layout): - layout.prop(self, 'url', text='Url') - layout.prop(self, 'login', text='Login') - layout.prop(self, 'password', text='Password') - def get_projects(self): return gazu.project.all_open_projects() + def get_episode(self, episode, project=None): + project = self.get_project(project) + if episode: + episode_id = self.get_id(episode) + if episode_id: + return episode_id + return gazu.shot.get_episode_by_name(project, episode) + + if os.environ.get('TRACKER_EPISODE_ID'): + return os.environ['TRACKER_EPISODE_ID'] + elif os.environ.get('TRACKER_EPISODE_NAME'): + return os.environ['TRACKER_EPISODE_NAME'] + def get_episodes(self, project): return gazu.shot.all_episodes_for_project(project) - def get_episode(self, name): - return gazu.shot.get_episode_by_name(self.project, name) + def get_task_type(self, task_type=None): + print('get_task_type', task_type) + return gazu.task.get_task_type_by_name(task_type) - def get_asset_types(self, project): + def get_shot_task_types(self, project=None): + project = self.get_project(project) + task_types = gazu.task.all_task_types_for_project(project) + return [t for t in task_types if t['for_entity'].lower() == 'shot'] + + def get_shots_metadata(self, project=None): + project = self.get_project(project) + metadatas = [] + + for metadata in gazu.project.all_metadata_descriptors(project): + if metadata['entity_type'] == 'Shot' and metadata['name']: + metadatas.append(metadata) + + return metadatas + + def get_task_status(self, status=None): + status_id = self.get_id(status) + if status_id: + return status_id + + return gazu.client.fetch_first('task-status', {"short_name": status.lower()}) + + def get_task_statuses(self, project=None): + project = self.get_project(project) + return gazu.task.all_task_statuses_for_project(project) + + def get_asset_types(self, project=None): + project = self.get_project(project) return gazu.asset.all_asset_types_for_project(project) - def get_assets(self, project): + def get_sequence(self, sequence, project=None): + print(f'get_sequence({sequence=}, {project=})') + project = self.get_project(project) + + sequence_id = self.get_id(sequence) + if sequence_id: + return sequence_id + + return gazu.shot.get_sequence_by_name(project, sequence) + + def get_shot(self, shot, sequence, project=None): + print(f'get_shot({shot=}, {sequence=}, {project=})') + project = self.get_project(project) + sequence = self.get_sequence(sequence, project) + + if not sequence: + return + + shot_id = self.get_id(shot) + if shot_id: + return shot_id + + return gazu.shot.get_shot_by_name(sequence, shot) + + def get_assets(self, project=None): + project = self.get_project(project) assets = gazu.asset.all_assets_for_project(project) entity_types = self.get_asset_types(project) entity_types_ids = {e['id']: e['name'] for e in entity_types} @@ -55,14 +184,54 @@ class Kitsu(Tracker): return assets - def get_shots_metadata(self, project): - metadatas = [] + def get_last_comment(self, task): + task = self.get_id(task) + return gazu.task.get_last_comment_for_task(task) - for metadata in gazu.project.all_metadata_descriptors(project): - if metadata['entity_type'] == 'Shot' and metadata['name']: - metadatas.append(metadata) + def get_task(self, task=None, entity=None): + entity = self.get_id(entity) - return metadatas + task_type = self.get_task_type(task) + + print('task_type', task_type) + print('task_type', task_type) + + task = gazu.task.get_task_by_name(entity, task_type) + #task = gazu.task.get_task(task['id']) + + task['last_comment'] = self.get_last_comment(task) + + return task + + def new_comment(self, task, status=None, comment='', preview=None, set_main_preview=False): + #task = self.get_task(task) + + #print('Add Comment', status) + if status is None: + #print(task) + status = {'id' : task['task_status_id']} + else: + status = self.get_task_status(status) + + + + comment = gazu.task.add_comment( + task=task, + task_status=status, + comment=comment ) + + if preview: + logging.info(f'Adding Preview to Kitsu {preview}') + preview = self.add_preview( + task=task, + comment=comment, + preview=str(preview) ) + + if set_main_preview: + gazu.task.set_main_preview(preview) + + + return comment def download_preview(self, preview_id, filepath): if isinstance(filepath, str): @@ -74,100 +243,43 @@ class Kitsu(Tracker): filepath.parent.mkdir(parents=True, exist_ok=True) gazu.files.download_preview_file_thumbnail(preview_id, filepath.as_posix()) - def connect(self, url=None, login=None, password=None): - '''Connect to kitsu api using provided url, login and password''' + def new_sequence(self, sequence, episode=None, project=None): + project = self.get_project(project) - urllib3.disable_warnings() + params = dict(name=sequence, project=project) + episode = self.get_episode(episode) + if episode: + params['episode'] = episode - if not self.url: - print(f'Kitsu Url: {self.url} is empty') - return + return gazu.shot.new_sequence(**params) - url = self.url - if not url.endswith('/api'): - url += '/api' + def new_shot(self, shot, sequence, nb_frames=None, frame_in=None, + frame_out=None, description='', custom_data=None, project=None): - print(f'Info: Setting Host for kitsu {url}') - gazu.client.set_host(url) + project = self.get_project(project) + sequence = self.get_sequence(sequence) - if not gazu.client.host_is_up(): - print('Error: Kitsu Host is down') + custom_data = custom_data or {} - try: - print(f'Info: Log in to kitsu as {self.login}') - res = gazu.log_in(self.login, self.password) - print(f'Info: Sucessfully login to Kitsu as {res["user"]["full_name"]}') - return res['user'] - except Exception as e: - print(f'Error: {traceback.format_exc()}') + if frame_in is not None: + custom_data["frame_in"] = frame_in + if frame_out is not None: + custom_data["frame_out"] = frame_out - def get_asset_path(self, name, catalog, directory=None): - directory = directory or self.source_directory - return Path(directory, self.get_asset_relative_path(name, catalog)) + params = dict(name=shot, data=custom_data, + sequence_id=self.get_id(sequence), description=description) - def get_asset_info(self, data, asset_path): - modified = time.time_ns() - catalog = data['entity_type_name'].title() - asset_path = self.prop_rel_path(asset_path, 'source_directory') + if nb_frames is not None: + params["nb_frames"] = nb_frames - asset_info = dict( - filepath=asset_path, - modified=modified, - library_id=self.library.id, - assets=[dict( - catalog=catalog, - metadata=data.get('data', {}), - description=data['description'], - tags=[], - type=self.data_type, - name=data['name']) - ] - ) - - return asset_info + shot = self.get_shot(shot=shot, sequence=sequence) + if shot: + return shot + else: + path = f"data/projects/{self.get_id(project)}/shots" + return gazu.client.post(path, params) - def fetch(self): - """Gather in a list all assets found in the folder""" - print(f'Fetch Assets for {self.library.name}') - - self.connect() - - template_file = Template(self.template_file) - template_name = Template(self.template_name) - - project = gazu.client.fetch_first('projects', {'name': self.project_name}) - entity_types = gazu.client.fetch_all('entity-types') - entity_types_ids = {e['id']: e['name'] for e in entity_types} - - cache = self.read_cache() - - for asset_data in gazu.asset.all_assets_for_project(project): - asset_data['entity_type_name'] = entity_types_ids[asset_data.pop('entity_type_id')] - asset_name = asset_data['name'] - - asset_field_data = dict(asset_name=asset_name, type=asset_data['entity_type_name'], source_directory=self.source_directory) - - try: - asset_field_data.update(template_name.parse(asset_name)) - except Exception: - print(f'Warning: Could not parse {asset_name} with template {template_name}') - - asset_path = template_file.find(asset_field_data) - if not asset_path: - print(f'Warning: Could not find file for {template_file.format(asset_field_data)}') - continue - - - asset_path = self.prop_rel_path(asset_path, 'source_directory') - asset_cache_data = dict( - catalog=asset_data['entity_type_name'].title(), - metadata=asset_data.get('data', {}), - description=asset_data['description'], - tags=[], - type=self.data_type, - name=asset_data['name'] - ) - - cache.add_asset_cache(asset_cache_data, filepath=asset_path) - - return cache \ No newline at end of file + def draw_prefs(self, layout): + layout.prop(self, 'url', text='Url') + layout.prop(self, 'login', text='Login') + layout.prop(self, 'password', text='Password') \ No newline at end of file diff --git a/resources/trackers/tracker.py b/resources/trackers/tracker.py index 4334387..8a3733f 100644 --- a/resources/trackers/tracker.py +++ b/resources/trackers/tracker.py @@ -1,4 +1,9 @@ +import bpy from bpy.types import PropertyGroup +from bpy.props import PointerProperty, StringProperty +from vse_toolbox.file_utils import norm_str + class Tracker(PropertyGroup): - pass \ No newline at end of file + pass + \ No newline at end of file diff --git a/sequencer_utils.py b/sequencer_utils.py index 5c26a10..7497425 100644 --- a/sequencer_utils.py +++ b/sequencer_utils.py @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-or-later -import bpy import re - -from bpy.app.handlers import persistent from pathlib import Path + +import bpy +from bpy.app.handlers import persistent + from vse_toolbox.bl_utils import get_scene_settings, get_strip_settings from vse_toolbox.constants import SOUND_SUFFIXES @@ -122,9 +123,31 @@ def set_channels(): for i, c in enumerate(items.keys(), start=1): scn.sequence_editor.channels[i].name = c.title() -def render_strips(strips): +def get_strip_render_path(strip, template): + return template.format(strip_name=strip.name, ext=Path(scn.render.frame_path()).suffix) + +def render_strips(strips, template): + scn = bpy.context.scene + scene_start = scn.frame_start + scene_end = scn.frame_end + for strip in strips: - print(strip.name) + #print(render_template, strip.name, path) + + scn.frame_start = strip.frame_final_start + scn.frame_end = strip.frame_final_end - 1 + + ## 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) + #print(scn.render.filepath) + print(f'Render Strip to {scn.render.filepath}') + #bpy.ops.render.render(animation=True) + bpy.ops.render.opengl(animation=True, sequencer=True) + + + scn.frame_start = scene_start + scn.frame_end = scene_end def import_edit(filepath, adapter="cmx_3600", clean_sequencer=False): import opentimelineio as otio