From 8dcc2eabff6a39c47f4fae519cc9de658039312d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 18 Feb 2022 22:24:37 +0100 Subject: [PATCH 01/85] flame: adding batch utils for creating batch in desktop --- openpype/hosts/flame/api/batch_utils.py | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 openpype/hosts/flame/api/batch_utils.py diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py new file mode 100644 index 00000000000..3a155c4b8df --- /dev/null +++ b/openpype/hosts/flame/api/batch_utils.py @@ -0,0 +1,37 @@ +import flame + + +def create_batch(name, frame_start, frame_end, **kwargs): + schematicReels = ['LoadedReel1'] + shelfReels = ['ShelfReel1'] + + handle_start = kwargs.get("handleStart") + handle_end = kwargs.get("handleEnd") + + if handle_start: + frame_start -= handle_start + if handle_end: + frame_end += handle_end + + # Create batch group with name, start_frame value, duration value, + # set of schematic reel names, set of shelf reel names + flame.batch.create_batch_group( + name, + start_frame=frame_start, + duration=frame_end, + reels=schematicReels, + shelf_reels=shelfReels + ) + + if kwargs.get("switch_batch_tab"): + # use this command to switch to the batch tab + flame.batch.go_to() + + comp = flame.batch.create_node("Comp") + writeFile = flame.batch.create_node("Write File") + + # connect nodes + flame.batch.connect_nodes(comp, "Result", writeFile, "Front") + + # sort batch nodes + flame.batch.organize() From 5391f1fff3abe36dde6289778ab2823e79616ab7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 18 Feb 2022 22:46:27 +0100 Subject: [PATCH 02/85] flame: adding write node to batch utils --- openpype/hosts/flame/api/batch_utils.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 3a155c4b8df..2c808349286 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -5,6 +5,7 @@ def create_batch(name, frame_start, frame_end, **kwargs): schematicReels = ['LoadedReel1'] shelfReels = ['ShelfReel1'] + write_pref = kwargs["write_pref"] handle_start = kwargs.get("handleStart") handle_end = kwargs.get("handleEnd") @@ -27,11 +28,23 @@ def create_batch(name, frame_start, frame_end, **kwargs): # use this command to switch to the batch tab flame.batch.go_to() - comp = flame.batch.create_node("Comp") - writeFile = flame.batch.create_node("Write File") + comp_node = flame.batch.create_node("Comp") + + # create write node + write_node = flame.batch.create_node('Write File') + write_node.media_path = write_pref["media_path"] + write_node.media_path_pattern = write_pref["media_path_pattern"] + write_node.create_clip = write_pref["create_clip"] + write_node.include_setup = write_pref["include_setup"] + write_node.create_clip_path = write_pref["create_clip_path"] + write_node.include_setup_path = write_pref["include_setup_path"] + write_node.file_type = write_pref["file_type"] + write_node.bit_depth = write_pref["bit_depth"] + write_node.frame_index_mode = write_pref["frame_index_mode"] + write_node.frame_padding = int(write_pref["frame_padding"]) # connect nodes - flame.batch.connect_nodes(comp, "Result", writeFile, "Front") + flame.batch.connect_nodes(comp_node, "Result", write_node, "Front") # sort batch nodes flame.batch.organize() From cdc3d0be792e718b17fb1290f3f05a5ea8c4380f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 22 Feb 2022 12:49:11 +0100 Subject: [PATCH 03/85] flame: batch utils 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 56bbadd2fc8..98a1a23e896 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -70,6 +70,9 @@ export_clip, get_preset_path_by_xml_name ) +from .batch_utils import ( + create_batch +) __all__ = [ # constants @@ -140,5 +143,8 @@ # render utils "export_clip", - "get_preset_path_by_xml_name" + "get_preset_path_by_xml_name", + + # batch utils + "create_batch" ] From 5d8e3e293f46860f9de52f93736f9646023de501 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 22 Feb 2022 12:49:33 +0100 Subject: [PATCH 04/85] flame: adding docstrigs to create_batch --- openpype/hosts/flame/api/batch_utils.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 2c808349286..a1fe7961c4b 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -2,8 +2,15 @@ def create_batch(name, frame_start, frame_end, **kwargs): - schematicReels = ['LoadedReel1'] - shelfReels = ['ShelfReel1'] + """Create Batch Group in active project's Desktop + + Args: + name (str): name of batch group to be created + frame_start (int): start frame of batch + frame_end (int): end frame of batch + """ + schematic_reels = kwargs.get("shematic_reels") or ['LoadedReel1'] + shelf_reels = kwargs.get("shelf_reels") or ['ShelfReel1'] write_pref = kwargs["write_pref"] handle_start = kwargs.get("handleStart") @@ -20,8 +27,8 @@ def create_batch(name, frame_start, frame_end, **kwargs): name, start_frame=frame_start, duration=frame_end, - reels=schematicReels, - shelf_reels=shelfReels + reels=schematic_reels, + shelf_reels=shelf_reels ) if kwargs.get("switch_batch_tab"): From 162df8c0aca975da19778cd05e22771f651458a9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 22 Feb 2022 13:00:14 +0100 Subject: [PATCH 05/85] flame: itegrator wip --- .../flame/plugins/publish/integrate_batch_group.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 openpype/hosts/flame/plugins/publish/integrate_batch_group.py diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py new file mode 100644 index 00000000000..fd88ed318e5 --- /dev/null +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -0,0 +1,14 @@ +import pyblish +import openpype.hosts.flame.api as opfapi + +@pyblish.api.log +class IntegrateBatchGroup(pyblish.api.InstancePlugin): + """Integrate published shot to batch group""" + + order = pyblish.api.IntegratorOrder + 0.45 + label = "Integrate Batch Groups" + hosts = ["flame"] + families = ["clip"] + + def process(self, instance): + opfapi.create_batch \ No newline at end of file From c120135f1fa467587a1b41f0e3c6282a2285b200 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 21:08:35 +0100 Subject: [PATCH 06/85] Removed submodule openpype/modules/default_modules/ftrack/python2_vendor/arrow --- openpype/modules/default_modules/ftrack/python2_vendor/arrow | 1 - 1 file changed, 1 deletion(-) delete mode 160000 openpype/modules/default_modules/ftrack/python2_vendor/arrow diff --git a/openpype/modules/default_modules/ftrack/python2_vendor/arrow b/openpype/modules/default_modules/ftrack/python2_vendor/arrow deleted file mode 160000 index b746fedf728..00000000000 --- a/openpype/modules/default_modules/ftrack/python2_vendor/arrow +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b746fedf7286c3755a46f07ab72f4c414cd41fc0 From d1a733cf885e8955746ef6e71db01b76fe7c96be Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 21:08:43 +0100 Subject: [PATCH 07/85] Removed submodule openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api --- .../default_modules/ftrack/python2_vendor/ftrack-python-api | 1 - 1 file changed, 1 deletion(-) delete mode 160000 openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api diff --git a/openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api b/openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api deleted file mode 160000 index d277f474ab0..00000000000 --- a/openpype/modules/default_modules/ftrack/python2_vendor/ftrack-python-api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d277f474ab016e7b53479c36af87cb861d0cc53e From fdb9f0da77f1a996be4160f52acdc1036238bc39 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 21:08:50 +0100 Subject: [PATCH 08/85] Removed submodule repos/avalon-unreal-integration --- repos/avalon-unreal-integration | 1 - 1 file changed, 1 deletion(-) delete mode 160000 repos/avalon-unreal-integration diff --git a/repos/avalon-unreal-integration b/repos/avalon-unreal-integration deleted file mode 160000 index 43f6ea94398..00000000000 --- a/repos/avalon-unreal-integration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 43f6ea943980b29c02a170942b566ae11f2b7080 From db03b47b8ee5615bb19ee90f1dc52f8f05cf379b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 22 Mar 2022 15:14:38 +0100 Subject: [PATCH 09/85] hound fix --- openpype/hosts/flame/api/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 7c03186ff03..97f83ccf075 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -146,7 +146,6 @@ "export_clip", "get_preset_path_by_xml_name", "modify_preset_file", - # batch utils "create_batch" From aab2ed17f8d582056f5835613845d13ea7205b24 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 22 Mar 2022 15:15:04 +0100 Subject: [PATCH 10/85] flame: ingegrate batch wip --- .../plugins/publish/integrate_batch_group.py | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index fd88ed318e5..aaa405343c0 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -1,6 +1,7 @@ import pyblish import openpype.hosts.flame.api as opfapi + @pyblish.api.log class IntegrateBatchGroup(pyblish.api.InstancePlugin): """Integrate published shot to batch group""" @@ -11,4 +12,67 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): families = ["clip"] def process(self, instance): - opfapi.create_batch \ No newline at end of file + frame_start = instance.data["frameStart"] + frame_end = instance.data["frameEnd"] + handle_start = instance.data["handleStart"] + handle_end = instance.data["handleEnd"] + + asset_name = instance.data["asset"] + write_pref_data = self._get_write_prefs(instance) + + batch_data = { + "shematic_reels": [ + "OP_LoadedReel" + ], + "write_pref": write_pref_data, + "handleStart": handle_start, + "handleEnd": handle_end + } + + opfapi.create_batch(asset_name, frame_start, frame_end, batch_data) + + def _get_write_prefs(self, instance): + # The path attribute where the rendered clip is exported + # /path/to/file.[0001-0010].exr + media_path = "{render_path}".format() + # name of file represented by tokens + media_path_pattern = "_v." + # The Create Open Clip attribute of the Write File node. \ + # Determines if an Open Clip is created by the Write File node. + create_clip = True + # The Include Setup attribute of the Write File node. + # Determines if a Batch Setup file is created by the Write File node. + include_setup = True + # The path attribute where the Open Clip file is exported by + # the Write File node. + create_clip_path = "" + include_setup_path = None + # The file type for the files written by the Write File node. + # Setting this attribute also overwrites format_extension, + # bit_depth and compress_mode to match the defaults for + # this file type. + file_type = "OpenEXR" + # The bit depth for the files written by the Write File node. + # This attribute resets to match file_type whenever file_type is set. + bit_depth = "16" + frame_index_mode = None + frame_padding = 0 + # The versioning mode of the Open Clip exported by the Write File node. + # Only available if create_clip = True. + version_mode = "Follow Iteration" + version_name = "v" + + return { + "media_path": media_path, + "media_path_pattern": media_path_pattern, + "create_clip": create_clip, + "include_setup": include_setup, + "create_clip_path": create_clip_path, + "include_setup_path": include_setup_path, + "file_type": file_type, + "bit_depth": bit_depth, + "frame_index_mode": frame_index_mode, + "frame_padding": frame_padding, + "version_mode": version_mode, + "version_name": version_name + } From 12aeb88e0a2cd7ccfa6dae9dd7f20d83bf4577b5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 22 Mar 2022 17:13:11 +0100 Subject: [PATCH 11/85] flame: integrate batch [wip] --- .../plugins/publish/integrate_batch_group.py | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index aaa405343c0..780531287b0 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -1,3 +1,4 @@ +import os import pyblish import openpype.hosts.flame.api as opfapi @@ -32,9 +33,12 @@ def process(self, instance): opfapi.create_batch(asset_name, frame_start, frame_end, batch_data) def _get_write_prefs(self, instance): + shot_path = instance.data[""] + render_dir_path = os.path.join( + shot_path, "work", task, "render", "flame") # The path attribute where the rendered clip is exported # /path/to/file.[0001-0010].exr - media_path = "{render_path}".format() + media_path = render_dir_path # name of file represented by tokens media_path_pattern = "_v." # The Create Open Clip attribute of the Write File node. \ @@ -46,17 +50,33 @@ def _get_write_prefs(self, instance): # The path attribute where the Open Clip file is exported by # the Write File node. create_clip_path = "" - include_setup_path = None + # The path attribute where the Batch setup file + # is exported by the Write File node. + include_setup_path = "./_v" # The file type for the files written by the Write File node. # Setting this attribute also overwrites format_extension, # bit_depth and compress_mode to match the defaults for # this file type. file_type = "OpenEXR" + # The file extension for the files written by the Write File node. + # This attribute resets to match file_type whenever file_type + # is set. If you require a specific extension, you must + # set format_extension after setting file_type. + format_extension = "exr" # The bit depth for the files written by the Write File node. # This attribute resets to match file_type whenever file_type is set. bit_depth = "16" - frame_index_mode = None - frame_padding = 0 + # The compressing attribute for the files exported by the Write + # File node. Only relevant when file_type in 'OpenEXR', 'Sgi', 'Tiff' + compress = True + # The compression format attribute for the specific File Types + # export by the Write File node. You must set compress_mode + # after setting file_type. + compress_mode = "DWAB" + # The frame index mode attribute of the Write File node. + # Value range: `Use Timecode` or `Use Start Frame` + frame_index_mode = "Use Start Frame" + frame_padding = 6 # The versioning mode of the Open Clip exported by the Write File node. # Only available if create_clip = True. version_mode = "Follow Iteration" @@ -70,7 +90,10 @@ def _get_write_prefs(self, instance): "create_clip_path": create_clip_path, "include_setup_path": include_setup_path, "file_type": file_type, + "format_extension": format_extension, "bit_depth": bit_depth, + "compress": compress, + "compress_mode": compress_mode, "frame_index_mode": frame_index_mode, "frame_padding": frame_padding, "version_mode": version_mode, From 7dd0c86a17f0a95ac6e16416473978169cf793a7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Mar 2022 17:11:48 +0100 Subject: [PATCH 12/85] flame: collect timeline instances settings --- .../defaults/project_settings/flame.json | 31 +++++++ .../projects_schema/schema_project_flame.json | 81 +++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index c7188b10b5a..939752c778a 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -20,6 +20,37 @@ } }, "publish": { + "CollectTimelineInstances": { + "xml_preset_attrs_from_comments": [ + { + "name": "width", + "type": "number" + }, + { + "name": "height", + "type": "number" + }, + { + "name": "pixelRatio", + "type": "float" + }, + { + "name": "resizeType", + "type": "string" + }, + { + "name": "resizeFilter", + "type": "string" + } + ], + "add_tasks": [ + { + "name": "compositing", + "type": "Compositing", + "create_batch_group": true + } + ] + }, "ExtractSubsetResources": { "keep_original_representation": false, "export_presets_mapping": { 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 e352f8b1327..8057b07d9c4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -136,6 +136,87 @@ "key": "publish", "label": "Publish plugins", "children": [ + { + "type": "dict", + "collapsible": true, + "key": "CollectTimelineInstances", + "label": "Collect Timeline Instances", + "is_group": true, + "children": [ + { + "type": "collapsible-wrap", + "label": "XML presets attributes parsable from segment comments", + "collapsible": true, + "collapsed": true, + "children": [ + { + "type": "list", + "key": "xml_preset_attrs_from_comments", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Attribute name" + }, + { + "key": "type", + "label": "Attribute type", + "type": "enum", + "default": "number", + "enum_items": [ + { + "number": "number" + }, + { + "float": "float" + }, + { + "string": "string" + } + ] + } + ] + } + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Add tasks", + "collapsible": true, + "collapsed": true, + "children": [ + { + "type": "list", + "key": "add_tasks", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Task name" + }, + { + "key": "type", + "label": "Task type", + "multiselection": false, + "type": "task-types-enum" + }, + { + "type": "boolean", + "key": "create_batch_group", + "label": "Create batch group" + } + ] + } + } + ] + } + ] + }, { "type": "dict", "collapsible": true, From 074703f8cff5b4432a98d1fb28f9a6f42943c694 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Mar 2022 17:13:09 +0100 Subject: [PATCH 13/85] flame: use settings in collect timeline instances --- .../publish/collect_timeline_instances.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 70340ad7a22..94348601b24 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -21,15 +21,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): audio_track_items = [] - # TODO: add to settings # settings - xml_preset_attrs_from_comments = { - "width": "number", - "height": "number", - "pixelRatio": "float", - "resizeType": "string", - "resizeFilter": "string" - } + xml_preset_attrs_from_comments = [] + add_tasks = [] def process(self, context): project = context.data["flameProject"] @@ -106,7 +100,11 @@ def process(self, context): "fps": self.fps, "flameSourceClip": source_clip, "sourceFirstFrame": int(first_frame), - "path": file_path + "path": file_path, + "flameAddTasks": self.add_tasks, + "tasks": { + task["name"]: {"type": task["type"]} + for task in self.add_tasks} }) # get otio clip data @@ -181,14 +179,17 @@ def _get_xml_preset_attrs(self, attributes, split): # split to key and value key, value = split.split(":") - for a_name, a_type in self.xml_preset_attrs_from_comments.items(): + for attr_data in self.xml_preset_attrs_from_comments: + a_name = attr_data["name"] + a_type = attr_data["type"] + # exclude all not related attributes if a_name.lower() not in key.lower(): continue # get pattern defined by type pattern = TXT_PATERN - if a_type in ("number" , "float"): + if a_type in ("number", "float"): pattern = NUM_PATERN res_goup = pattern.findall(value) From 029135acf5513756ffc3e01fafa66e8ddd565d32 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Mar 2022 20:38:46 +0100 Subject: [PATCH 14/85] flame: integrator bath group [finishing] --- openpype/hosts/flame/api/batch_utils.py | 12 +++- .../plugins/publish/integrate_batch_group.py | 65 +++++++++++++++---- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index a1fe7961c4b..d309c5985d6 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -37,8 +37,12 @@ def create_batch(name, frame_start, frame_end, **kwargs): comp_node = flame.batch.create_node("Comp") + # TODO: convert this to iterational processing, + # so it could be driven from `imageio` settigns # create write node write_node = flame.batch.create_node('Write File') + # assign attrs + write_node.name = write_pref["name"] write_node.media_path = write_pref["media_path"] write_node.media_path_pattern = write_pref["media_path_pattern"] write_node.create_clip = write_pref["create_clip"] @@ -46,11 +50,15 @@ def create_batch(name, frame_start, frame_end, **kwargs): write_node.create_clip_path = write_pref["create_clip_path"] write_node.include_setup_path = write_pref["include_setup_path"] write_node.file_type = write_pref["file_type"] + write_node.format_extension = write_pref["format_extension"] write_node.bit_depth = write_pref["bit_depth"] + write_node.compress = write_pref["compress"] + write_node.compress_mode = write_pref["compress_mode"] write_node.frame_index_mode = write_pref["frame_index_mode"] - write_node.frame_padding = int(write_pref["frame_padding"]) + write_node.frame_padding = write_pref["frame_padding"] + write_node.version_mode = write_pref["version_mode"] + write_node.version_name = write_pref["version_name"] - # connect nodes flame.batch.connect_nodes(comp_node, "Result", write_node, "Front") # sort batch nodes diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 780531287b0..808c0598165 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -1,5 +1,7 @@ import os +from pprint import pformat import pyblish +from openpype.lib import get_workdir import openpype.hosts.flame.api as opfapi @@ -17,25 +19,52 @@ def process(self, instance): frame_end = instance.data["frameEnd"] handle_start = instance.data["handleStart"] handle_end = instance.data["handleEnd"] - asset_name = instance.data["asset"] - write_pref_data = self._get_write_prefs(instance) + add_tasks = instance.data["flameAddTasks"] - batch_data = { - "shematic_reels": [ - "OP_LoadedReel" - ], - "write_pref": write_pref_data, - "handleStart": handle_start, - "handleEnd": handle_end - } + # iterate all tasks from settings + for task_data in add_tasks: + # exclude batch group + if not task_data["create_batch_group"]: + continue + task_name = task_data["name"] + batchgroup_name = "{}_{}".format(asset_name, task_name) + write_pref_data = self._get_write_prefs(instance, task_data) + + batch_data = { + "shematic_reels": [ + "OP_LoadedReel" + ], + "write_pref": write_pref_data, + "handleStart": handle_start, + "handleEnd": handle_end + } + self.log.debug( + "__ batch_data: {}".format(pformat(batch_data))) + + # create batch with utils + opfapi.create_batch( + batchgroup_name, + frame_start, + frame_end, + batch_data + ) + + def _get_write_prefs(self, instance, task_data): + anatomy_data = instance.data["anatomyData"] - opfapi.create_batch(asset_name, frame_start, frame_end, batch_data) + task_workfile_path = self._get_shot_task_dir_path(instance, task_data) + self.log.debug("__ task_workfile_path: {}".format(task_workfile_path)) - def _get_write_prefs(self, instance): - shot_path = instance.data[""] + # TODO: this might be done with template in settings render_dir_path = os.path.join( - shot_path, "work", task, "render", "flame") + task_workfile_path, "render", "flame") + + # TODO: add most of these to `imageio/flame/batch/write_node` + name = "{project[code]}_{asset}_{task[name]}".format( + **anatomy_data + ) + # The path attribute where the rendered clip is exported # /path/to/file.[0001-0010].exr media_path = render_dir_path @@ -83,6 +112,7 @@ def _get_write_prefs(self, instance): version_name = "v" return { + "name": name, "media_path": media_path, "media_path_pattern": media_path_pattern, "create_clip": create_clip, @@ -99,3 +129,10 @@ def _get_write_prefs(self, instance): "version_mode": version_mode, "version_name": version_name } + + def _get_shot_task_dir_path(self, instance, task_data): + project_doc = instance.data["projectEntity"] + asset_entity = instance.data["assetEntity"] + + return get_workdir( + project_doc, asset_entity, task_data["name"], "flame") From 9fb6d7a7230149b9d1c7b273eb8fcc532a84a3fb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 12:04:29 +0100 Subject: [PATCH 15/85] flame: batchgroup uses duration --- openpype/hosts/flame/api/batch_utils.py | 14 ++++++-------- .../flame/plugins/publish/integrate_batch_group.py | 5 +++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index d309c5985d6..a47d62a10ed 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -1,7 +1,7 @@ import flame -def create_batch(name, frame_start, frame_end, **kwargs): +def create_batch(name, frame_start, frame_duration, **kwargs): """Create Batch Group in active project's Desktop Args: @@ -13,20 +13,18 @@ def create_batch(name, frame_start, frame_end, **kwargs): shelf_reels = kwargs.get("shelf_reels") or ['ShelfReel1'] write_pref = kwargs["write_pref"] - handle_start = kwargs.get("handleStart") - handle_end = kwargs.get("handleEnd") + handle_start = kwargs.get("handleStart") or 0 + handle_end = kwargs.get("handleEnd") or 0 - if handle_start: - frame_start -= handle_start - if handle_end: - frame_end += handle_end + frame_start -= handle_start + frame_duration += handle_start + handle_end # Create batch group with name, start_frame value, duration value, # set of schematic reel names, set of shelf reel names flame.batch.create_batch_group( name, start_frame=frame_start, - duration=frame_end, + duration=frame_duration, reels=schematic_reels, shelf_reels=shelf_reels ) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 808c0598165..0a21d6ca2da 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -19,6 +19,7 @@ def process(self, instance): frame_end = instance.data["frameEnd"] handle_start = instance.data["handleStart"] handle_end = instance.data["handleEnd"] + frame_duration = (frame_end - frame_start) + 1 asset_name = instance.data["asset"] add_tasks = instance.data["flameAddTasks"] @@ -46,8 +47,8 @@ def process(self, instance): opfapi.create_batch( batchgroup_name, frame_start, - frame_end, - batch_data + frame_duration, + **batch_data ) def _get_write_prefs(self, instance, task_data): From a87f778f1e95d16d09097d923ca3f1d519e86126 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 12:56:18 +0100 Subject: [PATCH 16/85] flame: reuse batch groups --- openpype/hosts/flame/api/__init__.py | 4 ++- openpype/hosts/flame/api/lib.py | 9 +++++++ .../plugins/publish/integrate_batch_group.py | 26 ++++++++++++++----- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 97f83ccf075..561aaab3def 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -29,7 +29,8 @@ get_frame_from_filename, get_padding_from_filename, maintained_object_duplication, - get_clip_segment + get_clip_segment, + get_batch_group_from_desktop ) from .utils import ( setup, @@ -105,6 +106,7 @@ "get_padding_from_filename", "maintained_object_duplication", "get_clip_segment", + "get_batch_group_from_desktop", # pipeline "install", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 74d9e7607af..9a6b86209d5 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -708,3 +708,12 @@ def get_clip_segment(flame_clip): raise ValueError("Clip `{}` has too many segments!".format(name)) return segments[0] + + +def get_batch_group_from_desktop(name): + project = get_current_project() + project_desktop = project.current_workspace.desktop + + for bgroup in project_desktop.batch_groups: + if bgroup.name.get_value() == name: + return bgroup diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 0a21d6ca2da..3a8173791ad 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -43,13 +43,25 @@ def process(self, instance): self.log.debug( "__ batch_data: {}".format(pformat(batch_data))) - # create batch with utils - opfapi.create_batch( - batchgroup_name, - frame_start, - frame_duration, - **batch_data - ) + # check if the batch group already exists + bgroup = opfapi.get_batch_group_from_desktop(batchgroup_name) + + if not bgroup: + self.log.info( + "Creating new batch group: {}".format(batchgroup_name)) + # create batch with utils + opfapi.create_batch( + batchgroup_name, + frame_start, + frame_duration, + **batch_data + ) + else: + self.log.info( + "Updating batch group: {}".format(batchgroup_name)) + # update already created batch group + bgroup.start_frame = frame_start + bgroup.duration = frame_duration def _get_write_prefs(self, instance, task_data): anatomy_data = instance.data["anatomyData"] From c26ff2ab544fadc7121d54ec49a2b35433e6122a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 12:56:36 +0100 Subject: [PATCH 17/85] flame: fix task name on write file node --- openpype/hosts/flame/plugins/publish/integrate_batch_group.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 3a8173791ad..af2b0fad659 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -65,6 +65,8 @@ def process(self, instance): def _get_write_prefs(self, instance, task_data): anatomy_data = instance.data["anatomyData"] + # update task data in anatomy data + anatomy_data.update(task_data) task_workfile_path = self._get_shot_task_dir_path(instance, task_data) self.log.debug("__ task_workfile_path: {}".format(task_workfile_path)) From 590e966a7d18a4c1f7dea0e08a3056a202607670 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 14:29:49 +0100 Subject: [PATCH 18/85] flame: updating anatomy data with correct task data --- .../plugins/publish/integrate_batch_group.py | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index af2b0fad659..c54eeec05cf 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -1,4 +1,5 @@ import os +import copy from pprint import pformat import pyblish from openpype.lib import get_workdir @@ -63,10 +64,28 @@ def process(self, instance): bgroup.start_frame = frame_start bgroup.duration = frame_duration - def _get_write_prefs(self, instance, task_data): - anatomy_data = instance.data["anatomyData"] + def _get_anamoty_data_with_current_task(self, instance, task_data): + anatomy_data = copy.deepcopy(instance.data["anatomyData"]) + task_name = task_data["name"] + task_type = task_data["type"] + anatomy_obj = instance.context.data["anatomy"] + # update task data in anatomy data - anatomy_data.update(task_data) + project_task_types = anatomy_obj["tasks"] + task_code = project_task_types.get(task_type, {}).get("short_name") + anatomy_data.update({ + "task": { + "name": task_name, + "type": task_type, + "short": task_code + } + }) + return anatomy_data + + def _get_write_prefs(self, instance, task_data): + # update task in anatomy data + anatomy_data = self._get_anamoty_data_with_current_task( + instance, task_data) task_workfile_path = self._get_shot_task_dir_path(instance, task_data) self.log.debug("__ task_workfile_path: {}".format(task_workfile_path)) From d5521ae8407a9f9bb4d1f05e8a1ef048700acf45 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 15:37:05 +0100 Subject: [PATCH 19/85] flame: adding loading of plate into integrator --- openpype/hosts/flame/api/batch_utils.py | 4 +- .../plugins/publish/integrate_batch_group.py | 93 +++++++++++-------- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index a47d62a10ed..99e053faf13 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -21,7 +21,7 @@ def create_batch(name, frame_start, frame_duration, **kwargs): # Create batch group with name, start_frame value, duration value, # set of schematic reel names, set of shelf reel names - flame.batch.create_batch_group( + bgroup = flame.batch.create_batch_group( name, start_frame=frame_start, duration=frame_duration, @@ -61,3 +61,5 @@ def create_batch(name, frame_start, frame_duration, **kwargs): # sort batch nodes flame.batch.organize() + + return bgroup diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index c54eeec05cf..97b456c18c0 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -16,12 +16,6 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): families = ["clip"] def process(self, instance): - frame_start = instance.data["frameStart"] - frame_end = instance.data["frameEnd"] - handle_start = instance.data["handleStart"] - handle_end = instance.data["handleEnd"] - frame_duration = (frame_end - frame_start) + 1 - asset_name = instance.data["asset"] add_tasks = instance.data["flameAddTasks"] # iterate all tasks from settings @@ -29,40 +23,59 @@ def process(self, instance): # exclude batch group if not task_data["create_batch_group"]: continue - task_name = task_data["name"] - batchgroup_name = "{}_{}".format(asset_name, task_name) - write_pref_data = self._get_write_prefs(instance, task_data) - - batch_data = { - "shematic_reels": [ - "OP_LoadedReel" - ], - "write_pref": write_pref_data, - "handleStart": handle_start, - "handleEnd": handle_end - } - self.log.debug( - "__ batch_data: {}".format(pformat(batch_data))) - - # check if the batch group already exists - bgroup = opfapi.get_batch_group_from_desktop(batchgroup_name) - - if not bgroup: - self.log.info( - "Creating new batch group: {}".format(batchgroup_name)) - # create batch with utils - opfapi.create_batch( - batchgroup_name, - frame_start, - frame_duration, - **batch_data - ) - else: - self.log.info( - "Updating batch group: {}".format(batchgroup_name)) - # update already created batch group - bgroup.start_frame = frame_start - bgroup.duration = frame_duration + + # create or get already created batch group + bgroup = self._get_batch_group(instance, task_data) + + # load plate to batch group + self.log.info("Loading subset `{}` into batch `{}`".format( + instance.data["subset"], bgroup.name.get_value() + )) + + def _get_batch_group(self, instance, task_data): + frame_start = instance.data["frameStart"] + frame_end = instance.data["frameEnd"] + handle_start = instance.data["handleStart"] + handle_end = instance.data["handleEnd"] + frame_duration = (frame_end - frame_start) + 1 + asset_name = instance.data["asset"] + + task_name = task_data["name"] + batchgroup_name = "{}_{}".format(asset_name, task_name) + write_pref_data = self._get_write_prefs(instance, task_data) + + batch_data = { + "shematic_reels": [ + "OP_LoadedReel" + ], + "write_pref": write_pref_data, + "handleStart": handle_start, + "handleEnd": handle_end + } + self.log.debug( + "__ batch_data: {}".format(pformat(batch_data))) + + # check if the batch group already exists + bgroup = opfapi.get_batch_group_from_desktop(batchgroup_name) + + if not bgroup: + self.log.info( + "Creating new batch group: {}".format(batchgroup_name)) + # create batch with utils + bgroup = opfapi.create_batch( + batchgroup_name, + frame_start, + frame_duration, + **batch_data + ) + else: + self.log.info( + "Updating batch group: {}".format(batchgroup_name)) + # update already created batch group + bgroup.start_frame = frame_start + bgroup.duration = frame_duration + + return bgroup def _get_anamoty_data_with_current_task(self, instance, task_data): anatomy_data = copy.deepcopy(instance.data["anatomyData"]) From 38268bc83102964c22db15129516a32ba5d5f455 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 16:28:46 +0100 Subject: [PATCH 20/85] flame: let extractor drive loading to batch group --- .../settings/defaults/project_settings/flame.json | 4 +++- .../projects_schema/schema_project_flame.json | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index 939752c778a..a2b9bef103a 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -62,7 +62,9 @@ "ignore_comment_attrs": false, "colorspace_out": "ACES - ACEScg", "representation_add_range": true, - "representation_tags": [] + "representation_tags": [], + "load_to_batch_group": true, + "batch_group_loader_name": "LoadClip" } } } 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 8057b07d9c4..c9915777997 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -302,6 +302,20 @@ "type": "text", "multiline": false } + }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "load_to_batch_group", + "label": "Load to batch group reel", + "default": false + }, + { + "type": "text", + "key": "batch_group_loader_name", + "label": "Use loader name" } ] } From 0407465ee1a2438d8a84d9d0704bb38dd56c1a2c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 17:24:21 +0100 Subject: [PATCH 21/85] flame: add loadable arguments to extracted repres --- .../hosts/flame/plugins/publish/extract_subset_resources.py | 6 +++++- 1 file changed, 5 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 32f6b9508fc..7c29bcf944d 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -211,7 +211,11 @@ def process(self, instance): "tags": repre_tags, "data": { "colorspace": color_out - } + }, + "load_to_batch_group": preset_config.get( + "load_to_batch_group"), + "batch_group_loader_name": preset_config.get( + "batch_group_loader_name") } # collect all available content of export dir From 638864150493e0f46b2fd41a6fbe0609434dc536 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 17:24:43 +0100 Subject: [PATCH 22/85] flame: finalize loading procedure in batch integrator --- .../plugins/publish/integrate_batch_group.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 97b456c18c0..62211d7ace8 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -4,6 +4,7 @@ import pyblish from openpype.lib import get_workdir import openpype.hosts.flame.api as opfapi +import openpype.pipeline as op_pipeline @pyblish.api.log @@ -15,6 +16,9 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin): hosts = ["flame"] families = ["clip"] + # settings + default_loader = "LoadClip" + def process(self, instance): add_tasks = instance.data["flameAddTasks"] @@ -31,6 +35,77 @@ def process(self, instance): self.log.info("Loading subset `{}` into batch `{}`".format( instance.data["subset"], bgroup.name.get_value() )) + self._load_clip_to_context(instance, bgroup) + + def _load_clip_to_context(self, instance, bgroup): + # get all loaders for host + loaders = op_pipeline.discover_loader_plugins() + + # get all published representations + published_representations = instance.data["published_representations"] + + # get all loadable representations + representations = instance.data["representations"] + + # get repre_id for the loadable representations + loadable_representations = [ + { + "name": _repr["name"], + "loader": _repr.get("batch_group_loader_name"), + # match loader to the loadable representation + "_id": next( + ( + id + for id, repr in published_representations.items() + if repr["representation"]["name"] == _repr["name"] + ), + None + ) + } + for _repr in representations + if _repr.get("load_to_batch_group") is not None + ] + + # get representation context from the repre_id + representation_ids = [ + repre["_id"] + for repre in loadable_representations + if repre["_id"] is not None + ] + repre_contexts = op_pipeline.load.get_repres_contexts( + representation_ids) + + # loop all returned repres from repre_context dict + for repre_id, repre_context in repre_contexts.items(): + # get loader name by representation id + loader_name = next( + ( + repr["loader"] + for repr in loadable_representations + if repr["_id"] == repre_id + ), + self.default_loader + ) + # get loader plugin + Loader = next( + ( + loader_plugin + for loader_plugin in loaders + if loader_plugin.__name__ == loader_name + ), + None + ) + if Loader: + # load to flame by representation context + op_pipeline.load.load_with_repre_context(Loader, repre_context) + else: + self.log.warning( + "Something got wrong and there is not Loader found for " + "following data: {}".format( + pformat(loadable_representations)) + ) + + def _get_batch_group(self, instance, task_data): frame_start = instance.data["frameStart"] From cde1caaa9180fcc7e4165995e9760429f2a55e07 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 17:40:27 +0100 Subject: [PATCH 23/85] flame: clean args types --- .../publish/extract_subset_resources.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 7c29bcf944d..00b87c05a08 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -108,6 +108,18 @@ def process(self, instance): ignore_comment_attrs = preset_config["ignore_comment_attrs"] color_out = preset_config["colorspace_out"] + # get attribures related loading in integrate_batch_group + load_to_batch_group = preset_config.get( + "load_to_batch_group") + batch_group_loader_name = preset_config.get( + "batch_group_loader_name") + + # convert to None if empty string + if batch_group_loader_name: + batch_group_loader_name = str(batch_group_loader_name) + if batch_group_loader_name == "": + batch_group_loader_name = None + # get frame range with handles for representation range frame_start_handle = frame_start - handle_start source_duration_handles = ( @@ -212,10 +224,8 @@ def process(self, instance): "data": { "colorspace": color_out }, - "load_to_batch_group": preset_config.get( - "load_to_batch_group"), - "batch_group_loader_name": preset_config.get( - "batch_group_loader_name") + "load_to_batch_group": load_to_batch_group, + "batch_group_loader_name": batch_group_loader_name } # collect all available content of export dir From 4cfd22b6393b3a4d7e5c18046f7d0340ce124e27 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 17:47:06 +0100 Subject: [PATCH 24/85] flame: improving loading with exception --- .../hosts/flame/plugins/publish/integrate_batch_group.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 62211d7ace8..08632c30184 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -97,7 +97,13 @@ def _load_clip_to_context(self, instance, bgroup): ) if Loader: # load to flame by representation context - op_pipeline.load.load_with_repre_context(Loader, repre_context) + try: + op_pipeline.load.load_with_repre_context( + Loader, repre_context) + except op_pipeline.load.IncompatibleLoaderError as msg: + self.log.error( + "Check allowed representations for Loader `{}` " + "in settings > error: {}".format(Loader.__name__, msg)) else: self.log.warning( "Something got wrong and there is not Loader found for " From a4f8cdb76962f6f4c9e4efef49079c3926e486e6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Mar 2022 17:51:22 +0100 Subject: [PATCH 25/85] flame: better logging for loading fail --- .../hosts/flame/plugins/publish/integrate_batch_group.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 08632c30184..f1049e46974 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -103,7 +103,14 @@ def _load_clip_to_context(self, instance, bgroup): except op_pipeline.load.IncompatibleLoaderError as msg: self.log.error( "Check allowed representations for Loader `{}` " - "in settings > error: {}".format(Loader.__name__, msg)) + "in settings > error: {}".format( + Loader.__name__, msg)) + self.log.error( + "Representaton context >>{}<< is not compatible " + "with loader `{}`".format( + pformat(repre_context), Loader.__name__ + ) + ) else: self.log.warning( "Something got wrong and there is not Loader found for " From 585d53deee223f359e6732620ea0188f8d00ec5c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Mar 2022 15:21:22 +0100 Subject: [PATCH 26/85] flame: improving loading in integrate batch plugin --- .../plugins/publish/extract_subset_resources.py | 6 ++---- .../flame/plugins/publish/integrate_batch_group.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 00b87c05a08..31f7b6d5740 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -115,10 +115,8 @@ def process(self, instance): "batch_group_loader_name") # convert to None if empty string - if batch_group_loader_name: - batch_group_loader_name = str(batch_group_loader_name) - if batch_group_loader_name == "": - batch_group_loader_name = None + if batch_group_loader_name == "": + batch_group_loader_name = None # get frame range with handles for representation range frame_start_handle = frame_start - handle_start diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index f1049e46974..81b304ff0b4 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -66,6 +66,9 @@ def _load_clip_to_context(self, instance, bgroup): if _repr.get("load_to_batch_group") is not None ] + self.log.debug("__ loadable_representations: {}".format(pformat( + loadable_representations))) + # get representation context from the repre_id representation_ids = [ repre["_id"] @@ -75,17 +78,20 @@ def _load_clip_to_context(self, instance, bgroup): repre_contexts = op_pipeline.load.get_repres_contexts( representation_ids) + self.log.debug("__ repre_contexts: {}".format(pformat( + repre_contexts))) + # loop all returned repres from repre_context dict for repre_id, repre_context in repre_contexts.items(): + self.log.debug("__ repre_id: {}".format(repre_id)) # get loader name by representation id loader_name = next( ( repr["loader"] for repr in loadable_representations if repr["_id"] == repre_id - ), - self.default_loader - ) + )) or self.default_loader + # get loader plugin Loader = next( ( @@ -118,8 +124,6 @@ def _load_clip_to_context(self, instance, bgroup): pformat(loadable_representations)) ) - - def _get_batch_group(self, instance, task_data): frame_start = instance.data["frameStart"] frame_end = instance.data["frameEnd"] From ed4388184ad768dbf00ce050efff8eaf11d3cf7c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Mar 2022 15:56:12 +0100 Subject: [PATCH 27/85] flame: adding clip loader to current batch --- .../flame/plugins/load/load_clip_batch.py | 135 ++++++++++++++++++ .../defaults/project_settings/flame.json | 22 +++ .../projects_schema/schema_project_flame.json | 42 ++++++ 3 files changed, 199 insertions(+) create mode 100644 openpype/hosts/flame/plugins/load/load_clip_batch.py diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py new file mode 100644 index 00000000000..81af34744e0 --- /dev/null +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -0,0 +1,135 @@ +import os +import flame +from pprint import pformat +import openpype.hosts.flame.api as opfapi + + +class LoadClipBatch(opfapi.ClipLoader): + """Load a subset to timeline as clip + + Place clip to timeline on its asset origin timings collected + during conforming to project + """ + + families = ["render2d", "source", "plate", "render", "review"] + representations = ["exr", "dpx", "jpg", "jpeg", "png", "h264"] + + label = "Load as clip to current batch" + order = -10 + icon = "code-fork" + color = "orange" + + # settings + reel_name = "OP_LoadedReel" + clip_name_template = "{asset}_{subset}_{representation}" + + def load(self, context, name, namespace, options): + + # get flame objects + self.batch = flame.batch + + # load clip to timeline and get main variables + namespace = namespace + version = context['version'] + version_data = version.get("data", {}) + version_name = version.get("name", None) + colorspace = version_data.get("colorspace", None) + clip_name = self.clip_name_template.format( + **context["representation"]["context"]) + + # todo: settings in imageio + # convert colorspace with ocio to flame mapping + # in imageio flame section + colorspace = colorspace + + # create workfile path + workfile_dir = os.environ["AVALON_WORKDIR"] + openclip_dir = os.path.join( + workfile_dir, clip_name + ) + openclip_path = os.path.join( + openclip_dir, clip_name + ".clip" + ) + if not os.path.exists(openclip_dir): + os.makedirs(openclip_dir) + + # prepare clip data from context ad send it to openClipLoader + loading_context = { + "path": self.fname.replace("\\", "/"), + "colorspace": colorspace, + "version": "v{:0>3}".format(version_name), + "logger": self.log + + } + self.log.debug(pformat( + loading_context + )) + self.log.debug(openclip_path) + + # make openpype clip file + opfapi.OpenClipSolver(openclip_path, loading_context).make() + + # prepare Reel group in actual desktop + opc = self._get_clip( + clip_name, + openclip_path + ) + + # add additional metadata from the version to imprint Avalon knob + add_keys = [ + "frameStart", "frameEnd", "source", "author", + "fps", "handleStart", "handleEnd" + ] + + # move all version data keys to tag data + data_imprint = { + key: version_data.get(key, str(None)) + for key in add_keys + } + # add variables related to version context + data_imprint.update({ + "version": version_name, + "colorspace": colorspace, + "objectName": clip_name + }) + + # TODO: finish the containerisation + # opc_segment = opfapi.get_clip_segment(opc) + + # return opfapi.containerise( + # opc_segment, + # name, namespace, context, + # self.__class__.__name__, + # data_imprint) + + return opc + + def _get_clip(self, name, clip_path): + reel = self._get_reel() + + # with maintained openclip as opc + matching_clip = next( + ( + cl for cl in reel.clips + if cl.name.get_value() == name + ) + ) + + if not matching_clip: + created_clips = flame.import_clips(str(clip_path), reel) + return created_clips.pop() + + return matching_clip + + def _get_reel(self): + + matching_reel = [ + rg for rg in self.batch.reels + if rg.name.get_value() == self.reel_name + ] + + return ( + matching_reel.pop() + if matching_reel + else self.batch.create_reel(str(self.reel_name)) + ) diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index a2b9bef103a..afd0834c9dc 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -92,6 +92,28 @@ "reel_group_name": "OpenPype_Reels", "reel_name": "Loaded", "clip_name_template": "{asset}_{subset}_{representation}" + }, + "LoadClipBatch": { + "enabled": true, + "families": [ + "render2d", + "source", + "plate", + "render", + "review" + ], + "representations": [ + "exr", + "dpx", + "jpg", + "jpeg", + "png", + "h264", + "mov", + "mp4" + ], + "reel_name": "OP_LoadedReel", + "clip_name_template": "{asset}_{subset}_{representation}" } } } \ 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 c9915777997..fe11d63ac2c 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -376,6 +376,48 @@ "label": "Clip name template" } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "LoadClipBatch", + "label": "Load as clip to current batch", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "families", + "label": "Families", + "object_type": "text" + }, + { + "type": "list", + "key": "representations", + "label": "Representations", + "object_type": "text" + }, + { + "type": "separator" + }, + { + "type": "text", + "key": "reel_name", + "label": "Reel name" + }, + { + "type": "separator" + }, + { + "type": "text", + "key": "clip_name_template", + "label": "Clip name template" + } + ] } ] } From 267a3e04ed4fda8c3837af28379e3a8812312fb2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Mar 2022 16:52:03 +0100 Subject: [PATCH 28/85] flame: improving batch attributes --- openpype/hosts/flame/api/batch_utils.py | 1 + openpype/hosts/flame/plugins/load/load_clip_batch.py | 11 +++++------ .../flame/plugins/publish/integrate_batch_group.py | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 99e053faf13..43742c6e4f9 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -56,6 +56,7 @@ def create_batch(name, frame_start, frame_duration, **kwargs): write_node.frame_padding = write_pref["frame_padding"] write_node.version_mode = write_pref["version_mode"] write_node.version_name = write_pref["version_name"] + write_node.version_padding = write_pref["version_padding"] flame.batch.connect_nodes(comp_node, "Result", write_node, "Front") diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index 81af34744e0..bf0bbb51681 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -108,12 +108,11 @@ def _get_clip(self, name, clip_path): reel = self._get_reel() # with maintained openclip as opc - matching_clip = next( - ( - cl for cl in reel.clips - if cl.name.get_value() == name - ) - ) + matching_clip = None + for cl in reel.clips: + if cl.name.get_value() != name: + continue + matching_clip = cl if not matching_clip: created_clips = flame.import_clips(str(clip_path), reel) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 81b304ff0b4..536bf0d8070 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -249,6 +249,7 @@ def _get_write_prefs(self, instance, task_data): # Only available if create_clip = True. version_mode = "Follow Iteration" version_name = "v" + version_padding = 3 return { "name": name, @@ -266,7 +267,8 @@ def _get_write_prefs(self, instance, task_data): "frame_index_mode": frame_index_mode, "frame_padding": frame_padding, "version_mode": version_mode, - "version_name": version_name + "version_name": version_name, + "version_padding": version_padding } def _get_shot_task_dir_path(self, instance, task_data): From f8e99f38c97cf37b8001e4c5848d93e00a7f9107 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Mar 2022 16:53:47 +0100 Subject: [PATCH 29/85] flame: make dirs for batch renders add one more directory layer for renders --- .../hosts/flame/plugins/publish/integrate_batch_group.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 536bf0d8070..eaab4291119 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -199,6 +199,9 @@ def _get_write_prefs(self, instance, task_data): render_dir_path = os.path.join( task_workfile_path, "render", "flame") + if not os.path.exists(render_dir_path): + os.makedirs(render_dir_path, mode=0o777) + # TODO: add most of these to `imageio/flame/batch/write_node` name = "{project[code]}_{asset}_{task[name]}".format( **anatomy_data @@ -208,7 +211,7 @@ def _get_write_prefs(self, instance, task_data): # /path/to/file.[0001-0010].exr media_path = render_dir_path # name of file represented by tokens - media_path_pattern = "_v." + media_path_pattern = "_v/_v." # The Create Open Clip attribute of the Write File node. \ # Determines if an Open Clip is created by the Write File node. create_clip = True From 08f80ecf15911f1e96808fd8c6032b55d4f596e7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 13:29:16 +0200 Subject: [PATCH 30/85] flame: make sure only one clip in xml --- openpype/hosts/flame/api/plugin.py | 53 ++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 4c9d3c53838..3a322e52080 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,24 +1,22 @@ +import itertools import os import re import shutil import sys +import xml.etree.cElementTree as cET +from copy import deepcopy from xml.etree import ElementTree as ET -import six -import qargparse -from Qt import QtWidgets, QtCore + import openpype.api as openpype -from openpype.pipeline import ( - LegacyCreator, - LoaderPlugin, -) +import qargparse +import six from openpype import style -from . import ( - lib as flib, - pipeline as fpipeline, - constants -) +from openpype.pipeline import LegacyCreator, LoaderPlugin +from Qt import QtCore, QtWidgets -from copy import deepcopy +from . import constants +from . import lib as flib +from . import pipeline as fpipeline log = openpype.Logger.get_logger(__name__) @@ -749,10 +747,39 @@ def _generate_media_info_file(self): # execute creation of clip xml template data try: openpype.run_subprocess(cmd_args) + self._make_single_clip_media_info() except TypeError: self.log.error("Error creating self.tmp_file") six.reraise(*sys.exc_info()) + def _make_single_clip_media_info(self): + with open(self.tmp_file) as f: + lines = f.readlines() + _added_root = itertools.chain( + "", deepcopy(lines)[1:], "") + new_root = ET.fromstringlist(_added_root) + + # find the clip which is matching to my input name + xml_clips = new_root.findall("clip") + matching_clip = None + for xml_clip in xml_clips: + if xml_clip.find("name").text == self.feed_basename: + matching_clip = xml_clip + + if not matching_clip: + # return warning there is missing clip + raise ET.ParseError( + "Missing clip in `{}`. Available clips {}".format( + self.feed_basename, [ + xml_clip.find("name").text + for xml_clip in xml_clips + ] + )) + # save it as new file + tree = cET.ElementTree(matching_clip) + tree.write(self.tmp_file, xml_declaration=True, + method='xml', encoding='UTF-8') + def _clear_tmp_file(self): if os.path.isfile(self.tmp_file): os.remove(self.tmp_file) From 34a65cb646e5d267899f3f3df5eed4de72ac2074 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 13:29:40 +0200 Subject: [PATCH 31/85] flame: ignore clip file with zero lines --- openpype/hosts/flame/api/plugin.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 3a322e52080..949e8ad4067 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -708,19 +708,32 @@ def __init__(self, openclip_file_path, feed_data): self.feed_dir = os.path.dirname(feed_path) self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - if not os.path.isfile(openclip_file_path): + if not self._is_valid_tmp_file(openclip_file_path): # openclip does not exist yet and will be created self.tmp_file = self.out_file = openclip_file_path self.create_new_clip = True else: + # update already created clip # output a temp file self.out_file = openclip_file_path self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) + + # remove previously generated temp files + # it will be regenerated self._clear_tmp_file() self.log.info("Temp File: {}".format(self.tmp_file)) + def _is_valid_tmp_file(self, file): + # check if file exists + if os.path.isfile(file): + with open(self.tmp_file) as f: + lines = f.readlines() + if len(lines) < 1: + self._clear_tmp_file() + return False + def make(self): self._generate_media_info_file() From 1c6ab37f351e87a0a4a01a93eb14de47668333f6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 20:02:58 +0200 Subject: [PATCH 32/85] flame: improving tmp file validation --- openpype/hosts/flame/api/plugin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 949e8ad4067..ab60bbad118 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -730,9 +730,12 @@ def _is_valid_tmp_file(self, file): if os.path.isfile(file): with open(self.tmp_file) as f: lines = f.readlines() - if len(lines) < 1: - self._clear_tmp_file() - return False + if len(lines) > 2: + return True + + # file is probably corrupted + self._clear_tmp_file() + return False def make(self): self._generate_media_info_file() From 0abc8ae61367a3ee03a896704557b324ffc1e1bd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 20:18:59 +0200 Subject: [PATCH 33/85] flame: rework xml write file --- openpype/hosts/flame/api/plugin.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index ab60bbad118..0eba06a86db 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -791,10 +791,8 @@ def _make_single_clip_media_info(self): for xml_clip in xml_clips ] )) - # save it as new file - tree = cET.ElementTree(matching_clip) - tree.write(self.tmp_file, xml_declaration=True, - method='xml', encoding='UTF-8') + + self._write_result_xml_to_file(self.tmp_file, matching_clip) def _clear_tmp_file(self): if os.path.isfile(self.tmp_file): @@ -901,7 +899,7 @@ def _update_open_clip(self): self.log.info("Adding feed version: {}".format( self.feed_version_name)) - self._write_result_xml_to_file(xml_data) + self._write_result_xml_to_file(self.out_file, xml_data) self.log.info("openClip Updated: {}".format(self.out_file)) @@ -940,9 +938,11 @@ def _fix_xml_data(self, xml_data): self._clear_handler(xml_root) return ET.tostring(xml_root).decode('utf-8') - def _write_result_xml_to_file(self, xml_data): - with open(self.out_file, "w") as f: - f.write(xml_data) + def _write_result_xml_to_file(self, file, xml_data): + # save it as new file + tree = cET.ElementTree(xml_data) + tree.write(file, xml_declaration=True, + method='xml', encoding='UTF-8') def _create_openclip_backup_file(self, file): bck_file = "{}.bak".format(file) From ae36d089690f9acb078cc185e5315667523669dc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 20:54:12 +0200 Subject: [PATCH 34/85] flame: little fixes of loading --- openpype/hosts/flame/api/plugin.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 0eba06a86db..3673dc6671b 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -728,13 +728,14 @@ def __init__(self, openclip_file_path, feed_data): def _is_valid_tmp_file(self, file): # check if file exists if os.path.isfile(file): - with open(self.tmp_file) as f: + # test also if file is not empty + with open(file) as f: lines = f.readlines() if len(lines) > 2: return True # file is probably corrupted - self._clear_tmp_file() + os.remove(file) return False def make(self): @@ -779,7 +780,7 @@ def _make_single_clip_media_info(self): xml_clips = new_root.findall("clip") matching_clip = None for xml_clip in xml_clips: - if xml_clip.find("name").text == self.feed_basename: + if xml_clip.find("name").text in self.feed_basename: matching_clip = xml_clip if not matching_clip: From 2bf75d270a3fbfa0054d750159439a52e1f0369f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 21:05:35 +0200 Subject: [PATCH 35/85] flame: fix loaded name to `output` make condition for fix if output is not in context data --- openpype/hosts/flame/plugins/load/load_clip.py | 2 +- openpype/hosts/flame/plugins/load/load_clip_batch.py | 7 ++++++- openpype/settings/defaults/project_settings/flame.json | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/plugins/load/load_clip.py b/openpype/hosts/flame/plugins/load/load_clip.py index 8980f72cb87..b27600db1f1 100644 --- a/openpype/hosts/flame/plugins/load/load_clip.py +++ b/openpype/hosts/flame/plugins/load/load_clip.py @@ -22,7 +22,7 @@ class LoadClip(opfapi.ClipLoader): # settings reel_group_name = "OpenPype_Reels" reel_name = "Loaded" - clip_name_template = "{asset}_{subset}_{representation}" + clip_name_template = "{asset}_{subset}_{output}" def load(self, context, name, namespace, options): diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index bf0bbb51681..1f87f94cc60 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -21,7 +21,7 @@ class LoadClipBatch(opfapi.ClipLoader): # settings reel_name = "OP_LoadedReel" - clip_name_template = "{asset}_{subset}_{representation}" + clip_name_template = "{asset}_{subset}_{output}" def load(self, context, name, namespace, options): @@ -34,6 +34,11 @@ def load(self, context, name, namespace, options): version_data = version.get("data", {}) version_name = version.get("name", None) colorspace = version_data.get("colorspace", None) + + # in case output is not in context replace key to representation + if not context["representation"]["context"].get("output"): + self.clip_name_template.replace("output", "representation") + clip_name = self.clip_name_template.format( **context["representation"]["context"]) diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index afd0834c9dc..ef7a2a4467b 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -91,7 +91,7 @@ ], "reel_group_name": "OpenPype_Reels", "reel_name": "Loaded", - "clip_name_template": "{asset}_{subset}_{representation}" + "clip_name_template": "{asset}_{subset}_{output}" }, "LoadClipBatch": { "enabled": true, @@ -113,7 +113,7 @@ "mp4" ], "reel_name": "OP_LoadedReel", - "clip_name_template": "{asset}_{subset}_{representation}" + "clip_name_template": "{asset}_{subset}_{output}" } } } \ No newline at end of file From eda39b5de29e9bce283a3326427db8508d2cfb05 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 28 Mar 2022 21:12:56 +0200 Subject: [PATCH 36/85] flame: fix write to xml file input args --- openpype/hosts/flame/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 3673dc6671b..750609f7d66 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -829,7 +829,7 @@ def _create_new_open_clip(self): xml_data = self._fix_xml_data(tmp_xml) self.log.info("Adding feed version: {}".format(self.feed_basename)) - self._write_result_xml_to_file(xml_data) + self._write_result_xml_to_file(self.out_file, xml_data) self.log.info("openClip Updated: {}".format(self.tmp_file)) From 44257be4863cc0eb0522ef14aa431bb10344c14c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 29 Mar 2022 08:43:38 +0200 Subject: [PATCH 37/85] flame: fix utf8 error `'unicode' object has no attribute 'getiterator'` --- openpype/hosts/flame/api/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 750609f7d66..d5790d2f109 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -783,7 +783,7 @@ def _make_single_clip_media_info(self): if xml_clip.find("name").text in self.feed_basename: matching_clip = xml_clip - if not matching_clip: + if matching_clip is not None: # return warning there is missing clip raise ET.ParseError( "Missing clip in `{}`. Available clips {}".format( @@ -937,7 +937,7 @@ def _feed_exists(self, xml_data, path): def _fix_xml_data(self, xml_data): xml_root = xml_data.getroot() self._clear_handler(xml_root) - return ET.tostring(xml_root).decode('utf-8') + return xml_root def _write_result_xml_to_file(self, file, xml_data): # save it as new file From 69f5ace08485f0aea46a586602629a21416b779c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 29 Mar 2022 08:46:50 +0200 Subject: [PATCH 38/85] flame: fix condition direction --- openpype/hosts/flame/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index d5790d2f109..464f5ce89bd 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -783,7 +783,7 @@ def _make_single_clip_media_info(self): if xml_clip.find("name").text in self.feed_basename: matching_clip = xml_clip - if matching_clip is not None: + if matching_clip is None: # return warning there is missing clip raise ET.ParseError( "Missing clip in `{}`. Available clips {}".format( From 3459cec3a9adf5537d11c5963e7b33ec9b5d5c2b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 29 Mar 2022 10:29:17 +0200 Subject: [PATCH 39/85] flame: task workdir for .clip when integrating batch --- openpype/hosts/flame/plugins/load/load_clip_batch.py | 2 +- .../flame/plugins/publish/integrate_batch_group.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index 1f87f94cc60..252c92516dc 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -48,7 +48,7 @@ def load(self, context, name, namespace, options): colorspace = colorspace # create workfile path - workfile_dir = os.environ["AVALON_WORKDIR"] + workfile_dir = options.get("workdir") or os.environ["AVALON_WORKDIR"] openclip_dir = os.path.join( workfile_dir, clip_name ) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index eaab4291119..7c61ed62b5a 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -105,7 +105,9 @@ def _load_clip_to_context(self, instance, bgroup): # load to flame by representation context try: op_pipeline.load.load_with_repre_context( - Loader, repre_context) + Loader, repre_context, **{ + "data": {"workdir": self.task_workdir} + }) except op_pipeline.load.IncompatibleLoaderError as msg: self.log.error( "Check allowed representations for Loader `{}` " @@ -192,12 +194,14 @@ def _get_write_prefs(self, instance, task_data): anatomy_data = self._get_anamoty_data_with_current_task( instance, task_data) - task_workfile_path = self._get_shot_task_dir_path(instance, task_data) - self.log.debug("__ task_workfile_path: {}".format(task_workfile_path)) + self.task_workdir = self._get_shot_task_dir_path( + instance, task_data) + self.log.debug("__ task_workdir: {}".format( + self.task_workdir)) # TODO: this might be done with template in settings render_dir_path = os.path.join( - task_workfile_path, "render", "flame") + self.task_workdir, "render", "flame") if not os.path.exists(render_dir_path): os.makedirs(render_dir_path, mode=0o777) From df6499868bca5b0a3eea579591f309b3e78e1f59 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Mar 2022 12:22:43 +0200 Subject: [PATCH 40/85] flame: cleaning code --- openpype/hosts/flame/api/__init__.py | 4 ---- openpype/hosts/flame/api/lib.py | 17 +---------------- openpype/hosts/flame/api/scripts/wiretap_com.py | 2 +- openpype/hosts/flame/plugins/load/load_clip.py | 2 +- .../hosts/flame/plugins/load/load_clip_batch.py | 2 +- .../plugins/publish/integrate_batch_group.py | 3 +++ 6 files changed, 7 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 561aaab3def..28511458c2e 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -11,10 +11,8 @@ from .lib import ( CTX, FlameAppFramework, - get_project_manager, get_current_project, get_current_sequence, - create_bin, create_segment_data_marker, get_segment_data_marker, set_segment_data_marker, @@ -87,10 +85,8 @@ # lib "CTX", "FlameAppFramework", - "get_project_manager", "get_current_project", "get_current_sequence", - "create_bin", "create_segment_data_marker", "get_segment_data_marker", "set_segment_data_marker", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index dd91252a006..7316fa1c5bd 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -227,16 +227,6 @@ def save_prefs(self): return True -def get_project_manager(): - # TODO: get_project_manager - return - - -def get_media_storage(): - # TODO: get_media_storage - return - - def get_current_project(): import flame return flame.project.current_project @@ -266,11 +256,6 @@ def segment_to_sequence(_segment): return process_timeline -def create_bin(name, root=None): - # TODO: create_bin - return - - def rescan_hooks(): import flame try: @@ -724,5 +709,5 @@ def get_batch_group_from_desktop(name): project_desktop = project.current_workspace.desktop for bgroup in project_desktop.batch_groups: - if bgroup.name.get_value() == name: + if bgroup.name.get_value() in name: return bgroup diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index 54993d34eb3..14fbcec9548 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -254,7 +254,7 @@ def _user_prep(self, user_name): filtered_users = [user for user in used_names if user_name in user] if filtered_users: - # todo: need to find lastly created following regex pattern for + # TODO: need to find lastly created following regex pattern for # date used in name return filtered_users.pop() diff --git a/openpype/hosts/flame/plugins/load/load_clip.py b/openpype/hosts/flame/plugins/load/load_clip.py index b27600db1f1..e0a7297381b 100644 --- a/openpype/hosts/flame/plugins/load/load_clip.py +++ b/openpype/hosts/flame/plugins/load/load_clip.py @@ -39,7 +39,7 @@ def load(self, context, name, namespace, options): clip_name = self.clip_name_template.format( **context["representation"]["context"]) - # todo: settings in imageio + # TODO: settings in imageio # convert colorspace with ocio to flame mapping # in imageio flame section colorspace = colorspace diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index 252c92516dc..3c13d88d3a0 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -42,7 +42,7 @@ def load(self, context, name, namespace, options): clip_name = self.clip_name_template.format( **context["representation"]["context"]) - # todo: settings in imageio + # TODO: settings in imageio # convert colorspace with ocio to flame mapping # in imageio flame section colorspace = colorspace diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 7c61ed62b5a..253a1d6192e 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -166,8 +166,11 @@ def _get_batch_group(self, instance, task_data): self.log.info( "Updating batch group: {}".format(batchgroup_name)) # update already created batch group + bgroup.name = batchgroup_name bgroup.start_frame = frame_start bgroup.duration = frame_duration + # TODO: also update write node if there is any + # TODO: also update loaders to start from correct frameStart return bgroup From 5580ef083bd51bba96e11e1d68156d9dbedc4809 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 30 Mar 2022 12:24:12 +0200 Subject: [PATCH 41/85] hound catch --- openpype/hosts/flame/plugins/publish/integrate_batch_group.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 253a1d6192e..4dd60811704 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -218,7 +218,8 @@ def _get_write_prefs(self, instance, task_data): # /path/to/file.[0001-0010].exr media_path = render_dir_path # name of file represented by tokens - media_path_pattern = "_v/_v." + media_path_pattern = ( + "_v/_v.") # The Create Open Clip attribute of the Write File node. \ # Determines if an Open Clip is created by the Write File node. create_clip = True From 1926e107659790d10a63770f70b72a6f7cf88ef1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 4 Apr 2022 20:50:58 +0200 Subject: [PATCH 42/85] flame: redundant code --- openpype/hosts/flame/plugins/publish/integrate_batch_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 4dd60811704..fc5f4cfcd08 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -7,7 +7,7 @@ import openpype.pipeline as op_pipeline -@pyblish.api.log + class IntegrateBatchGroup(pyblish.api.InstancePlugin): """Integrate published shot to batch group""" From 54897163caee3dbb783bcafd68b7648c99434c9d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 4 Apr 2022 20:52:09 +0200 Subject: [PATCH 43/85] haunch catch --- openpype/hosts/flame/plugins/publish/integrate_batch_group.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index fc5f4cfcd08..a9ccd6b4a1d 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -7,7 +7,6 @@ import openpype.pipeline as op_pipeline - class IntegrateBatchGroup(pyblish.api.InstancePlugin): """Integrate published shot to batch group""" From 0dfca2ff4589e996708c9d27108e282d9107d847 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 11:38:00 +0200 Subject: [PATCH 44/85] Flame: refining the code for better understanding of flow --- .../plugins/publish/integrate_batch_group.py | 78 ++++++++----------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index a9ccd6b4a1d..979134bbfe6 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -38,44 +38,40 @@ def process(self, instance): def _load_clip_to_context(self, instance, bgroup): # get all loaders for host - loaders = op_pipeline.discover_loader_plugins() + loaders_by_name = { + loader.__name__: loader + for loader in op_pipeline.discover_loader_plugins() + } # get all published representations published_representations = instance.data["published_representations"] + repres_db_id_by_name = { + repre_info["representation"]["name"]: repre_id + for repre_id, repre_info in published_representations.items() + } # get all loadable representations - representations = instance.data["representations"] + repres_by_name = { + repre["name"]: repre for repre in instance.data["representations"] + } # get repre_id for the loadable representations - loadable_representations = [ - { - "name": _repr["name"], - "loader": _repr.get("batch_group_loader_name"), - # match loader to the loadable representation - "_id": next( - ( - id - for id, repr in published_representations.items() - if repr["representation"]["name"] == _repr["name"] - ), - None - ) + loader_name_by_repre_id = { + repres_db_id_by_name[repr_name]: { + "loader": repr_data["batch_group_loader_name"], + # add repre data for exception logging + "_repre_data": repr_data } - for _repr in representations - if _repr.get("load_to_batch_group") is not None - ] + for repr_name, repr_data in repres_by_name.items() + if repr_data.get("load_to_batch_group") + } - self.log.debug("__ loadable_representations: {}".format(pformat( - loadable_representations))) + self.log.debug("__ loader_name_by_repre_id: {}".format(pformat( + loader_name_by_repre_id))) # get representation context from the repre_id - representation_ids = [ - repre["_id"] - for repre in loadable_representations - if repre["_id"] is not None - ] repre_contexts = op_pipeline.load.get_repres_contexts( - representation_ids) + loader_name_by_repre_id.keys()) self.log.debug("__ repre_contexts: {}".format(pformat( repre_contexts))) @@ -84,45 +80,37 @@ def _load_clip_to_context(self, instance, bgroup): for repre_id, repre_context in repre_contexts.items(): self.log.debug("__ repre_id: {}".format(repre_id)) # get loader name by representation id - loader_name = next( - ( - repr["loader"] - for repr in loadable_representations - if repr["_id"] == repre_id - )) or self.default_loader + loader_name = ( + loader_name_by_repre_id[repre_id]["loader"] + # if nothing was added to settings fallback to default + or self.default_loader + ) # get loader plugin - Loader = next( - ( - loader_plugin - for loader_plugin in loaders - if loader_plugin.__name__ == loader_name - ), - None - ) - if Loader: + loader_plugin = loaders_by_name.get(loader_name) + if loader_plugin: # load to flame by representation context try: op_pipeline.load.load_with_repre_context( - Loader, repre_context, **{ + loader_plugin, repre_context, **{ "data": {"workdir": self.task_workdir} }) except op_pipeline.load.IncompatibleLoaderError as msg: self.log.error( "Check allowed representations for Loader `{}` " "in settings > error: {}".format( - Loader.__name__, msg)) + loader_plugin.__name__, msg)) self.log.error( "Representaton context >>{}<< is not compatible " "with loader `{}`".format( - pformat(repre_context), Loader.__name__ + pformat(repre_context), loader_plugin.__name__ ) ) else: self.log.warning( "Something got wrong and there is not Loader found for " "following data: {}".format( - pformat(loadable_representations)) + pformat(loader_name_by_repre_id)) ) def _get_batch_group(self, instance, task_data): From 5b260afc6af67bbcda403e7ce35922e7a8350ff6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 15:47:57 +0200 Subject: [PATCH 45/85] flame: avoid hidden segment processing --- openpype/hosts/flame/otio/flame_export.py | 43 +++++++++++------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 8c240fc9d53..f9dbe68421b 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -401,8 +401,10 @@ def get_clips_in_reels(project): version = clip.versions[-1] track = version.tracks[-1] + # each reel clip is also having one segment for segment in track.segments: - segment_data = _get_segment_attributes(segment) + segment_data = _get_segment_attributes( + segment, from_clip=True) clip_data.update(segment_data) output_clips.append(clip_data) @@ -489,12 +491,14 @@ def add_otio_metadata(otio_item, item, **kwargs): otio_item.metadata.update({key: value}) -def _get_shot_tokens_values(clip, tokens): +def _get_shot_tokens_values(clip, tokens, from_clip=False): old_value = None output = {} - if not clip.shot_name: - return output + # in case it is segment from reel clip + # avoiding duplicity of segement data + if from_clip: + return {} old_value = clip.shot_name.get_value() @@ -512,16 +516,19 @@ def _get_shot_tokens_values(clip, tokens): return output -def _get_segment_attributes(segment): +def _get_segment_attributes(segment, from_clip=False): # log.debug(dir(segment)) - - if str(segment.name)[1:-1] == "": + if ( + segment.name.get_value() == "" + or segment.hidden + ): return None # Add timeline segment to tree clip_data = { "segment_name": segment.name.get_value(), "segment_comment": segment.comment.get_value(), + "shot_name": segment.shot_name.get_value(), "tape_name": segment.tape_name, "source_name": segment.source_name, "fpath": segment.file_path, @@ -531,7 +538,7 @@ def _get_segment_attributes(segment): # add all available shot tokens shot_tokens = _get_shot_tokens_values(segment, [ "", "", "", "", - ]) + ], from_clip) clip_data.update(shot_tokens) # populate shot source metadata @@ -597,11 +604,7 @@ def create_otio_timeline(sequence): continue all_segments.append(clip_data) - segments_ordered = { - itemindex: clip_data - for itemindex, clip_data in enumerate( - all_segments) - } + segments_ordered = dict(enumerate(all_segments)) log.debug("_ segments_ordered: {}".format( pformat(segments_ordered) )) @@ -612,15 +615,11 @@ def create_otio_timeline(sequence): log.debug("_ itemindex: {}".format(itemindex)) # Add Gap if needed - if itemindex == 0: - # if it is first track item at track then add - # it to previous item - prev_item = segment_data - - else: - # get previous item - prev_item = segments_ordered[itemindex - 1] - + prev_item = ( + segment_data + if itemindex == 0 + else segments_ordered[itemindex - 1] + ) log.debug("_ segment_data: {}".format(segment_data)) # calculate clip frame range difference from each other From 575898490f4aad68256207da0dea4f9960e2948f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 15:57:16 +0200 Subject: [PATCH 46/85] flame: fixing broken get_clips_in_reels --- openpype/hosts/flame/otio/flame_export.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index f9dbe68421b..78e5ceecb66 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -405,7 +405,8 @@ def get_clips_in_reels(project): for segment in track.segments: segment_data = _get_segment_attributes( segment, from_clip=True) - clip_data.update(segment_data) + if segment_data: + clip_data.update(segment_data) output_clips.append(clip_data) From 8d4541d68da458f5121029353119ab8ae7ff4791 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 16:29:04 +0200 Subject: [PATCH 47/85] flame: hidden attribute is PyAttribute so need to get value --- openpype/hosts/flame/otio/flame_export.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 78e5ceecb66..1b5980b40a7 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -518,10 +518,13 @@ def _get_shot_tokens_values(clip, tokens, from_clip=False): def _get_segment_attributes(segment, from_clip=False): - # log.debug(dir(segment)) + + log.debug("Segment name|hidden: {}|{}".format( + segment.name.get_value(), segment.hidden + )) if ( segment.name.get_value() == "" - or segment.hidden + or segment.hidden.get_value() ): return None @@ -591,7 +594,12 @@ def create_otio_timeline(sequence): # create otio tracks and clips for ver in sequence.versions: for track in ver.tracks: - if len(track.segments) == 0 and track.hidden: + # avoid all empty tracks + # or hidden tracks + if ( + len(track.segments) == 0 + or track.hidden.get_value() + ): return None # convert track to otio From 60118298b6c3dcf2f41488624a3b6d3bf9166990 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 16:29:31 +0200 Subject: [PATCH 48/85] flame: make reel clip validation optional --- openpype/hosts/flame/plugins/publish/validate_source_clip.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/validate_source_clip.py b/openpype/hosts/flame/plugins/publish/validate_source_clip.py index 9ff015f6280..345c00e05ab 100644 --- a/openpype/hosts/flame/plugins/publish/validate_source_clip.py +++ b/openpype/hosts/flame/plugins/publish/validate_source_clip.py @@ -9,6 +9,8 @@ class ValidateSourceClip(pyblish.api.InstancePlugin): label = "Validate Source Clip" hosts = ["flame"] families = ["clip"] + optional = True + active = False def process(self, instance): flame_source_clip = instance.data["flameSourceClip"] From 818c3fe91f420de9b08ff4ebfc1bdae301a42927 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 16:58:25 +0200 Subject: [PATCH 49/85] flame: fallback if reel clip is not available --- .../publish/extract_subset_resources.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 31f7b6d5740..341f12be16d 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -63,7 +63,10 @@ def process(self, instance): segment = instance.data["item"] sequence_clip = instance.context.data["flameSequence"] clip_data = instance.data["flameSourceClip"] - clip = clip_data["PyClip"] + + reel_clip = None + if clip_data: + reel_clip = clip_data["PyClip"] # segment's parent track name s_track_name = segment.parent.name.get_value() @@ -127,8 +130,20 @@ def process(self, instance): in_mark = (source_start_handles - source_first_frame) + 1 out_mark = in_mark + source_duration_handles + # make test for type of preset and available reel_clip + if ( + not reel_clip + and export_type != "Sequence Publish" + ): + self.log.warning(( + "Skipping preset {}. Not available " + "reel clip for {}").format( + preset_file, segment.name.get_value() + )) + continue + # by default export source clips - exporting_clip = clip + exporting_clip = reel_clip if export_type == "Sequence Publish": # change export clip to sequence @@ -344,7 +359,7 @@ def hide_other_tracks(self, sequence_clip, track_name): # create otio tracks and clips for ver in sequence_clip.versions: for track in ver.tracks: - if len(track.segments) == 0 and track.hidden: + if len(track.segments) == 0 and track.hidden.get_value(): continue if track.name.get_value() != track_name: From 246127c73b7d06bf5e7a2b68a7fb0bd31e949b22 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 5 Apr 2022 17:20:41 +0200 Subject: [PATCH 50/85] flame: hidding all unrelated segments and tracks --- .../plugins/publish/extract_subset_resources.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 341f12be16d..a780f8c9e5c 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -61,6 +61,7 @@ def process(self, instance): # flame objects segment = instance.data["item"] + segment_name = segment.name.get_value() sequence_clip = instance.context.data["flameSequence"] clip_data = instance.data["flameSourceClip"] @@ -138,7 +139,7 @@ def process(self, instance): self.log.warning(( "Skipping preset {}. Not available " "reel clip for {}").format( - preset_file, segment.name.get_value() + preset_file, segment_name )) continue @@ -175,7 +176,7 @@ def process(self, instance): if export_type == "Sequence Publish": # only keep visible layer where instance segment is child - self.hide_other_tracks(duplclip, s_track_name) + self.hide_others(duplclip, segment_name, s_track_name) # validate xml preset file is filled if preset_file == "": @@ -349,11 +350,12 @@ def _unfolds_nested_folders(self, stage_dir, files_list, ext): return new_stage_dir, new_files_list - def hide_other_tracks(self, sequence_clip, track_name): + def hide_others(self, sequence_clip, segment_name, track_name): """Helper method used only if sequence clip is used Args: sequence_clip (flame.Clip): sequence clip + segment_name (str): segment name track_name (str): track name """ # create otio tracks and clips @@ -362,5 +364,12 @@ def hide_other_tracks(self, sequence_clip, track_name): if len(track.segments) == 0 and track.hidden.get_value(): continue + # hide tracks which are not parent track if track.name.get_value() != track_name: track.hidden = True + continue + + # hidde all other segments + for segment in track.segments: + if segment.name.get_value() != segment_name: + segment.hidden = True From 87f40d53e098ce3cef8bc31fc733e73df36f9cc3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Apr 2022 14:44:03 +0200 Subject: [PATCH 51/85] flame: adding media info getter obj --- openpype/hosts/flame/api/lib.py | 116 +++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 7316fa1c5bd..4e989abb9ec 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -1,9 +1,14 @@ import sys import os import re +import six import json import pickle +import itertools import contextlib +import xml.etree.cElementTree as cET +from copy import deepcopy +from xml.etree import ElementTree as ET from pprint import pformat from .constants import ( MARKER_COLOR, @@ -12,9 +17,10 @@ COLOR_MAP, MARKER_PUBLISH_DEFAULT ) -from openpype.api import Logger -log = Logger.get_logger(__name__) +import openpype.api as openpype + +log = openpype.Logger.get_logger(__name__) FRAME_PATTERN = re.compile(r"[\._](\d+)[\.]") @@ -711,3 +717,109 @@ def get_batch_group_from_desktop(name): for bgroup in project_desktop.batch_groups: if bgroup.name.get_value() in name: return bgroup + + +class MediaInfoFile: + media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" + tmp_name = "_tmp.clip" + tmp_file = None + + out_feed_nb_ticks = None + out_feed_fps = None + out_feed_drop_mode = None + + log = log + + def __init__(self, path): + # test if media script paht exists + self._validate_media_script_path() + + # derivate other feed variables + self.feed_basename = os.path.basename(path) + self.feed_dir = os.path.dirname(path) + self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() + + self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) + + # remove previously generated temp files + # it will be regenerated + self._clear_tmp_file() + + self.log.info("Temp File: {}".format(self.tmp_file)) + + def _validate_media_script_path(self): + if not os.path.isfile(self.media_script_path): + raise IOError("Media Scirpt does not exist: `{}`".format( + self.media_script_path)) + + def _generate_media_info_file(self): + # Create cmd arguments for gettig xml file info file + cmd_args = [ + self.media_script_path, + "-e", self.feed_ext, + "-o", self.tmp_file, + self.feed_dir + ] + + # execute creation of clip xml template data + try: + openpype.run_subprocess(cmd_args) + self._make_single_clip_media_info() + except TypeError: + self.log.error("Error creating self.tmp_file") + six.reraise(*sys.exc_info()) + + def _make_single_clip_media_info(self): + with open(self.tmp_file) as f: + lines = f.readlines() + _added_root = itertools.chain( + "", deepcopy(lines)[1:], "") + new_root = ET.fromstringlist(_added_root) + + # find the clip which is matching to my input name + xml_clips = new_root.findall("clip") + matching_clip = None + for xml_clip in xml_clips: + if xml_clip.find("name").text in self.feed_basename: + matching_clip = xml_clip + + if matching_clip is None: + # return warning there is missing clip + raise ET.ParseError( + "Missing clip in `{}`. Available clips {}".format( + self.feed_basename, [ + xml_clip.find("name").text + for xml_clip in xml_clips + ] + )) + + self._write_result_xml_to_file(self.tmp_file, matching_clip) + + def _clear_tmp_file(self): + if os.path.isfile(self.tmp_file): + os.remove(self.tmp_file) + + def _get_time_info_from_origin(self, xml_data): + try: + for out_track in xml_data.iter('track'): + for out_feed in out_track.iter('feeds'): + out_feed_nb_ticks_obj = out_feed.find( + 'startTimecode/nbTicks') + self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text + out_feed_fps_obj = out_feed.find( + 'startTimecode/rate') + self.out_feed_fps = out_feed_fps_obj.text + out_feed_drop_mode_obj = out_feed.find( + 'startTimecode/dropMode') + self.out_feed_drop_mode = out_feed_drop_mode_obj.text + break + else: + continue + except Exception as msg: + self.log.warning(msg) + + def _write_result_xml_to_file(self, file, xml_data): + # save it as new file + tree = cET.ElementTree(xml_data) + tree.write(file, xml_declaration=True, + method='xml', encoding='UTF-8') \ No newline at end of file From 7dc2c618a0b6ffd80529620d576f929afaa01cf0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Apr 2022 14:53:13 +0200 Subject: [PATCH 52/85] flame: update media info file generator --- openpype/hosts/flame/api/lib.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 4e989abb9ec..633854168a0 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -724,6 +724,7 @@ class MediaInfoFile: tmp_name = "_tmp.clip" tmp_file = None + clip_data = None out_feed_nb_ticks = None out_feed_fps = None out_feed_drop_mode = None @@ -752,7 +753,7 @@ def _validate_media_script_path(self): raise IOError("Media Scirpt does not exist: `{}`".format( self.media_script_path)) - def _generate_media_info_file(self): + def generate_media_info_file(self): # Create cmd arguments for gettig xml file info file cmd_args = [ self.media_script_path, @@ -793,6 +794,8 @@ def _make_single_clip_media_info(self): ] )) + self._get_time_info_from_origin(matching_clip) + self.clip_data = matching_clip self._write_result_xml_to_file(self.tmp_file, matching_clip) def _clear_tmp_file(self): From d79773e801b428ee629521a65374406cda8b8bfc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Apr 2022 15:42:58 +0200 Subject: [PATCH 53/85] flame: fixing feeds to feed tag --- openpype/hosts/flame/api/lib.py | 2 +- openpype/hosts/flame/api/plugin.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 633854168a0..508dc0155f9 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -805,7 +805,7 @@ def _clear_tmp_file(self): def _get_time_info_from_origin(self, xml_data): try: for out_track in xml_data.iter('track'): - for out_feed in out_track.iter('feeds'): + for out_feed in out_track.iter('feed'): out_feed_nb_ticks_obj = out_feed.find( 'startTimecode/nbTicks') self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 464f5ce89bd..bd0f9f1a815 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -679,6 +679,7 @@ class ClipLoader(LoaderPlugin): ] +# TODO: inheritance from flame.api.lib.MediaInfoFile class OpenClipSolver: media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" tmp_name = "_tmp.clip" From 67c759ae2d72558c2caa5cfcc30266a1d16cddb9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Apr 2022 15:45:42 +0200 Subject: [PATCH 54/85] flame: add media info file class to api --- openpype/hosts/flame/api/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 28511458c2e..1308b04a7d9 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -28,7 +28,8 @@ get_padding_from_filename, maintained_object_duplication, get_clip_segment, - get_batch_group_from_desktop + get_batch_group_from_desktop, + MediaInfoFile ) from .utils import ( setup, @@ -103,6 +104,7 @@ "maintained_object_duplication", "get_clip_segment", "get_batch_group_from_desktop", + "MediaInfoFile", # pipeline "install", From 6c9f9c18119cc286078cd109d3253073a3d273aa Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Apr 2022 16:08:31 +0200 Subject: [PATCH 55/85] flame: add generator into init of class --- openpype/hosts/flame/api/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 508dc0155f9..998d7dfa7ec 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -748,12 +748,14 @@ def __init__(self, path): self.log.info("Temp File: {}".format(self.tmp_file)) + self._generate_media_info_file() + def _validate_media_script_path(self): if not os.path.isfile(self.media_script_path): raise IOError("Media Scirpt does not exist: `{}`".format( self.media_script_path)) - def generate_media_info_file(self): + def _generate_media_info_file(self): # Create cmd arguments for gettig xml file info file cmd_args = [ self.media_script_path, From f43e6f0fcdfdb82876c06f6631654a3318edf053 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Apr 2022 16:08:48 +0200 Subject: [PATCH 56/85] flame: get real source_in --- openpype/hosts/flame/otio/flame_export.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 1b5980b40a7..3e769689637 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -7,6 +7,7 @@ import logging import opentimelineio as otio from . import utils +from openpype.hosts.flame.api import MediaInfoFile import flame from pprint import pformat @@ -345,7 +346,13 @@ def create_otio_clip(clip_data): media_reference = create_otio_reference(clip_data) # calculate source in - first_frame = utils.get_frame_from_filename(clip_data["fpath"]) or 0 + media_info = MediaInfoFile(clip_data["fpath"]) + xml_timecode_ticks = media_info.out_feed_nb_ticks + if xml_timecode_ticks: + first_frame = int(xml_timecode_ticks) + else: + 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 From c2788070a370a39536e134acc13a5f5243b2530f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 12:22:34 +0200 Subject: [PATCH 57/85] flame: refactoring batch group creation --- openpype/hosts/flame/api/__init__.py | 6 +- openpype/hosts/flame/api/batch_utils.py | 152 +++++++++++++----- .../plugins/publish/integrate_batch_group.py | 98 +++++++---- 3 files changed, 185 insertions(+), 71 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 1308b04a7d9..a0c40904ed2 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -72,7 +72,8 @@ modify_preset_file ) from .batch_utils import ( - create_batch + create_batch_group, + create_batch_group_conent ) __all__ = [ @@ -148,5 +149,6 @@ "modify_preset_file", # batch utils - "create_batch" + "create_batch_group", + "create_batch_group_conent" ] diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 43742c6e4f9..26f324090be 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -1,66 +1,134 @@ import flame -def create_batch(name, frame_start, frame_duration, **kwargs): +def create_batch_group( + name, + frame_start, + frame_duration, + update_batch_group=None, + **kwargs +): """Create Batch Group in active project's Desktop Args: name (str): name of batch group to be created frame_start (int): start frame of batch frame_end (int): end frame of batch + update_batch_group (PyBatch)[optional]: batch group to update + + Return: + PyBatch: active flame batch group """ + # make sure some batch obj is present + batch_group = update_batch_group or flame.batch + schematic_reels = kwargs.get("shematic_reels") or ['LoadedReel1'] shelf_reels = kwargs.get("shelf_reels") or ['ShelfReel1'] - write_pref = kwargs["write_pref"] handle_start = kwargs.get("handleStart") or 0 handle_end = kwargs.get("handleEnd") or 0 frame_start -= handle_start frame_duration += handle_start + handle_end - # Create batch group with name, start_frame value, duration value, - # set of schematic reel names, set of shelf reel names - bgroup = flame.batch.create_batch_group( - name, - start_frame=frame_start, - duration=frame_duration, - reels=schematic_reels, - shelf_reels=shelf_reels - ) + if not update_batch_group: + # Create batch group with name, start_frame value, duration value, + # set of schematic reel names, set of shelf reel names + batch_group = batch_group.create_batch_group( + name, + start_frame=frame_start, + duration=frame_duration, + reels=schematic_reels, + shelf_reels=shelf_reels + ) + else: + batch_group.name = name + batch_group.start_frame = frame_start + batch_group.duration = frame_duration + + # add reels to batch group + _add_reels_to_batch_group( + batch_group, schematic_reels, shelf_reels) + + # TODO: also update write node if there is any + # TODO: also update loaders to start from correct frameStart if kwargs.get("switch_batch_tab"): # use this command to switch to the batch tab - flame.batch.go_to() - - comp_node = flame.batch.create_node("Comp") - - # TODO: convert this to iterational processing, - # so it could be driven from `imageio` settigns - # create write node - write_node = flame.batch.create_node('Write File') - # assign attrs - write_node.name = write_pref["name"] - write_node.media_path = write_pref["media_path"] - write_node.media_path_pattern = write_pref["media_path_pattern"] - write_node.create_clip = write_pref["create_clip"] - write_node.include_setup = write_pref["include_setup"] - write_node.create_clip_path = write_pref["create_clip_path"] - write_node.include_setup_path = write_pref["include_setup_path"] - write_node.file_type = write_pref["file_type"] - write_node.format_extension = write_pref["format_extension"] - write_node.bit_depth = write_pref["bit_depth"] - write_node.compress = write_pref["compress"] - write_node.compress_mode = write_pref["compress_mode"] - write_node.frame_index_mode = write_pref["frame_index_mode"] - write_node.frame_padding = write_pref["frame_padding"] - write_node.version_mode = write_pref["version_mode"] - write_node.version_name = write_pref["version_name"] - write_node.version_padding = write_pref["version_padding"] - - flame.batch.connect_nodes(comp_node, "Result", write_node, "Front") + batch_group.go_to() + + return batch_group - # sort batch nodes - flame.batch.organize() - return bgroup +def _add_reels_to_batch_group(batch_group, reels, shelf_reels): + # update or create defined reels + # helper variables + reel_names = [ + r.name.get_value() + for r in batch_group.reels + ] + shelf_reel_names = [ + r.name.get_value() + for r in batch_group.shelf_reels + ] + # add schematic reels + for _r in reels: + if _r in reel_names: + continue + batch_group.create_reel(_r) + + # add shelf reels + for _sr in shelf_reels: + if _sr in shelf_reel_names: + continue + batch_group.create_shelf_reel(_sr) + + +def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): + """Creating batch group with links + + Args: + batch_nodes (list of dict): each dict is node definition + batch_links (list of dict): each dict is link definition + batch_group (PyBatch, optional): batch group. Defaults to None. + """ + # make sure some batch obj is present + batch_group = batch_group or flame.batch + + created_nodes = {} + for node in batch_nodes: + # NOTE: node_props needs to be ideally OrederDict type + node_id, node_type, node_props = ( + node["id"], node["type"], node["properties"]) + + # create batch node + batch_node = batch_group.create_node(node_type) + + # set attributes found in node props + for key, value in node_props.items(): + if not hasattr(batch_node, key): + continue + setattr(batch_node, key, value) + + # add created node for possible linking + created_nodes[node_id] = batch_node + + # link nodes to each other + for link in batch_links: + _from_n, _to_n = link["from_node"], link["to_node"] + + # check if all linking nodes are available + if not all([ + created_nodes.get(_from_n["id"]), + created_nodes.get(_to_n["id"]) + ]): + continue + + # link nodes in defined link + batch_group.connect_nodes( + created_nodes[_from_n["id"]], _from_n["connector"], + created_nodes[_to_n["id"]], _to_n["connector"] + ) + + # sort batch nodes + batch_group.organize() diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 979134bbfe6..524d9b1ac2a 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -1,5 +1,6 @@ import os import copy +from collections import OrderedDict from pprint import pformat import pyblish from openpype.lib import get_workdir @@ -30,12 +31,48 @@ def process(self, instance): # create or get already created batch group bgroup = self._get_batch_group(instance, task_data) + # add batch group content + self._add_nodes_to_batch_with_links(instance, task_data, bgroup) + # load plate to batch group self.log.info("Loading subset `{}` into batch `{}`".format( instance.data["subset"], bgroup.name.get_value() )) self._load_clip_to_context(instance, bgroup) + def _add_nodes_to_batch_with_links(self, instance, task_data, batch_group): + # get write file node properties > OrederDict because order does mater + write_pref_data = self._get_write_prefs(instance, task_data) + + batch_nodes = [ + { + "type": "comp", + "properties": {}, + "id": "comp_node01" + }, + { + "type": "Write File", + "properties": write_pref_data, + "id": "write_file_node01" + } + ] + batch_links = [ + { + "from_node": { + "id": "comp_node01", + "connector": "Result" + }, + "to_node": { + "id": "write_file_node01", + "connector": "Front" + } + } + ] + + # add nodes into batch group + opfapi.create_batch_group_conent( + batch_nodes, batch_links, batch_group) + def _load_clip_to_context(self, instance, bgroup): # get all loaders for host loaders_by_name = { @@ -123,13 +160,11 @@ def _get_batch_group(self, instance, task_data): task_name = task_data["name"] batchgroup_name = "{}_{}".format(asset_name, task_name) - write_pref_data = self._get_write_prefs(instance, task_data) batch_data = { "shematic_reels": [ "OP_LoadedReel" ], - "write_pref": write_pref_data, "handleStart": handle_start, "handleEnd": handle_end } @@ -143,21 +178,24 @@ def _get_batch_group(self, instance, task_data): self.log.info( "Creating new batch group: {}".format(batchgroup_name)) # create batch with utils - bgroup = opfapi.create_batch( + bgroup = opfapi.create_batch_group( batchgroup_name, frame_start, frame_duration, **batch_data ) + else: self.log.info( "Updating batch group: {}".format(batchgroup_name)) # update already created batch group - bgroup.name = batchgroup_name - bgroup.start_frame = frame_start - bgroup.duration = frame_duration - # TODO: also update write node if there is any - # TODO: also update loaders to start from correct frameStart + bgroup = opfapi.create_batch_group( + batchgroup_name, + frame_start, + frame_duration, + update_batch_group=bgroup, + **batch_data + ) return bgroup @@ -249,25 +287,31 @@ def _get_write_prefs(self, instance, task_data): version_name = "v" version_padding = 3 - return { - "name": name, - "media_path": media_path, - "media_path_pattern": media_path_pattern, - "create_clip": create_clip, - "include_setup": include_setup, - "create_clip_path": create_clip_path, - "include_setup_path": include_setup_path, - "file_type": file_type, - "format_extension": format_extension, - "bit_depth": bit_depth, - "compress": compress, - "compress_mode": compress_mode, - "frame_index_mode": frame_index_mode, - "frame_padding": frame_padding, - "version_mode": version_mode, - "version_name": version_name, - "version_padding": version_padding - } + # return it as ordered dict + reutrn_dict = OrderedDict() + # need to make sure the order of keys is correct + for item in ( + ("name", name), + ("media_path", media_path), + ("media_path_pattern", media_path_pattern), + ("create_clip", create_clip), + ("include_setup", include_setup), + ("create_clip_path", create_clip_path), + ("include_setup_path", include_setup_path), + ("file_type", file_type), + ("format_extension", format_extension), + ("bit_depth", bit_depth), + ("compress", compress), + ("compress_mode", compress_mode), + ("frame_index_mode", frame_index_mode), + ("frame_padding", frame_padding), + ("version_mode", version_mode), + ("version_name", version_name), + ("version_padding", version_padding) + ): + reutrn_dict.update({item[0]: item[1]}) + + return reutrn_dict def _get_shot_task_dir_path(self, instance, task_data): project_doc = instance.data["projectEntity"] From c2a41760676afceb065351520db9cfeec62b9d33 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 13:30:43 +0200 Subject: [PATCH 58/85] flame: fix integrating batch group to loader with bgroup --- openpype/hosts/flame/plugins/load/load_clip_batch.py | 2 +- .../hosts/flame/plugins/publish/integrate_batch_group.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/load/load_clip_batch.py b/openpype/hosts/flame/plugins/load/load_clip_batch.py index 3c13d88d3a0..5de32260358 100644 --- a/openpype/hosts/flame/plugins/load/load_clip_batch.py +++ b/openpype/hosts/flame/plugins/load/load_clip_batch.py @@ -26,7 +26,7 @@ class LoadClipBatch(opfapi.ClipLoader): def load(self, context, name, namespace, options): # get flame objects - self.batch = flame.batch + self.batch = options.get("batch") or flame.batch # load clip to timeline and get main variables namespace = namespace diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 524d9b1ac2a..c70c2baa4a9 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -130,7 +130,10 @@ def _load_clip_to_context(self, instance, bgroup): try: op_pipeline.load.load_with_repre_context( loader_plugin, repre_context, **{ - "data": {"workdir": self.task_workdir} + "data": { + "workdir": self.task_workdir, + "batch": bgroup + } }) except op_pipeline.load.IncompatibleLoaderError as msg: self.log.error( From 614c49b57f4acd32d9d6db9cf97f6e0c628ce33d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 14:27:00 +0200 Subject: [PATCH 59/85] flame: fix wiretap with umask --- openpype/hosts/flame/api/scripts/wiretap_com.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index 14fbcec9548..d904e7850a2 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -185,7 +185,9 @@ def _project_prep(self, project_name): exit_code = subprocess.call( project_create_cmd, - cwd=os.path.expanduser('~')) + cwd=os.path.expanduser('~'), + preexec_fn=_subprocess_preexec_fn + ) if exit_code != 0: RuntimeError("Cannot create project in flame db") @@ -448,7 +450,9 @@ def _set_project_colorspace(self, project_name, color_policy): exit_code = subprocess.call( project_colorspace_cmd, - cwd=os.path.expanduser('~')) + cwd=os.path.expanduser('~'), + preexec_fn=_subprocess_preexec_fn + ) if exit_code != 0: RuntimeError("Cannot set colorspace {} on project {}".format( @@ -456,6 +460,11 @@ def _set_project_colorspace(self, project_name, color_policy): )) +def _subprocess_preexec_fn(): + os.setpgrp() + os.umask(0o022) + + if __name__ == "__main__": # get json exchange data json_path = sys.argv[-1] From ce4aa40f217857ae527800667d243e048ff40159 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 14:52:13 +0200 Subject: [PATCH 60/85] flame: fixing umask to 0o000 to reflect permissions to 0777 --- openpype/hosts/flame/api/scripts/wiretap_com.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index d904e7850a2..f78102c0a13 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -462,7 +462,7 @@ def _set_project_colorspace(self, project_name, color_policy): def _subprocess_preexec_fn(): os.setpgrp() - os.umask(0o022) + os.umask(0o000) if __name__ == "__main__": From cd59b3af66e6bd39e20cce1ec2d1195a26225e9e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 14:53:29 +0200 Subject: [PATCH 61/85] flame: make sure nodes are not duplicated and update --- openpype/hosts/flame/api/batch_utils.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 26f324090be..23d16f8d1ac 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -94,15 +94,25 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): """ # make sure some batch obj is present batch_group = batch_group or flame.batch - + all_batch_nodes = { + b.name.get_value(): b + for b in batch_group.nodes + } created_nodes = {} for node in batch_nodes: # NOTE: node_props needs to be ideally OrederDict type node_id, node_type, node_props = ( node["id"], node["type"], node["properties"]) - # create batch node - batch_node = batch_group.create_node(node_type) + # get node name for checking if exists + node_name = node_props.get("name") or node_id + + if all_batch_nodes.get(node_name): + # update existing batch node + batch_node = all_batch_nodes[node_name] + else: + # create new batch node + batch_node = batch_group.create_node(node_type) # set attributes found in node props for key, value in node_props.items(): From 4ed6c0257ece24cc254afb4edc9a4cc698c4dd53 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 15:05:46 +0200 Subject: [PATCH 62/85] flame: returning all batch nodes --- openpype/hosts/flame/api/batch_utils.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 23d16f8d1ac..20118c249c5 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -91,6 +91,9 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): batch_nodes (list of dict): each dict is node definition batch_links (list of dict): each dict is link definition batch_group (PyBatch, optional): batch group. Defaults to None. + + Return: + dict: all batch nodes {name or id: PyNode} """ # make sure some batch obj is present batch_group = batch_group or flame.batch @@ -98,7 +101,6 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): b.name.get_value(): b for b in batch_group.nodes } - created_nodes = {} for node in batch_nodes: # NOTE: node_props needs to be ideally OrederDict type node_id, node_type, node_props = ( @@ -121,7 +123,7 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): setattr(batch_node, key, value) # add created node for possible linking - created_nodes[node_id] = batch_node + all_batch_nodes[node_id] = batch_node # link nodes to each other for link in batch_links: @@ -129,16 +131,18 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): # check if all linking nodes are available if not all([ - created_nodes.get(_from_n["id"]), - created_nodes.get(_to_n["id"]) + all_batch_nodes.get(_from_n["id"]), + all_batch_nodes.get(_to_n["id"]) ]): continue # link nodes in defined link batch_group.connect_nodes( - created_nodes[_from_n["id"]], _from_n["connector"], - created_nodes[_to_n["id"]], _to_n["connector"] + all_batch_nodes[_from_n["id"]], _from_n["connector"], + all_batch_nodes[_to_n["id"]], _to_n["connector"] ) # sort batch nodes batch_group.organize() + + return all_batch_nodes From 4da106cf3def8ee5d2dfb91076f0fecb7f05bc0e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 15:06:14 +0200 Subject: [PATCH 63/85] flame: debug log nodes attrs --- .../flame/plugins/publish/integrate_batch_group.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index c70c2baa4a9..350acdfa903 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -32,7 +32,16 @@ def process(self, instance): bgroup = self._get_batch_group(instance, task_data) # add batch group content - self._add_nodes_to_batch_with_links(instance, task_data, bgroup) + all_batch_nodes = self._add_nodes_to_batch_with_links( + instance, task_data, bgroup) + + for name, node in all_batch_nodes: + self.log.debug("name: {}, dir: {}".format( + name, dir(node) + )) + self.log.debug("__ node.attributes: {}".format( + node.attributes + )) # load plate to batch group self.log.info("Loading subset `{}` into batch `{}`".format( @@ -70,7 +79,7 @@ def _add_nodes_to_batch_with_links(self, instance, task_data, batch_group): ] # add nodes into batch group - opfapi.create_batch_group_conent( + return opfapi.create_batch_group_conent( batch_nodes, batch_links, batch_group) def _load_clip_to_context(self, instance, bgroup): From d0a2a781aea6009a2b928c7752ef64ae87c47fab Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 15:08:45 +0200 Subject: [PATCH 64/85] flame: fixing head and tail --- .../hosts/flame/plugins/publish/collect_timeline_instances.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index fe9bce5232a..0af769a3804 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -72,9 +72,9 @@ def process(self, context): # solve handles length marker_data["handleStart"] = min( - marker_data["handleStart"], head) + marker_data["handleStart"], abs(head)) marker_data["handleEnd"] = min( - marker_data["handleEnd"], tail) + marker_data["handleEnd"], abs(tail)) with_audio = bool(marker_data.pop("audio")) From 1535b4712409a57bbbbddb73a65261106423ec7b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 15:10:23 +0200 Subject: [PATCH 65/85] flame: add hack comment --- .../hosts/flame/plugins/publish/collect_timeline_instances.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 0af769a3804..95c2002bd93 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -247,6 +247,7 @@ def _get_head_tail(self, clip_data, first_frame): head = clip_data.get("segment_head") tail = clip_data.get("segment_tail") + # HACK: it is here to serve for versions bellow 2021.1 if not head: head = int(clip_data["source_in"]) - int(first_frame) if not tail: From 304584573b7c67aac46f6b255f1ceb09fe1b4d7f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 15:14:32 +0200 Subject: [PATCH 66/85] flame: missing dict items function call --- openpype/hosts/flame/plugins/publish/integrate_batch_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 350acdfa903..cac99a25acd 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -35,7 +35,7 @@ def process(self, instance): all_batch_nodes = self._add_nodes_to_batch_with_links( instance, task_data, bgroup) - for name, node in all_batch_nodes: + for name, node in all_batch_nodes.items(): self.log.debug("name: {}, dir: {}".format( name, dir(node) )) From 16bd11083da52c5836e183e8895524279378f08e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 15:22:09 +0200 Subject: [PATCH 67/85] flame: set node name if it doesn't exists in node props then set it from node_id --- openpype/hosts/flame/api/batch_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index 20118c249c5..d4c82944665 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -107,7 +107,7 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): node["id"], node["type"], node["properties"]) # get node name for checking if exists - node_name = node_props.get("name") or node_id + node_name = node_props.pop("name", None) or node_id if all_batch_nodes.get(node_name): # update existing batch node @@ -116,6 +116,9 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): # create new batch node batch_node = batch_group.create_node(node_type) + # set name + setattr(batch_node, "name", node_name) + # set attributes found in node props for key, value in node_props.items(): if not hasattr(batch_node, key): From afbacb1944b690f03e31fb129d39476880c3934a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 17:14:33 +0200 Subject: [PATCH 68/85] flame: fixing setter for node name --- openpype/hosts/flame/api/batch_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/batch_utils.py b/openpype/hosts/flame/api/batch_utils.py index d4c82944665..9d419a4a903 100644 --- a/openpype/hosts/flame/api/batch_utils.py +++ b/openpype/hosts/flame/api/batch_utils.py @@ -117,7 +117,7 @@ def create_batch_group_conent(batch_nodes, batch_links, batch_group=None): batch_node = batch_group.create_node(node_type) # set name - setattr(batch_node, "name", node_name) + batch_node.name.set_value(node_name) # set attributes found in node props for key, value in node_props.items(): From fc6d01d0043df4e019ab89cf44d8c592e71be8e3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Apr 2022 17:15:44 +0200 Subject: [PATCH 69/85] hound catch --- openpype/hosts/flame/api/lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 998d7dfa7ec..a4d8a7f9f0b 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -826,5 +826,7 @@ def _get_time_info_from_origin(self, xml_data): def _write_result_xml_to_file(self, file, xml_data): # save it as new file tree = cET.ElementTree(xml_data) - tree.write(file, xml_declaration=True, - method='xml', encoding='UTF-8') \ No newline at end of file + tree.write( + file, xml_declaration=True, + method='xml', encoding='UTF-8' + ) From 5f1940a9982ffee2266776d0f70812853241c941 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 11:17:22 +0200 Subject: [PATCH 70/85] flame: adding maintainable temp file path --- openpype/hosts/flame/api/__init__.py | 2 ++ openpype/hosts/flame/api/lib.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index a0c40904ed2..6744a7ff11d 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -27,6 +27,7 @@ get_frame_from_filename, get_padding_from_filename, maintained_object_duplication, + maintained_temp_file_path, get_clip_segment, get_batch_group_from_desktop, MediaInfoFile @@ -103,6 +104,7 @@ "get_frame_from_filename", "get_padding_from_filename", "maintained_object_duplication", + "maintained_temp_file_path", "get_clip_segment", "get_batch_group_from_desktop", "MediaInfoFile", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index a4d8a7f9f0b..51d48becf1d 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -4,6 +4,7 @@ import six import json import pickle +import tempfile import itertools import contextlib import xml.etree.cElementTree as cET @@ -695,6 +696,25 @@ def maintained_object_duplication(item): flame.delete(duplicate) +@contextlib.contextmanager +def maintained_temp_file_path(suffix=None): + _suffix = suffix or "" + + try: + # Store dumped json to temporary file + temporary_file = tempfile.mktemp( + suffix=_suffix, prefix="flame_maintained_") + yield temporary_file.name.replace("\\", "/") + + except IOError as _error: + raise IOError( + "Not able to create temp json file: {}".format(_error)) from _error + + finally: + # Remove the temporary json + os.remove(temporary_file) + + def get_clip_segment(flame_clip): name = flame_clip.name.get_value() version = flame_clip.versions[0] From 03bf240816d98bd47e0944eac0a57706c7cc0869 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 11:18:10 +0200 Subject: [PATCH 71/85] flame: refactoring MediaInfoFile class so it is parentable --- openpype/hosts/flame/api/lib.py | 161 +++++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 45 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 51d48becf1d..de3467aa76a 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -1,7 +1,6 @@ import sys import os import re -import six import json import pickle import tempfile @@ -740,19 +739,34 @@ def get_batch_group_from_desktop(name): class MediaInfoFile: - media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" - tmp_name = "_tmp.clip" - tmp_file = None + """Class to get media info file clip data - clip_data = None - out_feed_nb_ticks = None - out_feed_fps = None - out_feed_drop_mode = None + Raises: + IOError: MEDIA_SCRIPT_PATH path doesn't exists + TypeError: Not able to generate clip xml data file + ET.ParseError: Missing clip in xml clip data + IOError: Not able to save xml clip data to file + + Attributes: + str: `MEDIA_SCRIPT_PATH` path to flame binary + logging.Logger: `log` logger + """ + MEDIA_SCRIPT_PATH = "/opt/Autodesk/mio/current/dl_get_media_info" log = log - def __init__(self, path): - # test if media script paht exists + _clip_data = None + _start_frame = None + _fps = None + _drop_mode = None + + def __init__(self, path, **kwargs): + + # replace log if any + if kwargs.get("log"): + self.log = kwargs["log"] + + # test if `dl_get_media_info` paht exists self._validate_media_script_path() # derivate other feed variables @@ -760,40 +774,93 @@ def __init__(self, path): self.feed_dir = os.path.dirname(path) self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) + with maintained_temp_file_path(".clip") as tmp_path: + self.log.info("Temp File: {}".format(tmp_path)) + self._generate_media_info_file(tmp_path) + + # get clip data and make them single if there is multiple + # clips data + xml_data = self._make_single_clip_media_info(tmp_path) + + # get all time related data and assign them + self._get_time_info_from_origin(xml_data) + self.set_clip_data(xml_data) + + @property + def clip_data(self): + """Clip's xml clip data + + Returns: + xml.etree.ElementTree: xml data + """ + return self._clip_data + + @clip_data.setter + def clip_data(self, data): + self._clip_data = data + + @property + def start_frame(self): + """ Clip's starting frame found in timecode - # remove previously generated temp files - # it will be regenerated - self._clear_tmp_file() + Returns: + int: number of frames + """ + return self._start_frame - self.log.info("Temp File: {}".format(self.tmp_file)) + @start_frame.setter + def start_frame(self, number): + self._start_frame = int(number) - self._generate_media_info_file() + @property + def fps(self): + """ Clip's frame rate + + Returns: + float: frame rate + """ + return self._fps + + @fps.setter + def fps(self, fl_number): + self._fps = float(fl_number) + + @property + def drop_mode(self): + """ Clip's drop frame mode + + Returns: + str: drop frame flag + """ + return self._drop_mode + + @drop_mode.setter + def drop_mode(self, text): + self._drop_mode = str(text) def _validate_media_script_path(self): - if not os.path.isfile(self.media_script_path): + if not os.path.isfile(self.MEDIA_SCRIPT_PATH): raise IOError("Media Scirpt does not exist: `{}`".format( - self.media_script_path)) + self.MEDIA_SCRIPT_PATH)) - def _generate_media_info_file(self): + def _generate_media_info_file(self, fpath): # Create cmd arguments for gettig xml file info file cmd_args = [ - self.media_script_path, + self.MEDIA_SCRIPT_PATH, "-e", self.feed_ext, - "-o", self.tmp_file, + "-o", fpath, self.feed_dir ] - # execute creation of clip xml template data try: + # execute creation of clip xml template data openpype.run_subprocess(cmd_args) - self._make_single_clip_media_info() - except TypeError: - self.log.error("Error creating self.tmp_file") - six.reraise(*sys.exc_info()) + except TypeError as error: + raise TypeError( + "Error creating `{}` due: {}".format(fpath, error)) from error - def _make_single_clip_media_info(self): - with open(self.tmp_file) as f: + def _make_single_clip_media_info(self, fpath): + with open(fpath) as f: lines = f.readlines() _added_root = itertools.chain( "", deepcopy(lines)[1:], "") @@ -816,37 +883,41 @@ def _make_single_clip_media_info(self): ] )) - self._get_time_info_from_origin(matching_clip) - self.clip_data = matching_clip - self._write_result_xml_to_file(self.tmp_file, matching_clip) - - def _clear_tmp_file(self): - if os.path.isfile(self.tmp_file): - os.remove(self.tmp_file) + return matching_clip def _get_time_info_from_origin(self, xml_data): try: for out_track in xml_data.iter('track'): for out_feed in out_track.iter('feed'): + # start frame out_feed_nb_ticks_obj = out_feed.find( 'startTimecode/nbTicks') - self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text + self.start_frame(out_feed_nb_ticks_obj.text) + + # fps out_feed_fps_obj = out_feed.find( 'startTimecode/rate') - self.out_feed_fps = out_feed_fps_obj.text + self.fps(out_feed_fps_obj.text) + + # drop frame mode out_feed_drop_mode_obj = out_feed.find( 'startTimecode/dropMode') - self.out_feed_drop_mode = out_feed_drop_mode_obj.text + self.drop_mode(out_feed_drop_mode_obj.text) break else: continue except Exception as msg: self.log.warning(msg) - def _write_result_xml_to_file(self, file, xml_data): - # save it as new file - tree = cET.ElementTree(xml_data) - tree.write( - file, xml_declaration=True, - method='xml', encoding='UTF-8' - ) + @staticmethod + def write_clip_data_to_file(fpath, xml_data): + try: + # save it as new file + tree = cET.ElementTree(xml_data) + tree.write( + fpath, xml_declaration=True, + method='xml', encoding='UTF-8' + ) + except IOError as error: + raise IOError( + "Not able to write data to file: {}".format(error)) from error From adbd3593424fe9ea981705505038bcf89b9d81b9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 11:54:36 +0200 Subject: [PATCH 72/85] flame: otio removing reel clip dependency --- openpype/hosts/flame/otio/flame_export.py | 70 ++++------------------- 1 file changed, 11 insertions(+), 59 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 3e769689637..25be310d017 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -261,24 +261,15 @@ def create_otio_markers(otio_item, item): otio_item.markers.append(otio_marker) -def create_otio_reference(clip_data): +def create_otio_reference(clip_data, fps=None): metadata = _get_metadata(clip_data) # get file info for path and start frame frame_start = 0 - fps = CTX.get_fps() + fps = fps or CTX.get_fps() path = clip_data["fpath"] - reel_clip = None - match_reel_clip = [ - clip for clip in CTX.clips - if clip["fpath"] == path - ] - if match_reel_clip: - reel_clip = match_reel_clip.pop() - fps = reel_clip["fps"] - file_name = os.path.basename(path) file_head, extension = os.path.splitext(file_name) @@ -342,16 +333,17 @@ def create_otio_reference(clip_data): def create_otio_clip(clip_data): segment = clip_data["PySegment"] - # create media reference - media_reference = create_otio_reference(clip_data) - # calculate source in media_info = MediaInfoFile(clip_data["fpath"]) - xml_timecode_ticks = media_info.out_feed_nb_ticks - if xml_timecode_ticks: - first_frame = int(xml_timecode_ticks) - else: - first_frame = utils.get_frame_from_filename(clip_data["fpath"]) or 0 + media_timecode_start = media_info.start_frame + media_fps = media_info.fps + + # create media reference + media_reference = create_otio_reference(clip_data, media_fps) + + # define first frame + first_frame = media_timecode_start or utils.get_frame_from_filename( + clip_data["fpath"]) or 0 source_in = int(clip_data["source_in"]) - int(first_frame) @@ -385,41 +377,6 @@ def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): ) -def get_clips_in_reels(project): - output_clips = [] - project_desktop = project.current_workspace.desktop - - for reel_group in project_desktop.reel_groups: - for reel in reel_group.reels: - for clip in reel.clips: - clip_data = { - "PyClip": clip, - "fps": float(str(clip.frame_rate)[:-4]) - } - - attrs = [ - "name", "width", "height", - "ratio", "sample_rate", "bit_depth" - ] - - for attr in attrs: - val = getattr(clip, attr) - clip_data[attr] = val - - version = clip.versions[-1] - track = version.tracks[-1] - # each reel clip is also having one segment - for segment in track.segments: - segment_data = _get_segment_attributes( - segment, from_clip=True) - if segment_data: - clip_data.update(segment_data) - - output_clips.append(clip_data) - - return output_clips - - def _get_colourspace_policy(): output = {} @@ -579,11 +536,6 @@ def create_otio_timeline(sequence): log.info(sequence.attributes) CTX.project = get_current_flame_project() - CTX.clips = get_clips_in_reels(CTX.project) - - log.debug(pformat( - CTX.clips - )) # get current timeline CTX.set_fps( From 4797f584981bd5d07370f417ebe073c6cedbd76f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 11:59:34 +0200 Subject: [PATCH 73/85] flame: add todos for metadata feature --- openpype/hosts/flame/api/lib.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index de3467aa76a..e9e01304019 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -271,6 +271,7 @@ def rescan_hooks(): def get_metadata(project_name, _log=None): + # TODO: can be replaced by MediaInfoFile class method from adsk.libwiretapPythonClientAPI import ( WireTapClient, WireTapServerHandle, @@ -750,6 +751,8 @@ class MediaInfoFile: Attributes: str: `MEDIA_SCRIPT_PATH` path to flame binary logging.Logger: `log` logger + + TODO: add method for getting metadata to dict """ MEDIA_SCRIPT_PATH = "/opt/Autodesk/mio/current/dl_get_media_info" From bfae95a8475981e014561c1eec8b9b54de5a8424 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 12:23:16 +0200 Subject: [PATCH 74/85] flame: OpenClipSolver inheriting from MediaInfoFile class --- openpype/hosts/flame/api/lib.py | 4 +- openpype/hosts/flame/api/plugin.py | 162 ++++++----------------------- 2 files changed, 36 insertions(+), 130 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index e9e01304019..91f5c265628 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -766,8 +766,8 @@ class MediaInfoFile: def __init__(self, path, **kwargs): # replace log if any - if kwargs.get("log"): - self.log = kwargs["log"] + if kwargs.get("logger"): + self.log = kwargs["logger"] # test if `dl_get_media_info` paht exists self._validate_media_script_path() diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index bd0f9f1a815..a23be946ba7 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -679,53 +679,39 @@ class ClipLoader(LoaderPlugin): ] -# TODO: inheritance from flame.api.lib.MediaInfoFile -class OpenClipSolver: - media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" - tmp_name = "_tmp.clip" - tmp_file = None +class OpenClipSolver(flib.MediaInfoFile): create_new_clip = False - out_feed_nb_ticks = None - out_feed_fps = None - out_feed_drop_mode = None - log = log def __init__(self, openclip_file_path, feed_data): - # test if media script paht exists - self._validate_media_script_path() + self.out_file = openclip_file_path # new feed variables: - feed_path = feed_data["path"] - self.feed_version_name = feed_data["version"] - self.feed_colorspace = feed_data.get("colorspace") + feed_path = feed_data.pop("path") + # initialize parent class + super(OpenClipSolver).__init__( + feed_path, + **feed_data + ) + + # get logger if any if feed_data.get("logger"): self.log = feed_data["logger"] + # get other metadata + self.feed_version_name = feed_data["version"] + self.feed_colorspace = feed_data.get("colorspace") + # derivate other feed variables self.feed_basename = os.path.basename(feed_path) self.feed_dir = os.path.dirname(feed_path) self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - if not self._is_valid_tmp_file(openclip_file_path): - # openclip does not exist yet and will be created - self.tmp_file = self.out_file = openclip_file_path + if not self._is_valid_tmp_file(self.out_file): self.create_new_clip = True - else: - # update already created clip - # output a temp file - self.out_file = openclip_file_path - self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) - - # remove previously generated temp files - # it will be regenerated - self._clear_tmp_file() - - self.log.info("Temp File: {}".format(self.tmp_file)) - def _is_valid_tmp_file(self, file): # check if file exists if os.path.isfile(file): @@ -740,7 +726,6 @@ def _is_valid_tmp_file(self, file): return False def make(self): - self._generate_media_info_file() if self.create_new_clip: # New openClip @@ -748,58 +733,6 @@ def make(self): else: self._update_open_clip() - def _validate_media_script_path(self): - if not os.path.isfile(self.media_script_path): - raise IOError("Media Scirpt does not exist: `{}`".format( - self.media_script_path)) - - def _generate_media_info_file(self): - # Create cmd arguments for gettig xml file info file - cmd_args = [ - self.media_script_path, - "-e", self.feed_ext, - "-o", self.tmp_file, - self.feed_dir - ] - - # execute creation of clip xml template data - try: - openpype.run_subprocess(cmd_args) - self._make_single_clip_media_info() - except TypeError: - self.log.error("Error creating self.tmp_file") - six.reraise(*sys.exc_info()) - - def _make_single_clip_media_info(self): - with open(self.tmp_file) as f: - lines = f.readlines() - _added_root = itertools.chain( - "", deepcopy(lines)[1:], "") - new_root = ET.fromstringlist(_added_root) - - # find the clip which is matching to my input name - xml_clips = new_root.findall("clip") - matching_clip = None - for xml_clip in xml_clips: - if xml_clip.find("name").text in self.feed_basename: - matching_clip = xml_clip - - if matching_clip is None: - # return warning there is missing clip - raise ET.ParseError( - "Missing clip in `{}`. Available clips {}".format( - self.feed_basename, [ - xml_clip.find("name").text - for xml_clip in xml_clips - ] - )) - - self._write_result_xml_to_file(self.tmp_file, matching_clip) - - def _clear_tmp_file(self): - if os.path.isfile(self.tmp_file): - os.remove(self.tmp_file) - def _clear_handler(self, xml_object): for handler in xml_object.findall("./handler"): self.log.debug("Handler found") @@ -808,9 +741,8 @@ def _clear_handler(self, xml_object): def _create_new_open_clip(self): self.log.info("Building new openClip") - tmp_xml = ET.parse(self.tmp_file) - - tmp_xml_feeds = tmp_xml.find('tracks/track/feeds') + # clip data comming from MediaInfoFile + tmp_xml_feeds = self.clip_data.find('tracks/track/feeds') tmp_xml_feeds.set('currentVersion', self.feed_version_name) for tmp_feed in tmp_xml_feeds: tmp_feed.set('vuid', self.feed_version_name) @@ -821,46 +753,47 @@ def _create_new_open_clip(self): self._clear_handler(tmp_feed) - tmp_xml_versions_obj = tmp_xml.find('versions') + tmp_xml_versions_obj = self.clip_data.find('versions') tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) for xml_new_version in tmp_xml_versions_obj: xml_new_version.set('uid', self.feed_version_name) xml_new_version.set('type', 'version') - xml_data = self._fix_xml_data(tmp_xml) + xml_data = self._fix_xml_data(self.clip_data) self.log.info("Adding feed version: {}".format(self.feed_basename)) - self._write_result_xml_to_file(self.out_file, xml_data) - - self.log.info("openClip Updated: {}".format(self.tmp_file)) + self.write_clip_data_to_file(self.out_file, xml_data) def _update_open_clip(self): self.log.info("Updating openClip ..") out_xml = ET.parse(self.out_file) - tmp_xml = ET.parse(self.tmp_file) self.log.debug(">> out_xml: {}".format(out_xml)) - self.log.debug(">> tmp_xml: {}".format(tmp_xml)) + self.log.debug(">> self.clip_data: {}".format(self.clip_data)) # Get new feed from tmp file - tmp_xml_feed = tmp_xml.find('tracks/track/feeds/feed') + tmp_xml_feed = self.clip_data.find('tracks/track/feeds/feed') self._clear_handler(tmp_xml_feed) - self._get_time_info_from_origin(out_xml) - if self.out_feed_fps: + # update fps from MediaInfoFile class + if self.fps: tmp_feed_fps_obj = tmp_xml_feed.find( "startTimecode/rate") - tmp_feed_fps_obj.text = self.out_feed_fps - if self.out_feed_nb_ticks: + tmp_feed_fps_obj.text = self.fps + + # update start_frame from MediaInfoFile class + if self.start_frame: tmp_feed_nb_ticks_obj = tmp_xml_feed.find( "startTimecode/nbTicks") - tmp_feed_nb_ticks_obj.text = self.out_feed_nb_ticks - if self.out_feed_drop_mode: + tmp_feed_nb_ticks_obj.text = self.start_frame + + # update drop_mode from MediaInfoFile class + if self.drop_mode: tmp_feed_drop_mode_obj = tmp_xml_feed.find( "startTimecode/dropMode") - tmp_feed_drop_mode_obj.text = self.out_feed_drop_mode + tmp_feed_drop_mode_obj.text = self.drop_mode new_path_obj = tmp_xml_feed.find( "spans/span/path") @@ -901,31 +834,10 @@ def _update_open_clip(self): self.log.info("Adding feed version: {}".format( self.feed_version_name)) - self._write_result_xml_to_file(self.out_file, xml_data) + self.write_clip_data_to_file(self.out_file, xml_data) self.log.info("openClip Updated: {}".format(self.out_file)) - self._clear_tmp_file() - - def _get_time_info_from_origin(self, xml_data): - try: - for out_track in xml_data.iter('track'): - for out_feed in out_track.iter('feed'): - out_feed_nb_ticks_obj = out_feed.find( - 'startTimecode/nbTicks') - self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text - out_feed_fps_obj = out_feed.find( - 'startTimecode/rate') - self.out_feed_fps = out_feed_fps_obj.text - out_feed_drop_mode_obj = out_feed.find( - 'startTimecode/dropMode') - self.out_feed_drop_mode = out_feed_drop_mode_obj.text - break - else: - continue - except Exception as msg: - self.log.warning(msg) - def _feed_exists(self, xml_data, path): # loop all available feed paths and check if # the path is not already in file @@ -940,12 +852,6 @@ def _fix_xml_data(self, xml_data): self._clear_handler(xml_root) return xml_root - def _write_result_xml_to_file(self, file, xml_data): - # save it as new file - tree = cET.ElementTree(xml_data) - tree.write(file, xml_declaration=True, - method='xml', encoding='UTF-8') - def _create_openclip_backup_file(self, file): bck_file = "{}.bak".format(file) # if backup does not exist From 72ecb6192a0ac609b4ad951dbd35712144b012a7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 12:52:27 +0200 Subject: [PATCH 75/85] flame: fixing flame compatibility and python2 --- openpype/hosts/flame/api/__init__.py | 1 - openpype/hosts/flame/api/lib.py | 6 +++--- openpype/hosts/flame/api/plugin.py | 5 +---- openpype/hosts/flame/otio/flame_export.py | 5 ++--- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 6744a7ff11d..2c461e5f16f 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -57,7 +57,6 @@ PublishableClip, ClipLoader, OpenClipSolver - ) from .workio import ( open_file, diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 91f5c265628..6d93018fef9 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -708,7 +708,7 @@ def maintained_temp_file_path(suffix=None): except IOError as _error: raise IOError( - "Not able to create temp json file: {}".format(_error)) from _error + "Not able to create temp json file: {}".format(_error)) finally: # Remove the temporary json @@ -860,7 +860,7 @@ def _generate_media_info_file(self, fpath): openpype.run_subprocess(cmd_args) except TypeError as error: raise TypeError( - "Error creating `{}` due: {}".format(fpath, error)) from error + "Error creating `{}` due: {}".format(fpath, error)) def _make_single_clip_media_info(self, fpath): with open(fpath) as f: @@ -923,4 +923,4 @@ def write_clip_data_to_file(fpath, xml_data): ) except IOError as error: raise IOError( - "Not able to write data to file: {}".format(error)) from error + "Not able to write data to file: {}".format(error)) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index a23be946ba7..ab74bb46057 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,15 +1,11 @@ -import itertools import os import re import shutil -import sys -import xml.etree.cElementTree as cET from copy import deepcopy from xml.etree import ElementTree as ET import openpype.api as openpype import qargparse -import six from openpype import style from openpype.pipeline import LegacyCreator, LoaderPlugin from Qt import QtCore, QtWidgets @@ -740,6 +736,7 @@ def _clear_handler(self, xml_object): def _create_new_open_clip(self): self.log.info("Building new openClip") + self.log.debug(">> self.clip_data: {}".format(self.clip_data)) # clip data comming from MediaInfoFile tmp_xml_feeds = self.clip_data.find('tracks/track/feeds') diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 25be310d017..0b9c9ce8178 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -7,13 +7,10 @@ import logging import opentimelineio as otio from . import utils -from openpype.hosts.flame.api import MediaInfoFile import flame from pprint import pformat -reload(utils) # noqa - log = logging.getLogger(__name__) @@ -331,6 +328,8 @@ def create_otio_reference(clip_data, fps=None): def create_otio_clip(clip_data): + from openpype.hosts.flame.api import MediaInfoFile + segment = clip_data["PySegment"] # calculate source in From 9256e022909bb363d8e4cf4be532248363579d40 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 13:09:29 +0200 Subject: [PATCH 76/85] flame: setter getter error --- openpype/hosts/flame/api/lib.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 6d93018fef9..d68dd2a8863 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -704,7 +704,7 @@ def maintained_temp_file_path(suffix=None): # Store dumped json to temporary file temporary_file = tempfile.mktemp( suffix=_suffix, prefix="flame_maintained_") - yield temporary_file.name.replace("\\", "/") + yield temporary_file.replace("\\", "/") except IOError as _error: raise IOError( @@ -787,7 +787,7 @@ def __init__(self, path, **kwargs): # get all time related data and assign them self._get_time_info_from_origin(xml_data) - self.set_clip_data(xml_data) + self.clip_data = xml_data @property def clip_data(self): @@ -895,17 +895,17 @@ def _get_time_info_from_origin(self, xml_data): # start frame out_feed_nb_ticks_obj = out_feed.find( 'startTimecode/nbTicks') - self.start_frame(out_feed_nb_ticks_obj.text) + self.start_frame = out_feed_nb_ticks_obj.text # fps out_feed_fps_obj = out_feed.find( 'startTimecode/rate') - self.fps(out_feed_fps_obj.text) + self.fps = out_feed_fps_obj.text # drop frame mode out_feed_drop_mode_obj = out_feed.find( 'startTimecode/dropMode') - self.drop_mode(out_feed_drop_mode_obj.text) + self.drop_mode = out_feed_drop_mode_obj.text break else: continue From 3f9b06139d44448416deae9d251dabffdcfb9506 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 14:13:43 +0200 Subject: [PATCH 77/85] flame: fix getroot --- openpype/hosts/flame/api/lib.py | 2 +- openpype/hosts/flame/api/plugin.py | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index d68dd2a8863..6ee0eb6a824 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -739,7 +739,7 @@ def get_batch_group_from_desktop(name): return bgroup -class MediaInfoFile: +class MediaInfoFile(object): """Class to get media info file clip data Raises: diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index ab74bb46057..6136c4922ce 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -687,7 +687,7 @@ def __init__(self, openclip_file_path, feed_data): feed_path = feed_data.pop("path") # initialize parent class - super(OpenClipSolver).__init__( + super(OpenClipSolver, self).__init__( feed_path, **feed_data ) @@ -756,7 +756,7 @@ def _create_new_open_clip(self): xml_new_version.set('uid', self.feed_version_name) xml_new_version.set('type', 'version') - xml_data = self._fix_xml_data(self.clip_data) + xml_data = self._clear_handler(self.clip_data) self.log.info("Adding feed version: {}".format(self.feed_basename)) self.write_clip_data_to_file(self.out_file, xml_data) @@ -823,7 +823,7 @@ def _update_open_clip(self): "version", {"type": "version", "uid": self.feed_version_name}) out_xml_versions_obj.insert(0, new_version_obj) - xml_data = self._fix_xml_data(out_xml) + xml_data = self._clear_handler(out_xml) # fist create backup self._create_openclip_backup_file(self.out_file) @@ -844,11 +844,6 @@ def _feed_exists(self, xml_data, path): "Not appending file as it already is in .clip file") return True - def _fix_xml_data(self, xml_data): - xml_root = xml_data.getroot() - self._clear_handler(xml_root) - return xml_root - def _create_openclip_backup_file(self, file): bck_file = "{}.bak".format(file) # if backup does not exist From 8420a164c2c5e7537c584f30e1e3b8469417cacf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 15:13:05 +0200 Subject: [PATCH 78/85] flame: temp reverse commit bfae95a8475981e014561c1eec8b9b54de5a8424 --- openpype/hosts/flame/api/plugin.py | 174 +++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 36 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 6136c4922ce..bd0f9f1a815 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,11 +1,15 @@ +import itertools import os import re import shutil +import sys +import xml.etree.cElementTree as cET from copy import deepcopy from xml.etree import ElementTree as ET import openpype.api as openpype import qargparse +import six from openpype import style from openpype.pipeline import LegacyCreator, LoaderPlugin from Qt import QtCore, QtWidgets @@ -675,39 +679,53 @@ class ClipLoader(LoaderPlugin): ] -class OpenClipSolver(flib.MediaInfoFile): +# TODO: inheritance from flame.api.lib.MediaInfoFile +class OpenClipSolver: + media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" + tmp_name = "_tmp.clip" + tmp_file = None create_new_clip = False + out_feed_nb_ticks = None + out_feed_fps = None + out_feed_drop_mode = None + log = log def __init__(self, openclip_file_path, feed_data): - self.out_file = openclip_file_path + # test if media script paht exists + self._validate_media_script_path() # new feed variables: - feed_path = feed_data.pop("path") - - # initialize parent class - super(OpenClipSolver, self).__init__( - feed_path, - **feed_data - ) + feed_path = feed_data["path"] + self.feed_version_name = feed_data["version"] + self.feed_colorspace = feed_data.get("colorspace") - # get logger if any if feed_data.get("logger"): self.log = feed_data["logger"] - # get other metadata - self.feed_version_name = feed_data["version"] - self.feed_colorspace = feed_data.get("colorspace") - # derivate other feed variables self.feed_basename = os.path.basename(feed_path) self.feed_dir = os.path.dirname(feed_path) self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - if not self._is_valid_tmp_file(self.out_file): + if not self._is_valid_tmp_file(openclip_file_path): + # openclip does not exist yet and will be created + self.tmp_file = self.out_file = openclip_file_path self.create_new_clip = True + else: + # update already created clip + # output a temp file + self.out_file = openclip_file_path + self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) + + # remove previously generated temp files + # it will be regenerated + self._clear_tmp_file() + + self.log.info("Temp File: {}".format(self.tmp_file)) + def _is_valid_tmp_file(self, file): # check if file exists if os.path.isfile(file): @@ -722,6 +740,7 @@ def _is_valid_tmp_file(self, file): return False def make(self): + self._generate_media_info_file() if self.create_new_clip: # New openClip @@ -729,6 +748,58 @@ def make(self): else: self._update_open_clip() + def _validate_media_script_path(self): + if not os.path.isfile(self.media_script_path): + raise IOError("Media Scirpt does not exist: `{}`".format( + self.media_script_path)) + + def _generate_media_info_file(self): + # Create cmd arguments for gettig xml file info file + cmd_args = [ + self.media_script_path, + "-e", self.feed_ext, + "-o", self.tmp_file, + self.feed_dir + ] + + # execute creation of clip xml template data + try: + openpype.run_subprocess(cmd_args) + self._make_single_clip_media_info() + except TypeError: + self.log.error("Error creating self.tmp_file") + six.reraise(*sys.exc_info()) + + def _make_single_clip_media_info(self): + with open(self.tmp_file) as f: + lines = f.readlines() + _added_root = itertools.chain( + "", deepcopy(lines)[1:], "") + new_root = ET.fromstringlist(_added_root) + + # find the clip which is matching to my input name + xml_clips = new_root.findall("clip") + matching_clip = None + for xml_clip in xml_clips: + if xml_clip.find("name").text in self.feed_basename: + matching_clip = xml_clip + + if matching_clip is None: + # return warning there is missing clip + raise ET.ParseError( + "Missing clip in `{}`. Available clips {}".format( + self.feed_basename, [ + xml_clip.find("name").text + for xml_clip in xml_clips + ] + )) + + self._write_result_xml_to_file(self.tmp_file, matching_clip) + + def _clear_tmp_file(self): + if os.path.isfile(self.tmp_file): + os.remove(self.tmp_file) + def _clear_handler(self, xml_object): for handler in xml_object.findall("./handler"): self.log.debug("Handler found") @@ -736,10 +807,10 @@ def _clear_handler(self, xml_object): def _create_new_open_clip(self): self.log.info("Building new openClip") - self.log.debug(">> self.clip_data: {}".format(self.clip_data)) - # clip data comming from MediaInfoFile - tmp_xml_feeds = self.clip_data.find('tracks/track/feeds') + tmp_xml = ET.parse(self.tmp_file) + + tmp_xml_feeds = tmp_xml.find('tracks/track/feeds') tmp_xml_feeds.set('currentVersion', self.feed_version_name) for tmp_feed in tmp_xml_feeds: tmp_feed.set('vuid', self.feed_version_name) @@ -750,47 +821,46 @@ def _create_new_open_clip(self): self._clear_handler(tmp_feed) - tmp_xml_versions_obj = self.clip_data.find('versions') + tmp_xml_versions_obj = tmp_xml.find('versions') tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) for xml_new_version in tmp_xml_versions_obj: xml_new_version.set('uid', self.feed_version_name) xml_new_version.set('type', 'version') - xml_data = self._clear_handler(self.clip_data) + xml_data = self._fix_xml_data(tmp_xml) self.log.info("Adding feed version: {}".format(self.feed_basename)) - self.write_clip_data_to_file(self.out_file, xml_data) + self._write_result_xml_to_file(self.out_file, xml_data) + + self.log.info("openClip Updated: {}".format(self.tmp_file)) def _update_open_clip(self): self.log.info("Updating openClip ..") out_xml = ET.parse(self.out_file) + tmp_xml = ET.parse(self.tmp_file) self.log.debug(">> out_xml: {}".format(out_xml)) - self.log.debug(">> self.clip_data: {}".format(self.clip_data)) + self.log.debug(">> tmp_xml: {}".format(tmp_xml)) # Get new feed from tmp file - tmp_xml_feed = self.clip_data.find('tracks/track/feeds/feed') + tmp_xml_feed = tmp_xml.find('tracks/track/feeds/feed') self._clear_handler(tmp_xml_feed) + self._get_time_info_from_origin(out_xml) - # update fps from MediaInfoFile class - if self.fps: + if self.out_feed_fps: tmp_feed_fps_obj = tmp_xml_feed.find( "startTimecode/rate") - tmp_feed_fps_obj.text = self.fps - - # update start_frame from MediaInfoFile class - if self.start_frame: + tmp_feed_fps_obj.text = self.out_feed_fps + if self.out_feed_nb_ticks: tmp_feed_nb_ticks_obj = tmp_xml_feed.find( "startTimecode/nbTicks") - tmp_feed_nb_ticks_obj.text = self.start_frame - - # update drop_mode from MediaInfoFile class - if self.drop_mode: + tmp_feed_nb_ticks_obj.text = self.out_feed_nb_ticks + if self.out_feed_drop_mode: tmp_feed_drop_mode_obj = tmp_xml_feed.find( "startTimecode/dropMode") - tmp_feed_drop_mode_obj.text = self.drop_mode + tmp_feed_drop_mode_obj.text = self.out_feed_drop_mode new_path_obj = tmp_xml_feed.find( "spans/span/path") @@ -823,7 +893,7 @@ def _update_open_clip(self): "version", {"type": "version", "uid": self.feed_version_name}) out_xml_versions_obj.insert(0, new_version_obj) - xml_data = self._clear_handler(out_xml) + xml_data = self._fix_xml_data(out_xml) # fist create backup self._create_openclip_backup_file(self.out_file) @@ -831,10 +901,31 @@ def _update_open_clip(self): self.log.info("Adding feed version: {}".format( self.feed_version_name)) - self.write_clip_data_to_file(self.out_file, xml_data) + self._write_result_xml_to_file(self.out_file, xml_data) self.log.info("openClip Updated: {}".format(self.out_file)) + self._clear_tmp_file() + + def _get_time_info_from_origin(self, xml_data): + try: + for out_track in xml_data.iter('track'): + for out_feed in out_track.iter('feed'): + out_feed_nb_ticks_obj = out_feed.find( + 'startTimecode/nbTicks') + self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text + out_feed_fps_obj = out_feed.find( + 'startTimecode/rate') + self.out_feed_fps = out_feed_fps_obj.text + out_feed_drop_mode_obj = out_feed.find( + 'startTimecode/dropMode') + self.out_feed_drop_mode = out_feed_drop_mode_obj.text + break + else: + continue + except Exception as msg: + self.log.warning(msg) + def _feed_exists(self, xml_data, path): # loop all available feed paths and check if # the path is not already in file @@ -844,6 +935,17 @@ def _feed_exists(self, xml_data, path): "Not appending file as it already is in .clip file") return True + def _fix_xml_data(self, xml_data): + xml_root = xml_data.getroot() + self._clear_handler(xml_root) + return xml_root + + def _write_result_xml_to_file(self, file, xml_data): + # save it as new file + tree = cET.ElementTree(xml_data) + tree.write(file, xml_declaration=True, + method='xml', encoding='UTF-8') + def _create_openclip_backup_file(self, file): bck_file = "{}.bak".format(file) # if backup does not exist From d4182e5cea64f6374b2a8ef443215f678b0c40bd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 18:02:48 +0200 Subject: [PATCH 79/85] dev_test_plugin --- openpype/hosts/flame/api/test_plugin.py | 428 ++++++++++++++++++++++++ openpype/version.py | 9 +- 2 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/flame/api/test_plugin.py diff --git a/openpype/hosts/flame/api/test_plugin.py b/openpype/hosts/flame/api/test_plugin.py new file mode 100644 index 00000000000..d75819a9e33 --- /dev/null +++ b/openpype/hosts/flame/api/test_plugin.py @@ -0,0 +1,428 @@ +import os +import tempfile +import itertools +import contextlib +import xml.etree.cElementTree as cET +from copy import deepcopy +import shutil +from xml.etree import ElementTree as ET + +import openpype.api as openpype + +import logging + +log = logging.getLogger(__name__) + + +@contextlib.contextmanager +def maintained_temp_file_path(suffix=None): + _suffix = suffix or "" + + try: + # Store dumped json to temporary file + temporary_file = tempfile.mktemp( + suffix=_suffix, prefix="flame_maintained_") + yield temporary_file.replace("\\", "/") + + except IOError as _error: + raise IOError( + "Not able to create temp json file: {}".format(_error)) + + finally: + # Remove the temporary json + os.remove(temporary_file) + + +class MediaInfoFile(object): + """Class to get media info file clip data + + Raises: + IOError: MEDIA_SCRIPT_PATH path doesn't exists + TypeError: Not able to generate clip xml data file + ET.ParseError: Missing clip in xml clip data + IOError: Not able to save xml clip data to file + + Attributes: + str: `MEDIA_SCRIPT_PATH` path to flame binary + logging.Logger: `log` logger + + TODO: add method for getting metadata to dict + """ + MEDIA_SCRIPT_PATH = "/opt/Autodesk/mio/current/dl_get_media_info" + + log = log + + _clip_data = None + _start_frame = None + _fps = None + _drop_mode = None + + def __init__(self, path, **kwargs): + + # replace log if any + if kwargs.get("logger"): + self.log = kwargs["logger"] + + # test if `dl_get_media_info` paht exists + self._validate_media_script_path() + + # derivate other feed variables + self.feed_basename = os.path.basename(path) + self.feed_dir = os.path.dirname(path) + self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() + + with maintained_temp_file_path(".clip") as tmp_path: + self.log.info("Temp File: {}".format(tmp_path)) + self._generate_media_info_file(tmp_path) + + # get clip data and make them single if there is multiple + # clips data + xml_data = self._make_single_clip_media_info(tmp_path) + self.log.info("xml_data: {}".format(xml_data)) + self.log.info("type: {}".format(type(xml_data))) + + # get all time related data and assign them + self._get_time_info_from_origin(xml_data) + self.log.info("start_frame: {}".format(self.start_frame)) + self.log.info("fps: {}".format(self.fps)) + self.log.info("drop frame: {}".format(self.drop_mode)) + self.clip_data = xml_data + + @property + def clip_data(self): + """Clip's xml clip data + + Returns: + xml.etree.ElementTree: xml data + """ + return self._clip_data + + @clip_data.setter + def clip_data(self, data): + self._clip_data = data + + @property + def start_frame(self): + """ Clip's starting frame found in timecode + + Returns: + int: number of frames + """ + return self._start_frame + + @start_frame.setter + def start_frame(self, number): + self._start_frame = int(number) + + @property + def fps(self): + """ Clip's frame rate + + Returns: + float: frame rate + """ + return self._fps + + @fps.setter + def fps(self, fl_number): + self._fps = float(fl_number) + + @property + def drop_mode(self): + """ Clip's drop frame mode + + Returns: + str: drop frame flag + """ + return self._drop_mode + + @drop_mode.setter + def drop_mode(self, text): + self._drop_mode = str(text) + + def _validate_media_script_path(self): + if not os.path.isfile(self.MEDIA_SCRIPT_PATH): + raise IOError("Media Scirpt does not exist: `{}`".format( + self.MEDIA_SCRIPT_PATH)) + + def _generate_media_info_file(self, fpath): + # Create cmd arguments for gettig xml file info file + cmd_args = [ + self.MEDIA_SCRIPT_PATH, + "-e", self.feed_ext, + "-o", fpath, + self.feed_dir + ] + + try: + # execute creation of clip xml template data + openpype.run_subprocess(cmd_args) + except TypeError as error: + raise TypeError( + "Error creating `{}` due: {}".format(fpath, error)) + + def _make_single_clip_media_info(self, fpath): + with open(fpath) as f: + lines = f.readlines() + _added_root = itertools.chain( + "", deepcopy(lines)[1:], "") + new_root = ET.fromstringlist(_added_root) + + # find the clip which is matching to my input name + xml_clips = new_root.findall("clip") + matching_clip = None + for xml_clip in xml_clips: + if xml_clip.find("name").text in self.feed_basename: + matching_clip = xml_clip + + if matching_clip is None: + # return warning there is missing clip + raise ET.ParseError( + "Missing clip in `{}`. Available clips {}".format( + self.feed_basename, [ + xml_clip.find("name").text + for xml_clip in xml_clips + ] + )) + + return matching_clip + + def _get_time_info_from_origin(self, xml_data): + try: + for out_track in xml_data.iter('track'): + for out_feed in out_track.iter('feed'): + # start frame + out_feed_nb_ticks_obj = out_feed.find( + 'startTimecode/nbTicks') + self.start_frame = out_feed_nb_ticks_obj.text + + # fps + out_feed_fps_obj = out_feed.find( + 'startTimecode/rate') + self.fps = out_feed_fps_obj.text + + # drop frame mode + out_feed_drop_mode_obj = out_feed.find( + 'startTimecode/dropMode') + self.drop_mode = out_feed_drop_mode_obj.text + break + else: + continue + except Exception as msg: + self.log.warning(msg) + + @staticmethod + def write_clip_data_to_file(fpath, xml_data): + log.info(">>> type of xml_data: {}".format(type(xml_data))) + if isinstance(xml_data, ET.ElementTree): + xml_data = xml_data.getroot() + try: + # save it as new file + tree = cET.ElementTree(xml_data) + tree.write( + fpath, xml_declaration=True, + method='xml', encoding='UTF-8' + ) + except IOError as error: + raise IOError( + "Not able to write data to file: {}".format(error)) + + +class OpenClipSolver(MediaInfoFile): + create_new_clip = False + + log = log + + def __init__(self, openclip_file_path, feed_data): + self.out_file = openclip_file_path + + # new feed variables: + feed_path = feed_data.pop("path") + + # initialize parent class + super(OpenClipSolver, self).__init__( + feed_path, + **feed_data + ) + + # get other metadata + self.feed_version_name = feed_data["version"] + self.feed_colorspace = feed_data.get("colorspace") + self.log.info("feed_version_name: {}".format(self.feed_version_name)) + + # derivate other feed variables + self.feed_basename = os.path.basename(feed_path) + self.feed_dir = os.path.dirname(feed_path) + self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() + self.log.info("feed_ext: {}".format(self.feed_ext)) + self.log.info("out_file: {}".format(self.out_file)) + if not self._is_valid_tmp_file(self.out_file): + self.create_new_clip = True + + def _is_valid_tmp_file(self, file): + # check if file exists + if os.path.isfile(file): + # test also if file is not empty + with open(file) as f: + lines = f.readlines() + if len(lines) > 2: + return True + + # file is probably corrupted + os.remove(file) + return False + + def make(self): + + if self.create_new_clip: + # New openClip + self._create_new_open_clip() + else: + self._update_open_clip() + + def _clear_handler(self, xml_object): + for handler in xml_object.findall("./handler"): + self.log.info("Handler found") + xml_object.remove(handler) + + def _create_new_open_clip(self): + self.log.info("Building new openClip") + self.log.info(">> self.clip_data: {}".format(self.clip_data)) + + # clip data comming from MediaInfoFile + tmp_xml_feeds = self.clip_data.find('tracks/track/feeds') + tmp_xml_feeds.set('currentVersion', self.feed_version_name) + for tmp_feed in tmp_xml_feeds: + tmp_feed.set('vuid', self.feed_version_name) + + # add colorspace if any is set + if self.feed_colorspace: + self._add_colorspace(tmp_feed, self.feed_colorspace) + + self._clear_handler(tmp_feed) + + tmp_xml_versions_obj = self.clip_data.find('versions') + tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) + for xml_new_version in tmp_xml_versions_obj: + xml_new_version.set('uid', self.feed_version_name) + xml_new_version.set('type', 'version') + + self._clear_handler(self.clip_data) + self.log.info("Adding feed version: {}".format(self.feed_basename)) + + self.write_clip_data_to_file(self.out_file, self.clip_data) + + def _update_open_clip(self): + self.log.info("Updating openClip ..") + + out_xml = ET.parse(self.out_file) + + self.log.info(">> out_xml: {}".format(out_xml)) + self.log.info(">> self.clip_data: {}".format(self.clip_data)) + + # Get new feed from tmp file + tmp_xml_feed = self.clip_data.find('tracks/track/feeds/feed') + + self._clear_handler(tmp_xml_feed) + + # update fps from MediaInfoFile class + if self.fps: + tmp_feed_fps_obj = tmp_xml_feed.find( + "startTimecode/rate") + tmp_feed_fps_obj.text = str(self.fps) + + # update start_frame from MediaInfoFile class + if self.start_frame: + tmp_feed_nb_ticks_obj = tmp_xml_feed.find( + "startTimecode/nbTicks") + tmp_feed_nb_ticks_obj.text = str(self.start_frame) + + # update drop_mode from MediaInfoFile class + if self.drop_mode: + tmp_feed_drop_mode_obj = tmp_xml_feed.find( + "startTimecode/dropMode") + tmp_feed_drop_mode_obj.text = str(self.drop_mode) + + new_path_obj = tmp_xml_feed.find( + "spans/span/path") + new_path = new_path_obj.text + + feed_added = False + if not self._feed_exists(out_xml, new_path): + tmp_xml_feed.set('vuid', self.feed_version_name) + # Append new temp file feed to .clip source out xml + out_track = out_xml.find("tracks/track") + # add colorspace if any is set + if self.feed_colorspace: + self._add_colorspace(tmp_xml_feed, self.feed_colorspace) + + out_feeds = out_track.find('feeds') + out_feeds.set('currentVersion', self.feed_version_name) + out_feeds.append(tmp_xml_feed) + + self.log.info( + "Appending new feed: {}".format( + self.feed_version_name)) + feed_added = True + + if feed_added: + # Append vUID to versions + out_xml_versions_obj = out_xml.find('versions') + out_xml_versions_obj.set( + 'currentVersion', self.feed_version_name) + new_version_obj = ET.Element( + "version", {"type": "version", "uid": self.feed_version_name}) + out_xml_versions_obj.insert(0, new_version_obj) + + self._clear_handler(out_xml) + + # fist create backup + self._create_openclip_backup_file(self.out_file) + + self.log.info("Adding feed version: {}".format( + self.feed_version_name)) + + self.write_clip_data_to_file(self.out_file, out_xml) + + self.log.info("openClip Updated: {}".format(self.out_file)) + + def _feed_exists(self, xml_data, path): + # loop all available feed paths and check if + # the path is not already in file + for src_path in xml_data.iter('path'): + if path == src_path.text: + self.log.warning( + "Not appending file as it already is in .clip file") + return True + + def _create_openclip_backup_file(self, file): + bck_file = "{}.bak".format(file) + # if backup does not exist + if not os.path.isfile(bck_file): + shutil.copy2(file, bck_file) + else: + # in case it exists and is already multiplied + created = False + for _i in range(1, 99): + bck_file = "{name}.bak.{idx:0>2}".format( + name=file, + idx=_i) + # create numbered backup file + if not os.path.isfile(bck_file): + shutil.copy2(file, bck_file) + created = True + break + # in case numbered does not exists + if not created: + bck_file = "{}.bak.last".format(file) + shutil.copy2(file, bck_file) + + def _add_colorspace(self, feed_obj, profile_name): + feed_storage_obj = feed_obj.find("storageFormat") + feed_clr_obj = feed_storage_obj.find("colourSpace") + if feed_clr_obj is not None: + feed_clr_obj = ET.Element( + "colourSpace", {"type": "string"}) + feed_storage_obj.append(feed_clr_obj) + + feed_clr_obj.text = profile_name diff --git a/openpype/version.py b/openpype/version.py index 97aa585ca76..d447d271725 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,10 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.3" +__version__ = "3.9.3-nightly.1-upp220408" + + +''' +includes: + - Flame: integrate batch groups: + https://github.com/pypeclub/OpenPype/pull/2928 +''' From 06d2e898654b9aca82c9a8c979938cc44ee1f766 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 18:03:30 +0200 Subject: [PATCH 80/85] testing file --- openpype/hosts/flame/tests/flame_test.py | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 openpype/hosts/flame/tests/flame_test.py diff --git a/openpype/hosts/flame/tests/flame_test.py b/openpype/hosts/flame/tests/flame_test.py new file mode 100644 index 00000000000..402983eeba6 --- /dev/null +++ b/openpype/hosts/flame/tests/flame_test.py @@ -0,0 +1,30 @@ +from openpype.lib import import_filepath + +plugin = import_filepath( + "/Users/pype.club/code/openpype/openpype/hosts/flame/api/test_plugin.py") + +openclip_file_path = "/Users/pype.club/FLAME_STORAGE/test_shot_fps_float/test.clip" +# feed_datas = [ +# { +# "path": "/Users/pype.club/pype_club_root/OP02_VFX_demo/shots/a/a0000001/publish/plate/plateMain/v007/op02vfx_a0000001_plateMain_v007_exr16fpdwaaCl.0997.exr", +# "version": "v007" +# }, +# { +# "path": "/Users/pype.club/pype_club_root/OP02_VFX_demo/shots/a/a0000001/publish/plate/plateMain/v008/op02vfx_a0000001_plateMain_v008_exr16fpdwaaCl.0997.exr", +# "version": "v008" +# } +# ] + +feed_datas = [ + { + "path": "/Users/pype.club/FLAME_STORAGE/test_shot_fps_float/v001/file_name_v001.1001.exr", + "version": "v001" + }, + { + "path": "/Users/pype.club/FLAME_STORAGE/test_shot_fps_float/v002/file_name_v002.1001.exr", + "version": "v002" + } +] +for feed_data in feed_datas: + oclip = plugin.OpenClipSolver(openclip_file_path, feed_data) + oclip.make() From a103eba505d9358c0c7058f614e36dd14b2edd4b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 19:03:54 +0200 Subject: [PATCH 81/85] flame: fixing OpenClipSolver --- openpype/hosts/flame/api/lib.py | 18 +- openpype/hosts/flame/api/plugin.py | 185 +++------- openpype/hosts/flame/api/test_plugin.py | 428 ----------------------- openpype/hosts/flame/tests/flame_test.py | 30 -- 4 files changed, 57 insertions(+), 604 deletions(-) delete mode 100644 openpype/hosts/flame/api/test_plugin.py delete mode 100644 openpype/hosts/flame/tests/flame_test.py diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 6ee0eb6a824..c7c444c1fb4 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -784,9 +784,14 @@ def __init__(self, path, **kwargs): # get clip data and make them single if there is multiple # clips data xml_data = self._make_single_clip_media_info(tmp_path) + self.log.debug("xml_data: {}".format(xml_data)) + self.log.debug("type: {}".format(type(xml_data))) # get all time related data and assign them self._get_time_info_from_origin(xml_data) + self.log.debug("start_frame: {}".format(self.start_frame)) + self.log.debug("fps: {}".format(self.fps)) + self.log.debug("drop frame: {}".format(self.drop_mode)) self.clip_data = xml_data @property @@ -913,10 +918,19 @@ def _get_time_info_from_origin(self, xml_data): self.log.warning(msg) @staticmethod - def write_clip_data_to_file(fpath, xml_data): + def write_clip_data_to_file(fpath, xml_element_data): + """ Write xml element of clip data to file + + Args: + fpath (string): file path + xml_element_data (xml.etree.ElementTree.Element): xml data + + Raises: + IOError: If data could not be written to file + """ try: # save it as new file - tree = cET.ElementTree(xml_data) + tree = cET.ElementTree(xml_element_data) tree.write( fpath, xml_declaration=True, method='xml', encoding='UTF-8' diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index bd0f9f1a815..42e6e199312 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,15 +1,11 @@ -import itertools import os import re import shutil -import sys -import xml.etree.cElementTree as cET from copy import deepcopy from xml.etree import ElementTree as ET import openpype.api as openpype import qargparse -import six from openpype import style from openpype.pipeline import LegacyCreator, LoaderPlugin from Qt import QtCore, QtWidgets @@ -658,8 +654,8 @@ def _create_parents(self): # Publishing plugin functions -# Loader plugin functions +# Loader plugin functions class ClipLoader(LoaderPlugin): """A basic clip loader for Flame @@ -679,53 +675,37 @@ class ClipLoader(LoaderPlugin): ] -# TODO: inheritance from flame.api.lib.MediaInfoFile -class OpenClipSolver: - media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" - tmp_name = "_tmp.clip" - tmp_file = None +class OpenClipSolver(flib.MediaInfoFile): create_new_clip = False - out_feed_nb_ticks = None - out_feed_fps = None - out_feed_drop_mode = None - log = log def __init__(self, openclip_file_path, feed_data): - # test if media script paht exists - self._validate_media_script_path() + self.out_file = openclip_file_path # new feed variables: - feed_path = feed_data["path"] + feed_path = feed_data.pop("path") + + # initialize parent class + super(OpenClipSolver, self).__init__( + feed_path, + **feed_data + ) + + # get other metadata self.feed_version_name = feed_data["version"] self.feed_colorspace = feed_data.get("colorspace") - - if feed_data.get("logger"): - self.log = feed_data["logger"] + self.log.debug("feed_version_name: {}".format(self.feed_version_name)) # derivate other feed variables self.feed_basename = os.path.basename(feed_path) self.feed_dir = os.path.dirname(feed_path) self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - - if not self._is_valid_tmp_file(openclip_file_path): - # openclip does not exist yet and will be created - self.tmp_file = self.out_file = openclip_file_path + self.log.debug("feed_ext: {}".format(self.feed_ext)) + self.log.debug("out_file: {}".format(self.out_file)) + if not self._is_valid_tmp_file(self.out_file): self.create_new_clip = True - else: - # update already created clip - # output a temp file - self.out_file = openclip_file_path - self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) - - # remove previously generated temp files - # it will be regenerated - self._clear_tmp_file() - - self.log.info("Temp File: {}".format(self.tmp_file)) - def _is_valid_tmp_file(self, file): # check if file exists if os.path.isfile(file): @@ -740,7 +720,6 @@ def _is_valid_tmp_file(self, file): return False def make(self): - self._generate_media_info_file() if self.create_new_clip: # New openClip @@ -748,69 +727,17 @@ def make(self): else: self._update_open_clip() - def _validate_media_script_path(self): - if not os.path.isfile(self.media_script_path): - raise IOError("Media Scirpt does not exist: `{}`".format( - self.media_script_path)) - - def _generate_media_info_file(self): - # Create cmd arguments for gettig xml file info file - cmd_args = [ - self.media_script_path, - "-e", self.feed_ext, - "-o", self.tmp_file, - self.feed_dir - ] - - # execute creation of clip xml template data - try: - openpype.run_subprocess(cmd_args) - self._make_single_clip_media_info() - except TypeError: - self.log.error("Error creating self.tmp_file") - six.reraise(*sys.exc_info()) - - def _make_single_clip_media_info(self): - with open(self.tmp_file) as f: - lines = f.readlines() - _added_root = itertools.chain( - "", deepcopy(lines)[1:], "") - new_root = ET.fromstringlist(_added_root) - - # find the clip which is matching to my input name - xml_clips = new_root.findall("clip") - matching_clip = None - for xml_clip in xml_clips: - if xml_clip.find("name").text in self.feed_basename: - matching_clip = xml_clip - - if matching_clip is None: - # return warning there is missing clip - raise ET.ParseError( - "Missing clip in `{}`. Available clips {}".format( - self.feed_basename, [ - xml_clip.find("name").text - for xml_clip in xml_clips - ] - )) - - self._write_result_xml_to_file(self.tmp_file, matching_clip) - - def _clear_tmp_file(self): - if os.path.isfile(self.tmp_file): - os.remove(self.tmp_file) - def _clear_handler(self, xml_object): for handler in xml_object.findall("./handler"): - self.log.debug("Handler found") + self.log.info("Handler found") xml_object.remove(handler) def _create_new_open_clip(self): self.log.info("Building new openClip") + self.log.debug(">> self.clip_data: {}".format(self.clip_data)) - tmp_xml = ET.parse(self.tmp_file) - - tmp_xml_feeds = tmp_xml.find('tracks/track/feeds') + # clip data comming from MediaInfoFile + tmp_xml_feeds = self.clip_data.find('tracks/track/feeds') tmp_xml_feeds.set('currentVersion', self.feed_version_name) for tmp_feed in tmp_xml_feeds: tmp_feed.set('vuid', self.feed_version_name) @@ -821,46 +748,48 @@ def _create_new_open_clip(self): self._clear_handler(tmp_feed) - tmp_xml_versions_obj = tmp_xml.find('versions') + tmp_xml_versions_obj = self.clip_data.find('versions') tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) for xml_new_version in tmp_xml_versions_obj: xml_new_version.set('uid', self.feed_version_name) xml_new_version.set('type', 'version') - xml_data = self._fix_xml_data(tmp_xml) + self._clear_handler(self.clip_data) self.log.info("Adding feed version: {}".format(self.feed_basename)) - self._write_result_xml_to_file(self.out_file, xml_data) - - self.log.info("openClip Updated: {}".format(self.tmp_file)) + self.write_clip_data_to_file(self.out_file, self.clip_data) def _update_open_clip(self): self.log.info("Updating openClip ..") out_xml = ET.parse(self.out_file) - tmp_xml = ET.parse(self.tmp_file) + out_xml = out_xml.getroot() self.log.debug(">> out_xml: {}".format(out_xml)) - self.log.debug(">> tmp_xml: {}".format(tmp_xml)) + self.log.debug(">> self.clip_data: {}".format(self.clip_data)) # Get new feed from tmp file - tmp_xml_feed = tmp_xml.find('tracks/track/feeds/feed') + tmp_xml_feed = self.clip_data.find('tracks/track/feeds/feed') self._clear_handler(tmp_xml_feed) - self._get_time_info_from_origin(out_xml) - if self.out_feed_fps: + # update fps from MediaInfoFile class + if self.fps: tmp_feed_fps_obj = tmp_xml_feed.find( "startTimecode/rate") - tmp_feed_fps_obj.text = self.out_feed_fps - if self.out_feed_nb_ticks: + tmp_feed_fps_obj.text = str(self.fps) + + # update start_frame from MediaInfoFile class + if self.start_frame: tmp_feed_nb_ticks_obj = tmp_xml_feed.find( "startTimecode/nbTicks") - tmp_feed_nb_ticks_obj.text = self.out_feed_nb_ticks - if self.out_feed_drop_mode: + tmp_feed_nb_ticks_obj.text = str(self.start_frame) + + # update drop_mode from MediaInfoFile class + if self.drop_mode: tmp_feed_drop_mode_obj = tmp_xml_feed.find( "startTimecode/dropMode") - tmp_feed_drop_mode_obj.text = self.out_feed_drop_mode + tmp_feed_drop_mode_obj.text = str(self.drop_mode) new_path_obj = tmp_xml_feed.find( "spans/span/path") @@ -893,7 +822,7 @@ def _update_open_clip(self): "version", {"type": "version", "uid": self.feed_version_name}) out_xml_versions_obj.insert(0, new_version_obj) - xml_data = self._fix_xml_data(out_xml) + self._clear_handler(out_xml) # fist create backup self._create_openclip_backup_file(self.out_file) @@ -901,30 +830,9 @@ def _update_open_clip(self): self.log.info("Adding feed version: {}".format( self.feed_version_name)) - self._write_result_xml_to_file(self.out_file, xml_data) - - self.log.info("openClip Updated: {}".format(self.out_file)) - - self._clear_tmp_file() - - def _get_time_info_from_origin(self, xml_data): - try: - for out_track in xml_data.iter('track'): - for out_feed in out_track.iter('feed'): - out_feed_nb_ticks_obj = out_feed.find( - 'startTimecode/nbTicks') - self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text - out_feed_fps_obj = out_feed.find( - 'startTimecode/rate') - self.out_feed_fps = out_feed_fps_obj.text - out_feed_drop_mode_obj = out_feed.find( - 'startTimecode/dropMode') - self.out_feed_drop_mode = out_feed_drop_mode_obj.text - break - else: - continue - except Exception as msg: - self.log.warning(msg) + self.write_clip_data_to_file(self.out_file, out_xml) + + self.log.debug("OpenClip Updated: {}".format(self.out_file)) def _feed_exists(self, xml_data, path): # loop all available feed paths and check if @@ -935,17 +843,6 @@ def _feed_exists(self, xml_data, path): "Not appending file as it already is in .clip file") return True - def _fix_xml_data(self, xml_data): - xml_root = xml_data.getroot() - self._clear_handler(xml_root) - return xml_root - - def _write_result_xml_to_file(self, file, xml_data): - # save it as new file - tree = cET.ElementTree(xml_data) - tree.write(file, xml_declaration=True, - method='xml', encoding='UTF-8') - def _create_openclip_backup_file(self, file): bck_file = "{}.bak".format(file) # if backup does not exist diff --git a/openpype/hosts/flame/api/test_plugin.py b/openpype/hosts/flame/api/test_plugin.py deleted file mode 100644 index d75819a9e33..00000000000 --- a/openpype/hosts/flame/api/test_plugin.py +++ /dev/null @@ -1,428 +0,0 @@ -import os -import tempfile -import itertools -import contextlib -import xml.etree.cElementTree as cET -from copy import deepcopy -import shutil -from xml.etree import ElementTree as ET - -import openpype.api as openpype - -import logging - -log = logging.getLogger(__name__) - - -@contextlib.contextmanager -def maintained_temp_file_path(suffix=None): - _suffix = suffix or "" - - try: - # Store dumped json to temporary file - temporary_file = tempfile.mktemp( - suffix=_suffix, prefix="flame_maintained_") - yield temporary_file.replace("\\", "/") - - except IOError as _error: - raise IOError( - "Not able to create temp json file: {}".format(_error)) - - finally: - # Remove the temporary json - os.remove(temporary_file) - - -class MediaInfoFile(object): - """Class to get media info file clip data - - Raises: - IOError: MEDIA_SCRIPT_PATH path doesn't exists - TypeError: Not able to generate clip xml data file - ET.ParseError: Missing clip in xml clip data - IOError: Not able to save xml clip data to file - - Attributes: - str: `MEDIA_SCRIPT_PATH` path to flame binary - logging.Logger: `log` logger - - TODO: add method for getting metadata to dict - """ - MEDIA_SCRIPT_PATH = "/opt/Autodesk/mio/current/dl_get_media_info" - - log = log - - _clip_data = None - _start_frame = None - _fps = None - _drop_mode = None - - def __init__(self, path, **kwargs): - - # replace log if any - if kwargs.get("logger"): - self.log = kwargs["logger"] - - # test if `dl_get_media_info` paht exists - self._validate_media_script_path() - - # derivate other feed variables - self.feed_basename = os.path.basename(path) - self.feed_dir = os.path.dirname(path) - self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - - with maintained_temp_file_path(".clip") as tmp_path: - self.log.info("Temp File: {}".format(tmp_path)) - self._generate_media_info_file(tmp_path) - - # get clip data and make them single if there is multiple - # clips data - xml_data = self._make_single_clip_media_info(tmp_path) - self.log.info("xml_data: {}".format(xml_data)) - self.log.info("type: {}".format(type(xml_data))) - - # get all time related data and assign them - self._get_time_info_from_origin(xml_data) - self.log.info("start_frame: {}".format(self.start_frame)) - self.log.info("fps: {}".format(self.fps)) - self.log.info("drop frame: {}".format(self.drop_mode)) - self.clip_data = xml_data - - @property - def clip_data(self): - """Clip's xml clip data - - Returns: - xml.etree.ElementTree: xml data - """ - return self._clip_data - - @clip_data.setter - def clip_data(self, data): - self._clip_data = data - - @property - def start_frame(self): - """ Clip's starting frame found in timecode - - Returns: - int: number of frames - """ - return self._start_frame - - @start_frame.setter - def start_frame(self, number): - self._start_frame = int(number) - - @property - def fps(self): - """ Clip's frame rate - - Returns: - float: frame rate - """ - return self._fps - - @fps.setter - def fps(self, fl_number): - self._fps = float(fl_number) - - @property - def drop_mode(self): - """ Clip's drop frame mode - - Returns: - str: drop frame flag - """ - return self._drop_mode - - @drop_mode.setter - def drop_mode(self, text): - self._drop_mode = str(text) - - def _validate_media_script_path(self): - if not os.path.isfile(self.MEDIA_SCRIPT_PATH): - raise IOError("Media Scirpt does not exist: `{}`".format( - self.MEDIA_SCRIPT_PATH)) - - def _generate_media_info_file(self, fpath): - # Create cmd arguments for gettig xml file info file - cmd_args = [ - self.MEDIA_SCRIPT_PATH, - "-e", self.feed_ext, - "-o", fpath, - self.feed_dir - ] - - try: - # execute creation of clip xml template data - openpype.run_subprocess(cmd_args) - except TypeError as error: - raise TypeError( - "Error creating `{}` due: {}".format(fpath, error)) - - def _make_single_clip_media_info(self, fpath): - with open(fpath) as f: - lines = f.readlines() - _added_root = itertools.chain( - "", deepcopy(lines)[1:], "") - new_root = ET.fromstringlist(_added_root) - - # find the clip which is matching to my input name - xml_clips = new_root.findall("clip") - matching_clip = None - for xml_clip in xml_clips: - if xml_clip.find("name").text in self.feed_basename: - matching_clip = xml_clip - - if matching_clip is None: - # return warning there is missing clip - raise ET.ParseError( - "Missing clip in `{}`. Available clips {}".format( - self.feed_basename, [ - xml_clip.find("name").text - for xml_clip in xml_clips - ] - )) - - return matching_clip - - def _get_time_info_from_origin(self, xml_data): - try: - for out_track in xml_data.iter('track'): - for out_feed in out_track.iter('feed'): - # start frame - out_feed_nb_ticks_obj = out_feed.find( - 'startTimecode/nbTicks') - self.start_frame = out_feed_nb_ticks_obj.text - - # fps - out_feed_fps_obj = out_feed.find( - 'startTimecode/rate') - self.fps = out_feed_fps_obj.text - - # drop frame mode - out_feed_drop_mode_obj = out_feed.find( - 'startTimecode/dropMode') - self.drop_mode = out_feed_drop_mode_obj.text - break - else: - continue - except Exception as msg: - self.log.warning(msg) - - @staticmethod - def write_clip_data_to_file(fpath, xml_data): - log.info(">>> type of xml_data: {}".format(type(xml_data))) - if isinstance(xml_data, ET.ElementTree): - xml_data = xml_data.getroot() - try: - # save it as new file - tree = cET.ElementTree(xml_data) - tree.write( - fpath, xml_declaration=True, - method='xml', encoding='UTF-8' - ) - except IOError as error: - raise IOError( - "Not able to write data to file: {}".format(error)) - - -class OpenClipSolver(MediaInfoFile): - create_new_clip = False - - log = log - - def __init__(self, openclip_file_path, feed_data): - self.out_file = openclip_file_path - - # new feed variables: - feed_path = feed_data.pop("path") - - # initialize parent class - super(OpenClipSolver, self).__init__( - feed_path, - **feed_data - ) - - # get other metadata - self.feed_version_name = feed_data["version"] - self.feed_colorspace = feed_data.get("colorspace") - self.log.info("feed_version_name: {}".format(self.feed_version_name)) - - # derivate other feed variables - self.feed_basename = os.path.basename(feed_path) - self.feed_dir = os.path.dirname(feed_path) - self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - self.log.info("feed_ext: {}".format(self.feed_ext)) - self.log.info("out_file: {}".format(self.out_file)) - if not self._is_valid_tmp_file(self.out_file): - self.create_new_clip = True - - def _is_valid_tmp_file(self, file): - # check if file exists - if os.path.isfile(file): - # test also if file is not empty - with open(file) as f: - lines = f.readlines() - if len(lines) > 2: - return True - - # file is probably corrupted - os.remove(file) - return False - - def make(self): - - if self.create_new_clip: - # New openClip - self._create_new_open_clip() - else: - self._update_open_clip() - - def _clear_handler(self, xml_object): - for handler in xml_object.findall("./handler"): - self.log.info("Handler found") - xml_object.remove(handler) - - def _create_new_open_clip(self): - self.log.info("Building new openClip") - self.log.info(">> self.clip_data: {}".format(self.clip_data)) - - # clip data comming from MediaInfoFile - tmp_xml_feeds = self.clip_data.find('tracks/track/feeds') - tmp_xml_feeds.set('currentVersion', self.feed_version_name) - for tmp_feed in tmp_xml_feeds: - tmp_feed.set('vuid', self.feed_version_name) - - # add colorspace if any is set - if self.feed_colorspace: - self._add_colorspace(tmp_feed, self.feed_colorspace) - - self._clear_handler(tmp_feed) - - tmp_xml_versions_obj = self.clip_data.find('versions') - tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) - for xml_new_version in tmp_xml_versions_obj: - xml_new_version.set('uid', self.feed_version_name) - xml_new_version.set('type', 'version') - - self._clear_handler(self.clip_data) - self.log.info("Adding feed version: {}".format(self.feed_basename)) - - self.write_clip_data_to_file(self.out_file, self.clip_data) - - def _update_open_clip(self): - self.log.info("Updating openClip ..") - - out_xml = ET.parse(self.out_file) - - self.log.info(">> out_xml: {}".format(out_xml)) - self.log.info(">> self.clip_data: {}".format(self.clip_data)) - - # Get new feed from tmp file - tmp_xml_feed = self.clip_data.find('tracks/track/feeds/feed') - - self._clear_handler(tmp_xml_feed) - - # update fps from MediaInfoFile class - if self.fps: - tmp_feed_fps_obj = tmp_xml_feed.find( - "startTimecode/rate") - tmp_feed_fps_obj.text = str(self.fps) - - # update start_frame from MediaInfoFile class - if self.start_frame: - tmp_feed_nb_ticks_obj = tmp_xml_feed.find( - "startTimecode/nbTicks") - tmp_feed_nb_ticks_obj.text = str(self.start_frame) - - # update drop_mode from MediaInfoFile class - if self.drop_mode: - tmp_feed_drop_mode_obj = tmp_xml_feed.find( - "startTimecode/dropMode") - tmp_feed_drop_mode_obj.text = str(self.drop_mode) - - new_path_obj = tmp_xml_feed.find( - "spans/span/path") - new_path = new_path_obj.text - - feed_added = False - if not self._feed_exists(out_xml, new_path): - tmp_xml_feed.set('vuid', self.feed_version_name) - # Append new temp file feed to .clip source out xml - out_track = out_xml.find("tracks/track") - # add colorspace if any is set - if self.feed_colorspace: - self._add_colorspace(tmp_xml_feed, self.feed_colorspace) - - out_feeds = out_track.find('feeds') - out_feeds.set('currentVersion', self.feed_version_name) - out_feeds.append(tmp_xml_feed) - - self.log.info( - "Appending new feed: {}".format( - self.feed_version_name)) - feed_added = True - - if feed_added: - # Append vUID to versions - out_xml_versions_obj = out_xml.find('versions') - out_xml_versions_obj.set( - 'currentVersion', self.feed_version_name) - new_version_obj = ET.Element( - "version", {"type": "version", "uid": self.feed_version_name}) - out_xml_versions_obj.insert(0, new_version_obj) - - self._clear_handler(out_xml) - - # fist create backup - self._create_openclip_backup_file(self.out_file) - - self.log.info("Adding feed version: {}".format( - self.feed_version_name)) - - self.write_clip_data_to_file(self.out_file, out_xml) - - self.log.info("openClip Updated: {}".format(self.out_file)) - - def _feed_exists(self, xml_data, path): - # loop all available feed paths and check if - # the path is not already in file - for src_path in xml_data.iter('path'): - if path == src_path.text: - self.log.warning( - "Not appending file as it already is in .clip file") - return True - - def _create_openclip_backup_file(self, file): - bck_file = "{}.bak".format(file) - # if backup does not exist - if not os.path.isfile(bck_file): - shutil.copy2(file, bck_file) - else: - # in case it exists and is already multiplied - created = False - for _i in range(1, 99): - bck_file = "{name}.bak.{idx:0>2}".format( - name=file, - idx=_i) - # create numbered backup file - if not os.path.isfile(bck_file): - shutil.copy2(file, bck_file) - created = True - break - # in case numbered does not exists - if not created: - bck_file = "{}.bak.last".format(file) - shutil.copy2(file, bck_file) - - def _add_colorspace(self, feed_obj, profile_name): - feed_storage_obj = feed_obj.find("storageFormat") - feed_clr_obj = feed_storage_obj.find("colourSpace") - if feed_clr_obj is not None: - feed_clr_obj = ET.Element( - "colourSpace", {"type": "string"}) - feed_storage_obj.append(feed_clr_obj) - - feed_clr_obj.text = profile_name diff --git a/openpype/hosts/flame/tests/flame_test.py b/openpype/hosts/flame/tests/flame_test.py deleted file mode 100644 index 402983eeba6..00000000000 --- a/openpype/hosts/flame/tests/flame_test.py +++ /dev/null @@ -1,30 +0,0 @@ -from openpype.lib import import_filepath - -plugin = import_filepath( - "/Users/pype.club/code/openpype/openpype/hosts/flame/api/test_plugin.py") - -openclip_file_path = "/Users/pype.club/FLAME_STORAGE/test_shot_fps_float/test.clip" -# feed_datas = [ -# { -# "path": "/Users/pype.club/pype_club_root/OP02_VFX_demo/shots/a/a0000001/publish/plate/plateMain/v007/op02vfx_a0000001_plateMain_v007_exr16fpdwaaCl.0997.exr", -# "version": "v007" -# }, -# { -# "path": "/Users/pype.club/pype_club_root/OP02_VFX_demo/shots/a/a0000001/publish/plate/plateMain/v008/op02vfx_a0000001_plateMain_v008_exr16fpdwaaCl.0997.exr", -# "version": "v008" -# } -# ] - -feed_datas = [ - { - "path": "/Users/pype.club/FLAME_STORAGE/test_shot_fps_float/v001/file_name_v001.1001.exr", - "version": "v001" - }, - { - "path": "/Users/pype.club/FLAME_STORAGE/test_shot_fps_float/v002/file_name_v002.1001.exr", - "version": "v002" - } -] -for feed_data in feed_datas: - oclip = plugin.OpenClipSolver(openclip_file_path, feed_data) - oclip.make() From bb222642fa295e46357f1a6cc364ac34b5f6bce1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 19:06:32 +0200 Subject: [PATCH 82/85] reversing version rename --- openpype/version.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/openpype/version.py b/openpype/version.py index d447d271725..97aa585ca76 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,10 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.3-nightly.1-upp220408" - - -''' -includes: - - Flame: integrate batch groups: - https://github.com/pypeclub/OpenPype/pull/2928 -''' +__version__ = "3.9.3" From c5683f50d44f99469cc6393c68c2047dd40efd49 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 8 Apr 2022 20:07:53 +0200 Subject: [PATCH 83/85] flame: fixing skip condition --- openpype/hosts/flame/otio/flame_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 0b9c9ce8178..9f0bec62eab 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -558,7 +558,7 @@ def create_otio_timeline(sequence): len(track.segments) == 0 or track.hidden.get_value() ): - return None + continue # convert track to otio otio_track = create_otio_track( From 16e84073329b5dbc992dfadfd0818233f794cb0d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 11 Apr 2022 10:50:34 +0200 Subject: [PATCH 84/85] flame: processing comments --- openpype/hosts/flame/api/plugin.py | 14 ++++++++------ openpype/hosts/flame/api/scripts/wiretap_com.py | 4 ++++ openpype/hosts/flame/otio/flame_export.py | 16 ++++++---------- .../plugins/publish/integrate_batch_group.py | 9 ++------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 42e6e199312..c87445fdd31 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -4,11 +4,12 @@ from copy import deepcopy from xml.etree import ElementTree as ET +from Qt import QtCore, QtWidgets + import openpype.api as openpype import qargparse from openpype import style from openpype.pipeline import LegacyCreator, LoaderPlugin -from Qt import QtCore, QtWidgets from . import constants from . import lib as flib @@ -712,12 +713,13 @@ def _is_valid_tmp_file(self, file): # test also if file is not empty with open(file) as f: lines = f.readlines() - if len(lines) > 2: - return True - # file is probably corrupted - os.remove(file) - return False + if len(lines) > 2: + return True + + # file is probably corrupted + os.remove(file) + return False def make(self): diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index f78102c0a13..4825ff43865 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -461,6 +461,10 @@ def _set_project_colorspace(self, project_name, color_policy): def _subprocess_preexec_fn(): + """ Helper function + + Setting permission mask to 0777 + """ os.setpgrp() os.umask(0o000) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 9f0bec62eab..4fe05ec1d89 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -455,15 +455,10 @@ def add_otio_metadata(otio_item, item, **kwargs): otio_item.metadata.update({key: value}) -def _get_shot_tokens_values(clip, tokens, from_clip=False): +def _get_shot_tokens_values(clip, tokens): old_value = None output = {} - # in case it is segment from reel clip - # avoiding duplicity of segement data - if from_clip: - return {} - old_value = clip.shot_name.get_value() for token in tokens: @@ -480,7 +475,7 @@ def _get_shot_tokens_values(clip, tokens, from_clip=False): return output -def _get_segment_attributes(segment, from_clip=False): +def _get_segment_attributes(segment): log.debug("Segment name|hidden: {}|{}".format( segment.name.get_value(), segment.hidden @@ -503,9 +498,10 @@ def _get_segment_attributes(segment, from_clip=False): } # add all available shot tokens - shot_tokens = _get_shot_tokens_values(segment, [ - "", "", "", "", - ], from_clip) + shot_tokens = _get_shot_tokens_values( + segment, + ["", "", "", ""] + ) clip_data.update(shot_tokens) # populate shot source metadata diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index cac99a25acd..3615f06a3d2 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -299,10 +299,8 @@ def _get_write_prefs(self, instance, task_data): version_name = "v" version_padding = 3 - # return it as ordered dict - reutrn_dict = OrderedDict() # need to make sure the order of keys is correct - for item in ( + return OrderedDict( ("name", name), ("media_path", media_path), ("media_path_pattern", media_path_pattern), @@ -320,10 +318,7 @@ def _get_write_prefs(self, instance, task_data): ("version_mode", version_mode), ("version_name", version_name), ("version_padding", version_padding) - ): - reutrn_dict.update({item[0]: item[1]}) - - return reutrn_dict + ) def _get_shot_task_dir_path(self, instance, task_data): project_doc = instance.data["projectEntity"] From 7a40cc269239e522155692c4f4899f8bcc9f6cc4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 11 Apr 2022 17:19:36 +0200 Subject: [PATCH 85/85] fix orderdict --- openpype/hosts/flame/plugins/publish/integrate_batch_group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py index 3615f06a3d2..da9553cc2a5 100644 --- a/openpype/hosts/flame/plugins/publish/integrate_batch_group.py +++ b/openpype/hosts/flame/plugins/publish/integrate_batch_group.py @@ -300,7 +300,7 @@ def _get_write_prefs(self, instance, task_data): version_padding = 3 # need to make sure the order of keys is correct - return OrderedDict( + return OrderedDict(( ("name", name), ("media_path", media_path), ("media_path_pattern", media_path_pattern), @@ -318,7 +318,7 @@ def _get_write_prefs(self, instance, task_data): ("version_mode", version_mode), ("version_name", version_name), ("version_padding", version_padding) - ) + )) def _get_shot_task_dir_path(self, instance, task_data): project_doc = instance.data["projectEntity"]