From 6c2204c92d577a9cddc9533b13d16fd2829f1974 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 12 Jan 2022 18:44:33 +0100 Subject: [PATCH 01/26] added ability to hide publish if plugin need it --- openpype/tools/pyblish_pype/window.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/tools/pyblish_pype/window.py b/openpype/tools/pyblish_pype/window.py index fdd2d80e23c..edcf6f53b6b 100644 --- a/openpype/tools/pyblish_pype/window.py +++ b/openpype/tools/pyblish_pype/window.py @@ -909,6 +909,13 @@ def on_about_to_process(self, plugin, instance): self.tr("Processing"), plugin_item.data(QtCore.Qt.DisplayRole) )) + visibility = True + if hasattr(plugin, "hide_ui_on_process") and plugin.hide_ui_on_process: + visibility = False + + if self.isVisible() != visibility: + self.setVisible(visibility) + def on_plugin_action_menu_requested(self, pos): """The user right-clicked on a plug-in __________ From 365368554f4ebf6d34ff6139e4193de4edd022d4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 13 Jan 2022 10:38:39 +0100 Subject: [PATCH 02/26] flame: starting render utlis modul --- openpype/hosts/flame/api/render_utils.py | 46 ++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 openpype/hosts/flame/api/render_utils.py diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py new file mode 100644 index 00000000000..01efc9e5b9a --- /dev/null +++ b/openpype/hosts/flame/api/render_utils.py @@ -0,0 +1,46 @@ +import os + +SHARED_PRESET_PATH = '/opt/Autodesk/shared/export/presets/file_sequence' +SHARED_PRESETS = ['Default Jpeg'] + [ + preset[:-4] for preset in os.listdir(SHARED_PRESET_PATH)] + + +def export_clip(export_path, clip, export_preset, **kwargs): + import flame + + # Set exporter + exporter = flame.PyExporter() + exporter.foreground = True + exporter.export_between_marks = True + + if "in_mark" not in kwargs.keys(): + exporter.export_between_marks = False + + # Duplicate the clip to avoid modifying the original clip + duplicate_clip = flame.duplicate(clip) + + # Set export preset path + if export_preset == 'Default Jpeg': + # Get default export preset path + preset_dir = flame.PyExporter.get_presets_dir( + flame.PyExporter.PresetVisibility.Autodesk, + flame.PyExporter.PresetType.Image_Sequence) + export_preset_path = os.path.join( + preset_dir, "Jpeg", "Jpeg (8-bit).xml") + else: + export_preset_path = os.path.join( + SHARED_PRESET_PATH, export_preset + '.xml') + + try: + if kwargs.get("in_mark") and kwargs.get("out_mark"): + duplicate_clip.in_mark = int(kwargs["in_mark"]) + duplicate_clip.in_mark = int(kwargs["out_mark"]) + + exporter.export(duplicate_clip, export_preset_path, export_path) + finally: + print('Exported: {} at {}-{}'.format( + clip.name.get_value(), + duplicate_clip.in_mark, + duplicate_clip.out_mark + )) + flame.delete(duplicate_clip) \ No newline at end of file From aa19d699d3283c7db94d1de5d13a53700bf011a8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 13 Jan 2022 12:03:19 +0100 Subject: [PATCH 03/26] flame: updating render_utils modul --- openpype/hosts/flame/api/render_utils.py | 88 ++++++++++++++++++++---- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index 01efc9e5b9a..d2e312785fd 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -1,41 +1,99 @@ import os -SHARED_PRESET_PATH = '/opt/Autodesk/shared/export/presets/file_sequence' -SHARED_PRESETS = ['Default Jpeg'] + [ - preset[:-4] for preset in os.listdir(SHARED_PRESET_PATH)] +SHARED_PRESET_PATH = '/opt/Autodesk/shared/export/presets' def export_clip(export_path, clip, export_preset, **kwargs): + """Flame exported wrapper + + Args: + export_path (str): exporting directory path + clip (PyClip): flame api object + export_preset (str): name of exporting preset xml file + + Kwargs: + export_type (str)[optional]: name of export type folder + thumb_frame_number (int)[optional]: source frame number + in_mark (int)[optional]: cut in mark + out_mark (int)[optional]: cut out mark + + Raises: + KeyError: Missing input kwarg `thumb_frame_number` + in case `thumbnail` in `export_preset` + KeyError: Missing input kwarg `export_type` + in case of other `export_preset` then `thumbnail` + FileExistsError: Missing export preset in shared folder + """ import flame + in_mark = out_mark = None + # Set exporter exporter = flame.PyExporter() exporter.foreground = True exporter.export_between_marks = True - if "in_mark" not in kwargs.keys(): - exporter.export_between_marks = False - # Duplicate the clip to avoid modifying the original clip duplicate_clip = flame.duplicate(clip) - # Set export preset path - if export_preset == 'Default Jpeg': - # Get default export preset path + if export_preset == 'thumbnail': + thumb_frame_number = kwargs.get("thumb_frame_number") + # make sure it exists in kwargs + if not thumb_frame_number: + raise KeyError( + "Missing key `thumb_frame_number` in input kwargs") + + in_mark = int(thumb_frame_number) + out_mark = int(thumb_frame_number) + 1 + + # In case Thumbnail is needed preset_dir = flame.PyExporter.get_presets_dir( flame.PyExporter.PresetVisibility.Autodesk, flame.PyExporter.PresetType.Image_Sequence) export_preset_path = os.path.join( preset_dir, "Jpeg", "Jpeg (8-bit).xml") + else: + # In case other output is needed + # get compulsory kwargs + export_type = kwargs.get("export_type") + # make sure it exists in kwargs + if not export_type: + raise KeyError( + "Missing key `export_type` in input kwargs") + + # create full shared preset path + shared_preset_dir = os.path.join( + SHARED_PRESET_PATH, export_type + ) + + # check if export preset is available in shared presets + shared_presets = [ + preset[:-4] for preset in os.listdir(shared_preset_dir)] + if export_preset not in shared_presets: + raise FileExistsError( + "Missing preset file `{}` in `{}`".format( + export_preset, + shared_preset_dir + )) + export_preset_path = os.path.join( - SHARED_PRESET_PATH, export_preset + '.xml') + shared_preset_dir, export_preset + '.xml') - try: + # check if mark in/out is set in kwargs if kwargs.get("in_mark") and kwargs.get("out_mark"): - duplicate_clip.in_mark = int(kwargs["in_mark"]) - duplicate_clip.in_mark = int(kwargs["out_mark"]) + in_mark = int(kwargs["in_mark"]) + out_mark = int(kwargs["out_mark"]) + else: + exporter.export_between_marks = False + try: + # set in and out marks if they are available + if in_mark and out_mark: + duplicate_clip.in_mark = in_mark + duplicate_clip.out_mark = out_mark + + # export with exporter exporter.export(duplicate_clip, export_preset_path, export_path) finally: print('Exported: {} at {}-{}'.format( @@ -43,4 +101,6 @@ def export_clip(export_path, clip, export_preset, **kwargs): duplicate_clip.in_mark, duplicate_clip.out_mark )) - flame.delete(duplicate_clip) \ No newline at end of file + + # delete duplicated clip it is not needed anymore + flame.delete(duplicate_clip) From 574466f6dcea84d6a09bff0ac13493d4a5179c36 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 13 Jan 2022 12:52:22 +0100 Subject: [PATCH 04/26] flame: adding export clip to api --- openpype/hosts/flame/api/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 308682b884b..fce59af5063 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -59,6 +59,9 @@ file_extensions, work_root ) +from .render_utils import ( + export_clip +) __all__ = [ # constants @@ -119,5 +122,8 @@ "current_file", "has_unsaved_changes", "file_extensions", - "work_root" + "work_root", + + # render utils + "export_clip" ] From a2d414c64657d11ddbbacfd546db3613ee91ab85 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 13 Jan 2022 12:53:02 +0100 Subject: [PATCH 05/26] flame: adding exporter plugin --- .../publish/extract_subset_resources.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 openpype/hosts/flame/plugins/publish/extract_subset_resources.py diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py new file mode 100644 index 00000000000..ffa01eb1b3c --- /dev/null +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -0,0 +1,91 @@ +import os +import pyblish.api +import openpype.api +from openpype.hosts.flame import api as opfapi + + +class ExtractSubsetResources(openpype.api.Extractor): + """ + Extractor for transcoding files from Flame clip + """ + + label = "Extract subset resources" + order = pyblish.api.CollectorOrder + 0.49 + families = ["clip"] + hosts = ["flame"] + + # hide publisher during exporting + hide_ui_on_process = True + + export_presets_mapping = { + "thumbnail": { + "ext": "jpg", + "uniqueName": "thumbnail" + }, + "OpenEXR (16-bit fp DWAA)_custom": { + "ext": "exr", + "preset_type": "file_sequence", + "uniqueName": "exr16fpdwaa" + }, + "QuickTime (H.264 1080p 8Mbits)_custom": { + "ext": "mov", + "preset_type": "movie_file", + "uniqueName": "ftrackpreview" + } + } + + def process(self, instance): + # create representation data + if "representations" not in instance.data: + instance.data["representations"] = [] + + name = instance.data["name"] + clip = instance.data["flameSourceClip"] + staging_dir = self.staging_dir(instance) + + # prepare full export path + export_dir_path = os.path.join( + staging_dir, name + ) + # loop all preset names and + for preset_name, preset_config in self.export_presets_mapping: + kwargs = {} + unique_name = preset_config["uniqueName"] + preset_type = None + + # define kwargs based on preset type + if "thumbnail" in preset_name: + kwargs["thumb_frame_number"] = 2 + else: + preset_type = preset_config["preset_type"] + kwargs.update({ + "in_mark": 2, + "out_mark": 5, + "preset_type": preset_type + }) + + _export_dir_path = os.path.join( + export_dir_path, unique_name + ) + # export + opfapi.export_clip( + _export_dir_path, clip, preset_name, **kwargs) + + # create representation data + representation_data = { + 'name': unique_name, + 'ext': preset_config["ext"], + "stagingDir": _export_dir_path, + } + + files = os.listdir(_export_dir_path) + + if preset_type and preset_type == "movie_file": + representation_data["files"] = files + else: + representation_data["files"] = files.pop() + + instance.data["representations"].append(representation_data) + + self.log.info("Added representation: {}".format( + representation_data)) From 9c20580d699c77ad5f5f3462050069172145d3dd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 13 Jan 2022 15:32:40 +0100 Subject: [PATCH 06/26] flame: export clip to correct frame range --- .../publish/extract_subset_resources.py | 55 ++++++++++++------- .../plugins/publish/precollect_instances.py | 2 +- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index ffa01eb1b3c..ea782845ef3 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -15,7 +15,7 @@ class ExtractSubsetResources(openpype.api.Extractor): hosts = ["flame"] # hide publisher during exporting - hide_ui_on_process = True + # hide_ui_on_process = True export_presets_mapping = { "thumbnail": { @@ -39,51 +39,66 @@ def process(self, instance): if "representations" not in instance.data: instance.data["representations"] = [] - name = instance.data["name"] - clip = instance.data["flameSourceClip"] + source_first_frame = instance.data["sourceFirstFrame"] + source_start_handles = instance.data["sourceStartH"] + source_end_handles = instance.data["sourceEndH"] + source_duration_handles = ( + source_end_handles - source_start_handles) + 1 + + clip_data = instance.data["flameSourceClip"] + clip = clip_data["PyClip"] + + in_mark = (source_start_handles - source_first_frame) + 1 + out_mark = in_mark + source_duration_handles + staging_dir = self.staging_dir(instance) - # prepare full export path - export_dir_path = os.path.join( - staging_dir, name - ) # loop all preset names and - for preset_name, preset_config in self.export_presets_mapping: + for preset_name, preset_config in self.export_presets_mapping.items(): kwargs = {} unique_name = preset_config["uniqueName"] preset_type = None # define kwargs based on preset type if "thumbnail" in preset_name: - kwargs["thumb_frame_number"] = 2 + kwargs["thumb_frame_number"] = in_mark + ( + source_duration_handles / 2) else: preset_type = preset_config["preset_type"] kwargs.update({ - "in_mark": 2, - "out_mark": 5, - "preset_type": preset_type + "in_mark": in_mark, + "out_mark": out_mark, + "export_type": preset_type }) - _export_dir_path = os.path.join( - export_dir_path, unique_name + export_dir_path = os.path.join( + staging_dir, unique_name ) + os.makedirs(export_dir_path) + # export opfapi.export_clip( - _export_dir_path, clip, preset_name, **kwargs) + export_dir_path, clip, preset_name, **kwargs) # create representation data representation_data = { 'name': unique_name, 'ext': preset_config["ext"], - "stagingDir": _export_dir_path, + "stagingDir": export_dir_path, } - files = os.listdir(_export_dir_path) + files = os.listdir(export_dir_path) - if preset_type and preset_type == "movie_file": - representation_data["files"] = files - else: + # add files to represetation but add + # imagesequence as list + if ( + preset_type + and preset_type == "movie_file" + or preset_name == "thumbnail" + ): representation_data["files"] = files.pop() + else: + representation_data["files"] = files instance.data["representations"].append(representation_data) diff --git a/openpype/hosts/flame/plugins/publish/precollect_instances.py b/openpype/hosts/flame/plugins/publish/precollect_instances.py index b4b2ebf63fa..bda583fe8e4 100644 --- a/openpype/hosts/flame/plugins/publish/precollect_instances.py +++ b/openpype/hosts/flame/plugins/publish/precollect_instances.py @@ -92,7 +92,7 @@ def process(self, context): "publish": marker_data["publish"], "fps": self.fps, "flameSourceClip": source_clip, - "sourceFirstFrame": first_frame, + "sourceFirstFrame": int(first_frame), "path": file_path }) From 9fa024daae060fbbfc88aecb7b9639bf2cc7c087 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 13 Jan 2022 17:45:58 +0100 Subject: [PATCH 07/26] flame: hide gui when processing plugin --- .../hosts/flame/plugins/publish/extract_subset_resources.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index ea782845ef3..6061c807625 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -10,12 +10,12 @@ class ExtractSubsetResources(openpype.api.Extractor): """ label = "Extract subset resources" - order = pyblish.api.CollectorOrder + 0.49 + order = pyblish.api.ExtractorOrder families = ["clip"] hosts = ["flame"] # hide publisher during exporting - # hide_ui_on_process = True + hide_ui_on_process = True export_presets_mapping = { "thumbnail": { From 4a230b710ea605fce9b9edadb455d0277301032a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 11:53:04 +0100 Subject: [PATCH 08/26] flame: add function to get flame version and root install path --- openpype/hosts/flame/api/__init__.py | 6 +++++- openpype/hosts/flame/api/utils.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index fce59af5063..e7590bb36e4 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -30,7 +30,9 @@ get_padding_from_path ) from .utils import ( - setup + setup, + get_flame_version, + get_flame_install_root ) from .pipeline import ( install, @@ -107,6 +109,8 @@ # utils "setup", + "get_flame_version", + "get_flame_install_root", # menu "FlameMenuProjectConnect", diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index b9899900f5d..0e40e40aa7e 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -125,3 +125,18 @@ def setup(env=None): _sync_utility_scripts(env) log.info("Flame OpenPype wrapper has been installed") + + +def get_flame_version(): + import flame + + return { + "full": flame.get_version(), + "major": flame.get_version_major(), + "minor": flame.get_version_minor(), + "patch": flame.get_version_patch() + } + + +def get_flame_install_root(): + return "/opt/Autodesk" \ No newline at end of file From cc20a22e3ad70639d58c55e65e68b67e24264fca Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 11:53:35 +0100 Subject: [PATCH 09/26] flame: add function to maintain object duplication --- openpype/hosts/flame/api/__init__.py | 4 +++- openpype/hosts/flame/api/lib.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index e7590bb36e4..7f516fb11f7 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -27,7 +27,8 @@ get_clips_in_reels, get_reformated_path, get_frame_from_path, - get_padding_from_path + get_padding_from_path, + maintained_object_duplication ) from .utils import ( setup, @@ -93,6 +94,7 @@ "get_reformated_path", "get_frame_from_path", "get_padding_from_path", + "maintained_object_duplication", # pipeline "install", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index b963a1cb39b..800afebf412 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -675,3 +675,26 @@ def get_frame_from_path(path): return found.pop() else: return None + + + +@contextlib.contextmanager +def maintained_object_duplication(item): + """Maintain input item duplication + + Attributes: + item (any flame.PyObject): python api object + + Yield: + duplicate input PyObject type + """ + import flame + # Duplicate the clip to avoid modifying the original clip + duplicate = flame.duplicate(item) + + try: + # do the operation on selected segments + yield duplicate + finally: + # delete the item at the end + flame.delete(duplicate) From f6ab7f2cbaef91afa7ca5a35f3a540c22b7529e6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 12:06:35 +0100 Subject: [PATCH 10/26] flame: adding settings for `ExtractSubsetResources` plugin --- .../defaults/project_settings/flame.json | 12 ++++ .../projects_schema/schema_project_flame.json | 55 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index b6fbdecc955..ed54d631be6 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -16,5 +16,17 @@ "handleStart": 10, "handleEnd": 10 } + }, + "publish": { + "ExtractSubsetResources": { + "export_presets_mapping": { + "exr16fpdwaa": { + "ext": "exr", + "xmlPresetDir": "", + "xmlPresetFile": "OpenEXR (16-bit fp DWAA).xml", + "representationTags": [] + } + } + } } } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index d713c376207..6ca5fc049d1 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -119,6 +119,61 @@ ] } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "ExtractSubsetResources", + "label": "Extract Subset Resources", + "is_group": true, + "children": [ + { + "key": "export_presets_mapping", + "label": "Export presets mapping", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "xmlPresetFile", + "label": "XML preset file (with ext)", + "type": "text" + }, + { + "key": "xmlPresetDir", + "label": "XML preset folder (optional)", + "type": "text" + }, + { + "type": "separator" + }, + { + "type": "list", + "key": "representationTags", + "label": "Add representation tags", + "object_type": { + "type": "text", + "multiline": false + } + } + ] + } + } + ] + } + ] } ] } From 0e96a2e3b1d4f0481ec425ecfa0275e885185099 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 12:50:55 +0100 Subject: [PATCH 11/26] flame: update utils modules --- openpype/hosts/flame/api/__init__.py | 6 +- openpype/hosts/flame/api/render_utils.py | 135 +++++++++++++---------- openpype/hosts/flame/api/utils.py | 2 +- 3 files changed, 82 insertions(+), 61 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 7f516fb11f7..656ba11617c 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -63,7 +63,8 @@ work_root ) from .render_utils import ( - export_clip + export_clip, + get_preset_path_by_xml_name ) __all__ = [ @@ -131,5 +132,6 @@ "work_root", # render utils - "export_clip" + "export_clip", + "get_preset_path_by_xml_name" ] diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index d2e312785fd..1cc94f6548d 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -1,18 +1,15 @@ import os -SHARED_PRESET_PATH = '/opt/Autodesk/shared/export/presets' - -def export_clip(export_path, clip, export_preset, **kwargs): +def export_clip(export_path, clip, preset_path, **kwargs): """Flame exported wrapper Args: export_path (str): exporting directory path clip (PyClip): flame api object - export_preset (str): name of exporting preset xml file + preset_path (str): full export path to xml file Kwargs: - export_type (str)[optional]: name of export type folder thumb_frame_number (int)[optional]: source frame number in_mark (int)[optional]: cut in mark out_mark (int)[optional]: cut out mark @@ -20,8 +17,6 @@ def export_clip(export_path, clip, export_preset, **kwargs): Raises: KeyError: Missing input kwarg `thumb_frame_number` in case `thumbnail` in `export_preset` - KeyError: Missing input kwarg `export_type` - in case of other `export_preset` then `thumbnail` FileExistsError: Missing export preset in shared folder """ import flame @@ -33,11 +28,8 @@ def export_clip(export_path, clip, export_preset, **kwargs): exporter.foreground = True exporter.export_between_marks = True - # Duplicate the clip to avoid modifying the original clip - duplicate_clip = flame.duplicate(clip) - - if export_preset == 'thumbnail': - thumb_frame_number = kwargs.get("thumb_frame_number") + if kwargs.get("thumb_frame_number"): + thumb_frame_number = kwargs["thumb_frame_number"] # make sure it exists in kwargs if not thumb_frame_number: raise KeyError( @@ -46,61 +38,88 @@ def export_clip(export_path, clip, export_preset, **kwargs): in_mark = int(thumb_frame_number) out_mark = int(thumb_frame_number) + 1 - # In case Thumbnail is needed - preset_dir = flame.PyExporter.get_presets_dir( - flame.PyExporter.PresetVisibility.Autodesk, - flame.PyExporter.PresetType.Image_Sequence) - export_preset_path = os.path.join( - preset_dir, "Jpeg", "Jpeg (8-bit).xml") - + elif kwargs.get("in_mark") and kwargs.get("out_mark"): + in_mark = int(kwargs["in_mark"]) + out_mark = int(kwargs["out_mark"]) else: - # In case other output is needed - # get compulsory kwargs - export_type = kwargs.get("export_type") - # make sure it exists in kwargs - if not export_type: - raise KeyError( - "Missing key `export_type` in input kwargs") - - # create full shared preset path - shared_preset_dir = os.path.join( - SHARED_PRESET_PATH, export_type - ) - - # check if export preset is available in shared presets - shared_presets = [ - preset[:-4] for preset in os.listdir(shared_preset_dir)] - if export_preset not in shared_presets: - raise FileExistsError( - "Missing preset file `{}` in `{}`".format( - export_preset, - shared_preset_dir - )) - - export_preset_path = os.path.join( - shared_preset_dir, export_preset + '.xml') - - # check if mark in/out is set in kwargs - if kwargs.get("in_mark") and kwargs.get("out_mark"): - in_mark = int(kwargs["in_mark"]) - out_mark = int(kwargs["out_mark"]) - else: - exporter.export_between_marks = False + exporter.export_between_marks = False try: # set in and out marks if they are available if in_mark and out_mark: - duplicate_clip.in_mark = in_mark - duplicate_clip.out_mark = out_mark + clip.in_mark = in_mark + clip.out_mark = out_mark # export with exporter - exporter.export(duplicate_clip, export_preset_path, export_path) + exporter.export(clip, preset_path, export_path) finally: print('Exported: {} at {}-{}'.format( clip.name.get_value(), - duplicate_clip.in_mark, - duplicate_clip.out_mark + clip.in_mark, + clip.out_mark )) - # delete duplicated clip it is not needed anymore - flame.delete(duplicate_clip) + +def get_preset_path_by_xml_name(xml_preset_name): + def _search_path(root): + output = [] + for root, dirs, files in os.walk(root): + for f in files: + if f != xml_preset_name: + continue + file_path = os.path.join(root, f) + output.append(file_path) + return output + + def _validate_results(results): + if results and len(results) == 1: + return results.pop() + elif results and len(results) > 1: + print(( + "More matching presets for `{}`: /n" + "{}").format(xml_preset_name, results)) + return results.pop() + else: + return None + + from .utils import ( + get_flame_install_root, + get_flame_version + ) + + # get actual flame version and install path + _version = get_flame_version()["full"] + _install_root = get_flame_install_root() + + # search path templates + shared_search_root = "{install_root}/shared/export/presets" + install_search_root = ( + "{install_root}/presets/{version}/export/presets/flame") + + # fill templates + shared_search_root = shared_search_root.format( + install_root=_install_root + ) + install_search_root = install_search_root.format( + install_root=_install_root, + version=_version + ) + + # get search results + shared_results = _search_path(shared_search_root) + installed_results = _search_path(install_search_root) + + # first try to return shared results + shared_preset_path = _validate_results(shared_results) + + if shared_preset_path: + return os.path.dirname(shared_preset_path) + + # then try installed results + installed_preset_path = _validate_results(installed_results) + + if installed_preset_path: + return os.path.dirname(installed_preset_path) + + # if nothing found then return None + return False diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index 0e40e40aa7e..99393713586 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -139,4 +139,4 @@ def get_flame_version(): def get_flame_install_root(): - return "/opt/Autodesk" \ No newline at end of file + return "/opt/Autodesk" From 183acf4bd3b34dd046401434c67932ad9e8b6050 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 12:51:48 +0100 Subject: [PATCH 12/26] flame: update export plugin with more dynamic preset path abstraction --- .../publish/extract_subset_resources.py | 165 +++++++++++------- 1 file changed, 101 insertions(+), 64 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 6061c807625..3a8fd631d8f 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -1,4 +1,5 @@ import os +from copy import deepcopy import pyblish.api import openpype.api from openpype.hosts.flame import api as opfapi @@ -14,25 +15,29 @@ class ExtractSubsetResources(openpype.api.Extractor): families = ["clip"] hosts = ["flame"] - # hide publisher during exporting - hide_ui_on_process = True - - export_presets_mapping = { + # plugin defaults + default_presets = { "thumbnail": { "ext": "jpg", - "uniqueName": "thumbnail" - }, - "OpenEXR (16-bit fp DWAA)_custom": { - "ext": "exr", - "preset_type": "file_sequence", - "uniqueName": "exr16fpdwaa" + "xmlPresetFile": "Jpeg (8-bit).xml", + "xmlPresetDir": "", + "representationTags": ["thumbnail"] }, - "QuickTime (H.264 1080p 8Mbits)_custom": { + "ftrackpreview": { "ext": "mov", - "preset_type": "movie_file", - "uniqueName": "ftrackpreview" + "xmlPresetFile": "Apple iPad (1920x1080).xml", + "xmlPresetDir": "", + "representationTags": [ + "review", + "delete" + ] } } + # hide publisher during exporting + hide_ui_on_process = True + + # settings + export_presets_mapping = {} def process(self, instance): # create representation data @@ -53,54 +58,86 @@ def process(self, instance): staging_dir = self.staging_dir(instance) - # loop all preset names and - for preset_name, preset_config in self.export_presets_mapping.items(): - kwargs = {} - unique_name = preset_config["uniqueName"] - preset_type = None - - # define kwargs based on preset type - if "thumbnail" in preset_name: - kwargs["thumb_frame_number"] = in_mark + ( - source_duration_handles / 2) - else: - preset_type = preset_config["preset_type"] - kwargs.update({ - "in_mark": in_mark, - "out_mark": out_mark, - "export_type": preset_type - }) - - export_dir_path = os.path.join( - staging_dir, unique_name - ) - os.makedirs(export_dir_path) - - # export - opfapi.export_clip( - export_dir_path, clip, preset_name, **kwargs) - - # create representation data - representation_data = { - 'name': unique_name, - 'ext': preset_config["ext"], - "stagingDir": export_dir_path, - } - - files = os.listdir(export_dir_path) - - # add files to represetation but add - # imagesequence as list - if ( - preset_type - and preset_type == "movie_file" - or preset_name == "thumbnail" - ): - representation_data["files"] = files.pop() - else: - representation_data["files"] = files - - instance.data["representations"].append(representation_data) - - self.log.info("Added representation: {}".format( - representation_data)) + # add default preset type for thumbnail and reviewable video + # update them with settings and overide in case the same + # are found in there + export_presets = deepcopy(self.default_presets) + export_presets.update(self.export_presets_mapping) + + # with maintained duplication loop all presets + with opfapi.maintained_object_duplication(clip) as duplclip: + # loop all preset names and + for unique_name, preset_config in export_presets.items(): + kwargs = {} + preset_file = preset_config["xmlPresetFile"] + preset_dir = preset_config["xmlPresetDir"] + + # validate xml preset file is filled + if preset_file == "": + raise ValueError( + ("Check Settings for {} preset: " + "`XML preset file` is not filled").format( + unique_name) + ) + + # resolve xml preset dir if not filled + if preset_dir == "": + preset_dir = opfapi.get_preset_path_by_xml_name( + preset_file) + + if not preset_dir: + raise ValueError( + ("Check Settings for {} preset: " + "`XML preset file` {} is not found").format( + unique_name, preset_file) + ) + + # create preset path + preset_path = os.path.join( + preset_dir, preset_file + ) + + # define kwargs based on preset type + if "thumbnail" in unique_name: + kwargs["thumb_frame_number"] = in_mark + ( + source_duration_handles / 2) + else: + kwargs.update({ + "in_mark": in_mark, + "out_mark": out_mark + }) + + export_dir_path = os.path.join( + staging_dir, unique_name + ) + os.makedirs(export_dir_path) + + # export + opfapi.export_clip( + export_dir_path, duplclip, preset_path, **kwargs) + + # create representation data + representation_data = { + "name": unique_name, + "outputName": unique_name, + "ext": preset_config["ext"], + "stagingDir": export_dir_path, + "tags": preset_config["representationTags"] + } + + files = os.listdir(export_dir_path) + + # add files to represetation but add + # imagesequence as list + if ( + "movie_file" in preset_path + or unique_name == "thumbnail" + ): + representation_data["files"] = files.pop() + else: + representation_data["files"] = files + + instance.data["representations"].append(representation_data) + + self.log.info("Added representation: {}".format( + representation_data)) From aa39f98ae626b702d02b5b57b786ee22bd0c8252 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 12:57:26 +0100 Subject: [PATCH 13/26] flame: add bool to settings to control if range will be added to repres --- .../hosts/flame/plugins/publish/extract_subset_resources.py | 2 ++ openpype/settings/defaults/project_settings/flame.json | 1 + .../schemas/projects_schema/schema_project_flame.json | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 3a8fd631d8f..b2a737cbcb9 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -21,12 +21,14 @@ class ExtractSubsetResources(openpype.api.Extractor): "ext": "jpg", "xmlPresetFile": "Jpeg (8-bit).xml", "xmlPresetDir": "", + "representationAddRange": False, "representationTags": ["thumbnail"] }, "ftrackpreview": { "ext": "mov", "xmlPresetFile": "Apple iPad (1920x1080).xml", "xmlPresetDir": "", + "representationAddRange": False, "representationTags": [ "review", "delete" diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index ed54d631be6..dfecd8a12ec 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -24,6 +24,7 @@ "ext": "exr", "xmlPresetDir": "", "xmlPresetFile": "OpenEXR (16-bit fp DWAA).xml", + "representationAddRange": false, "representationTags": [] } } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 6ca5fc049d1..8ad2b116169 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -159,6 +159,11 @@ { "type": "separator" }, + { + "type": "boolean", + "key": "representationAddRange", + "label": "Add frame range to representation" + }, { "type": "list", "key": "representationTags", From 26c3ba7e1be4e7a94c3cf8d6869e813dd716bc25 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 17:37:19 +0100 Subject: [PATCH 14/26] flame: add frame ranges to representation --- .../plugins/publish/extract_subset_resources.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index b2a737cbcb9..34953094093 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -28,7 +28,7 @@ class ExtractSubsetResources(openpype.api.Extractor): "ext": "mov", "xmlPresetFile": "Apple iPad (1920x1080).xml", "xmlPresetDir": "", - "representationAddRange": False, + "representationAddRange": True, "representationTags": [ "review", "delete" @@ -46,6 +46,9 @@ def process(self, instance): if "representations" not in instance.data: instance.data["representations"] = [] + frame_start = instance.data["frameStart"] + handle_start = instance.data["handleStart"] + frame_start_handle = frame_start - handle_start source_first_frame = instance.data["sourceFirstFrame"] source_start_handles = instance.data["sourceStartH"] source_end_handles = instance.data["sourceEndH"] @@ -139,6 +142,15 @@ def process(self, instance): else: representation_data["files"] = files + # add frame range + if preset_config["representationAddRange"]: + representation_data.update({ + "frameStart": frame_start_handle, + "frameEnd": ( + frame_start_handle + source_duration_handles), + "fps": instance.data["fps"] + }) + instance.data["representations"].append(representation_data) self.log.info("Added representation: {}".format( From ea469e213031bedc412a9368e4668e6b0d18bc98 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 17:55:43 +0100 Subject: [PATCH 15/26] flame: fixing extract exporter --- .../plugins/publish/extract_subset_resources.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 34953094093..8bdcf989b61 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -76,6 +76,7 @@ def process(self, instance): kwargs = {} preset_file = preset_config["xmlPresetFile"] preset_dir = preset_config["xmlPresetDir"] + repre_tags = preset_config["representationTags"] # validate xml preset file is filled if preset_file == "": @@ -98,9 +99,9 @@ def process(self, instance): ) # create preset path - preset_path = os.path.join( + preset_path = str(os.path.join( preset_dir, preset_file - ) + )) # define kwargs based on preset type if "thumbnail" in unique_name: @@ -112,9 +113,9 @@ def process(self, instance): "out_mark": out_mark }) - export_dir_path = os.path.join( + export_dir_path = str(os.path.join( staging_dir, unique_name - ) + )) os.makedirs(export_dir_path) # export @@ -127,7 +128,7 @@ def process(self, instance): "outputName": unique_name, "ext": preset_config["ext"], "stagingDir": export_dir_path, - "tags": preset_config["representationTags"] + "tags": repre_tags } files = os.listdir(export_dir_path) @@ -153,5 +154,9 @@ def process(self, instance): instance.data["representations"].append(representation_data) + # add review family if found in tags + if "review" in repre_tags: + instance.data["families"].append("review") + self.log.info("Added representation: {}".format( representation_data)) From 25b54be8727d35a608e64236f1189468ca9fe4ae Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 17:56:16 +0100 Subject: [PATCH 16/26] flame: adding host to some extract plugins --- openpype/plugins/publish/extract_burnin.py | 3 ++- openpype/plugins/publish/extract_otio_audio_tracks.py | 2 +- openpype/plugins/publish/extract_otio_review.py | 2 +- openpype/plugins/publish/extract_otio_trimming_video.py | 2 +- openpype/plugins/publish/extract_review.py | 3 ++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index 459c66ee432..7ff1b246895 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -48,7 +48,8 @@ class ExtractBurnin(openpype.api.Extractor): "tvpaint", "webpublisher", "aftereffects", - "photoshop" + "photoshop", + "flame" # "resolve" ] optional = True diff --git a/openpype/plugins/publish/extract_otio_audio_tracks.py b/openpype/plugins/publish/extract_otio_audio_tracks.py index be0bae5cdce..00c1748cdc4 100644 --- a/openpype/plugins/publish/extract_otio_audio_tracks.py +++ b/openpype/plugins/publish/extract_otio_audio_tracks.py @@ -19,7 +19,7 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): order = pyblish.api.ExtractorOrder - 0.44 label = "Extract OTIO Audio Tracks" - hosts = ["hiero", "resolve"] + hosts = ["hiero", "resolve", "flame"] # FFmpeg tools paths ffmpeg_path = get_ffmpeg_tool_path("ffmpeg") diff --git a/openpype/plugins/publish/extract_otio_review.py b/openpype/plugins/publish/extract_otio_review.py index ed2ba017d5c..79d5b2fc8f3 100644 --- a/openpype/plugins/publish/extract_otio_review.py +++ b/openpype/plugins/publish/extract_otio_review.py @@ -41,7 +41,7 @@ class ExtractOTIOReview(openpype.api.Extractor): order = api.ExtractorOrder - 0.45 label = "Extract OTIO review" families = ["review"] - hosts = ["resolve", "hiero"] + hosts = ["resolve", "hiero", "flame"] # plugin default attributes temp_file_head = "tempFile." diff --git a/openpype/plugins/publish/extract_otio_trimming_video.py b/openpype/plugins/publish/extract_otio_trimming_video.py index 3e2d39c99cb..30b57e2c696 100644 --- a/openpype/plugins/publish/extract_otio_trimming_video.py +++ b/openpype/plugins/publish/extract_otio_trimming_video.py @@ -19,7 +19,7 @@ class ExtractOTIOTrimmingVideo(openpype.api.Extractor): order = api.ExtractorOrder label = "Extract OTIO trim longer video" families = ["trim"] - hosts = ["resolve", "hiero"] + hosts = ["resolve", "hiero", "flame"] def process(self, instance): self.staging_dir = self.staging_dir(instance) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index b6c2e49385c..b27cca00851 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -51,7 +51,8 @@ class ExtractReview(pyblish.api.InstancePlugin): "tvpaint", "resolve", "webpublisher", - "aftereffects" + "aftereffects", + "flame" ] # Supported extensions From 7dbb46fcbfb1e24824f3927b86b127ff5329cc39 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 17 Jan 2022 12:29:11 +0100 Subject: [PATCH 17/26] flame: update settings, remove camel case --- .../publish/extract_subset_resources.py | 22 ++++++++++++++----- .../defaults/project_settings/flame.json | 20 +++++++++-------- .../projects_schema/schema_project_flame.json | 13 +++++++---- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 8bdcf989b61..adb3b1ae9b1 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -1,4 +1,5 @@ import os +from pprint import pformat from copy import deepcopy import pyblish.api import openpype.api @@ -35,6 +36,8 @@ class ExtractSubsetResources(openpype.api.Extractor): ] } } + keep_original_representation = False + # hide publisher during exporting hide_ui_on_process = True @@ -42,8 +45,12 @@ class ExtractSubsetResources(openpype.api.Extractor): export_presets_mapping = {} def process(self, instance): - # create representation data - if "representations" not in instance.data: + + if ( + self.keep_original_representation + and "representations" not in instance.data + or not self.keep_original_representation + ): instance.data["representations"] = [] frame_start = instance.data["frameStart"] @@ -74,9 +81,9 @@ def process(self, instance): # loop all preset names and for unique_name, preset_config in export_presets.items(): kwargs = {} - preset_file = preset_config["xmlPresetFile"] - preset_dir = preset_config["xmlPresetDir"] - repre_tags = preset_config["representationTags"] + preset_file = preset_config["xml_preset_file"] + preset_dir = preset_config["xml_preset_dir"] + repre_tags = preset_config["representation_tags"] # validate xml preset file is filled if preset_file == "": @@ -144,7 +151,7 @@ def process(self, instance): representation_data["files"] = files # add frame range - if preset_config["representationAddRange"]: + if preset_config["representation_add_range"]: representation_data.update({ "frameStart": frame_start_handle, "frameEnd": ( @@ -160,3 +167,6 @@ def process(self, instance): self.log.info("Added representation: {}".format( representation_data)) + + self.log.debug("All representations: {}".format( + pformat(instance.data["representations"]))) diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index dfecd8a12ec..c81069ef5c1 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -3,29 +3,31 @@ "CreateShotClip": { "hierarchy": "{folder}/{sequence}", "clipRename": true, - "clipName": "{track}{sequence}{shot}", + "clipName": "{sequence}{shot}", + "segmentIndex": true, "countFrom": 10, "countSteps": 10, "folder": "shots", "episode": "ep01", - "sequence": "sq01", + "sequence": "a", "track": "{_track_}", - "shot": "sh###", + "shot": "####", "vSyncOn": false, "workfileFrameStart": 1001, - "handleStart": 10, - "handleEnd": 10 + "handleStart": 5, + "handleEnd": 5 } }, "publish": { "ExtractSubsetResources": { + "keep_original_representation": false, "export_presets_mapping": { "exr16fpdwaa": { "ext": "exr", - "xmlPresetDir": "", - "xmlPresetFile": "OpenEXR (16-bit fp DWAA).xml", - "representationAddRange": false, - "representationTags": [] + "xml_preset_dir": "", + "xml_preset_file": "OpenEXR (16-bit fp DWAA).xml", + "representation_add_range": true, + "representation_tags": [] } } } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 8ad2b116169..b1b1f3539b2 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -133,6 +133,11 @@ "label": "Extract Subset Resources", "is_group": true, "children": [ + { + "type": "boolean", + "key": "keep_original_representation", + "label": "Publish clip's original media" + }, { "key": "export_presets_mapping", "label": "Export presets mapping", @@ -147,12 +152,12 @@ "type": "text" }, { - "key": "xmlPresetFile", + "key": "xml_preset_file", "label": "XML preset file (with ext)", "type": "text" }, { - "key": "xmlPresetDir", + "key": "xml_preset_dir", "label": "XML preset folder (optional)", "type": "text" }, @@ -161,12 +166,12 @@ }, { "type": "boolean", - "key": "representationAddRange", + "key": "representation_add_range", "label": "Add frame range to representation" }, { "type": "list", - "key": "representationTags", + "key": "representation_tags", "label": "Add representation tags", "object_type": { "type": "text", From d10d6c65be1a800e4146df11eb481e40a2c033e1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 17 Jan 2022 13:55:02 +0100 Subject: [PATCH 18/26] hound: classics ;) --- openpype/hosts/flame/api/lib.py | 1 - openpype/hosts/flame/api/render_utils.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 800afebf412..c76d944e909 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -677,7 +677,6 @@ def get_frame_from_path(path): return None - @contextlib.contextmanager def maintained_object_duplication(item): """Maintain input item duplication diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index 1cc94f6548d..f8dbfe2025f 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -63,7 +63,7 @@ def export_clip(export_path, clip, preset_path, **kwargs): def get_preset_path_by_xml_name(xml_preset_name): def _search_path(root): output = [] - for root, dirs, files in os.walk(root): + for root, _dirs, files in os.walk(root): for f in files: if f != xml_preset_name: continue From b0a71a86361b9cd6fc3a7532c56174f0de016f8d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 19 Jan 2022 15:40:45 +0100 Subject: [PATCH 19/26] typo --- openpype/hosts/flame/api/render_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index f8dbfe2025f..1b086646cc2 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -121,5 +121,5 @@ def _validate_results(results): if installed_preset_path: return os.path.dirname(installed_preset_path) - # if nothing found then return None + # if nothing found then return False return False From 943dfc2568c7a49cf8c98d0f3caccb5911b07d93 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 19 Jan 2022 21:01:53 +0100 Subject: [PATCH 20/26] flame: updating filename operations also otio export and utils --- openpype/hosts/flame/api/__init__.py | 12 ++-- openpype/hosts/flame/api/lib.py | 16 +++--- openpype/hosts/flame/otio/flame_export.py | 25 ++++----- openpype/hosts/flame/otio/utils.py | 56 +++++++++---------- .../publish/collect_timeline_instances.py | 2 +- 5 files changed, 52 insertions(+), 59 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 656ba11617c..8e5418c78ba 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -25,9 +25,9 @@ reset_segment_selection, get_segment_attributes, get_clips_in_reels, - get_reformated_path, - get_frame_from_path, - get_padding_from_path, + get_reformated_filename, + get_frame_from_filename, + get_padding_from_filename, maintained_object_duplication ) from .utils import ( @@ -92,9 +92,9 @@ "reset_segment_selection", "get_segment_attributes", "get_clips_in_reels", - "get_reformated_path", - "get_frame_from_path", - "get_padding_from_path", + "get_reformated_filename", + "get_frame_from_filename", + "get_padding_from_filename", "maintained_object_duplication", # pipeline diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index f524ea0ede3..f3c918caabd 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -602,7 +602,7 @@ def get_clips_in_reels(project): return output_clips -def get_reformated_path(filename, padded=True): +def get_reformated_filename(filename, padded=True): """ Return fixed python expression path @@ -613,7 +613,7 @@ def get_reformated_path(filename, padded=True): type: string with reformated path Example: - get_reformated_path("plate.1001.exr") > plate.%04d.exr + get_reformated_filename("plate.1001.exr") > plate.%04d.exr """ found = FRAME_PATTERN.search(filename) @@ -622,7 +622,7 @@ def get_reformated_path(filename, padded=True): log.info("File name is not sequence: {}".format(filename)) return filename - padding = get_padding_from_path(filename) + padding = get_padding_from_filename(filename) replacement = "%0{}d".format(padding) if padded else "%d" start_idx, end_idx = found.span(1) @@ -632,7 +632,7 @@ def get_reformated_path(filename, padded=True): ) -def get_padding_from_path(filename): +def get_padding_from_filename(filename): """ Return padding number from Flame path style @@ -643,15 +643,15 @@ def get_padding_from_path(filename): int: padding number Example: - get_padding_from_path("plate.0001.exr") > 4 + get_padding_from_filename("plate.0001.exr") > 4 """ - found = get_frame_from_path(filename) + found = get_frame_from_filename(filename) return len(found) if found else None -def get_frame_from_path(filename): +def get_frame_from_filename(filename): """ Return sequence number from Flame path style @@ -662,7 +662,7 @@ def get_frame_from_path(filename): int: sequence frame number Example: - def get_frame_from_path(path): + def get_frame_from_filename(path): ("plate.0001.exr") > 0001 """ diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 615904ec092..562a3682152 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -284,23 +284,20 @@ def create_otio_reference(clip_data): # get padding and other file infos log.debug("_ path: {}".format(path)) - is_sequence = padding = utils.get_frame_from_path(path) - if is_sequence: - number = utils.get_frame_from_path(path) - file_head = file_name.split(number)[:-1] - frame_start = int(number) - frame_duration = clip_data["source_duration"] + otio_ex_ref_item = None + is_sequence = frame_number = utils.get_frame_from_filename(file_name) if is_sequence: + file_head = file_name.split(frame_number)[:-1] + frame_start = int(frame_number) + padding = len(frame_number) + metadata.update({ "isSequence": True, - "padding": len(padding) + "padding": padding }) - otio_ex_ref_item = None - - if is_sequence: # if it is file sequence try to create `ImageSequenceReference` # the OTIO might not be compatible so return nothing and do it old way try: @@ -322,10 +319,12 @@ def create_otio_reference(clip_data): pass if not otio_ex_ref_item: - reformat_path = utils.get_reformated_path(path, padded=False) + dirname, file_name = os.path.split(path) + file_name = utils.get_reformated_filename(file_name, padded=False) + reformated_path = os.path.join(dirname, file_name) # in case old OTIO or video file create `ExternalReference` otio_ex_ref_item = otio.schema.ExternalReference( - target_url=reformat_path, + target_url=reformated_path, available_range=create_otio_time_range( frame_start, frame_duration, @@ -346,7 +345,7 @@ def create_otio_clip(clip_data): media_reference = create_otio_reference(clip_data) # calculate source in - first_frame = utils.get_frame_from_path(clip_data["fpath"]) or 0 + first_frame = utils.get_frame_from_filename(clip_data["fpath"]) or 0 source_in = int(clip_data["source_in"]) - int(first_frame) # creatae source range diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index 57a15d65a14..e3ffdfce95a 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -4,6 +4,8 @@ import logging log = logging.getLogger(__name__) +FRAME_PATTERN = re.compile(r"[\._](\d+)[\.]") + def timecode_to_frames(timecode, framerate): rt = otio.opentime.from_timecode(timecode, framerate) @@ -20,79 +22,71 @@ def frames_to_seconds(frames, framerate): return otio.opentime.to_seconds(rt) -def get_reformated_path(path, padded=True): +def get_reformated_filename(filename, padded=True): """ Return fixed python expression path Args: - path (str): path url or simple file name + filename (str): file name Returns: type: string with reformated path Example: - get_reformated_path("plate.1001.exr") > plate.%04d.exr + get_reformated_filename("plate.1001.exr") > plate.%04d.exr """ - basename = os.path.basename(path) - dirpath = os.path.dirname(path) - padding = get_padding_from_path(basename) - found = get_frame_from_path(basename) + found = FRAME_PATTERN.search(filename) if not found: - log.info("Path is not sequence: {}".format(path)) - return path + log.info("File name is not sequence: {}".format(filename)) + return filename + + padding = get_padding_from_filename(filename) - if padded: - basename = basename.replace(found, "%0{}d".format(padding)) - else: - basename = basename.replace(found, "%d") + replacement = "%0{}d".format(padding) if padded else "%d" + start_idx, end_idx = found.span(1) - return os.path.join(dirpath, basename) + return replacement.join( + [filename[:start_idx], filename[end_idx:]] + ) -def get_padding_from_path(path): +def get_padding_from_filename(filename): """ Return padding number from Flame path style Args: - path (str): path url or simple file name + filename (str): file name Returns: int: padding number Example: - get_padding_from_path("plate.0001.exr") > 4 + get_padding_from_filename("plate.0001.exr") > 4 """ - found = get_frame_from_path(path) + found = get_frame_from_filename(filename) - if found: - return len(found) - else: - return None + return len(found) if found else None -def get_frame_from_path(path): +def get_frame_from_filename(filename): """ Return sequence number from Flame path style Args: - path (str): path url or simple file name + filename (str): file name Returns: int: sequence frame number Example: - def get_frame_from_path(path): + def get_frame_from_filename(path): ("plate.0001.exr") > 0001 """ - frame_pattern = re.compile(r"[._](\d+)[.]") - found = re.findall(frame_pattern, path) + found = re.findall(FRAME_PATTERN, filename) - if found: - return found.pop() - else: - return None + return found.pop() if found else None diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index df7b0026fb4..6424bce3bc9 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -47,7 +47,7 @@ def process(self, context): # get source clip source_clip = self._get_reel_clip(file_path) - first_frame = opfapi.get_frame_from_path(file_path) or 0 + first_frame = opfapi.get_frame_from_filename(file_path) or 0 head, tail = self._get_head_tail(clip_data, first_frame) From fdc1d5acb865a0b6ab48fee2bd31671854333a29 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 19 Jan 2022 21:25:31 +0100 Subject: [PATCH 21/26] global: rename otio plugins and change order --- openpype/plugins/publish/collect_otio_frame_ranges.py | 4 ++-- openpype/plugins/publish/collect_otio_review.py | 4 ++-- openpype/plugins/publish/collect_otio_subset_resources.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/collect_otio_frame_ranges.py b/openpype/plugins/publish/collect_otio_frame_ranges.py index 511ed757b3b..ee7b7957ade 100644 --- a/openpype/plugins/publish/collect_otio_frame_ranges.py +++ b/openpype/plugins/publish/collect_otio_frame_ranges.py @@ -12,13 +12,13 @@ from pprint import pformat -class CollectOcioFrameRanges(pyblish.api.InstancePlugin): +class CollectOtioFrameRanges(pyblish.api.InstancePlugin): """Getting otio ranges from otio_clip Adding timeline and source ranges to instance data""" label = "Collect OTIO Frame Ranges" - order = pyblish.api.CollectorOrder - 0.48 + order = pyblish.api.CollectorOrder - 0.08 families = ["shot", "clip"] hosts = ["resolve", "hiero", "flame"] diff --git a/openpype/plugins/publish/collect_otio_review.py b/openpype/plugins/publish/collect_otio_review.py index 6634be06717..35c77a24cb4 100644 --- a/openpype/plugins/publish/collect_otio_review.py +++ b/openpype/plugins/publish/collect_otio_review.py @@ -16,11 +16,11 @@ from pprint import pformat -class CollectOcioReview(pyblish.api.InstancePlugin): +class CollectOtioReview(pyblish.api.InstancePlugin): """Get matching otio track from defined review layer""" label = "Collect OTIO Review" - order = pyblish.api.CollectorOrder - 0.47 + order = pyblish.api.CollectorOrder - 0.078 families = ["clip"] hosts = ["resolve", "hiero", "flame"] diff --git a/openpype/plugins/publish/collect_otio_subset_resources.py b/openpype/plugins/publish/collect_otio_subset_resources.py index d740ceb5082..7c11462ef0a 100644 --- a/openpype/plugins/publish/collect_otio_subset_resources.py +++ b/openpype/plugins/publish/collect_otio_subset_resources.py @@ -14,11 +14,11 @@ from openpype.lib import editorial -class CollectOcioSubsetResources(pyblish.api.InstancePlugin): +class CollectOtioSubsetResources(pyblish.api.InstancePlugin): """Get Resources for a subset version""" label = "Collect OTIO Subset Resources" - order = pyblish.api.CollectorOrder - 0.47 + order = pyblish.api.CollectorOrder - 0.077 families = ["clip"] hosts = ["resolve", "hiero", "flame"] @@ -64,7 +64,7 @@ def process(self, instance): a_frame_start_h = media_in - handle_start a_frame_end_h = media_out + handle_end - # create trimmed ocio time range + # create trimmed otio time range trimmed_media_range_h = editorial.range_from_frames( a_frame_start_h, (a_frame_end_h - a_frame_start_h + 1), media_fps From 3afdaf655817c036d2f424bd46417edca21b0ada Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 19 Jan 2022 21:26:17 +0100 Subject: [PATCH 22/26] flame: remove camel case in extract resources plugin --- .../plugins/publish/extract_subset_resources.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index adb3b1ae9b1..291e440cbe3 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -20,17 +20,17 @@ class ExtractSubsetResources(openpype.api.Extractor): default_presets = { "thumbnail": { "ext": "jpg", - "xmlPresetFile": "Jpeg (8-bit).xml", - "xmlPresetDir": "", - "representationAddRange": False, - "representationTags": ["thumbnail"] + "xml_preset_file": "Jpeg (8-bit).xml", + "xml_preset_dir": "", + "representation_add_range": False, + "representation_tags": ["thumbnail"] }, "ftrackpreview": { "ext": "mov", - "xmlPresetFile": "Apple iPad (1920x1080).xml", - "xmlPresetDir": "", - "representationAddRange": True, - "representationTags": [ + "xml_preset_file": "Apple iPad (1920x1080).xml", + "xml_preset_dir": "", + "representation_add_range": True, + "representation_tags": [ "review", "delete" ] From 9df277fd840e070455b15c25db5f0237904dd3a4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 19 Jan 2022 21:27:23 +0100 Subject: [PATCH 23/26] flame: removing unused module --- openpype/hosts/flame/otio/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index e3ffdfce95a..7ded8e55d86 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -1,5 +1,4 @@ import re -import os import opentimelineio as otio import logging log = logging.getLogger(__name__) From c5d4374e6078dfd73a32efa45b7d9846353c49cf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 20 Jan 2022 14:19:49 +0100 Subject: [PATCH 24/26] ftrack: adding profile for flame to have ftrack family --- .../settings/defaults/project_settings/ftrack.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index b3ea77a584e..513611ebfbb 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -318,6 +318,19 @@ "tasks": [], "add_ftrack_family": true, "advanced_filtering": [] + }, + { + "hosts": [ + "flame" + ], + "families": [ + "plate", + "take" + ], + "task_types": [], + "tasks": [], + "add_ftrack_family": true, + "advanced_filtering": [] } ] }, From 4f01991693dc8781f22bb3dec8098abb9144f1be Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 20 Jan 2022 14:20:09 +0100 Subject: [PATCH 25/26] flame: adding segment index attribute to settings --- .../schemas/projects_schema/schema_project_flame.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index b1b1f3539b2..76576ebf737 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -38,6 +38,11 @@ "key": "clipName", "label": "Clip name template" }, + { + "type": "boolean", + "key": "segmentIndex", + "label": "Accept segment order" + }, { "type": "number", "key": "countFrom", From f4655e7ff388f17eba4e75d5a4a4051c4d01413f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 20 Jan 2022 14:20:52 +0100 Subject: [PATCH 26/26] global: collect hierarchy order moved higher --- openpype/plugins/publish/collect_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_hierarchy.py b/openpype/plugins/publish/collect_hierarchy.py index 7f7306f73b2..efb40407d9c 100644 --- a/openpype/plugins/publish/collect_hierarchy.py +++ b/openpype/plugins/publish/collect_hierarchy.py @@ -13,7 +13,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): """ label = "Collect Hierarchy" - order = pyblish.api.CollectorOrder - 0.47 + order = pyblish.api.CollectorOrder - 0.076 families = ["shot"] hosts = ["resolve", "hiero", "flame"]