From edc10cd85f232d132266b00dae6392ab33945b3f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 001/202] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 8 +- .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 0b4e4c74e6..167f7611ce 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -70,6 +70,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ @@ -442,7 +446,9 @@ "template": "{family}{Task}" }, { - "families": ["render"], + "families": [ + "render" + ], "hosts": [ "aftereffects" ], diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From c9996bb0c00b2fdb3d4db964efe1933bc99438e1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 002/202] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 57279d0380..6899811ed5 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1037,3 +1037,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From 23a5f21f6b54f4f01d40c73ef5bdb032a2c7440f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 003/202] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From 9f8107df36dd8d281482d93fb6a0e3530d91fe91 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 004/202] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From d588ee7ed967e34adfd4017824d9f323e2ab7ad4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 005/202] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 6899811ed5..792e8ddd1e 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1042,6 +1042,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1058,6 +1059,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1072,6 +1074,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From 5ca4f825006d28bed4d0c01df71af3b3ffe21deb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 006/202] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 4854b521d408d326f741cc73cda0ddc3e67795ce Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 007/202] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From 39b8c111cfd418b74e0cf915590bcae2547c30ae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 008/202] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From a63ddebc19c7078c33b49029e5de3059b0fcdd9d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 009/202] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From eced917d1a06fb6c973396c22c37db70bf71e4d4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 010/202] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From 82a8fda4d6fd319be4f570d0731881d0effb3dac Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 011/202] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From 6f7e1c3cb49fe0162220919d398e86cddf68d0fa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 012/202] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From d27dab7c970903e483aa6335ebee62ffffbab191 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 013/202] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From 24abe69437801ea4f022d3a0816664e93b2072f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 18:22:08 +0100 Subject: [PATCH 014/202] OP-4643 - refactored according to review Function turned into single filepath input. --- openpype/lib/transcoding.py | 43 ++++++----- .../publish/extract_color_transcode.py | 72 ++++++++++--------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 792e8ddd1e..8e3432e0e9 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1039,12 +1039,12 @@ def convert_ffprobe_fps_to_float(value): return dividend / divisor -def convert_colorspace_for_input_paths( - input_paths, - output_dir, +def convert_colorspace( + input_path, + out_filepath, config_path, - source_color_space, - target_color_space, + source_colorspace, + target_colorspace, logger=None ): """Convert source files from one color space to another. @@ -1055,13 +1055,13 @@ def convert_colorspace_for_input_paths( frame template Args: - input_paths (str): Paths that should be converted. It is expected that + input_path (str): Paths that should be converted. It is expected that contains single file or image sequence of samy type. - output_dir (str): Path to directory where output will be rendered. + out_filepath (str): Path to directory where output will be rendered. Must not be same as input's directory. config_path (str): path to OCIO config file - source_color_space (str): ocio valid color space of source files - target_color_space (str): ocio valid target color space + source_colorspace (str): ocio valid color space of source files + target_colorspace (str): ocio valid target color space logger (logging.Logger): Logger used for logging. """ @@ -1075,21 +1075,18 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_color_space, target_color_space + "--colorconvert", source_colorspace, target_colorspace ] - for input_path in input_paths: - # Prepare subprocess arguments + # Prepare subprocess arguments - oiio_cmd.extend([ - input_arg, input_path, - ]) + oiio_cmd.extend([ + input_arg, input_path, + ]) - # Add last argument - path to output - base_filename = os.path.basename(input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) + # Add last argument - path to output + oiio_cmd.extend([ + "-o", out_filepath + ]) - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..3a05426432 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -10,7 +10,7 @@ from openpype.lib import ( ) from openpype.lib.transcoding import ( - convert_colorspace_for_input_paths, + convert_colorspace, get_transcode_temp_directory, ) @@ -69,7 +69,7 @@ class ExtractColorTranscode(publish.Extractor): continue colorspace_data = repre["colorspaceData"] - source_color_space = colorspace_data["colorspace"] + source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") if not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") @@ -80,8 +80,8 @@ class ExtractColorTranscode(publish.Extractor): for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) - new_staging_dir = get_transcode_temp_directory() original_staging_dir = new_repre["stagingDir"] + new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir files_to_convert = new_repre["files"] if not isinstance(files_to_convert, list): @@ -92,27 +92,28 @@ class ExtractColorTranscode(publish.Extractor): output_extension = output_def["output_extension"] output_extension = output_extension.replace('.', '') if output_extension: - new_repre["name"] = output_extension + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension new_repre["ext"] = output_extension - files_to_convert = self._rename_output_files( - files_to_convert, output_extension) - - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - target_colorspace = output_def["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) + for file_name in files_to_convert: + input_filepath = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_filepath, + new_staging_dir, + output_extension) + convert_colorspace( + input_filepath, + output_path, + config_path, + source_colorspace, + target_colorspace, + self.log + ) instance.context.data["cleanupFullPaths"].extend( files_to_delete) @@ -130,16 +131,16 @@ class ExtractColorTranscode(publish.Extractor): instance.data["representations"].append(new_repre) - def _rename_output_files(self, files_to_convert, output_extension): - """Change extension of converted files.""" - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, + output_extension): + """Create output file name path.""" + file_name = os.path.basename(input_filepath) + file_name, input_extension = os.path.splitext(file_name) + if not output_extension: + output_extension = input_extension + new_file_name = '{}.{}'.format(file_name, + output_extension) + return os.path.join(output_dir, new_file_name) def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" @@ -161,10 +162,10 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) return profile @@ -181,18 +182,19 @@ class ExtractColorTranscode(publish.Extractor): if repre.get("ext") not in self.supported_exts: self.log.debug(( - "Representation \"{}\" of unsupported extension. Skipped." + "Representation '{}' of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): self.log.debug(( - "Representation \"{}\" have empty files. Skipped." + "Representation '{}' have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.debug("Repre has no colorspace data. Skipped.") + self.log.debug("Representation '{}' has no colorspace data. " + "Skipped.") return False return True From a9a86d112093d6b4dd47f6edc5f9ac75ace3b13d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:53:02 +0100 Subject: [PATCH 015/202] OP-4643 - updated schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 7155510fef..80c18ce118 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -267,8 +267,8 @@ "type": "dict", "children": [ { - "key": "output_extension", - "label": "Output extension", + "key": "extension", + "label": "Extension", "type": "text" }, { From 0858c16ce0882949c570beeefe050f71219d28dc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:54:46 +0100 Subject: [PATCH 016/202] OP-4643 - updated plugin name in schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 80c18ce118..357cbfb287 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -200,8 +200,8 @@ { "type": "dict", "collapsible": true, - "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode (ImageIO)", + "key": "ExtractOIIOTranscode", + "label": "Extract OIIO Transcode", "checkbox_key": "enabled", "is_group": true, "children": [ From 875cac007dd0a3630b87cf545f73d038c4de79c0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:55:57 +0100 Subject: [PATCH 017/202] OP-4643 - updated key in schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 357cbfb287..0281b0ded6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -272,8 +272,8 @@ "type": "text" }, { - "key": "output_colorspace", - "label": "Output colorspace", + "key": "colorspace", + "label": "Colorspace", "type": "text" }, { From 18b728aaf53a90422614a97ddf7e528ff9a50955 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:57:03 +0100 Subject: [PATCH 018/202] OP-4643 - changed oiio_cmd creation Co-authored-by: Toke Jepsen --- openpype/lib/transcoding.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 8e3432e0e9..828861e21e 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1068,25 +1068,15 @@ def convert_colorspace( if logger is None: logger = logging.getLogger(__name__) - input_arg = "-i" oiio_cmd = [ get_oiio_tools_path(), - + input_path, # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace - ] - # Prepare subprocess arguments - - oiio_cmd.extend([ - input_arg, input_path, - ]) - - # Add last argument - path to output - oiio_cmd.extend([ + "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath - ]) + ] logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 669e38d2c37481d089816d9dc663573f4a6895c3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:44:45 +0100 Subject: [PATCH 019/202] OP-4643 - updated new keys into settings --- .../settings/defaults/project_settings/global.json | 2 +- .../projects_schema/schemas/schema_global_publish.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 167f7611ce..f448f1a79a 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -70,7 +70,7 @@ "output": [] } }, - "ExtractColorTranscode": { + "ExtractOIIOTranscode": { "enabled": true, "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 0281b0ded6..74b81b13af 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -276,6 +276,16 @@ "label": "Colorspace", "type": "text" }, + { + "key": "display", + "label": "Display", + "type": "text" + }, + { + "key": "view", + "label": "View", + "type": "text" + }, { "type": "schema", "name": "schema_representation_tags" From 104dd91bba17cb59f5254426cfc98b7b48dbe91f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:45:42 +0100 Subject: [PATCH 020/202] OP-4643 - renanmed plugin, added new keys into outputs --- openpype/plugins/publish/extract_color_transcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3a05426432..cc63b35988 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -17,7 +17,7 @@ from openpype.lib.transcoding import ( from openpype.lib.profiles_filtering import filter_profiles -class ExtractColorTranscode(publish.Extractor): +class ExtractOIIOTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. @@ -89,14 +89,14 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) - output_extension = output_def["output_extension"] + output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: if new_repre["name"] == new_repre["ext"]: new_repre["name"] = output_extension new_repre["ext"] = output_extension - target_colorspace = output_def["output_colorspace"] + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 4179a8e48c5603ac8b17fbd48783e372135c3c33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:13 +0100 Subject: [PATCH 021/202] OP-4643 - fixed config path key --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cc63b35988..245faeb306 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -70,8 +70,8 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): + config_path = colorspace_data.get("config", {}).get("path") + if not config_path or not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") continue From 381f65bc8c973725f8d57a345d70a12ebcf95786 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:42 +0100 Subject: [PATCH 022/202] OP-4643 - fixed renaming files --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 245faeb306..c079dcf70e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -96,6 +96,14 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["name"] = output_extension new_repre["ext"] = output_extension + renamed_files = [] + _, orig_ext = os.path.splitext(files_to_convert[0]) + for file_name in files_to_convert: + file_name = file_name.replace(orig_ext, + "."+output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From c1bb93d0fbf75f978b1a04195edc102e3b113f70 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:04:44 +0100 Subject: [PATCH 023/202] OP-4643 - updated to calculate sequence format --- .../publish/extract_color_transcode.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c079dcf70e..09c86909cb 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,5 +1,6 @@ import os import copy +import clique import pyblish.api @@ -108,6 +109,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not target_colorspace: raise RuntimeError("Target colorspace must be set") + files_to_convert = self._translate_to_sequence( + files_to_convert) for file_name in files_to_convert: input_filepath = os.path.join(original_staging_dir, file_name) @@ -139,6 +142,40 @@ class ExtractOIIOTranscode(publish.Extractor): instance.data["representations"].append(new_repre) + def _translate_to_sequence(self, files_to_convert): + """Returns original list of files or single sequence format filename. + + Uses clique to find frame sequence, in this case it merges all frames + into sequence format (%0X) and returns it. + If sequence not found, it returns original list + + Args: + files_to_convert (list): list of file names + Returns: + (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + """ + pattern = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble( + files_to_convert, patterns=pattern, + assume_padded_when_ambiguous=True) + + if collections: + if len(collections) > 1: + raise ValueError( + "Too many collections {}".format(collections)) + + collection = collections[0] + padding = collection.padding + padding_str = "%0{}".format(padding) + frames = list(collection.indexes) + frame_str = "{}-{}#".format(frames[0], frames[-1]) + file_name = "{}{}{}".format(collection.head, frame_str, + collection.tail) + + files_to_convert = [file_name] + + return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, output_extension): """Create output file name path.""" From 5a386b58d6bc9125adf6148bbb854e0688a0adf0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:54:02 +0100 Subject: [PATCH 024/202] OP-4643 - implemented display and viewer color space --- openpype/lib/transcoding.py | 23 +++++++++++++++++-- .../publish/extract_color_transcode.py | 13 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 828861e21e..fab9eeaaad 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,6 +1045,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, + view, + display, logger=None ): """Convert source files from one color space to another. @@ -1062,8 +1064,11 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + view (str): name for viewer space (ocio valid) + display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. - + Raises: + ValueError: if misconfigured """ if logger is None: logger = logging.getLogger(__name__) @@ -1074,9 +1079,23 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath ] + if all([target_colorspace, view, display]): + raise ValueError("Colorspace and both screen and display" + " cannot be set together." + "Choose colorspace or screen and display") + if not target_colorspace and not all([view, display]): + raise ValueError("Both screen and display must be set.") + + if target_colorspace: + oiio_cmd.extend(["--colorconvert", + source_colorspace, + target_colorspace]) + if view and display: + oiio_cmd.extend(["--iscolorspace", source_colorspace]) + oiio_cmd.extend(["--ociodisplay", display, view]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 09c86909cb..cd8421c0cd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -106,8 +106,15 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files target_colorspace = output_def["colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, + # but could be overwritten + if view: + new_repre["colorspaceData"]["view"] = view + if display: + new_repre["colorspaceData"]["view"] = display files_to_convert = self._translate_to_sequence( files_to_convert) @@ -123,6 +130,8 @@ class ExtractOIIOTranscode(publish.Extractor): config_path, source_colorspace, target_colorspace, + view, + display, self.log ) From 2245869ffd0723177c2e76111723d9aeb43446c4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 19:03:10 +0100 Subject: [PATCH 025/202] OP-4643 - fix wrong order of deletion of representation --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cd8421c0cd..9cca5cc969 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -69,6 +69,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not self._repre_is_valid(repre): continue + added_representations = False + colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("config", {}).get("path") @@ -76,8 +78,6 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - repre = self._handle_original_repre(repre, profile) - for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) @@ -150,6 +150,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["tags"].append(tag) instance.data["representations"].append(new_repre) + added_representations = True + + if added_representations: + self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): """Returns original list of files or single sequence format filename. @@ -253,7 +257,8 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _handle_original_repre(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile): + """If new transcoded representation created, delete old.""" delete_original = profile["delete_original"] if delete_original: @@ -264,5 +269,3 @@ class ExtractOIIOTranscode(publish.Extractor): repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") - - return repre From ce784ac78207e1a16f2a14f7cdaaad5268fd6c26 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:26:27 +0100 Subject: [PATCH 026/202] OP-4643 - updated docstring, standardized arguments --- openpype/lib/transcoding.py | 19 +++++++---------- .../publish/extract_color_transcode.py | 21 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index fab9eeaaad..752712166f 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1041,7 +1041,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace( input_path, - out_filepath, + output_path, config_path, source_colorspace, target_colorspace, @@ -1049,18 +1049,13 @@ def convert_colorspace( display, logger=None ): - """Convert source files from one color space to another. - - Filenames of input files are kept so make sure that output directory - is not the same directory as input files have. - - This way it can handle gaps and can keep input filenames without handling - frame template + """Convert source file from one color space to another. Args: - input_path (str): Paths that should be converted. It is expected that - contains single file or image sequence of samy type. - out_filepath (str): Path to directory where output will be rendered. - Must not be same as input's directory. + input_path (str): Path that should be converted. It is expected that + contains single file or image sequence of same type + (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + output_path (str): Path to output filename. config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space @@ -1079,7 +1074,7 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "-o", out_filepath + "-o", output_path ] if all([target_colorspace, view, display]): diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 9cca5cc969..c4cef15ea6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -119,13 +119,13 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: - input_filepath = os.path.join(original_staging_dir, - file_name) - output_path = self._get_output_file_path(input_filepath, + input_path = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) convert_colorspace( - input_filepath, + input_path, output_path, config_path, source_colorspace, @@ -156,16 +156,17 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): - """Returns original list of files or single sequence format filename. + """Returns original list or list with filename formatted in single + sequence format. Uses clique to find frame sequence, in this case it merges all frames - into sequence format (%0X) and returns it. + into sequence format (FRAMESTART-FRAMEEND#) and returns it. If sequence not found, it returns original list Args: files_to_convert (list): list of file names Returns: - (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] collections, remainder = clique.assemble( @@ -178,8 +179,6 @@ class ExtractOIIOTranscode(publish.Extractor): "Too many collections {}".format(collections)) collection = collections[0] - padding = collection.padding - padding_str = "%0{}".format(padding) frames = list(collection.indexes) frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, @@ -189,10 +188,10 @@ class ExtractOIIOTranscode(publish.Extractor): return files_to_convert - def _get_output_file_path(self, input_filepath, output_dir, + def _get_output_file_path(self, input_path, output_dir, output_extension): """Create output file name path.""" - file_name = os.path.basename(input_filepath) + file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: output_extension = input_extension From 02ad1d1998235e5416f41b3a973577659cb0d971 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:27:06 +0100 Subject: [PATCH 027/202] OP-4643 - fix wrong assignment --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c4cef15ea6..4e899a519c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -114,7 +114,7 @@ class ExtractOIIOTranscode(publish.Extractor): if view: new_repre["colorspaceData"]["view"] = view if display: - new_repre["colorspaceData"]["view"] = display + new_repre["colorspaceData"]["display"] = display files_to_convert = self._translate_to_sequence( files_to_convert) From 7d4a17169377dc8f8f68b54ae5338c296fd362a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 028/202] OP-4643 - fix files to delete --- .../publish/extract_color_transcode.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4e899a519c..99e684ba21 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -84,26 +84,18 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir - files_to_convert = new_repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_delete = copy.deepcopy(files_to_convert) + if isinstance(new_repre["files"], list): + files_to_convert = copy.deepcopy(new_repre["files"]) + else: + files_to_convert = [new_repre["files"]] output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension - new_repre["ext"] = output_extension - - renamed_files = [] - _, orig_ext = os.path.splitext(files_to_convert[0]) - for file_name in files_to_convert: - file_name = file_name.replace(orig_ext, - "."+output_extension) - renamed_files.append(file_name) - new_repre["files"] = renamed_files + self._rename_in_representation(new_repre, + files_to_convert, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -135,8 +127,12 @@ class ExtractOIIOTranscode(publish.Extractor): self.log ) - instance.context.data["cleanupFullPaths"].extend( - files_to_delete) + # cleanup temporary transcoded files + for file_name in new_repre["files"]: + transcoded_file_path = os.path.join(new_staging_dir, + file_name) + instance.context.data["cleanupFullPaths"].append( + transcoded_file_path) custom_tags = output_def.get("custom_tags") if custom_tags: @@ -155,6 +151,21 @@ class ExtractOIIOTranscode(publish.Extractor): if added_representations: self._mark_original_repre_for_deletion(repre, profile) + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From 302a79095ec50031a2c411dd2704e75e6e51d6cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:17:59 +0100 Subject: [PATCH 029/202] OP-4643 - moved output argument to the end --- openpype/lib/transcoding.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 752712166f..1629058beb 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1073,8 +1073,7 @@ def convert_colorspace( input_path, # Don't add any additional attributes "--nosoftwareattrib", - "--colorconfig", config_path, - "-o", output_path + "--colorconfig", config_path ] if all([target_colorspace, view, display]): @@ -1092,5 +1091,7 @@ def convert_colorspace( oiio_cmd.extend(["--iscolorspace", source_colorspace]) oiio_cmd.extend(["--ociodisplay", display, view]) + oiio_cmd.extend(["-o", output_path]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 1de178e98fd0a59533826ae132b86ec85b742ce8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 030/202] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99e684ba21..3d897c6d9f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -142,6 +142,8 @@ class ExtractOIIOTranscode(publish.Extractor): # Add additional tags from output definition to representation for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 8ce2c151ce920e22089712e81b0d782a66e8fa38 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:26:07 +0100 Subject: [PATCH 031/202] OP-4643 - changed docstring Elaborated more that 'target_colorspace' and ('view', 'display') are disjunctive. --- openpype/lib/transcoding.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 1629058beb..6d91f514ec 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,8 +1045,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, - view, - display, + view=None, + display=None, logger=None ): """Convert source file from one color space to another. @@ -1059,7 +1059,9 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + if filled, 'view' and 'display' must be empty view (str): name for viewer space (ocio valid) + both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. Raises: From 9f17a4803f4f6fdfb8858a841e20dbe283b119f2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:14:07 +0100 Subject: [PATCH 032/202] OP-4663 - fix double dots in extension Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3d897c6d9f..bfed69c300 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -207,7 +207,7 @@ class ExtractOIIOTranscode(publish.Extractor): file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: - output_extension = input_extension + output_extension = input_extension.replace(".", "") new_file_name = '{}.{}'.format(file_name, output_extension) return os.path.join(output_dir, new_file_name) From e8d4a752a94e0f760bc2430536fdd8d87eda7636 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:22:20 +0100 Subject: [PATCH 033/202] Fix pyproject.toml version because of Poetry Automatization injects wrong format --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 634aeda5ac..2fc4f6fe39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.15.1-nightly.2" # OpenPype +version = "3.15.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From e7fbe105fdd312ecdf3560e1db5859dbbda39076 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:11:45 +0100 Subject: [PATCH 034/202] OP-4643 - update documentation in Settings schema --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 74b81b13af..3956f403f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -207,7 +207,7 @@ "children": [ { "type": "label", - "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + "label": "Configure Output Definition(s) for new representation(s). \nEmpty 'Extension' denotes keeping source extension. \nName(key) of output definition will be used as new representation name \nunless 'passthrough' value is used to keep existing name. \nFill either 'Colorspace' (for target colorspace) or \nboth 'Display' and 'View' (for display and viewer colorspaces)." }, { "type": "boolean", From ed95995fc7b688d7ea5a66e6a818364a1aadc551 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 035/202] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bfed69c300..e39ea3add9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -32,6 +32,25 @@ class ExtractOIIOTranscode(publish.Extractor): - task types - task names - subset names + + Can produce one or more representations (with different extensions) based + on output definition in format: + "output_name: { + "extension": "png", + "colorspace": "ACES - ACEScg", + "display": "", + "view": "", + "tags": [], + "custom_tags": [] + } + + If 'extension' is empty original representation extension is used. + 'output_name' will be used as name of new representation. In case of value + 'passthrough' name of original representation will be used. + + 'colorspace' denotes target colorspace to be transcoded into. Could be + empty if transcoding should be only into display and viewer colorspace. + (In that case both 'display' and 'view' must be filled.) """ label = "Transcode color spaces" @@ -78,7 +97,7 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for _, output_def in profile.get("outputs", {}).items(): + for output_name, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -92,10 +111,10 @@ class ExtractOIIOTranscode(publish.Extractor): output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') - if output_extension: - self._rename_in_representation(new_repre, - files_to_convert, - output_extension) + self._rename_in_representation(new_repre, + files_to_convert, + output_name, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -154,10 +173,22 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From a20646e82f5c39108c1ac5b0b9988226c49c1a56 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:42:48 +0100 Subject: [PATCH 036/202] OP-4643 - updated docstring for convert_colorspace --- openpype/lib/transcoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 6d91f514ec..18273dd432 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1054,8 +1054,11 @@ def convert_colorspace( Args: input_path (str): Path that should be converted. It is expected that contains single file or image sequence of same type - (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + (sequence in format 'file.FRAMESTART-FRAMEEND#.ext', see oiio docs, + eg `big.1-3#.tif`) output_path (str): Path to output filename. + (must follow format of 'input_path', eg. single file or + sequence in 'file.FRAMESTART-FRAMEEND#.ext', `output.1-3#.tif`) config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space From e24665cf536aa4d48538f229718de57a731e75f1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:22:10 +0100 Subject: [PATCH 037/202] OP-4643 - remove review from old representation If new representation gets created and adds 'review' tag it becomes new reviewable representation. --- .../publish/extract_color_transcode.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index e39ea3add9..d10b887a0b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -89,6 +89,7 @@ class ExtractOIIOTranscode(publish.Extractor): continue added_representations = False + added_review = False colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] @@ -166,11 +167,15 @@ class ExtractOIIOTranscode(publish.Extractor): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + if tag == "review": + added_review = True + instance.data["representations"].append(new_repre) added_representations = True if added_representations: - self._mark_original_repre_for_deletion(repre, profile) + self._mark_original_repre_for_deletion(repre, profile, + added_review) def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): @@ -300,15 +305,16 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _mark_original_repre_for_deletion(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile, added_review): """If new transcoded representation created, delete old.""" + if not repre.get("tags"): + repre["tags"] = [] + delete_original = profile["delete_original"] if delete_original: - if not repre.get("tags"): - repre["tags"] = [] - - if "review" in repre["tags"]: - repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") + + if added_review and "review" in repre["tags"]: + repre["tags"].remove("review") From 3aa74b231c2e7116ea792901ac53cfcd848513fc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:23:42 +0100 Subject: [PATCH 038/202] OP-4643 - remove representation that should be deleted Or old revieable representation would be reviewed too. --- openpype/plugins/publish/extract_color_transcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index d10b887a0b..93ee1ec44d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -177,6 +177,11 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile, added_review) + for repre in tuple(instance.data["representations"]): + tags = repre.get("tags") or [] + if "delete" in tags and "thumbnail" not in tags: + instance.data["representations"].remove(repre) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 5e0c4a3ab1432e120b8f0c324f899070f1a5f831 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:07:00 +0100 Subject: [PATCH 039/202] Fix - added missed scopes for Slack bot --- openpype/modules/slack/manifest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 7a65cc5915..233c39fbaf 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,6 +19,8 @@ oauth_config: - chat:write.public - files:write - channels:read + - users:read + - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 714454d7300cf2e067785c888dadddb415f88baf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 14:54:25 +0100 Subject: [PATCH 040/202] OP-4643 - fix logging Wrong variable used --- openpype/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index dcb43d7fa2..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -169,7 +169,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "Skipped representation. All output definitions from" " selected profile does not match to representation's" " custom tags. \"{}\"" - ).format(str(tags))) + ).format(str(custom_tags))) continue outputs_per_representations.append((repre, outputs)) From 2b5e4bc14e7a4241ee97a3bbb0716b148ec13513 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 041/202] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 93ee1ec44d..4a03e623fd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,15 +161,17 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation + if not new_repre.get("tags"): + new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) if tag == "review": added_review = True + new_repre["tags"].append("newly_added") + instance.data["representations"].append(new_repre) added_representations = True @@ -179,6 +181,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From 0abcbc152390c00aa596b80ccc40c6a64c4861c4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:04:59 +0100 Subject: [PATCH 042/202] OP-4642 - added additional command arguments to Settings --- .../schemas/schema_global_publish.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3956f403f4..5333d514b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -286,6 +286,20 @@ "label": "View", "type": "text" }, + { + "key": "oiiotool_args", + "label": "OIIOtool arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "additional_command_args", + "label": "Additional command line arguments", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "schema", "name": "schema_representation_tags" From 5ec5cda2bcd1e68de459496c20a1cc9699ba7144 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:08:06 +0100 Subject: [PATCH 043/202] OP-4642 - added additional command arguments for oiiotool Some extension requires special command line arguments (.dpx and binary depth). --- openpype/lib/transcoding.py | 6 ++++++ openpype/plugins/publish/extract_color_transcode.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 18273dd432..95042fb74c 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1047,6 +1047,7 @@ def convert_colorspace( target_colorspace, view=None, display=None, + additional_command_args=None, logger=None ): """Convert source file from one color space to another. @@ -1066,6 +1067,8 @@ def convert_colorspace( view (str): name for viewer space (ocio valid) both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) + additional_command_args (list): arguments for oiiotool (like binary + depth for .dpx) logger (logging.Logger): Logger used for logging. Raises: ValueError: if misconfigured @@ -1088,6 +1091,9 @@ def convert_colorspace( if not target_colorspace and not all([view, display]): raise ValueError("Both screen and display must be set.") + if additional_command_args: + oiio_cmd.extend(additional_command_args) + if target_colorspace: oiio_cmd.extend(["--colorconvert", source_colorspace, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4a03e623fd..3de404125d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -128,6 +128,9 @@ class ExtractOIIOTranscode(publish.Extractor): if display: new_repre["colorspaceData"]["display"] = display + additional_command_args = (output_def["oiiotool_args"] + ["additional_command_args"]) + files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: @@ -144,6 +147,7 @@ class ExtractOIIOTranscode(publish.Extractor): target_colorspace, view, display, + additional_command_args, self.log ) From b30979b8c1a4d4da0d998862516f95a89e522d9d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 044/202] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3de404125d..8c4ef59de9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -82,6 +82,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + new_representations = [] repres = instance.data.get("representations") or [] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) @@ -174,9 +175,7 @@ class ExtractOIIOTranscode(publish.Extractor): if tag == "review": added_review = True - new_repre["tags"].append("newly_added") - - instance.data["representations"].append(new_repre) + new_representations.append(new_repre) added_representations = True if added_representations: @@ -185,15 +184,11 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) + instance.data["representations"].extend(new_representations) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 73cca8299506e95b2ec515e6ac085b85a9b2049d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:24:53 +0100 Subject: [PATCH 045/202] OP-4642 - refactored query of representations line 73 returns if no representations. --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 8c4ef59de9..de36ea7d5f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -83,7 +83,7 @@ class ExtractOIIOTranscode(publish.Extractor): return new_representations = [] - repres = instance.data.get("representations") or [] + repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): From 78933eb56259045a27c1c56f06a31dfb39f38e37 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 10:44:10 +0100 Subject: [PATCH 046/202] OP-4643 - fixed subset filtering Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index de36ea7d5f..71124b527a 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -273,7 +273,7 @@ class ExtractOIIOTranscode(publish.Extractor): "families": family, "task_names": task_name, "task_types": task_type, - "subset": subset + "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) From deaad39437501f18fc3ba4be8b1fc5f0ee3be65d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 047/202] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/lib/transcoding.py | 29 +++++++++++++++++++++- openpype/plugins/publish/extract_review.py | 27 +++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 95042fb74c..a87300c280 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1092,7 +1092,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1106,3 +1106,30 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 83cb0b0b04a59a5f4acffb05659a893f2318c91c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:02:41 +0100 Subject: [PATCH 048/202] OP-4643 - refactor - changed existence check --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 71124b527a..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,12 +161,12 @@ class ExtractOIIOTranscode(publish.Extractor): custom_tags = output_def.get("custom_tags") if custom_tags: - if not new_repre.get("custom_tags"): + if new_repre.get("custom_tags") is None: new_repre["custom_tags"] = [] new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation - if not new_repre.get("tags"): + if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: if tag not in new_repre["tags"]: From 909f51b702825be0fe23fd946023279787d28e1c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:10:11 +0100 Subject: [PATCH 049/202] OP-4643 - changed label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5333d514b5..3e9467af61 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -294,7 +294,7 @@ "children": [ { "key": "additional_command_args", - "label": "Additional command line arguments", + "label": "Arguments", "type": "list", "object_type": "text" } From 71013e45052bb0775cc80e799cf4556a935372ef Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 050/202] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 12ba88e9573a3c33184264e2b6c86468a26b3a3b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 051/202] OP-4643 - added documentation --- .../assets/global_oiio_transcode.png | Bin 0 -> 29010 bytes .../project_settings/settings_project_global.md | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png new file mode 100644 index 0000000000000000000000000000000000000000..99396d5bb3f16d434a92c6079515128488b82489 GIT binary patch literal 29010 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 literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 37fed93e69..52671d2db6 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -45,6 +45,21 @@ The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](http The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: +### Extract OIIO Transcode +There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +`oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. + +Notable parameters: +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Extension`** - target extension, could be empty - original extension is used +- **`Colorspace`** - target colorspace - must be available in used color config +- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Arguments`** - special additional command line arguments for `oiiotool` + + +Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. +![global_oiio_transcode](assets/global_oiio_transcode.png) ## Profile filters From f1718284f9245dd24160d8975061d90cc6f8c11e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:41:42 +0100 Subject: [PATCH 052/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 52671d2db6..cc661a21fa 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,7 +46,7 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. From 337e695c17d1d5314ffac99a83330f875053703a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:06 +0100 Subject: [PATCH 053/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index cc661a21fa..8e557a381c 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -52,7 +52,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. -- **`Extension`** - target extension, could be empty - original extension is used +- **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace - must be available in used color config - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 931d0002ec80b0cabd1bbc0b1d74cbdb06ab2d88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:29 +0100 Subject: [PATCH 054/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 8e557a381c..166400cb7f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,7 +53,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace - must be available in used color config +- **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 14a8a1449a6e46d82192dece2fb08c8aa807a032 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:48 +0100 Subject: [PATCH 055/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 166400cb7f..908191f122 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -54,7 +54,7 @@ Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. - **`Arguments`** - special additional command line arguments for `oiiotool` From cb7b8d423e1591a9d0995dfba9d3cb697c551a57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:43:06 +0100 Subject: [PATCH 056/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 908191f122..0a73868d2d 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -55,7 +55,7 @@ Notable parameters: - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. -- **`Arguments`** - special additional command line arguments for `oiiotool` +- **`Arguments`** - special additional command line arguments for `oiiotool`. Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. From 748d8989496d6cbd4a5348fb9a78e75617a69cab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:21:20 +0100 Subject: [PATCH 057/202] OP-4643 - updates to documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- website/docs/project_settings/settings_project_global.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 0a73868d2d..9e2ee187cc 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,8 +46,8 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. -Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. + `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: From 421185f5216fd50fc11c6b1e04f80a5e2454ab48 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 058/202] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 29 +--------------------- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++--- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index a87300c280..95042fb74c 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1092,7 +1092,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1106,30 +1106,3 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - - -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 7e15a91217d4d74d2cddfe6d219742d5146ba52d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:02:17 +0100 Subject: [PATCH 059/202] OP-4643 - different splitting for oiio It seems that logic in ExtractReview does different thing. --- openpype/lib/transcoding.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 95042fb74c..8a80e88d3a 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1092,7 +1092,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1106,3 +1106,21 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + if not arg.strip(): + continue + splitted_args.extend(arg.split(" ")) + return splitted_args From 0871625951f2054392ba74cbe6ba5ea47b36d44b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:14:57 +0100 Subject: [PATCH 060/202] OP-4643 - allow colorspace to be empty and collected from DCC --- openpype/plugins/publish/extract_color_transcode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..82b92ec93e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,7 +118,8 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 2563d302fa0f9b1140b226e7e992b70bae403430 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 12:22:53 +0100 Subject: [PATCH 061/202] OP-4643 - fix colorspace from DCC representation["colorspaceData"]["colorspace"] is only input colorspace --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 82b92ec93e..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,8 +118,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 36b7fa32df20c9640ccda6a81ed61766017d607c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:33:20 +0100 Subject: [PATCH 062/202] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- openpype/lib/transcoding.py | 2 +- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++---- .../schemas/schema_global_publish.json | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 8a80e88d3a..42db374402 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1044,7 +1044,7 @@ def convert_colorspace( output_path, config_path, source_colorspace, - target_colorspace, + target_colorspace=None, view=None, display=None, additional_command_args=None, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..b0921688e9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,10 +118,17 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] - view = output_def["view"] or colorspace_data.get("view") - display = (output_def["display"] or - colorspace_data.get("display")) + transcoding_type = output_def["transcoding_type"] + + target_colorspace = view = display = None + if transcoding_type == "colorspace": + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) + else: + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, # but could be overwritten if view: diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3e9467af61..76574e8b9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -271,6 +271,15 @@ "label": "Extension", "type": "text" }, + { + "type": "enum", + "key": "transcoding_type", + "label": "Transcoding type", + "enum_items": [ + { "colorspace": "Use Colorspace" }, + { "display": "Use Display&View" } + ] + }, { "key": "colorspace", "label": "Colorspace", From 90afe4185110f2571242007f88566be7dce78dff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:34:03 +0100 Subject: [PATCH 063/202] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- .../assets/global_oiio_transcode.png | Bin 29010 -> 17936 bytes .../settings_project_global.md | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png index 99396d5bb3f16d434a92c6079515128488b82489..d818ecfe19f93366e5f4664734d68c7b448d7b4f 100644 GIT binary patch literal 17936 zcmeIaXIN8RyDl0PML<9Uh=PDXAQ420^j@Msz(}Yf(mT?mN>_>+6lp;^LZtWJi}a54 z-g~b?=$whZzTaBs+iRU~t-a2*|Lh-3GE2rBbIkGF6f?I-O3xU)&NeKR5pij;GA|=&`WP&NmWNS;Tddycb5U=@9#E)my|& zZ(hG+{`BJ5R&A4!>}j}(T3}=N)f;Cw*63K*e4CrPGfn)thUK{;y3BIBPq7npCr2@Y zDoM!b)Ka&NHRobds}-iS^%SL~<(n!CJqx4Dl~uUTZWX786H;-j8 z0Wi>;Yv&vDf4ecEh;$S^S}tLwoN(WWv>LmfdzU?-GevbICQyGNYrL5E)ylG;zxVwn zxqEi07n&}iy9>UIR`$AdYcGc1%9rykoNA`A(xMh~J^s0zW$wB-)#rXtn>IH$HKU@= zL?J)NU52}J*s+`phWA-B;Uw-;$zg;EHT)$`B;-lcoa$Bv6@A`OR?vYz@MRiWwfz zf6P`?v{KUoFEG|S!pc^?cuWhXM$)*LOy6HLOma8yHXt}v6sy}SiH@GDi-E;MgJkUzr!5yYA55NSv*f3phs=7M_qOUj! zP>iS=K?V~9h0#OqF5V5-z0Iv2WZYic+fZ+%WE%GbGqIBbQqxR^gM& zvv1+1(4G$GA$`m|*&{!Dc`rn^Qi@JhRG``qP1I9|`W7E|=fQiu2EkAqYLAYpU`M@;k4PZqwm8EQpZG;y`JpU% z#VX&5N2=9RAH%I16*SkV&=>QCz?bKA`iT~WA%rc}&>iswdXxgm#?n-(QZmo}PixI8 zHmjK>-Mdf-A)?lb|02yhxP<9G>wWR}z;%gk`2Kw-p$ytj&g23YmQUpZL-*wZ#ocAd zG4eK&SLk>Ws|-Sv!`8Egz6MuI`z^5pGx!79T{YlVMG_t}{j4gxKSOu?G_tt&{WOe& zDqpR%QK;7&PzLaUFK6iObQnMVq*=b%Z-4SL8?cw7=1k|++X4>2&{nyPosB(K4+s?= zx=ii`zW_4$?`$ND`m>&&22;|7m2wk_He)KcpzyT_bz58%m|D$dD1rzpNs4uf)#TY>Vtm)eCQO#iB zM13K#Y!2w*e`6j9Goe6Y`E>*6UD!cfaBQqf08G4$5jFh1nK6$Ur?MK9w6`lyqpla) zYv_{IgOtl5t8aeO7EW`a={mamN-hHPT8PaDeqopf*h`edFcr0L5UWs%Q1k|79M{O@ zSz7s*e@a9dVuv5*xOK%>6&mYxYA5W0%^BDrD(fPqYa+>vV8ZAb0<(AVqc4n+AcKz8 ztK#ouFuV#6QSl#12U2ZlSVmmd?7F7cUMmwN+6;O=d9E}?-|Ap!H3U&8mR+#(^mKPyZNdiKL*uOSx`b{F|k(Rn|!^n?VrIdMp%zjcVbXA&gQ? zdAjjFvF_bl{IgHOSK0{P90JeZe17|vpQcY&=^(yU>A8*OBPQ773h&#hj@aZ2enSWX zhhqLKl3V{xn%(->b9bj zz3L;K0Y|+8d%thKRTiP=fOl64ou_5`ia`bg?v*ExbGANsk7}Fz9^=gAliGtrFq8xZ z!Bq2tj{u`wc}hO@)Y_?>Q)zk&-ppbfz2q(?b1|4O=w=V31-~dgrwL7Wz!*8mQGfov zx<#dBKatV@HPPg6qHQ+3iVRdZZU(|jb}B5~3$Erdf^JU-JLUUc0W-l<|YT3NTz#z^v%%aNHY=_RRaJ12`XKM?-Q*K)T-~$9<{LJP^ z@!O@(rr^2RKy?V=n|A@QX!;=7)3p_p7sZdYHTO6OJiilxseTk3%5b2o79+SF@X33% zAk~cry#0JIvvAjJ_yE#4V-@2LdT`G%lNYq+17Es!Pam^Zp%m);bosI)?|m6^cI33C ztHCGm&6X*O|D=V%@#V?8G&Bx!pzuj$A5kI=pNu~#wEtNdv^DS(imMRY! zST{dRkQr-tISq-h9FifA9Si)`$Gvnl4$Dq^W!V6-@vBlHkjH_+Fs(@GFN-!6*X1yi z2~@Y1zPL#UbzCSm2>JE6NNo8|N7*Zf%Oxvo$Tx7)8>-l_@CQ|+_g|(?;12E7H+j*$ zqyp$^Vy^f~8cie?FD>3EgW!QJ2q^$evWop%36G<$f)jWikujYPHR#maZAmEO0k-bo zJ?&#wn|dm*6`E}RVYO8o+49NzQg*-dd0&}2oD=HNcTvxSh3o=6pqwRE&G0@sau;Tn zRou5o{Nrf!obif7u&tAjF0D|Dq-}6-D9>VbiNgKOT6)DN@k-g;}R0+ItY{t80CL?6RToA zez2Ohkom&(sKTn!UaHBn93z?Z+`{GY=jbZunc=#OG(J)AZ$f+JE-3_|ku0z5!h9vV zrX%NnhPEn3p;M0S$sjaQ(;NXI2kh))H`sFl;q*_q>kJEixxz z9?fFGNy)8ot|c2Sx4)d>VJ@4F&o!SWcQ-yZ@@vlIPq`n-?+0oER-*EJb@qF>!*%b$ zPBgZGSk~(j1xUA~`OSyeEfYfuG3hV+_HFM1Z+adQNb@iP!oy{M-!w_VW&XQd&Ancd zD#e1Yr^63z%zyf^B8}u<{%oU&IlZ_ojwNRgg6-8Qb9_3tX^yglQu{1PJ@N!*gyM`n z#FwfwECVyi5%n$Nzrb_Je;_c{T!_#}!za6^S~xbMapu6oKr;e#(CBL#Gy_oqb8#C= zV9u{(f6rygs|Ljbe#Bb>aJ!vtU|MZTOdVPbpRBFWg+B%v_`vUJaicsdN2s4Jl2qt9 z?*vO@(O5BBzT1;>Pcg4&cF*ZBbFqVX9MPXJ{d8JU8`g1N6anScRejmCJX+E?!0s@KBv8MJ{Maq)X zo3O?cyoUDd-c)<`h6K1y9*|sVN9>O(BThpZew_1U_N`Ecw}4`Th0l6l6E&0f zXl6;_7}L%ZgL?T>^=Jd)?K)IE6KKoI>$D+5Ep>T`eSx}2%XE{vr~~3Q(;v>7>)ZU` zt`)Er*Uv})h`CkvdMab(a7h$_+>c7n%KQK`b;>1zT|r+2HT{on zJXLd?7V@%U%51jQa6kDhn?1|Tr-2@7SG=;A{&L#D2K2^t=BC0maDq$-dO3Y$Bw7-0 zg3$HYuBPZBozv5>kbI-ts#)V3$d!4gaOzL%`DAPTCz~1`pr62h;(GU~TX-Dv$onwLsXzhs7o~ z5#%}T+$cc&_UWP-{xjM7@EbS*7q3)o*K8qVR3jJ!6Cpcl3u8`aMCo1;l$e!a3Lj;Bhk?tIneM@-l1OB zaH#IyF(CJ;f;C?$s`I`AWE}yR=~6Z62J<5`CWPIcNfjjutk|x`Jv}QGK;pJDj!@-h zdFv}|=}&i>hZW=YB|6%Gxk4-IK76k?Mqo3@ny{cYOL;A+YVXq>YPT8R;M+Y^BDOwB zRd0j?U_+=G5Dmnh+4n?|7nL)M|IQ4Ngn1gu7XjN#4;6XL84^s!WVo>ov*N^ja2Wfo0!J0X3$xnC%Y!Av3Z-CV4QC#tYFSCmLn0=RH$;g&c2d- zU_|1OOlCa9S1nT`Y_tfzCa5aFgo2xIG~*8m|6jcL_b}tRYB9Rq`}LE@e%{x!fwR4j zXY^+txCVUccZF?hnwZ%*r;|%=q-?g&L7krgA+S8vmsROLRdv>PR(^CK5{ZctcC*x) z2-QPRM^1=kTQ?($ct93DaEUKkZYSlzOod&`>QEVTFbnb}Xjo1aojbdi@QaUwawo=v zE#oYP3DGUiLJ218TQXx?B4x)5k)+ZbkI$tis|#m+&Hnr;Gx0LL zQpiZqu#6j;fx#`=uK4uniUds=k;A}a_foN3NWx{t!k0JJHF;M>QlvQm*kIZy`sGwB zP^DZZ>`qs+T*iC51%Qzoq`!Kw{SaGWJ?-8;Z+a9Iu~!YXq!!TZ-{NO#F8lCxdB5yq zpR7B3^$p)%rGSs@Q7C242px0Q<`#fzdE-%{bLJk!5 zE%DGaA(V2}{4S%eu?3Oy*V%%u2lo?@cMD(Y?ycLk?76R5K3Khzv~sidi~U?(1*~5q zI|AX(-l4&f@hCE5MmSldKaXf-O&V#xT_z_>o)#$MbXYkJOkmGo23~i%%Zui_Ouoxu zZLP6MJ1@-w$DxI)_PW)^+YBX0nY9PQJMUcl9a`=ksg$LOp*&BAk6vlvLOQfCm>9vW zisTxZy!C4DzEKN&R%T&Ok;HX|oKXL$I6MNBbS$+D2EvSp%yZ{um&zx)@@Bm=5Iuboo8f-w&_X1I z1uZ^~4u+YHYd;-;8<`^j0kim+M&R)U5a!K)&%yUC6Rs{&3fA?!-+GipN?}zS&WvzobjP~BQZ|&j{<>!)(^G73{ZOxZ4$YJ;zvOz?Prx}zq_Y+4k zmPIA76cv`arFmj@@KfA=;CpnB?ZnsWNdnNauCcT}njylU?8Z+=RqP;{w1e?-gTESE z{|x@iU2FO!_ks7&wkJ*xa=%}>$y$nEM~6)(T?53-qb-C zdGi-k%6*ug6WI-NP_BWo^g}e)rBYUIP3z7XX=mN4H2W*uJkH}f7K&K;o)1&ZzU!4I z#NYHUYh#uYzRbwe%sNt7K`#wRA&g$jbr8_-}6ZRZR(<--?G zdWxg8QjSkE3DFm0Z#g&suNB+B@$DKhoKo|BCu&5S$)UEpNCJ$mDq*Ly_eV#(nHl^8an`wBaES*pm}A$quD2fiLPrN#51xr`PL7U05+CQyS{2*h z_w*>|II1hG@92}k#$JUWw63TkqV7;VpB&vezhwDnZpAi=CRPGx7}}9R zLO$cuNa>pI@zzZu2nM@|4hpUkdicKi0cI}ntxuKvjOdXk1f_rnW`Zh(8VT?TC_qXs zuKm?Y^4pP6XX&vTBR~mLqCLU+X4w1GS&wNymW!+^XJ5l1*EQ#F@}l7q?*mvnEAPsZ zlgEXSC8+v7d#oK3qKP#CLQo}uNRIUb8!n8;q*#OKy$C2EV5rT)z7{Pf-br?(`_pI&_N`PwkBNG=sB~;+~`8!r?i?IefA&S^nHUgsh$#&&0@N+Wu#s zj2NvfhU1ZU>S}$E`iFiL5z_K98f@Cg^7JJO^B0&#gZtsoU{n25(JDu^19S6ahmzN? zYRC~jU$sUjLof9(q7*z;k(m8kj{@I5vdg>|1T)K^dE~6(cWX1L(DSC}to3{La7ar< znFNbDKW=F3hm~mPvvu~g?WY|UXe**#sZ8--{DW z++Shlo|*hgK3li_t&hl~`66?0{RB&CKYJSk>ejcr+BTPmDIsW22H=h7SJc5dQzl)t zf&yS#PMZQP*agv9TXJq-bP0fo24QM3)?cu6{$V-v!!Cizm*a=?SR{XAIwck0S;2!K zkl;pGf4Z7i*si1(Fc;pyn`INx*usjVsgF4}cPW5b6#x?mcfuzBKzgdPUxAsB|2}k3 z-+aWEwd!bT6MIWTb$3kURn^}!SWKi7=Lg+a)xpecDr~qM0iVb}!<(BM3PL8d!YZ6p zzS^c;y1ajshZCtx)YA@t5c-N^XBX-=r>mUYI^yC^L*z!OX`5=lsJOl7+e1W^ml?;| z^87ZBB7f4KCx>lO_jXzj#4XojiqDR;H*6n6sesV{LScM&&yD5s;+0=*g2n*n?hU|2 zObuAbMf63xOTZKbJjj;fzbq8lpOJn%TaO?0Ug+&bS67|W#m#BM5Q@Faz^eod80o&< zT`uOLo4G?|BHp8#PGf`Ujv5v`Q`5(t9DLka{bS2b{=OY;(5t~B5 zpM3$aTnJ(ABWx@&Fp%`905e?a+FZHFgw!qbZ9U@=>sqD_K5k>;D85Z#;z4g&k8wETpWkn z=@-eErsU@NqK;PA-?I-h`Q0wvM<3ig{ITI?WBE~o9*(uUcz*v%fW_l=$KgwTiFR@6 z-K@8Dc{ZTVtF?BUZf_YjHQ(*n$Rg+EF58*$o+%I&IG!@iL;D$Jca$PjK$Xw;bLc(y z?zR*cS6O7H^OlUKnJ8!zucj6)ufDRiH` zv-d;}rA=BUn>}vC4-B#%ktrFgjE2byJsIO5zx@qPqS%MCVdyX3MV?rwOfvnb;=#av zxZV29>uCnQghaK#)~kyj}{db>ktqhW~R9Oh2pvIn{^RDDj&pEcSQjNfI;ae6R7>bZF*rN!kD8`2Y~*k9Ht z`4EjQRTg4Pd_w+2MIqYCHADlv+HsH6R<9H;JH{~!@hw83;n!c~Wk>_3aSF8Qgoy>iaEK{ws&4m|4c0t^j= zXn_H4h7*)3FZB4tcesTwxq2amKYWQDy@&dSS9OxVy#!{muMJ@N*~p;VY|!TlW>jVj zbd0c5HU(VLqT$x1T_#wQ)FjnD$iq=5sD?uOJx6@}obO?UlEsLJLN;Kgg z#C^UJKyLV@T`2+l5y4`rq?V!hH2DLqP!(Q=jBvZHQg_W;rY-p#7Cv<|ff=IILdGp* z&yQwDKkADWG_oeXz;Cv*!tbF+bzUjGiz@GWYUjy{PQ4l3+9HS=4nuq%47lpU0Gt%$ zLG?C5jP%SRF8ERkD7HVL0oUI}wE$r;VmmV|28bd5L7wOk7>o`uL;zlA=XWZA=!{jvrgv#|i*x7Y|BZhEXbC%sWys0j!Y`Bppa7tBiL>?Aw*S{iT;xNXzz@fZPW!FFIFI5g ztC?Y6TN)rr4q){5PJuN3>D21#LRG2G!FR8Yjo1rLgXOtrlQTr8TQ#d>Bl~XTn78oe z*6#>(u#^;0lnZUs$h?YMm8;ul+do_?@&~SEv96@=tzP-&zNh~ld-gAuau;Q@?PxsD zG31jg%$2=4U^Qy|2eu7-tBm{=|KND%J2aQ|k>Zbi=cMepKP(G@P1oi(6~ViGQt1fD zS9yP6rG3tTdgn5?oOMJ!CX10T=ifSLUl^4PR2_$zF68CVCta@x(tJE`%3us<`tl{v z-qIY_DGx?(-;99ErgaLX;)`faEw)O~h!q+5i_Fv-DU;9lbX16RMLaJKo#d|#%D;7W_cOP{?}a?o7DMlknK5{r@l!&bJ*9@(Fu^*_sWCs&UDxE2iKVR?3e@lq^@Id?(a zy`PHlj@*h8q>*;I!kg(}`^_8+5w@?exCPO2VE3??WL29tORK`17zeu=zGaMMgB z`yEMY#KRzk!0R-gb&O94D;j3rQ{H~BtXtiq*t zHSNhKNPo`JdL6Hd`M!@kqr1OV=)}q9BUXatAs@Bs;g9wC-$LzAn&|rmNFspz9lVS{ z*cOS1XkqrHS`EP34MIeW=jz1wyJL2@8(~}YpQ$Rt1QB+4Gr`-7G!JO&xr>~ITm+wz7^ar|PhX>vo;3ZG5oDTz+ z)8f2vT%!<0=qPW-6BWyWBGo>5j5S$LZr(@l^zLPWc;{h>;By4L46Z**5#x0vm!;ZW zDxL6;1OUv__=KzHNRB2LW;A=l9i&ezoldI8;IdUG2QEf~H#o{EN2X&`k2p75mbb!D(WCc5_SU?V-Vv$kRdz-q%6Hm>X}jd&u9R zr+HQ~6on#qQSkl8%pH9@>1xv>z4nO~_)i#63Ks#YB#RD;GjY_$rW>V&?Y01<4s2(L zC65yV*HgRhGe+u=V;k@I(L|ay-jm+>i>~5BeGl7ey&JDxxv5^@1qUejIoeS|Hzvw+ zCrX;%>Y1zeIpXS0{-$-OKgB2bVkR}-qCVG3Rcx%y0H64Qds7dny1VG^0T^kF$R~jA z9~=ySyL4vlu(zYFne93$vB1~$M%Y|y$YXQs^4V+MJ+#Hg=(NCJg0uWuI)-0>yYB0r zWY_E&4S$i?qHNxw z_;WTocnipd1YW2oQuZA0n)ES6@7sZNk1?4)fQ3W=BCnr4s+{{JKyT9|r2wWZ0=~?& z*ap8*TcLE?3HkKCD>Pc-lX>4m(e&v?R_=RX%UoNlr{=*jswspjxDh|^UdMIoSeZXc z$j%&_tlY^YAuQ9NOu2rI^=d)Ly`MF;#Bl-WWEr8YlS5#(0NGRof_x}>+3Td^%Fhrq z?Rc_^*ks9IzG~If_E}QXe_R5dk??stYFeEilBaUt8*oNdUu|G@brQeX%Noc28JkS}^6U!J@1}tR zI#|3NoqPVTZ~xV=v1ph8@S+|7Ll3NplGxZ6|0m3c7Ds*amwhykG~yq+yBT$0&R!mf zfBuKMd%gf|@%k7_dfx@`{-m+?lh9jfYZd4=`|gs8ln)=Mool+zDfISaH$_OFKTagy zT!7zhpkMI%PW-m$Q*D zFKS2Eo#FksnZA&fcQ+#(zGMc4zW-dsoEGny5|z00*x*f#ftva}jx96Yee_u4T&Xs3 zqV{Nyzv(A+kSf#Kw-5u_H+!_g%a{?P?k3*?DJ0)}hUNplvztI(9Pu`ST-MV|`l)~q zI{x|d`IGrJw+^KkaoW@E*%P|;jgjKQeze9?$p;K?~U&G8(eh7g@hW+^2%GkXZ{vF+qOX5``b3xEOmLAd!( z9Bl~NQZ#<*zW>Qe*$*E0q(hrntiwR>6e28nW2T*|z+_w)Z$^f5-R6%R~N3ui( zmgAq(SE}v+t|3B>dOk~Fu>*W32774mk58IEJ}=%ssP?xas<;p2x41~Ts6X@@G~)}x zz;NqVQnIH;jUC0R4qoV$RHe6w2^29^vMozs<8Kwar8xGonFFhzjlBz2{!>^e*J)>M zpvghNHP!NXPNLzaC*TBn%^28BpL(!M=n#_-Zcko?avg%|l(2b<>5tjXC95x&mnDZv z#z(Q!BqQ zT2-cR?C(<;TXmU>ow^PB*t<brT3mMxC~2q=O2n)FRrW4!mxS4W?0 z46z-BXXe9i%&TX)kS&Dmnq5Oq}*;`{z$ zB=bQqy^ZQ`_Gf{OYPiaPy-)_@UYURRqv9D*-ZAyQ9qQ`(m?mkQXx6r5)#ubvp;TKY zRV9F)t4W|4AfyJP3i|U1AkQVAzYDOXHnK-<4&XLtPH|ImG&Xt4=abl~K-yhYcg$(f zaq=FX?zj0e=3VbFO(5Yf_C-CD@2#Iz_g8p!C%>2`HCv6E6$cuo@d7x~>vRTtyx}Dm zn&I4btYK$`eVuc|?b9H{SBp1KOCpX^lG(Hu1_jFiil{{e5VMgE!aI%n6M`OnX9e){ z#3Cv6T@!3z0_&9{O0_7Qzn<`ZLQBw`E35T7u<)dL!|%09k!ERC&ZEbDTxMB2R;m%u%ijzwnXmMI+x)+C&(+I;|GPy4v^{4Ou!tk*W z*l?}?HPwFM9KE&c+lA(R`aw*4i*Ux@5I+_MgVCUa$bK)<=d7!W-|1{Vc&!d#2Sde$+apAId|Au3fM5i9c*`S3EcElA=WdC-+{TsIQ{eO=wS%d!( zR?O!)>SzA#22|e5cyjK`BkW9B)rkD<3sf2^;{EL`Ov<&g`0Wrx8cq!V7P8STl?8yK z91yF5A#N{&f4dD?{`(EOIvLJ&B&aKe9;R6ebdLY-1&~ow_I1J*LACoc^OPMaH?bxl zg6qiSDJ^&AhLKCNcC-$Uut;6fei3nWs<4@>3&{9~BGij_em@PM-sL6dmQHN#XkW(sYS1I<%l@cOVE>dHXA|9m%i0dp{;i7*gn}*h?2G^91jH zvm5kZY5VZb+e!VU29Lv5znKJFW!?Ky#A)i1R&Z-D8PNP*GVJ+*$)k+jo65S@*R=1@ zZ00IBE_NC1a(<OBvC`sjYkwXAhDYqWoE?H6RbUti9)5o*5h|VPMsM{5|JhH!WJe3$ ztz5P#^VQ`6VpT_3y>_>Wc&^enzuuoD8G0Hsz_d7!uDmHG8ZL@T9O>66oOiN2nYMT; zKMgl3s>XSa_ifw1Ze8g|jnWc6ft29hrqpZ%T-yU>*t{7>Y;<}%OqnHnzOY*@4!sLlW$Y+ymMCk zF8YoN4UQo;eL*5Wei_~E^Lx4y%8>_7YvcjxJl)u)m)WDt(Ci9&uMDUMf_9 zlOl*^kF_y#>b`4^&p2iF-%>uHT(a6YX$w2`FjS#K!U(Z z|1Sp@Y;r9P=b-GOI+Y_gU~P@~^Zq34TB_>0e;iD+(0^1z8R*py(dH66&XB`~-+mhNKyK*vPZU^U0~QqGC?QlfF)rQSblE@Urx|$+cI# zAXc@Rt~3a;1?weEPx=T=$rcNpXwAyF+YPdrlXl3Oko@v+LfV~WOG05Ql*q%bX-)}j zOYnQ<$7a~5eH2ZF@d5{w-ho9K>uSyLdMUwtpX&k|P&;#hFrdKb^j7`tk1_$?>?cFn zI(RcdD)Zxof}tjS5f@Ce6OPnv!HHrNK?RPLMeXHweMpROhm@@av-un_2!Dqc{)9j8 zqK?mt6z!baIT1}UH$TcC$#;Wa>p|!2L(TSWHr6vZRrK0FgsME+Bz z%5HnrN6~9ZPYQ6PmIq9y@VTEFODax&D6=}CaO2#SB9FPtR$lE(5+tLO{B-6-Jtt!)0rruER9=EjnuGTBk zMnk6tO;LC_WdN_e-PC;w1`<^`04E|<#hkZ!Cy_%OjhMaVoGnt)7HnM6Jq-s~*abAe zww&n7kI2-2`J<*oC&sDUoKr6NY2TtQ*f$6%DXQ&Y>c$_E_n6Ib|IlxzZRC$yKU{lu zqSm7N*4aV#`A0TLe`wLSi;B5Z3zW!btw!waOo%o8$oZxuA=2D_LVJ%e6*X+w++oqS z05GpizkY)>p#Szql#wM!5aem41OZ-AzAlmn_lrv7OtL6%MhNO^8(D-Et0aHc#FHYU z&^*;)8e}PPzb|(@m;G6QAV=wj9C_ZyS+_pXpwb^wb{6;$1l2EPygW~=r$zfA1PUad zWl8Up(MI+VH@809Y`TfwwUh_XLR}C0aq@0UJt}dhB|^Ha-(obcTCq<(-Rf6G@7Bij zW=-17%|q&tkM;1O1KiSzIHwQkJkr!1u<^r~@Z!8-WuTHT{Hr@_)vE;v_xsA5rZmROw`K+_|;fVlPG1E>dY1QZ8(F%=537rpM+1l21|so$Edo zzgvWY;vU$t_VSL7Y%&)BDIo9gXVM;wE~&Csk9&F8t@a+gZTW^Qp-+pi8TXQowi)?f zm;J!;f%FM)2C!kVTIuRYZO(S7U*+iQt9|F*JwSsZbxk2ip!j``oH@wMG>+kwjoxZ5 z89h3lDy~}uaNp3M!BLkyxqsvoSCh7I5Q*duWDB z5rcTjcAvm#7{w)bH>(G&TD)t}f+aMqSKJj9!SPi$^|d74!E)d?0J1oTD4-#RWJ#Ms zGkkxl0Zj2|fXtX1gtFuwn>Rv$M3L%~6tN(frB@jN)UqcmIJ2H)nj$v?96Uz9_)$>fXl8F1cU3f6O4l8ec|AG8ajh7 z=gI$Kk6-OEt$DMLc}1avjoH)wZSFsvzgug$u*dE@=Umn=w+ot?jURXnaC;&F7vj|t zD8gX6>E;uRHMaRG-+jGLU;)rQ+?QzLF%B}*jZDr7=cA)P^*{n1JTL4mrd9W2txtH@ zPWN{G10f7_XfnDqn6qPo>)te1%bPljT)GQo=;D-qMB$mgRXlJhtR>uay zfS+0LWFW`DqN}MI4L+)~&jJ_%BuSMzd4*YTv&p$~`IiH;{UTa1@^BT>Ly!pv3Wu@; ze4cwfB^}ru+2)O*T?hYCZ~0f& zQOtQYk=8W(BL~vo=Y73>|CA90+=NE_qo_zc%8ugqCGD|iKz)5mQdOIX)(&;i>5|xy z$NZj%dFX2YM6o^d5>U7EB2Y!%!Jhi}_2;N?yfp|^^Qicf5dE$z;+4gC?BeG|E8I9Q z1S~lov(m~+F3XKy5LrF`pIx14Jo_!0_K|^gxU8PS9ON7BB1#2M5CxR z+VVV&t~~wO&LEIy$C8Fm>EYy;M`%IMhMKLaIvJU`&Z7Bp$yZt}-Ccg#p#2a!n{b}n zyIo*e+X>{`&0UllIasJVvpNCE9&Tiv9fqFmO^8gIds?1%+Mk4^c@aJ_DjNx!5%wbPj|GF;RgeGXD8+L$R~CWg@k}q z!)80|B7gV@ZnJ9t!~@EHc9`XHkLVv~At?E%rdF|iK3n&_yfcP42BK?9I)mpK$1;0a zr#gjbGJ2x3QR>9wK5e(tqq87gLRSX|^P$R}GyGD9*);~v>%+hgwt!?GA>o;lI-dUz DbV~re literal 29010 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 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9e2ee187cc..6e78ee5d45 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,8 +53,9 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. +- **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. +- **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). +- **`Display & View`** - display and viewer colorspace. (If `Transcoding type` is `Use Display&View` values in configuration is used OR if empty values collected on instance from DCC). - **`Arguments`** - special additional command line arguments for `oiiotool`. From 1594d7753728820b4d84568c7c5fe80aaa0ebbab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:42:07 +0100 Subject: [PATCH 064/202] OP-4643 - added use case for Maya to documentation --- .../assets/global_oiio_transcode2.png | Bin 0 -> 17960 bytes .../project_settings/settings_project_global.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode2.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode2.png b/website/docs/project_settings/assets/global_oiio_transcode2.png new file mode 100644 index 0000000000000000000000000000000000000000..906f780830a96b4bc6f26d98484dd3f9cc3885f2 GIT binary patch literal 17960 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 F{{cSxQDp!C literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 6e78ee5d45..f58d2c2bf2 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -62,6 +62,9 @@ Notable parameters: Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. ![global_oiio_transcode](assets/global_oiio_transcode.png) +Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) + ## Profile filters Many of the settings are using a concept of **Profile filters** From a07e76ebb56d9dc325f11b40a5b336f4125725e0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:05:26 +0100 Subject: [PATCH 065/202] OP-4643 - updates to documentation Co-authored-by: Roy Nieterau --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index f58d2c2bf2..d904080ad1 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -51,7 +51,7 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: -- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation loses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. - **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). From 2921579164112453343a580f8f358c124eb9c1d7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 066/202] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 4 + .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index cedc2d6876..8485bec67b 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,6 +68,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From 8a67065fce1bd39de5870bb768365dffafdda8b2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 067/202] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 039255d937..2fc662f2a4 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,3 +1045,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From 76c00f9fa8888e991c61074ed894b09c211b55f0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 068/202] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From 455eb379c26ac8494d1aff4fb257cd01e80dbdd5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 069/202] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From 52a5865341039d03da72dee00d4eb996334a4dbd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 070/202] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 2fc662f2a4..ab86e44304 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1050,6 +1050,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1066,6 +1067,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1080,6 +1082,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From 0f1dcb64eb5c98ca1d78b48b4e9e4a9dcfc86ffa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 071/202] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 4d29e43a41a49dd805390fd03d3d7602d15ad38f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 072/202] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From 454f65dc50253afdb7c74c7c8b851fcbe54c372d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 073/202] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From a197c6820209db57dc3182ca392e51239be57bb5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 074/202] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From bd1a9c7342098e05e095f422200090ce918a56b5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 075/202] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From bb85bc330a514f088fd2fad6037f60976e0e993b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 076/202] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From 1fd17b9f597dfef41852572deae8edbec65c3280 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 077/202] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From ffec1179ad1c9c07fae71db44ff2dc96b6fd7ec2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 078/202] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From 5a562dc821b2cbefc780509b44c084bba786e80c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 18:22:08 +0100 Subject: [PATCH 079/202] OP-4643 - refactored according to review Function turned into single filepath input. --- openpype/lib/transcoding.py | 43 ++++++----- .../publish/extract_color_transcode.py | 72 ++++++++++--------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index ab86e44304..e1bd22d109 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1047,12 +1047,12 @@ def convert_ffprobe_fps_to_float(value): return dividend / divisor -def convert_colorspace_for_input_paths( - input_paths, - output_dir, +def convert_colorspace( + input_path, + out_filepath, config_path, - source_color_space, - target_color_space, + source_colorspace, + target_colorspace, logger=None ): """Convert source files from one color space to another. @@ -1063,13 +1063,13 @@ def convert_colorspace_for_input_paths( frame template Args: - input_paths (str): Paths that should be converted. It is expected that + input_path (str): Paths that should be converted. It is expected that contains single file or image sequence of samy type. - output_dir (str): Path to directory where output will be rendered. + out_filepath (str): Path to directory where output will be rendered. Must not be same as input's directory. config_path (str): path to OCIO config file - source_color_space (str): ocio valid color space of source files - target_color_space (str): ocio valid target color space + source_colorspace (str): ocio valid color space of source files + target_colorspace (str): ocio valid target color space logger (logging.Logger): Logger used for logging. """ @@ -1083,21 +1083,18 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_color_space, target_color_space + "--colorconvert", source_colorspace, target_colorspace ] - for input_path in input_paths: - # Prepare subprocess arguments + # Prepare subprocess arguments - oiio_cmd.extend([ - input_arg, input_path, - ]) + oiio_cmd.extend([ + input_arg, input_path, + ]) - # Add last argument - path to output - base_filename = os.path.basename(input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) + # Add last argument - path to output + oiio_cmd.extend([ + "-o", out_filepath + ]) - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..3a05426432 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -10,7 +10,7 @@ from openpype.lib import ( ) from openpype.lib.transcoding import ( - convert_colorspace_for_input_paths, + convert_colorspace, get_transcode_temp_directory, ) @@ -69,7 +69,7 @@ class ExtractColorTranscode(publish.Extractor): continue colorspace_data = repre["colorspaceData"] - source_color_space = colorspace_data["colorspace"] + source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") if not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") @@ -80,8 +80,8 @@ class ExtractColorTranscode(publish.Extractor): for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) - new_staging_dir = get_transcode_temp_directory() original_staging_dir = new_repre["stagingDir"] + new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir files_to_convert = new_repre["files"] if not isinstance(files_to_convert, list): @@ -92,27 +92,28 @@ class ExtractColorTranscode(publish.Extractor): output_extension = output_def["output_extension"] output_extension = output_extension.replace('.', '') if output_extension: - new_repre["name"] = output_extension + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension new_repre["ext"] = output_extension - files_to_convert = self._rename_output_files( - files_to_convert, output_extension) - - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - target_colorspace = output_def["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) + for file_name in files_to_convert: + input_filepath = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_filepath, + new_staging_dir, + output_extension) + convert_colorspace( + input_filepath, + output_path, + config_path, + source_colorspace, + target_colorspace, + self.log + ) instance.context.data["cleanupFullPaths"].extend( files_to_delete) @@ -130,16 +131,16 @@ class ExtractColorTranscode(publish.Extractor): instance.data["representations"].append(new_repre) - def _rename_output_files(self, files_to_convert, output_extension): - """Change extension of converted files.""" - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, + output_extension): + """Create output file name path.""" + file_name = os.path.basename(input_filepath) + file_name, input_extension = os.path.splitext(file_name) + if not output_extension: + output_extension = input_extension + new_file_name = '{}.{}'.format(file_name, + output_extension) + return os.path.join(output_dir, new_file_name) def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" @@ -161,10 +162,10 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) return profile @@ -181,18 +182,19 @@ class ExtractColorTranscode(publish.Extractor): if repre.get("ext") not in self.supported_exts: self.log.debug(( - "Representation \"{}\" of unsupported extension. Skipped." + "Representation '{}' of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): self.log.debug(( - "Representation \"{}\" have empty files. Skipped." + "Representation '{}' have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.debug("Repre has no colorspace data. Skipped.") + self.log.debug("Representation '{}' has no colorspace data. " + "Skipped.") return False return True From e0a163bc2e836c9292e52757abab43d26bc44c07 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:53:02 +0100 Subject: [PATCH 080/202] OP-4643 - updated schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 7155510fef..80c18ce118 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -267,8 +267,8 @@ "type": "dict", "children": [ { - "key": "output_extension", - "label": "Output extension", + "key": "extension", + "label": "Extension", "type": "text" }, { From 657c3156dfc046405cee116c6ccb141e60901d23 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:54:46 +0100 Subject: [PATCH 081/202] OP-4643 - updated plugin name in schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 80c18ce118..357cbfb287 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -200,8 +200,8 @@ { "type": "dict", "collapsible": true, - "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode (ImageIO)", + "key": "ExtractOIIOTranscode", + "label": "Extract OIIO Transcode", "checkbox_key": "enabled", "is_group": true, "children": [ From 4b5c14e46686e471f20bef6909d6287b2eae6859 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:55:57 +0100 Subject: [PATCH 082/202] OP-4643 - updated key in schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 357cbfb287..0281b0ded6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -272,8 +272,8 @@ "type": "text" }, { - "key": "output_colorspace", - "label": "Output colorspace", + "key": "colorspace", + "label": "Colorspace", "type": "text" }, { From 756661f71b762fb738bd793984787941f2051e4d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:57:03 +0100 Subject: [PATCH 083/202] OP-4643 - changed oiio_cmd creation Co-authored-by: Toke Jepsen --- openpype/lib/transcoding.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e1bd22d109..f22628dd28 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1076,25 +1076,15 @@ def convert_colorspace( if logger is None: logger = logging.getLogger(__name__) - input_arg = "-i" oiio_cmd = [ get_oiio_tools_path(), - + input_path, # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace - ] - # Prepare subprocess arguments - - oiio_cmd.extend([ - input_arg, input_path, - ]) - - # Add last argument - path to output - oiio_cmd.extend([ + "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath - ]) + ] logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From c3bf4734fdabc019c966bae93e131235f2560f34 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:44:45 +0100 Subject: [PATCH 084/202] OP-4643 - updated new keys into settings --- .../settings/defaults/project_settings/global.json | 2 +- .../projects_schema/schemas/schema_global_publish.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 8485bec67b..a5e2d25a88 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,7 +68,7 @@ "output": [] } }, - "ExtractColorTranscode": { + "ExtractOIIOTranscode": { "enabled": true, "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 0281b0ded6..74b81b13af 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -276,6 +276,16 @@ "label": "Colorspace", "type": "text" }, + { + "key": "display", + "label": "Display", + "type": "text" + }, + { + "key": "view", + "label": "View", + "type": "text" + }, { "type": "schema", "name": "schema_representation_tags" From 2b20ede6d86924a19e5bf5ad9697f451a85a757e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:45:42 +0100 Subject: [PATCH 085/202] OP-4643 - renanmed plugin, added new keys into outputs --- openpype/plugins/publish/extract_color_transcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3a05426432..cc63b35988 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -17,7 +17,7 @@ from openpype.lib.transcoding import ( from openpype.lib.profiles_filtering import filter_profiles -class ExtractColorTranscode(publish.Extractor): +class ExtractOIIOTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. @@ -89,14 +89,14 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) - output_extension = output_def["output_extension"] + output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: if new_repre["name"] == new_repre["ext"]: new_repre["name"] = output_extension new_repre["ext"] = output_extension - target_colorspace = output_def["output_colorspace"] + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 8b47a44d04aace508cc05608a18c4f7cce23cb9b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:13 +0100 Subject: [PATCH 086/202] OP-4643 - fixed config path key --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cc63b35988..245faeb306 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -70,8 +70,8 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): + config_path = colorspace_data.get("config", {}).get("path") + if not config_path or not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") continue From ed7faeef8f0a7678e99f70645d57a72864cc9461 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:42 +0100 Subject: [PATCH 087/202] OP-4643 - fixed renaming files --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 245faeb306..c079dcf70e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -96,6 +96,14 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["name"] = output_extension new_repre["ext"] = output_extension + renamed_files = [] + _, orig_ext = os.path.splitext(files_to_convert[0]) + for file_name in files_to_convert: + file_name = file_name.replace(orig_ext, + "."+output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 4b1418a79242eb63cfd4295d70d474b992c276ea Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:04:44 +0100 Subject: [PATCH 088/202] OP-4643 - updated to calculate sequence format --- .../publish/extract_color_transcode.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c079dcf70e..09c86909cb 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,5 +1,6 @@ import os import copy +import clique import pyblish.api @@ -108,6 +109,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not target_colorspace: raise RuntimeError("Target colorspace must be set") + files_to_convert = self._translate_to_sequence( + files_to_convert) for file_name in files_to_convert: input_filepath = os.path.join(original_staging_dir, file_name) @@ -139,6 +142,40 @@ class ExtractOIIOTranscode(publish.Extractor): instance.data["representations"].append(new_repre) + def _translate_to_sequence(self, files_to_convert): + """Returns original list of files or single sequence format filename. + + Uses clique to find frame sequence, in this case it merges all frames + into sequence format (%0X) and returns it. + If sequence not found, it returns original list + + Args: + files_to_convert (list): list of file names + Returns: + (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + """ + pattern = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble( + files_to_convert, patterns=pattern, + assume_padded_when_ambiguous=True) + + if collections: + if len(collections) > 1: + raise ValueError( + "Too many collections {}".format(collections)) + + collection = collections[0] + padding = collection.padding + padding_str = "%0{}".format(padding) + frames = list(collection.indexes) + frame_str = "{}-{}#".format(frames[0], frames[-1]) + file_name = "{}{}{}".format(collection.head, frame_str, + collection.tail) + + files_to_convert = [file_name] + + return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, output_extension): """Create output file name path.""" From 382074b54cc0eb7a7c6d403853f4350d485fa00a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:54:02 +0100 Subject: [PATCH 089/202] OP-4643 - implemented display and viewer color space --- openpype/lib/transcoding.py | 23 +++++++++++++++++-- .../publish/extract_color_transcode.py | 13 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f22628dd28..cc9cd4e1eb 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,6 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, + view, + display, logger=None ): """Convert source files from one color space to another. @@ -1070,8 +1072,11 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + view (str): name for viewer space (ocio valid) + display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. - + Raises: + ValueError: if misconfigured """ if logger is None: logger = logging.getLogger(__name__) @@ -1082,9 +1087,23 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath ] + if all([target_colorspace, view, display]): + raise ValueError("Colorspace and both screen and display" + " cannot be set together." + "Choose colorspace or screen and display") + if not target_colorspace and not all([view, display]): + raise ValueError("Both screen and display must be set.") + + if target_colorspace: + oiio_cmd.extend(["--colorconvert", + source_colorspace, + target_colorspace]) + if view and display: + oiio_cmd.extend(["--iscolorspace", source_colorspace]) + oiio_cmd.extend(["--ociodisplay", display, view]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 09c86909cb..cd8421c0cd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -106,8 +106,15 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files target_colorspace = output_def["colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, + # but could be overwritten + if view: + new_repre["colorspaceData"]["view"] = view + if display: + new_repre["colorspaceData"]["view"] = display files_to_convert = self._translate_to_sequence( files_to_convert) @@ -123,6 +130,8 @@ class ExtractOIIOTranscode(publish.Extractor): config_path, source_colorspace, target_colorspace, + view, + display, self.log ) From 8873c9f9056b9b672fe95be0707c9ab8753726b8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 19:03:10 +0100 Subject: [PATCH 090/202] OP-4643 - fix wrong order of deletion of representation --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cd8421c0cd..9cca5cc969 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -69,6 +69,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not self._repre_is_valid(repre): continue + added_representations = False + colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("config", {}).get("path") @@ -76,8 +78,6 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - repre = self._handle_original_repre(repre, profile) - for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) @@ -150,6 +150,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["tags"].append(tag) instance.data["representations"].append(new_repre) + added_representations = True + + if added_representations: + self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): """Returns original list of files or single sequence format filename. @@ -253,7 +257,8 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _handle_original_repre(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile): + """If new transcoded representation created, delete old.""" delete_original = profile["delete_original"] if delete_original: @@ -264,5 +269,3 @@ class ExtractOIIOTranscode(publish.Extractor): repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") - - return repre From 176f53117fe49410135121dc0b858eadb8041270 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:26:27 +0100 Subject: [PATCH 091/202] OP-4643 - updated docstring, standardized arguments --- openpype/lib/transcoding.py | 19 +++++++---------- .../publish/extract_color_transcode.py | 21 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index cc9cd4e1eb..0f6d35affe 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1049,7 +1049,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace( input_path, - out_filepath, + output_path, config_path, source_colorspace, target_colorspace, @@ -1057,18 +1057,13 @@ def convert_colorspace( display, logger=None ): - """Convert source files from one color space to another. - - Filenames of input files are kept so make sure that output directory - is not the same directory as input files have. - - This way it can handle gaps and can keep input filenames without handling - frame template + """Convert source file from one color space to another. Args: - input_path (str): Paths that should be converted. It is expected that - contains single file or image sequence of samy type. - out_filepath (str): Path to directory where output will be rendered. - Must not be same as input's directory. + input_path (str): Path that should be converted. It is expected that + contains single file or image sequence of same type + (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + output_path (str): Path to output filename. config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space @@ -1087,7 +1082,7 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "-o", out_filepath + "-o", output_path ] if all([target_colorspace, view, display]): diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 9cca5cc969..c4cef15ea6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -119,13 +119,13 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: - input_filepath = os.path.join(original_staging_dir, - file_name) - output_path = self._get_output_file_path(input_filepath, + input_path = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) convert_colorspace( - input_filepath, + input_path, output_path, config_path, source_colorspace, @@ -156,16 +156,17 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): - """Returns original list of files or single sequence format filename. + """Returns original list or list with filename formatted in single + sequence format. Uses clique to find frame sequence, in this case it merges all frames - into sequence format (%0X) and returns it. + into sequence format (FRAMESTART-FRAMEEND#) and returns it. If sequence not found, it returns original list Args: files_to_convert (list): list of file names Returns: - (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] collections, remainder = clique.assemble( @@ -178,8 +179,6 @@ class ExtractOIIOTranscode(publish.Extractor): "Too many collections {}".format(collections)) collection = collections[0] - padding = collection.padding - padding_str = "%0{}".format(padding) frames = list(collection.indexes) frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, @@ -189,10 +188,10 @@ class ExtractOIIOTranscode(publish.Extractor): return files_to_convert - def _get_output_file_path(self, input_filepath, output_dir, + def _get_output_file_path(self, input_path, output_dir, output_extension): """Create output file name path.""" - file_name = os.path.basename(input_filepath) + file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: output_extension = input_extension From 04ae5a28b415197f36d198e409e8343bc4bc6022 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:27:06 +0100 Subject: [PATCH 092/202] OP-4643 - fix wrong assignment --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c4cef15ea6..4e899a519c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -114,7 +114,7 @@ class ExtractOIIOTranscode(publish.Extractor): if view: new_repre["colorspaceData"]["view"] = view if display: - new_repre["colorspaceData"]["view"] = display + new_repre["colorspaceData"]["display"] = display files_to_convert = self._translate_to_sequence( files_to_convert) From c6571b9dfd520a77877c84a86c60999ab5257a3a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 093/202] OP-4643 - fix files to delete --- .../publish/extract_color_transcode.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4e899a519c..99e684ba21 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -84,26 +84,18 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir - files_to_convert = new_repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_delete = copy.deepcopy(files_to_convert) + if isinstance(new_repre["files"], list): + files_to_convert = copy.deepcopy(new_repre["files"]) + else: + files_to_convert = [new_repre["files"]] output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension - new_repre["ext"] = output_extension - - renamed_files = [] - _, orig_ext = os.path.splitext(files_to_convert[0]) - for file_name in files_to_convert: - file_name = file_name.replace(orig_ext, - "."+output_extension) - renamed_files.append(file_name) - new_repre["files"] = renamed_files + self._rename_in_representation(new_repre, + files_to_convert, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -135,8 +127,12 @@ class ExtractOIIOTranscode(publish.Extractor): self.log ) - instance.context.data["cleanupFullPaths"].extend( - files_to_delete) + # cleanup temporary transcoded files + for file_name in new_repre["files"]: + transcoded_file_path = os.path.join(new_staging_dir, + file_name) + instance.context.data["cleanupFullPaths"].append( + transcoded_file_path) custom_tags = output_def.get("custom_tags") if custom_tags: @@ -155,6 +151,21 @@ class ExtractOIIOTranscode(publish.Extractor): if added_representations: self._mark_original_repre_for_deletion(repre, profile) + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From 8598c1ec393a1c8c0f05ad12a2a66bbf7eb89136 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:17:59 +0100 Subject: [PATCH 094/202] OP-4643 - moved output argument to the end --- openpype/lib/transcoding.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 0f6d35affe..e74dab4ccc 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1081,8 +1081,7 @@ def convert_colorspace( input_path, # Don't add any additional attributes "--nosoftwareattrib", - "--colorconfig", config_path, - "-o", output_path + "--colorconfig", config_path ] if all([target_colorspace, view, display]): @@ -1100,5 +1099,7 @@ def convert_colorspace( oiio_cmd.extend(["--iscolorspace", source_colorspace]) oiio_cmd.extend(["--ociodisplay", display, view]) + oiio_cmd.extend(["-o", output_path]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From afe0a97bc5ace60a5f7740d22b3d1937e1b69fdf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 095/202] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99e684ba21..3d897c6d9f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -142,6 +142,8 @@ class ExtractOIIOTranscode(publish.Extractor): # Add additional tags from output definition to representation for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 190a79a836d6e981e57f9f23be58d146193c4d6c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:26:07 +0100 Subject: [PATCH 096/202] OP-4643 - changed docstring Elaborated more that 'target_colorspace' and ('view', 'display') are disjunctive. --- openpype/lib/transcoding.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e74dab4ccc..f7d5e222c8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,8 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, - view, - display, + view=None, + display=None, logger=None ): """Convert source file from one color space to another. @@ -1067,7 +1067,9 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + if filled, 'view' and 'display' must be empty view (str): name for viewer space (ocio valid) + both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. Raises: From 4967d91010dc03df5640364350095f0fae9aa52c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:14:07 +0100 Subject: [PATCH 097/202] OP-4663 - fix double dots in extension Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3d897c6d9f..bfed69c300 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -207,7 +207,7 @@ class ExtractOIIOTranscode(publish.Extractor): file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: - output_extension = input_extension + output_extension = input_extension.replace(".", "") new_file_name = '{}.{}'.format(file_name, output_extension) return os.path.join(output_dir, new_file_name) From 43df616692568352b9734715e3a48b71a60e2221 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:11:45 +0100 Subject: [PATCH 098/202] OP-4643 - update documentation in Settings schema --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 74b81b13af..3956f403f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -207,7 +207,7 @@ "children": [ { "type": "label", - "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + "label": "Configure Output Definition(s) for new representation(s). \nEmpty 'Extension' denotes keeping source extension. \nName(key) of output definition will be used as new representation name \nunless 'passthrough' value is used to keep existing name. \nFill either 'Colorspace' (for target colorspace) or \nboth 'Display' and 'View' (for display and viewer colorspaces)." }, { "type": "boolean", From 5b72bafcfc9a33302f588e29d314d91b98186f87 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 099/202] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bfed69c300..e39ea3add9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -32,6 +32,25 @@ class ExtractOIIOTranscode(publish.Extractor): - task types - task names - subset names + + Can produce one or more representations (with different extensions) based + on output definition in format: + "output_name: { + "extension": "png", + "colorspace": "ACES - ACEScg", + "display": "", + "view": "", + "tags": [], + "custom_tags": [] + } + + If 'extension' is empty original representation extension is used. + 'output_name' will be used as name of new representation. In case of value + 'passthrough' name of original representation will be used. + + 'colorspace' denotes target colorspace to be transcoded into. Could be + empty if transcoding should be only into display and viewer colorspace. + (In that case both 'display' and 'view' must be filled.) """ label = "Transcode color spaces" @@ -78,7 +97,7 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for _, output_def in profile.get("outputs", {}).items(): + for output_name, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -92,10 +111,10 @@ class ExtractOIIOTranscode(publish.Extractor): output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') - if output_extension: - self._rename_in_representation(new_repre, - files_to_convert, - output_extension) + self._rename_in_representation(new_repre, + files_to_convert, + output_name, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -154,10 +173,22 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 7eacd1f30f2ce92adcec6ce8e48fb62bfb92a148 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:42:48 +0100 Subject: [PATCH 100/202] OP-4643 - updated docstring for convert_colorspace --- openpype/lib/transcoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f7d5e222c8..b6edd863f8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1062,8 +1062,11 @@ def convert_colorspace( Args: input_path (str): Path that should be converted. It is expected that contains single file or image sequence of same type - (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + (sequence in format 'file.FRAMESTART-FRAMEEND#.ext', see oiio docs, + eg `big.1-3#.tif`) output_path (str): Path to output filename. + (must follow format of 'input_path', eg. single file or + sequence in 'file.FRAMESTART-FRAMEEND#.ext', `output.1-3#.tif`) config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space From 440c4e0c100a413f62aaaf582201384124ba916b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:22:10 +0100 Subject: [PATCH 101/202] OP-4643 - remove review from old representation If new representation gets created and adds 'review' tag it becomes new reviewable representation. --- .../publish/extract_color_transcode.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index e39ea3add9..d10b887a0b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -89,6 +89,7 @@ class ExtractOIIOTranscode(publish.Extractor): continue added_representations = False + added_review = False colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] @@ -166,11 +167,15 @@ class ExtractOIIOTranscode(publish.Extractor): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + if tag == "review": + added_review = True + instance.data["representations"].append(new_repre) added_representations = True if added_representations: - self._mark_original_repre_for_deletion(repre, profile) + self._mark_original_repre_for_deletion(repre, profile, + added_review) def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): @@ -300,15 +305,16 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _mark_original_repre_for_deletion(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile, added_review): """If new transcoded representation created, delete old.""" + if not repre.get("tags"): + repre["tags"] = [] + delete_original = profile["delete_original"] if delete_original: - if not repre.get("tags"): - repre["tags"] = [] - - if "review" in repre["tags"]: - repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") + + if added_review and "review" in repre["tags"]: + repre["tags"].remove("review") From 212cbe79a2eb05f3b426a01d836d42964838bbbb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:23:42 +0100 Subject: [PATCH 102/202] OP-4643 - remove representation that should be deleted Or old revieable representation would be reviewed too. --- openpype/plugins/publish/extract_color_transcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index d10b887a0b..93ee1ec44d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -177,6 +177,11 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile, added_review) + for repre in tuple(instance.data["representations"]): + tags = repre.get("tags") or [] + if "delete" in tags and "thumbnail" not in tags: + instance.data["representations"].remove(repre) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 6295d0f34a9efc8e86ebbbf1dbe4b40a391dbf7b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 14:54:25 +0100 Subject: [PATCH 103/202] OP-4643 - fix logging Wrong variable used --- openpype/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index dcb43d7fa2..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -169,7 +169,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "Skipped representation. All output definitions from" " selected profile does not match to representation's" " custom tags. \"{}\"" - ).format(str(tags))) + ).format(str(custom_tags))) continue outputs_per_representations.append((repre, outputs)) From 03e8661323622c39d91c647d2f6cdd615a4ca99c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 104/202] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 93ee1ec44d..4a03e623fd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,15 +161,17 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation + if not new_repre.get("tags"): + new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) if tag == "review": added_review = True + new_repre["tags"].append("newly_added") + instance.data["representations"].append(new_repre) added_representations = True @@ -179,6 +181,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From f4140d7664cb3a36c032f4ae4b6d0a240fc01d3a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:04:59 +0100 Subject: [PATCH 105/202] OP-4642 - added additional command arguments to Settings --- .../schemas/schema_global_publish.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3956f403f4..5333d514b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -286,6 +286,20 @@ "label": "View", "type": "text" }, + { + "key": "oiiotool_args", + "label": "OIIOtool arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "additional_command_args", + "label": "Additional command line arguments", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "schema", "name": "schema_representation_tags" From b1d30058b0cdb26e46618d37807b01d400c89e33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:08:06 +0100 Subject: [PATCH 106/202] OP-4642 - added additional command arguments for oiiotool Some extension requires special command line arguments (.dpx and binary depth). --- openpype/lib/transcoding.py | 6 ++++++ openpype/plugins/publish/extract_color_transcode.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index b6edd863f8..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1055,6 +1055,7 @@ def convert_colorspace( target_colorspace, view=None, display=None, + additional_command_args=None, logger=None ): """Convert source file from one color space to another. @@ -1074,6 +1075,8 @@ def convert_colorspace( view (str): name for viewer space (ocio valid) both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) + additional_command_args (list): arguments for oiiotool (like binary + depth for .dpx) logger (logging.Logger): Logger used for logging. Raises: ValueError: if misconfigured @@ -1096,6 +1099,9 @@ def convert_colorspace( if not target_colorspace and not all([view, display]): raise ValueError("Both screen and display must be set.") + if additional_command_args: + oiio_cmd.extend(additional_command_args) + if target_colorspace: oiio_cmd.extend(["--colorconvert", source_colorspace, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4a03e623fd..3de404125d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -128,6 +128,9 @@ class ExtractOIIOTranscode(publish.Extractor): if display: new_repre["colorspaceData"]["display"] = display + additional_command_args = (output_def["oiiotool_args"] + ["additional_command_args"]) + files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: @@ -144,6 +147,7 @@ class ExtractOIIOTranscode(publish.Extractor): target_colorspace, view, display, + additional_command_args, self.log ) From e8a79b4f7673a37657b54a62f74c81985a2b5636 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 107/202] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3de404125d..8c4ef59de9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -82,6 +82,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + new_representations = [] repres = instance.data.get("representations") or [] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) @@ -174,9 +175,7 @@ class ExtractOIIOTranscode(publish.Extractor): if tag == "review": added_review = True - new_repre["tags"].append("newly_added") - - instance.data["representations"].append(new_repre) + new_representations.append(new_repre) added_representations = True if added_representations: @@ -185,15 +184,11 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) + instance.data["representations"].extend(new_representations) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From e5ec6c4812aa5d5c59a04d834b631afb0917bd2e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:24:53 +0100 Subject: [PATCH 108/202] OP-4642 - refactored query of representations line 73 returns if no representations. --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 8c4ef59de9..de36ea7d5f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -83,7 +83,7 @@ class ExtractOIIOTranscode(publish.Extractor): return new_representations = [] - repres = instance.data.get("representations") or [] + repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): From 6235faab82af4ac7d3a50315b4c1dc9223a73c0a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 10:44:10 +0100 Subject: [PATCH 109/202] OP-4643 - fixed subset filtering Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index de36ea7d5f..71124b527a 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -273,7 +273,7 @@ class ExtractOIIOTranscode(publish.Extractor): "families": family, "task_names": task_name, "task_types": task_type, - "subset": subset + "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) From b304d63461704f7c2e0709bd5165683a0d890a10 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 110/202] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/lib/transcoding.py | 29 +++++++++++++++++++++- openpype/plugins/publish/extract_review.py | 27 +++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..4d2f72fc41 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,30 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 5d0dc43494452813d19f7ffa511b841e59b64209 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:02:41 +0100 Subject: [PATCH 111/202] OP-4643 - refactor - changed existence check --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 71124b527a..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,12 +161,12 @@ class ExtractOIIOTranscode(publish.Extractor): custom_tags = output_def.get("custom_tags") if custom_tags: - if not new_repre.get("custom_tags"): + if new_repre.get("custom_tags") is None: new_repre["custom_tags"] = [] new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation - if not new_repre.get("tags"): + if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: if tag not in new_repre["tags"]: From 8651a693f990d52e43c14845e82efc3033b5a054 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 112/202] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 68313f9215a54d94f4c18119a56679b7c277f937 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:10:11 +0100 Subject: [PATCH 113/202] OP-4643 - changed label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5333d514b5..3e9467af61 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -294,7 +294,7 @@ "children": [ { "key": "additional_command_args", - "label": "Additional command line arguments", + "label": "Arguments", "type": "list", "object_type": "text" } From 68a0892a1d20a17f2504382a3c16586de5039108 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 114/202] OP-4643 - added documentation --- .../assets/global_oiio_transcode.png | Bin 0 -> 29010 bytes .../project_settings/settings_project_global.md | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png new file mode 100644 index 0000000000000000000000000000000000000000..99396d5bb3f16d434a92c6079515128488b82489 GIT binary patch literal 29010 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 literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 37fed93e69..52671d2db6 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -45,6 +45,21 @@ The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](http The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: +### Extract OIIO Transcode +There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +`oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. + +Notable parameters: +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Extension`** - target extension, could be empty - original extension is used +- **`Colorspace`** - target colorspace - must be available in used color config +- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Arguments`** - special additional command line arguments for `oiiotool` + + +Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. +![global_oiio_transcode](assets/global_oiio_transcode.png) ## Profile filters From 265a08abcdf88d3180fc3a1da362351c28083b9b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:41:42 +0100 Subject: [PATCH 115/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 52671d2db6..cc661a21fa 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,7 +46,7 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. From b2d40c1cc39fbc42b0365a3edc9fb638db5c3584 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:06 +0100 Subject: [PATCH 116/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index cc661a21fa..8e557a381c 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -52,7 +52,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. -- **`Extension`** - target extension, could be empty - original extension is used +- **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace - must be available in used color config - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 2d601023f7db52033736e770b6ec3879fda04de5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:29 +0100 Subject: [PATCH 117/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 8e557a381c..166400cb7f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,7 +53,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace - must be available in used color config +- **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 6260ea0a91cce8f1a67707aa55553e6ea6afbcab Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:48 +0100 Subject: [PATCH 118/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 166400cb7f..908191f122 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -54,7 +54,7 @@ Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. - **`Arguments`** - special additional command line arguments for `oiiotool` From c1c8ca234f97c6b3f64bd91c52f91969656077e1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:43:06 +0100 Subject: [PATCH 119/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 908191f122..0a73868d2d 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -55,7 +55,7 @@ Notable parameters: - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. -- **`Arguments`** - special additional command line arguments for `oiiotool` +- **`Arguments`** - special additional command line arguments for `oiiotool`. Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. From 92768e004993311fae5e74ccc9e552ba8f171a2c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:21:20 +0100 Subject: [PATCH 120/202] OP-4643 - updates to documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- website/docs/project_settings/settings_project_global.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 0a73868d2d..9e2ee187cc 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,8 +46,8 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. -Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. + `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: From 94ee02879286ef75ce60001f8c94d818a24aea57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 121/202] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 29 +--------------------- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++--- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 4d2f72fc41..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,30 +1114,3 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - - -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 840f6811345241dd4e058d2d5940847c5b31c69f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:02:17 +0100 Subject: [PATCH 122/202] OP-4643 - different splitting for oiio It seems that logic in ExtractReview does different thing. --- openpype/lib/transcoding.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..376297ff32 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,21 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + if not arg.strip(): + continue + splitted_args.extend(arg.split(" ")) + return splitted_args From 5a7e84ab90042c3565e5ba13f11cf5674f02e4f8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:14:57 +0100 Subject: [PATCH 123/202] OP-4643 - allow colorspace to be empty and collected from DCC --- openpype/plugins/publish/extract_color_transcode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..82b92ec93e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,7 +118,8 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 4abac5ec783c29465d4eb6347ffbd87b6315c2df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 12:22:53 +0100 Subject: [PATCH 124/202] OP-4643 - fix colorspace from DCC representation["colorspaceData"]["colorspace"] is only input colorspace --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 82b92ec93e..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,8 +118,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 6cb8cbd6fc03b2d63f09bac0b25634cb1ef3f827 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:33:20 +0100 Subject: [PATCH 125/202] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- openpype/lib/transcoding.py | 2 +- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++---- .../schemas/schema_global_publish.json | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 376297ff32..c0bda2aa37 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1052,7 +1052,7 @@ def convert_colorspace( output_path, config_path, source_colorspace, - target_colorspace, + target_colorspace=None, view=None, display=None, additional_command_args=None, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..b0921688e9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,10 +118,17 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] - view = output_def["view"] or colorspace_data.get("view") - display = (output_def["display"] or - colorspace_data.get("display")) + transcoding_type = output_def["transcoding_type"] + + target_colorspace = view = display = None + if transcoding_type == "colorspace": + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) + else: + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, # but could be overwritten if view: diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3e9467af61..76574e8b9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -271,6 +271,15 @@ "label": "Extension", "type": "text" }, + { + "type": "enum", + "key": "transcoding_type", + "label": "Transcoding type", + "enum_items": [ + { "colorspace": "Use Colorspace" }, + { "display": "Use Display&View" } + ] + }, { "key": "colorspace", "label": "Colorspace", From 58ae146027ebe31b9fe2d7fcc6d4323efb93c883 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:34:03 +0100 Subject: [PATCH 126/202] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- .../assets/global_oiio_transcode.png | Bin 29010 -> 17936 bytes .../settings_project_global.md | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png index 99396d5bb3f16d434a92c6079515128488b82489..d818ecfe19f93366e5f4664734d68c7b448d7b4f 100644 GIT binary patch literal 17936 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 DbV~re literal 29010 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 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9e2ee187cc..6e78ee5d45 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,8 +53,9 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. +- **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. +- **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). +- **`Display & View`** - display and viewer colorspace. (If `Transcoding type` is `Use Display&View` values in configuration is used OR if empty values collected on instance from DCC). - **`Arguments`** - special additional command line arguments for `oiiotool`. From aae1430904962212e3c31345eb317d74cbd2385b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:42:07 +0100 Subject: [PATCH 127/202] OP-4643 - added use case for Maya to documentation --- .../assets/global_oiio_transcode2.png | Bin 0 -> 17960 bytes .../project_settings/settings_project_global.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode2.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode2.png b/website/docs/project_settings/assets/global_oiio_transcode2.png new file mode 100644 index 0000000000000000000000000000000000000000..906f780830a96b4bc6f26d98484dd3f9cc3885f2 GIT binary patch literal 17960 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 F{{cSxQDp!C literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 6e78ee5d45..f58d2c2bf2 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -62,6 +62,9 @@ Notable parameters: Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. ![global_oiio_transcode](assets/global_oiio_transcode.png) +Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) + ## Profile filters Many of the settings are using a concept of **Profile filters** From 82e4e3e5b76d195be63bfce7017e3cc2ede23704 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:05:26 +0100 Subject: [PATCH 128/202] OP-4643 - updates to documentation Co-authored-by: Roy Nieterau --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index f58d2c2bf2..d904080ad1 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -51,7 +51,7 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: -- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation loses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. - **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). From 04109103303c436873a1898de53a54735c524f10 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:57:51 +0100 Subject: [PATCH 129/202] OP-4643 - added Settings for ExtractColorTranscode --- .../defaults/project_settings/global.json | 4 + .../schemas/schema_global_publish.json | 73 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index cedc2d6876..8485bec67b 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,6 +68,10 @@ "output": [] } }, + "ExtractColorTranscode": { + "enabled": true, + "profiles": [] + }, "ExtractReview": { "enabled": true, "profiles": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5388d04bc9..46ae6ba554 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -197,6 +197,79 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractColorTranscode", + "label": "ExtractColorTranscode", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "profiles", + "label": "Profiles", + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "key": "hosts", + "label": "Host names", + "type": "hosts-enum", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "task_names", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "ext", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From 2f1888bbfbd8dbabcd50ed4d48ab2230d810ba53 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 11:58:51 +0100 Subject: [PATCH 130/202] OP-4643 - added ExtractColorTranscode Added method to convert from one colorspace to another to transcoding lib --- openpype/lib/transcoding.py | 53 ++++++++ .../publish/extract_color_transcode.py | 124 ++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 openpype/plugins/publish/extract_color_transcode.py diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 039255d937..2fc662f2a4 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1045,3 +1045,56 @@ def convert_ffprobe_fps_to_float(value): if divisor == 0.0: return 0.0 return dividend / divisor + + +def convert_colorspace_for_input_paths( + input_paths, + output_dir, + source_color_space, + target_color_space, + logger=None +): + """Convert source files from one color space to another. + + Filenames of input files are kept so make sure that output directory + is not the same directory as input files have. + - This way it can handle gaps and can keep input filenames without handling + frame template + + Args: + input_paths (str): Paths that should be converted. It is expected that + contains single file or image sequence of samy type. + output_dir (str): Path to directory where output will be rendered. + Must not be same as input's directory. + source_color_space (str): ocio valid color space of source files + target_color_space (str): ocio valid target color space + logger (logging.Logger): Logger used for logging. + + """ + if logger is None: + logger = logging.getLogger(__name__) + + input_arg = "-i" + oiio_cmd = [ + get_oiio_tools_path(), + + # Don't add any additional attributes + "--nosoftwareattrib", + "--colorconvert", source_color_space, target_color_space + ] + for input_path in input_paths: + # Prepare subprocess arguments + + oiio_cmd.extend([ + input_arg, input_path, + ]) + + # Add last argument - path to output + base_filename = os.path.basename(input_path) + output_path = os.path.join(output_dir, base_filename) + oiio_cmd.extend([ + "-o", output_path + ]) + + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py new file mode 100644 index 0000000000..58508ab18f --- /dev/null +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -0,0 +1,124 @@ +import pyblish.api + +from openpype.pipeline import publish +from openpype.lib import ( + + is_oiio_supported, +) + +from openpype.lib.transcoding import ( + convert_colorspace_for_input_paths, + get_transcode_temp_directory, +) + +from openpype.lib.profiles_filtering import filter_profiles + + +class ExtractColorTranscode(publish.Extractor): + """ + Extractor to convert colors from one colorspace to different. + """ + + label = "Transcode color spaces" + order = pyblish.api.ExtractorOrder + 0.01 + + optional = True + + # Configurable by Settings + profiles = None + options = None + + def process(self, instance): + if not self.profiles: + self.log.warning("No profiles present for create burnin") + return + + if "representations" not in instance.data: + self.log.warning("No representations, skipping.") + return + + if not is_oiio_supported(): + self.log.warning("OIIO not supported, no transcoding possible.") + return + + colorspace_data = instance.data.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Instance has not colorspace data, skipping") + return + source_color_space = colorspace_data["colorspace"] + + host_name = instance.context.data["hostName"] + family = instance.data["family"] + task_data = instance.data["anatomyData"].get("task", {}) + task_name = task_data.get("name") + task_type = task_data.get("type") + subset = instance.data["subset"] + + filtering_criteria = { + "hosts": host_name, + "families": family, + "task_names": task_name, + "task_types": task_type, + "subset": subset + } + profile = filter_profiles(self.profiles, filtering_criteria, + logger=self.log) + + if not profile: + self.log.info(( + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) + return + + self.log.debug("profile: {}".format(profile)) + + target_colorspace = profile["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(repres): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self.repre_is_valid(repre): + continue + + new_staging_dir = get_transcode_temp_directory() + repre["stagingDir"] = new_staging_dir + files_to_remove = repre["files"] + if not isinstance(files_to_remove, list): + files_to_remove = [files_to_remove] + instance.context.data["cleanupFullPaths"].extend(files_to_remove) + + convert_colorspace_for_input_paths( + repre["files"], + new_staging_dir, + source_color_space, + target_colorspace, + self.log + ) + + def repre_is_valid(self, repre): + """Validation if representation should be processed. + + Args: + repre (dict): Representation which should be checked. + + Returns: + bool: False if can't be processed else True. + """ + + if "review" not in (repre.get("tags") or []): + self.log.info(( + "Representation \"{}\" don't have \"review\" tag. Skipped." + ).format(repre["name"])) + return False + + if not repre.get("files"): + self.log.warning(( + "Representation \"{}\" have empty files. Skipped." + ).format(repre["name"])) + return False + return True From b932994e15ab43e5df93bcf3e81e71622594c6a2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 12:05:57 +0100 Subject: [PATCH 131/202] OP-4643 - extractor must run just before ExtractReview Nuke render local is set to 0.01 --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 58508ab18f..5163cd4045 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -20,7 +20,7 @@ class ExtractColorTranscode(publish.Extractor): """ label = "Transcode color spaces" - order = pyblish.api.ExtractorOrder + 0.01 + order = pyblish.api.ExtractorOrder + 0.019 optional = True From 48f24ef17d8929e84ac16868ad2d6a733d47b1f1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 132/202] OP-4643 - fix for full file paths --- .../publish/extract_color_transcode.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 5163cd4045..6ad7599f2c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,3 +1,4 @@ +import os import pyblish.api from openpype.pipeline import publish @@ -41,13 +42,6 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return - colorspace_data = instance.data.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Instance has not colorspace data, skipping") - return - source_color_space = colorspace_data["colorspace"] - host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) @@ -82,18 +76,32 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self.repre_is_valid(repre): + # if not self.repre_is_valid(repre): + # continue + + colorspace_data = repre.get("colorspaceData") + if not colorspace_data: + # TODO get_colorspace ?? + self.log.warning("Repre has not colorspace data, skipping") + continue + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") continue new_staging_dir = get_transcode_temp_directory() + original_staging_dir = repre["stagingDir"] repre["stagingDir"] = new_staging_dir - files_to_remove = repre["files"] - if not isinstance(files_to_remove, list): - files_to_remove = [files_to_remove] - instance.context.data["cleanupFullPaths"].extend(files_to_remove) + files_to_convert = repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + instance.context.data["cleanupFullPaths"].extend(files_to_convert) convert_colorspace_for_input_paths( - repre["files"], + files_to_convert, new_staging_dir, source_color_space, target_colorspace, From ec299f0d3ca379f73e6f06949506148e78ca5fa1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:04:06 +0100 Subject: [PATCH 133/202] OP-4643 - pass path for ocio config --- openpype/lib/transcoding.py | 3 +++ openpype/plugins/publish/extract_color_transcode.py | 1 + 2 files changed, 4 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 2fc662f2a4..ab86e44304 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1050,6 +1050,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace_for_input_paths( input_paths, output_dir, + config_path, source_color_space, target_color_space, logger=None @@ -1066,6 +1067,7 @@ def convert_colorspace_for_input_paths( contains single file or image sequence of samy type. output_dir (str): Path to directory where output will be rendered. Must not be same as input's directory. + config_path (str): path to OCIO config file source_color_space (str): ocio valid color space of source files target_color_space (str): ocio valid target color space logger (logging.Logger): Logger used for logging. @@ -1080,6 +1082,7 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", + "--colorconfig", config_path, "--colorconvert", source_color_space, target_color_space ] for input_path in input_paths: diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 6ad7599f2c..fdb13a47e8 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -103,6 +103,7 @@ class ExtractColorTranscode(publish.Extractor): convert_colorspace_for_input_paths( files_to_convert, new_staging_dir, + config_path, source_color_space, target_colorspace, self.log From 2bc8377dbcf856012489a49051da9952ab75546b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:15:33 +0100 Subject: [PATCH 134/202] OP-4643 - add custom_tags --- openpype/plugins/publish/extract_color_transcode.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index fdb13a47e8..ab932b2476 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -72,6 +72,7 @@ class ExtractColorTranscode(publish.Extractor): target_colorspace = profile["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") + custom_tags = profile["custom_tags"] repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): @@ -109,6 +110,11 @@ class ExtractColorTranscode(publish.Extractor): self.log ) + if custom_tags: + if not repre.get("custom_tags"): + repre["custom_tags"] = [] + repre["custom_tags"].extend(custom_tags) + def repre_is_valid(self, repre): """Validation if representation should be processed. From 4a80b7bb34efdf1dbd7c8f554d42f1caff035385 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:18:38 +0100 Subject: [PATCH 135/202] OP-4643 - added docstring --- openpype/plugins/publish/extract_color_transcode.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index ab932b2476..88e2eed90f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -18,6 +18,17 @@ from openpype.lib.profiles_filtering import filter_profiles class ExtractColorTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. + + Expects "colorspaceData" on representation. This dictionary is collected + previously and denotes that representation files should be converted. + This dict contains source colorspace information, collected by hosts. + + Target colorspace is selected by profiles in the Settings, based on: + - families + - host + - task types + - task names + - subset names """ label = "Transcode color spaces" From f92c74605b793db31bbee90ccbed656571e69c39 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:15:44 +0100 Subject: [PATCH 136/202] OP-4643 - updated Settings schema --- .../schemas/schema_global_publish.json | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 46ae6ba554..c2c911d7d6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -246,24 +246,44 @@ "type": "list", "object_type": "text" }, + { + "type": "boolean", + "key": "delete_original", + "label": "Delete Original Representation" + }, { "type": "splitter" }, { - "key": "ext", - "label": "Output extension", - "type": "text" - }, - { - "key": "output_colorspace", - "label": "Output colorspace", - "type": "text" - }, - { - "key": "custom_tags", - "label": "Custom Tags", - "type": "list", - "object_type": "text" + "key": "outputs", + "label": "Output Definitions", + "type": "dict-modifiable", + "highlight_content": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "output_extension", + "label": "Output extension", + "type": "text" + }, + { + "key": "output_colorspace", + "label": "Output colorspace", + "type": "text" + }, + { + "type": "schema", + "name": "schema_representation_tags" + }, + { + "key": "custom_tags", + "label": "Custom Tags", + "type": "list", + "object_type": "text" + } + ] + } } ] } From 2f79021aca117bf3ebea7a2bd1103a6c7e958525 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:17:25 +0100 Subject: [PATCH 137/202] OP-4643 - skip video files Only frames currently supported. --- .../plugins/publish/extract_color_transcode.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 88e2eed90f..a0714c9a33 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -36,6 +36,9 @@ class ExtractColorTranscode(publish.Extractor): optional = True + # Supported extensions + supported_exts = ["exr", "jpg", "jpeg", "png", "dpx"] + # Configurable by Settings profiles = None options = None @@ -88,13 +91,7 @@ class ExtractColorTranscode(publish.Extractor): repres = instance.data.get("representations") or [] for idx, repre in enumerate(repres): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - # if not self.repre_is_valid(repre): - # continue - - colorspace_data = repre.get("colorspaceData") - if not colorspace_data: - # TODO get_colorspace ?? - self.log.warning("Repre has not colorspace data, skipping") + if not self._repre_is_valid(repre): continue source_color_space = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") @@ -136,9 +133,9 @@ class ExtractColorTranscode(publish.Extractor): bool: False if can't be processed else True. """ - if "review" not in (repre.get("tags") or []): - self.log.info(( - "Representation \"{}\" don't have \"review\" tag. Skipped." + if repre.get("ext") not in self.supported_exts: + self.log.warning(( + "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False From e63dc4075629efe7499a510742c61a0f5e9c46fd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:19:08 +0100 Subject: [PATCH 138/202] OP-4643 - refactored profile, delete of original Implemented multiple outputs from single input representation --- .../publish/extract_color_transcode.py | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index a0714c9a33..b0c851d5f4 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,4 +1,6 @@ import os +import copy + import pyblish.api from openpype.pipeline import publish @@ -56,13 +58,94 @@ class ExtractColorTranscode(publish.Extractor): self.log.warning("OIIO not supported, no transcoding possible.") return + profile = self._get_profile(instance) + if not profile: + return + + repres = instance.data.get("representations") or [] + for idx, repre in enumerate(list(repres)): + self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) + if not self._repre_is_valid(repre): + continue + + colorspace_data = repre["colorspaceData"] + source_color_space = colorspace_data["colorspace"] + config_path = colorspace_data.get("configData", {}).get("path") + if not os.path.exists(config_path): + self.log.warning("Config file doesn't exist, skipping") + continue + + repre = self._handle_original_repre(repre, profile) + + for _, output_def in profile.get("outputs", {}).items(): + new_repre = copy.deepcopy(repre) + + new_staging_dir = get_transcode_temp_directory() + original_staging_dir = new_repre["stagingDir"] + new_repre["stagingDir"] = new_staging_dir + files_to_convert = new_repre["files"] + if not isinstance(files_to_convert, list): + files_to_convert = [files_to_convert] + + files_to_delete = copy.deepcopy(files_to_convert) + + output_extension = output_def["output_extension"] + files_to_convert = self._rename_output_files(files_to_convert, + output_extension) + + files_to_convert = [os.path.join(original_staging_dir, path) + for path in files_to_convert] + + target_colorspace = output_def["output_colorspace"] + if not target_colorspace: + raise RuntimeError("Target colorspace must be set") + + convert_colorspace_for_input_paths( + files_to_convert, + new_staging_dir, + config_path, + source_color_space, + target_colorspace, + self.log + ) + + instance.context.data["cleanupFullPaths"].extend( + files_to_delete) + + custom_tags = output_def.get("custom_tags") + if custom_tags: + if not new_repre.get("custom_tags"): + new_repre["custom_tags"] = [] + new_repre["custom_tags"].extend(custom_tags) + + # Add additional tags from output definition to representation + for tag in output_def["tags"]: + if tag not in new_repre["tags"]: + new_repre["tags"].append(tag) + + instance.data["representations"].append(new_repre) + + def _rename_output_files(self, files_to_convert, output_extension): + """Change extension of converted files.""" + if output_extension: + output_extension = output_extension.replace('.', '') + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files + return files_to_convert + + def _get_profile(self, instance): + """Returns profile if and how repre should be color transcoded.""" host_name = instance.context.data["hostName"] family = instance.data["family"] task_data = instance.data["anatomyData"].get("task", {}) task_name = task_data.get("name") task_type = task_data.get("type") subset = instance.data["subset"] - filtering_criteria = { "hosts": host_name, "families": family, @@ -75,55 +158,15 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) - return + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) + return profile - target_colorspace = profile["output_colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") - custom_tags = profile["custom_tags"] - - repres = instance.data.get("representations") or [] - for idx, repre in enumerate(repres): - self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) - if not self._repre_is_valid(repre): - continue - source_color_space = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): - self.log.warning("Config file doesn't exist, skipping") - continue - - new_staging_dir = get_transcode_temp_directory() - original_staging_dir = repre["stagingDir"] - repre["stagingDir"] = new_staging_dir - files_to_convert = repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - instance.context.data["cleanupFullPaths"].extend(files_to_convert) - - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) - - if custom_tags: - if not repre.get("custom_tags"): - repre["custom_tags"] = [] - repre["custom_tags"].extend(custom_tags) - - def repre_is_valid(self, repre): + def _repre_is_valid(self, repre): """Validation if representation should be processed. Args: @@ -144,4 +187,23 @@ class ExtractColorTranscode(publish.Extractor): "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False + + if not repre.get("colorspaceData"): + self.log.warning("Repre has not colorspace data, skipping") + return False + return True + + def _handle_original_repre(self, repre, profile): + delete_original = profile["delete_original"] + + if delete_original: + if not repre.get("tags"): + repre["tags"] = [] + + if "review" in repre["tags"]: + repre["tags"].remove("review") + if "delete" not in repre["tags"]: + repre["tags"].append("delete") + + return repre From 7341c618274ccdc8f469473ff05698499f6d5b72 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:23:01 +0100 Subject: [PATCH 139/202] OP-4643 - switched logging levels Do not use warning unnecessary. --- openpype/plugins/publish/extract_color_transcode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0c851d5f4..4d38514b8b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -47,11 +47,11 @@ class ExtractColorTranscode(publish.Extractor): def process(self, instance): if not self.profiles: - self.log.warning("No profiles present for create burnin") + self.log.debug("No profiles present for color transcode") return if "representations" not in instance.data: - self.log.warning("No representations, skipping.") + self.log.debug("No representations, skipping.") return if not is_oiio_supported(): @@ -177,19 +177,19 @@ class ExtractColorTranscode(publish.Extractor): """ if repre.get("ext") not in self.supported_exts: - self.log.warning(( + self.log.debug(( "Representation \"{}\" of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): - self.log.warning(( + self.log.debug(( "Representation \"{}\" have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.warning("Repre has not colorspace data, skipping") + self.log.debug("Repre has no colorspace data. Skipped.") return False return True From d5cc450e9cd18f7360d65140e48ea5eab810c8e2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:14 +0100 Subject: [PATCH 140/202] OP-4643 - propagate new extension to representation --- .../publish/extract_color_transcode.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4d38514b8b..62cf8f0dee 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -90,8 +90,13 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) output_extension = output_def["output_extension"] - files_to_convert = self._rename_output_files(files_to_convert, - output_extension) + output_extension = output_extension.replace('.', '') + if output_extension: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + files_to_convert = self._rename_output_files( + files_to_convert, output_extension) files_to_convert = [os.path.join(original_staging_dir, path) for path in files_to_convert] @@ -127,15 +132,13 @@ class ExtractColorTranscode(publish.Extractor): def _rename_output_files(self, files_to_convert, output_extension): """Change extension of converted files.""" - if output_extension: - output_extension = output_extension.replace('.', '') - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + new_file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(new_file_name) + files_to_convert = renamed_files return files_to_convert def _get_profile(self, instance): From 65b454c42c77fb75afc06dd34cf36c40ea52a751 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 18:46:35 +0100 Subject: [PATCH 141/202] OP-4643 - added label to Settings --- .../projects_schema/schemas/schema_global_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c2c911d7d6..7155510fef 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -201,10 +201,14 @@ "type": "dict", "collapsible": true, "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode", + "label": "ExtractColorTranscode (ImageIO)", "checkbox_key": "enabled", "is_group": true, "children": [ + { + "type": "label", + "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + }, { "type": "boolean", "key": "enabled", From 9fc4070e066499a89898c450263119dbf8e2f99a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 18:22:08 +0100 Subject: [PATCH 142/202] OP-4643 - refactored according to review Function turned into single filepath input. --- openpype/lib/transcoding.py | 43 ++++++----- .../publish/extract_color_transcode.py | 72 ++++++++++--------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index ab86e44304..e1bd22d109 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1047,12 +1047,12 @@ def convert_ffprobe_fps_to_float(value): return dividend / divisor -def convert_colorspace_for_input_paths( - input_paths, - output_dir, +def convert_colorspace( + input_path, + out_filepath, config_path, - source_color_space, - target_color_space, + source_colorspace, + target_colorspace, logger=None ): """Convert source files from one color space to another. @@ -1063,13 +1063,13 @@ def convert_colorspace_for_input_paths( frame template Args: - input_paths (str): Paths that should be converted. It is expected that + input_path (str): Paths that should be converted. It is expected that contains single file or image sequence of samy type. - output_dir (str): Path to directory where output will be rendered. + out_filepath (str): Path to directory where output will be rendered. Must not be same as input's directory. config_path (str): path to OCIO config file - source_color_space (str): ocio valid color space of source files - target_color_space (str): ocio valid target color space + source_colorspace (str): ocio valid color space of source files + target_colorspace (str): ocio valid target color space logger (logging.Logger): Logger used for logging. """ @@ -1083,21 +1083,18 @@ def convert_colorspace_for_input_paths( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_color_space, target_color_space + "--colorconvert", source_colorspace, target_colorspace ] - for input_path in input_paths: - # Prepare subprocess arguments + # Prepare subprocess arguments - oiio_cmd.extend([ - input_arg, input_path, - ]) + oiio_cmd.extend([ + input_arg, input_path, + ]) - # Add last argument - path to output - base_filename = os.path.basename(input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) + # Add last argument - path to output + oiio_cmd.extend([ + "-o", out_filepath + ]) - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) + run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 62cf8f0dee..3a05426432 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -10,7 +10,7 @@ from openpype.lib import ( ) from openpype.lib.transcoding import ( - convert_colorspace_for_input_paths, + convert_colorspace, get_transcode_temp_directory, ) @@ -69,7 +69,7 @@ class ExtractColorTranscode(publish.Extractor): continue colorspace_data = repre["colorspaceData"] - source_color_space = colorspace_data["colorspace"] + source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("configData", {}).get("path") if not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") @@ -80,8 +80,8 @@ class ExtractColorTranscode(publish.Extractor): for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) - new_staging_dir = get_transcode_temp_directory() original_staging_dir = new_repre["stagingDir"] + new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir files_to_convert = new_repre["files"] if not isinstance(files_to_convert, list): @@ -92,27 +92,28 @@ class ExtractColorTranscode(publish.Extractor): output_extension = output_def["output_extension"] output_extension = output_extension.replace('.', '') if output_extension: - new_repre["name"] = output_extension + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension new_repre["ext"] = output_extension - files_to_convert = self._rename_output_files( - files_to_convert, output_extension) - - files_to_convert = [os.path.join(original_staging_dir, path) - for path in files_to_convert] - target_colorspace = output_def["output_colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") - convert_colorspace_for_input_paths( - files_to_convert, - new_staging_dir, - config_path, - source_color_space, - target_colorspace, - self.log - ) + for file_name in files_to_convert: + input_filepath = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_filepath, + new_staging_dir, + output_extension) + convert_colorspace( + input_filepath, + output_path, + config_path, + source_colorspace, + target_colorspace, + self.log + ) instance.context.data["cleanupFullPaths"].extend( files_to_delete) @@ -130,16 +131,16 @@ class ExtractColorTranscode(publish.Extractor): instance.data["representations"].append(new_repre) - def _rename_output_files(self, files_to_convert, output_extension): - """Change extension of converted files.""" - renamed_files = [] - for file_name in files_to_convert: - file_name, _ = os.path.splitext(file_name) - new_file_name = '{}.{}'.format(file_name, - output_extension) - renamed_files.append(new_file_name) - files_to_convert = renamed_files - return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, + output_extension): + """Create output file name path.""" + file_name = os.path.basename(input_filepath) + file_name, input_extension = os.path.splitext(file_name) + if not output_extension: + output_extension = input_extension + new_file_name = '{}.{}'.format(file_name, + output_extension) + return os.path.join(output_dir, new_file_name) def _get_profile(self, instance): """Returns profile if and how repre should be color transcoded.""" @@ -161,10 +162,10 @@ class ExtractColorTranscode(publish.Extractor): if not profile: self.log.info(( - "Skipped instance. None of profiles in presets are for" - " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" - " | Task type \"{}\" | Subset \"{}\" " - ).format(host_name, family, task_name, task_type, subset)) + "Skipped instance. None of profiles in presets are for" + " Host: \"{}\" | Families: \"{}\" | Task \"{}\"" + " | Task type \"{}\" | Subset \"{}\" " + ).format(host_name, family, task_name, task_type, subset)) self.log.debug("profile: {}".format(profile)) return profile @@ -181,18 +182,19 @@ class ExtractColorTranscode(publish.Extractor): if repre.get("ext") not in self.supported_exts: self.log.debug(( - "Representation \"{}\" of unsupported extension. Skipped." + "Representation '{}' of unsupported extension. Skipped." ).format(repre["name"])) return False if not repre.get("files"): self.log.debug(( - "Representation \"{}\" have empty files. Skipped." + "Representation '{}' have empty files. Skipped." ).format(repre["name"])) return False if not repre.get("colorspaceData"): - self.log.debug("Repre has no colorspace data. Skipped.") + self.log.debug("Representation '{}' has no colorspace data. " + "Skipped.") return False return True From 5eb771333b9d0b0a7b2abf5a2487284f5043f478 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:53:02 +0100 Subject: [PATCH 143/202] OP-4643 - updated schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 7155510fef..80c18ce118 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -267,8 +267,8 @@ "type": "dict", "children": [ { - "key": "output_extension", - "label": "Output extension", + "key": "extension", + "label": "Extension", "type": "text" }, { From 49a06f873bc1a77e1dde35a9e6c7f1603508e6e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:54:46 +0100 Subject: [PATCH 144/202] OP-4643 - updated plugin name in schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 80c18ce118..357cbfb287 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -200,8 +200,8 @@ { "type": "dict", "collapsible": true, - "key": "ExtractColorTranscode", - "label": "ExtractColorTranscode (ImageIO)", + "key": "ExtractOIIOTranscode", + "label": "Extract OIIO Transcode", "checkbox_key": "enabled", "is_group": true, "children": [ From 2ec5221b282b4222f5ac179f63f5650050f3b331 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:55:57 +0100 Subject: [PATCH 145/202] OP-4643 - updated key in schema Co-authored-by: Toke Jepsen --- .../projects_schema/schemas/schema_global_publish.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 357cbfb287..0281b0ded6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -272,8 +272,8 @@ "type": "text" }, { - "key": "output_colorspace", - "label": "Output colorspace", + "key": "colorspace", + "label": "Colorspace", "type": "text" }, { From 83d21d9d7793350d6374c2c240f4f70e688c2e88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 12:57:03 +0100 Subject: [PATCH 146/202] OP-4643 - changed oiio_cmd creation Co-authored-by: Toke Jepsen --- openpype/lib/transcoding.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e1bd22d109..f22628dd28 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1076,25 +1076,15 @@ def convert_colorspace( if logger is None: logger = logging.getLogger(__name__) - input_arg = "-i" oiio_cmd = [ get_oiio_tools_path(), - + input_path, # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace - ] - # Prepare subprocess arguments - - oiio_cmd.extend([ - input_arg, input_path, - ]) - - # Add last argument - path to output - oiio_cmd.extend([ + "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath - ]) + ] logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 40f8cd4a93bfe76dcb91ee683c78033417f40525 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:44:45 +0100 Subject: [PATCH 147/202] OP-4643 - updated new keys into settings --- .../settings/defaults/project_settings/global.json | 2 +- .../projects_schema/schemas/schema_global_publish.json | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 8485bec67b..a5e2d25a88 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -68,7 +68,7 @@ "output": [] } }, - "ExtractColorTranscode": { + "ExtractOIIOTranscode": { "enabled": true, "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 0281b0ded6..74b81b13af 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -276,6 +276,16 @@ "label": "Colorspace", "type": "text" }, + { + "key": "display", + "label": "Display", + "type": "text" + }, + { + "key": "view", + "label": "View", + "type": "text" + }, { "type": "schema", "name": "schema_representation_tags" From e64389f11be2d072e9d8d59dce5334eebb727776 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 13:45:42 +0100 Subject: [PATCH 148/202] OP-4643 - renanmed plugin, added new keys into outputs --- openpype/plugins/publish/extract_color_transcode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3a05426432..cc63b35988 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -17,7 +17,7 @@ from openpype.lib.transcoding import ( from openpype.lib.profiles_filtering import filter_profiles -class ExtractColorTranscode(publish.Extractor): +class ExtractOIIOTranscode(publish.Extractor): """ Extractor to convert colors from one colorspace to different. @@ -89,14 +89,14 @@ class ExtractColorTranscode(publish.Extractor): files_to_delete = copy.deepcopy(files_to_convert) - output_extension = output_def["output_extension"] + output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: if new_repre["name"] == new_repre["ext"]: new_repre["name"] = output_extension new_repre["ext"] = output_extension - target_colorspace = output_def["output_colorspace"] + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 99d687c9a1366984116b5ef43d65cab465886b12 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:13 +0100 Subject: [PATCH 149/202] OP-4643 - fixed config path key --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cc63b35988..245faeb306 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -70,8 +70,8 @@ class ExtractOIIOTranscode(publish.Extractor): colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] - config_path = colorspace_data.get("configData", {}).get("path") - if not os.path.exists(config_path): + config_path = colorspace_data.get("config", {}).get("path") + if not config_path or not os.path.exists(config_path): self.log.warning("Config file doesn't exist, skipping") continue From 2a7fd01aada28eebf603e6bd8cc6d6b1a8a560a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:03:42 +0100 Subject: [PATCH 150/202] OP-4643 - fixed renaming files --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 245faeb306..c079dcf70e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -96,6 +96,14 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["name"] = output_extension new_repre["ext"] = output_extension + renamed_files = [] + _, orig_ext = os.path.splitext(files_to_convert[0]) + for file_name in files_to_convert: + file_name = file_name.replace(orig_ext, + "."+output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + target_colorspace = output_def["colorspace"] if not target_colorspace: raise RuntimeError("Target colorspace must be set") From 34d519524e9998c5436baf5b53f8740bf2eceff3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:04:44 +0100 Subject: [PATCH 151/202] OP-4643 - updated to calculate sequence format --- .../publish/extract_color_transcode.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c079dcf70e..09c86909cb 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,5 +1,6 @@ import os import copy +import clique import pyblish.api @@ -108,6 +109,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not target_colorspace: raise RuntimeError("Target colorspace must be set") + files_to_convert = self._translate_to_sequence( + files_to_convert) for file_name in files_to_convert: input_filepath = os.path.join(original_staging_dir, file_name) @@ -139,6 +142,40 @@ class ExtractOIIOTranscode(publish.Extractor): instance.data["representations"].append(new_repre) + def _translate_to_sequence(self, files_to_convert): + """Returns original list of files or single sequence format filename. + + Uses clique to find frame sequence, in this case it merges all frames + into sequence format (%0X) and returns it. + If sequence not found, it returns original list + + Args: + files_to_convert (list): list of file names + Returns: + (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + """ + pattern = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble( + files_to_convert, patterns=pattern, + assume_padded_when_ambiguous=True) + + if collections: + if len(collections) > 1: + raise ValueError( + "Too many collections {}".format(collections)) + + collection = collections[0] + padding = collection.padding + padding_str = "%0{}".format(padding) + frames = list(collection.indexes) + frame_str = "{}-{}#".format(frames[0], frames[-1]) + file_name = "{}{}{}".format(collection.head, frame_str, + collection.tail) + + files_to_convert = [file_name] + + return files_to_convert + def _get_output_file_path(self, input_filepath, output_dir, output_extension): """Create output file name path.""" From be176bbeb2feb40751be9c208fb4b0dd236f66ae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 18:54:02 +0100 Subject: [PATCH 152/202] OP-4643 - implemented display and viewer color space --- openpype/lib/transcoding.py | 23 +++++++++++++++++-- .../publish/extract_color_transcode.py | 13 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f22628dd28..cc9cd4e1eb 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,6 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, + view, + display, logger=None ): """Convert source files from one color space to another. @@ -1070,8 +1072,11 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + view (str): name for viewer space (ocio valid) + display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. - + Raises: + ValueError: if misconfigured """ if logger is None: logger = logging.getLogger(__name__) @@ -1082,9 +1087,23 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "--colorconvert", source_colorspace, target_colorspace, "-o", out_filepath ] + if all([target_colorspace, view, display]): + raise ValueError("Colorspace and both screen and display" + " cannot be set together." + "Choose colorspace or screen and display") + if not target_colorspace and not all([view, display]): + raise ValueError("Both screen and display must be set.") + + if target_colorspace: + oiio_cmd.extend(["--colorconvert", + source_colorspace, + target_colorspace]) + if view and display: + oiio_cmd.extend(["--iscolorspace", source_colorspace]) + oiio_cmd.extend(["--ociodisplay", display, view]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 09c86909cb..cd8421c0cd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -106,8 +106,15 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files target_colorspace = output_def["colorspace"] - if not target_colorspace: - raise RuntimeError("Target colorspace must be set") + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, + # but could be overwritten + if view: + new_repre["colorspaceData"]["view"] = view + if display: + new_repre["colorspaceData"]["view"] = display files_to_convert = self._translate_to_sequence( files_to_convert) @@ -123,6 +130,8 @@ class ExtractOIIOTranscode(publish.Extractor): config_path, source_colorspace, target_colorspace, + view, + display, self.log ) From 3dba4f3eb14c545038bb340614023f974c2c405a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 26 Jan 2023 19:03:10 +0100 Subject: [PATCH 153/202] OP-4643 - fix wrong order of deletion of representation --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index cd8421c0cd..9cca5cc969 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -69,6 +69,8 @@ class ExtractOIIOTranscode(publish.Extractor): if not self._repre_is_valid(repre): continue + added_representations = False + colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] config_path = colorspace_data.get("config", {}).get("path") @@ -76,8 +78,6 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - repre = self._handle_original_repre(repre, profile) - for _, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) @@ -150,6 +150,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["tags"].append(tag) instance.data["representations"].append(new_repre) + added_representations = True + + if added_representations: + self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): """Returns original list of files or single sequence format filename. @@ -253,7 +257,8 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _handle_original_repre(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile): + """If new transcoded representation created, delete old.""" delete_original = profile["delete_original"] if delete_original: @@ -264,5 +269,3 @@ class ExtractOIIOTranscode(publish.Extractor): repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") - - return repre From 7e9d707226dd1956e457e1d29a0d2df58334e26d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:26:27 +0100 Subject: [PATCH 154/202] OP-4643 - updated docstring, standardized arguments --- openpype/lib/transcoding.py | 19 +++++++---------- .../publish/extract_color_transcode.py | 21 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index cc9cd4e1eb..0f6d35affe 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1049,7 +1049,7 @@ def convert_ffprobe_fps_to_float(value): def convert_colorspace( input_path, - out_filepath, + output_path, config_path, source_colorspace, target_colorspace, @@ -1057,18 +1057,13 @@ def convert_colorspace( display, logger=None ): - """Convert source files from one color space to another. - - Filenames of input files are kept so make sure that output directory - is not the same directory as input files have. - - This way it can handle gaps and can keep input filenames without handling - frame template + """Convert source file from one color space to another. Args: - input_path (str): Paths that should be converted. It is expected that - contains single file or image sequence of samy type. - out_filepath (str): Path to directory where output will be rendered. - Must not be same as input's directory. + input_path (str): Path that should be converted. It is expected that + contains single file or image sequence of same type + (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + output_path (str): Path to output filename. config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space @@ -1087,7 +1082,7 @@ def convert_colorspace( # Don't add any additional attributes "--nosoftwareattrib", "--colorconfig", config_path, - "-o", out_filepath + "-o", output_path ] if all([target_colorspace, view, display]): diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 9cca5cc969..c4cef15ea6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -119,13 +119,13 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: - input_filepath = os.path.join(original_staging_dir, - file_name) - output_path = self._get_output_file_path(input_filepath, + input_path = os.path.join(original_staging_dir, + file_name) + output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) convert_colorspace( - input_filepath, + input_path, output_path, config_path, source_colorspace, @@ -156,16 +156,17 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _translate_to_sequence(self, files_to_convert): - """Returns original list of files or single sequence format filename. + """Returns original list or list with filename formatted in single + sequence format. Uses clique to find frame sequence, in this case it merges all frames - into sequence format (%0X) and returns it. + into sequence format (FRAMESTART-FRAMEEND#) and returns it. If sequence not found, it returns original list Args: files_to_convert (list): list of file names Returns: - (list) of [file.%04.exr] or [fileA.exr, fileB.exr] + (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] collections, remainder = clique.assemble( @@ -178,8 +179,6 @@ class ExtractOIIOTranscode(publish.Extractor): "Too many collections {}".format(collections)) collection = collections[0] - padding = collection.padding - padding_str = "%0{}".format(padding) frames = list(collection.indexes) frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, @@ -189,10 +188,10 @@ class ExtractOIIOTranscode(publish.Extractor): return files_to_convert - def _get_output_file_path(self, input_filepath, output_dir, + def _get_output_file_path(self, input_path, output_dir, output_extension): """Create output file name path.""" - file_name = os.path.basename(input_filepath) + file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: output_extension = input_extension From c50d9917a4428270db3cc0797da1c4f941d2022e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:27:06 +0100 Subject: [PATCH 155/202] OP-4643 - fix wrong assignment --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index c4cef15ea6..4e899a519c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -114,7 +114,7 @@ class ExtractOIIOTranscode(publish.Extractor): if view: new_repre["colorspaceData"]["view"] = view if display: - new_repre["colorspaceData"]["view"] = display + new_repre["colorspaceData"]["display"] = display files_to_convert = self._translate_to_sequence( files_to_convert) From f226dc60cf055d836614b540b0eca31611f568ce Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 156/202] OP-4643 - fix files to delete --- .../publish/extract_color_transcode.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4e899a519c..99e684ba21 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -84,26 +84,18 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_transcode_temp_directory() new_repre["stagingDir"] = new_staging_dir - files_to_convert = new_repre["files"] - if not isinstance(files_to_convert, list): - files_to_convert = [files_to_convert] - files_to_delete = copy.deepcopy(files_to_convert) + if isinstance(new_repre["files"], list): + files_to_convert = copy.deepcopy(new_repre["files"]) + else: + files_to_convert = [new_repre["files"]] output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') if output_extension: - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension - new_repre["ext"] = output_extension - - renamed_files = [] - _, orig_ext = os.path.splitext(files_to_convert[0]) - for file_name in files_to_convert: - file_name = file_name.replace(orig_ext, - "."+output_extension) - renamed_files.append(file_name) - new_repre["files"] = renamed_files + self._rename_in_representation(new_repre, + files_to_convert, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -135,8 +127,12 @@ class ExtractOIIOTranscode(publish.Extractor): self.log ) - instance.context.data["cleanupFullPaths"].extend( - files_to_delete) + # cleanup temporary transcoded files + for file_name in new_repre["files"]: + transcoded_file_path = os.path.join(new_staging_dir, + file_name) + instance.context.data["cleanupFullPaths"].append( + transcoded_file_path) custom_tags = output_def.get("custom_tags") if custom_tags: @@ -155,6 +151,21 @@ class ExtractOIIOTranscode(publish.Extractor): if added_representations: self._mark_original_repre_for_deletion(repre, profile) + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From 97a2014c125d7b8f766754e14ac4d74889a5f469 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:17:59 +0100 Subject: [PATCH 157/202] OP-4643 - moved output argument to the end --- openpype/lib/transcoding.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 0f6d35affe..e74dab4ccc 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1081,8 +1081,7 @@ def convert_colorspace( input_path, # Don't add any additional attributes "--nosoftwareattrib", - "--colorconfig", config_path, - "-o", output_path + "--colorconfig", config_path ] if all([target_colorspace, view, display]): @@ -1100,5 +1099,7 @@ def convert_colorspace( oiio_cmd.extend(["--iscolorspace", source_colorspace]) oiio_cmd.extend(["--ociodisplay", display, view]) + oiio_cmd.extend(["-o", output_path]) + logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) From 3d2f4319369d6459263cd79e69bec3898ee86efa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 158/202] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99e684ba21..3d897c6d9f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -142,6 +142,8 @@ class ExtractOIIOTranscode(publish.Extractor): # Add additional tags from output definition to representation for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 016111ab3381e2ba6c9b5b47fe361a25208c1a6b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:26:07 +0100 Subject: [PATCH 159/202] OP-4643 - changed docstring Elaborated more that 'target_colorspace' and ('view', 'display') are disjunctive. --- openpype/lib/transcoding.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e74dab4ccc..f7d5e222c8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1053,8 +1053,8 @@ def convert_colorspace( config_path, source_colorspace, target_colorspace, - view, - display, + view=None, + display=None, logger=None ): """Convert source file from one color space to another. @@ -1067,7 +1067,9 @@ def convert_colorspace( config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space + if filled, 'view' and 'display' must be empty view (str): name for viewer space (ocio valid) + both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) logger (logging.Logger): Logger used for logging. Raises: From e1d68ec387572f180844ca8677110cc32d8cf9df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 11:14:07 +0100 Subject: [PATCH 160/202] OP-4663 - fix double dots in extension Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3d897c6d9f..bfed69c300 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -207,7 +207,7 @@ class ExtractOIIOTranscode(publish.Extractor): file_name = os.path.basename(input_path) file_name, input_extension = os.path.splitext(file_name) if not output_extension: - output_extension = input_extension + output_extension = input_extension.replace(".", "") new_file_name = '{}.{}'.format(file_name, output_extension) return os.path.join(output_dir, new_file_name) From b5246cdf6587975cec5638666e82196e84854de3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:11:45 +0100 Subject: [PATCH 161/202] OP-4643 - update documentation in Settings schema --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 74b81b13af..3956f403f4 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -207,7 +207,7 @@ "children": [ { "type": "label", - "label": "Configure output format(s) and color spaces for matching representations. Empty 'Output extension' denotes keeping source extension." + "label": "Configure Output Definition(s) for new representation(s). \nEmpty 'Extension' denotes keeping source extension. \nName(key) of output definition will be used as new representation name \nunless 'passthrough' value is used to keep existing name. \nFill either 'Colorspace' (for target colorspace) or \nboth 'Display' and 'View' (for display and viewer colorspaces)." }, { "type": "boolean", From b4085288c34f0a035f5be5a536bb6cfdcc4f1a2c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 162/202] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bfed69c300..e39ea3add9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -32,6 +32,25 @@ class ExtractOIIOTranscode(publish.Extractor): - task types - task names - subset names + + Can produce one or more representations (with different extensions) based + on output definition in format: + "output_name: { + "extension": "png", + "colorspace": "ACES - ACEScg", + "display": "", + "view": "", + "tags": [], + "custom_tags": [] + } + + If 'extension' is empty original representation extension is used. + 'output_name' will be used as name of new representation. In case of value + 'passthrough' name of original representation will be used. + + 'colorspace' denotes target colorspace to be transcoded into. Could be + empty if transcoding should be only into display and viewer colorspace. + (In that case both 'display' and 'view' must be filled.) """ label = "Transcode color spaces" @@ -78,7 +97,7 @@ class ExtractOIIOTranscode(publish.Extractor): self.log.warning("Config file doesn't exist, skipping") continue - for _, output_def in profile.get("outputs", {}).items(): + for output_name, output_def in profile.get("outputs", {}).items(): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] @@ -92,10 +111,10 @@ class ExtractOIIOTranscode(publish.Extractor): output_extension = output_def["extension"] output_extension = output_extension.replace('.', '') - if output_extension: - self._rename_in_representation(new_repre, - files_to_convert, - output_extension) + self._rename_in_representation(new_repre, + files_to_convert, + output_name, + output_extension) target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") @@ -154,10 +173,22 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile) def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 925c7a9564fa1e2c8cc61d025de35d75af155939 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:42:48 +0100 Subject: [PATCH 163/202] OP-4643 - updated docstring for convert_colorspace --- openpype/lib/transcoding.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index f7d5e222c8..b6edd863f8 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1062,8 +1062,11 @@ def convert_colorspace( Args: input_path (str): Path that should be converted. It is expected that contains single file or image sequence of same type - (sequence in format 'file.FRAMESTART-FRAMEEND#.exr', see oiio docs) + (sequence in format 'file.FRAMESTART-FRAMEEND#.ext', see oiio docs, + eg `big.1-3#.tif`) output_path (str): Path to output filename. + (must follow format of 'input_path', eg. single file or + sequence in 'file.FRAMESTART-FRAMEEND#.ext', `output.1-3#.tif`) config_path (str): path to OCIO config file source_colorspace (str): ocio valid color space of source files target_colorspace (str): ocio valid target color space From d96775867a333566ff0681dbdb86688c3bda7f3d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:22:10 +0100 Subject: [PATCH 164/202] OP-4643 - remove review from old representation If new representation gets created and adds 'review' tag it becomes new reviewable representation. --- .../publish/extract_color_transcode.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index e39ea3add9..d10b887a0b 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -89,6 +89,7 @@ class ExtractOIIOTranscode(publish.Extractor): continue added_representations = False + added_review = False colorspace_data = repre["colorspaceData"] source_colorspace = colorspace_data["colorspace"] @@ -166,11 +167,15 @@ class ExtractOIIOTranscode(publish.Extractor): if tag not in new_repre["tags"]: new_repre["tags"].append(tag) + if tag == "review": + added_review = True + instance.data["representations"].append(new_repre) added_representations = True if added_representations: - self._mark_original_repre_for_deletion(repre, profile) + self._mark_original_repre_for_deletion(repre, profile, + added_review) def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): @@ -300,15 +305,16 @@ class ExtractOIIOTranscode(publish.Extractor): return True - def _mark_original_repre_for_deletion(self, repre, profile): + def _mark_original_repre_for_deletion(self, repre, profile, added_review): """If new transcoded representation created, delete old.""" + if not repre.get("tags"): + repre["tags"] = [] + delete_original = profile["delete_original"] if delete_original: - if not repre.get("tags"): - repre["tags"] = [] - - if "review" in repre["tags"]: - repre["tags"].remove("review") if "delete" not in repre["tags"]: repre["tags"].append("delete") + + if added_review and "review" in repre["tags"]: + repre["tags"].remove("review") From 7540f61791958b6043ad1a466900a41fc8b27e4c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Feb 2023 18:23:42 +0100 Subject: [PATCH 165/202] OP-4643 - remove representation that should be deleted Or old revieable representation would be reviewed too. --- openpype/plugins/publish/extract_color_transcode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index d10b887a0b..93ee1ec44d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -177,6 +177,11 @@ class ExtractOIIOTranscode(publish.Extractor): self._mark_original_repre_for_deletion(repre, profile, added_review) + for repre in tuple(instance.data["representations"]): + tags = repre.get("tags") or [] + if "delete" in tags and "thumbnail" not in tags: + instance.data["representations"].remove(repre) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 82b44da739625242d4e2a0ffddec317cad25e806 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 14:54:25 +0100 Subject: [PATCH 166/202] OP-4643 - fix logging Wrong variable used --- openpype/plugins/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index dcb43d7fa2..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -169,7 +169,7 @@ class ExtractReview(pyblish.api.InstancePlugin): "Skipped representation. All output definitions from" " selected profile does not match to representation's" " custom tags. \"{}\"" - ).format(str(tags))) + ).format(str(custom_tags))) continue outputs_per_representations.append((repre, outputs)) From 1d12316ee18889a2b18e02db06edf082e6a61d70 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 167/202] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 93ee1ec44d..4a03e623fd 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,15 +161,17 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation + if not new_repre.get("tags"): + new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) if tag == "review": added_review = True + new_repre["tags"].append("newly_added") + instance.data["representations"].append(new_repre) added_representations = True @@ -179,6 +181,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From b5c3e0931e0dc1d13d09cd2b8579e1fc86b0169e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:04:59 +0100 Subject: [PATCH 168/202] OP-4642 - added additional command arguments to Settings --- .../schemas/schema_global_publish.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3956f403f4..5333d514b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -286,6 +286,20 @@ "label": "View", "type": "text" }, + { + "key": "oiiotool_args", + "label": "OIIOtool arguments", + "type": "dict", + "highlight_content": true, + "children": [ + { + "key": "additional_command_args", + "label": "Additional command line arguments", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "schema", "name": "schema_representation_tags" From 3921982365792bb92912777c0cc130462c0e6e14 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:08:06 +0100 Subject: [PATCH 169/202] OP-4642 - added additional command arguments for oiiotool Some extension requires special command line arguments (.dpx and binary depth). --- openpype/lib/transcoding.py | 6 ++++++ openpype/plugins/publish/extract_color_transcode.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index b6edd863f8..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1055,6 +1055,7 @@ def convert_colorspace( target_colorspace, view=None, display=None, + additional_command_args=None, logger=None ): """Convert source file from one color space to another. @@ -1074,6 +1075,8 @@ def convert_colorspace( view (str): name for viewer space (ocio valid) both 'view' and 'display' must be filled (if 'target_colorspace') display (str): name for display-referred reference space (ocio valid) + additional_command_args (list): arguments for oiiotool (like binary + depth for .dpx) logger (logging.Logger): Logger used for logging. Raises: ValueError: if misconfigured @@ -1096,6 +1099,9 @@ def convert_colorspace( if not target_colorspace and not all([view, display]): raise ValueError("Both screen and display must be set.") + if additional_command_args: + oiio_cmd.extend(additional_command_args) + if target_colorspace: oiio_cmd.extend(["--colorconvert", source_colorspace, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4a03e623fd..3de404125d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -128,6 +128,9 @@ class ExtractOIIOTranscode(publish.Extractor): if display: new_repre["colorspaceData"]["display"] = display + additional_command_args = (output_def["oiiotool_args"] + ["additional_command_args"]) + files_to_convert = self._translate_to_sequence( files_to_convert) for file_name in files_to_convert: @@ -144,6 +147,7 @@ class ExtractOIIOTranscode(publish.Extractor): target_colorspace, view, display, + additional_command_args, self.log ) From 0834b7564b842832cba47a80a2b8c933d8bce918 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 170/202] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 3de404125d..8c4ef59de9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -82,6 +82,7 @@ class ExtractOIIOTranscode(publish.Extractor): if not profile: return + new_representations = [] repres = instance.data.get("representations") or [] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) @@ -174,9 +175,7 @@ class ExtractOIIOTranscode(publish.Extractor): if tag == "review": added_review = True - new_repre["tags"].append("newly_added") - - instance.data["representations"].append(new_repre) + new_representations.append(new_repre) added_representations = True if added_representations: @@ -185,15 +184,11 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) + instance.data["representations"].extend(new_representations) + def _rename_in_representation(self, new_repre, files_to_convert, output_name, output_extension): """Replace old extension with new one everywhere in representation. From 263d3dccc2bb2f2ddc3d18d725a9d16101404cbb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:24:53 +0100 Subject: [PATCH 171/202] OP-4642 - refactored query of representations line 73 returns if no representations. --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 8c4ef59de9..de36ea7d5f 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -83,7 +83,7 @@ class ExtractOIIOTranscode(publish.Extractor): return new_representations = [] - repres = instance.data.get("representations") or [] + repres = instance.data["representations"] for idx, repre in enumerate(list(repres)): self.log.debug("repre ({}): `{}`".format(idx + 1, repre["name"])) if not self._repre_is_valid(repre): From cf066d1441d5d356e4be59591aa8fadfc24bcd0b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 10:44:10 +0100 Subject: [PATCH 172/202] OP-4643 - fixed subset filtering Co-authored-by: Toke Jepsen --- openpype/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index de36ea7d5f..71124b527a 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -273,7 +273,7 @@ class ExtractOIIOTranscode(publish.Extractor): "families": family, "task_names": task_name, "task_types": task_type, - "subset": subset + "subsets": subset } profile = filter_profiles(self.profiles, filtering_criteria, logger=self.log) From 984974d7e01a48ebcd704e167c1e3fef42418d82 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 173/202] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/lib/transcoding.py | 29 +++++++++++++++++++++- openpype/plugins/publish/extract_review.py | 27 +++----------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..4d2f72fc41 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,30 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 51c54e1aa1bf602fe4cc2ba0ff1110dcfe5f5539 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:02:41 +0100 Subject: [PATCH 174/202] OP-4643 - refactor - changed existence check --- openpype/plugins/publish/extract_color_transcode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 71124b527a..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -161,12 +161,12 @@ class ExtractOIIOTranscode(publish.Extractor): custom_tags = output_def.get("custom_tags") if custom_tags: - if not new_repre.get("custom_tags"): + if new_repre.get("custom_tags") is None: new_repre["custom_tags"] = [] new_repre["custom_tags"].extend(custom_tags) # Add additional tags from output definition to representation - if not new_repre.get("tags"): + if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: if tag not in new_repre["tags"]: From cb551fe83acde2ea43e61706273bf33fec1c37d3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 175/202] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From e2ebbae14772b4f24b169c104cdabfcabfab6b66 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:10:11 +0100 Subject: [PATCH 176/202] OP-4643 - changed label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 5333d514b5..3e9467af61 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -294,7 +294,7 @@ "children": [ { "key": "additional_command_args", - "label": "Additional command line arguments", + "label": "Arguments", "type": "list", "object_type": "text" } From 4e755e193a6c1e6f2d074d98d2684b3e18cf5282 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 177/202] OP-4643 - added documentation --- .../assets/global_oiio_transcode.png | Bin 0 -> 29010 bytes .../project_settings/settings_project_global.md | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png new file mode 100644 index 0000000000000000000000000000000000000000..99396d5bb3f16d434a92c6079515128488b82489 GIT binary patch literal 29010 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 literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 37fed93e69..52671d2db6 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -45,6 +45,21 @@ The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](http The **colorspace name** value is a raw string input and no validation is run after saving project settings. We recommend to open the specified `config.ocio` file and copy pasting the exact colorspace names. ::: +### Extract OIIO Transcode +There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +`oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. + +Notable parameters: +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Extension`** - target extension, could be empty - original extension is used +- **`Colorspace`** - target colorspace - must be available in used color config +- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Arguments`** - special additional command line arguments for `oiiotool` + + +Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. +![global_oiio_transcode](assets/global_oiio_transcode.png) ## Profile filters From 54e92f02b9f1a31fd9b05005864b6f1e8e5b99ff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:41:42 +0100 Subject: [PATCH 178/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 52671d2db6..cc661a21fa 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,7 +46,7 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable (see lower) plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. +There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. From 665132d9feb19a6d566e2541ccb1207bef1dcc53 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:06 +0100 Subject: [PATCH 179/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index cc661a21fa..8e557a381c 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -52,7 +52,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. -- **`Extension`** - target extension, could be empty - original extension is used +- **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace - must be available in used color config - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From ceca9a5bb89c8da7915d7c4772cbd23448b49714 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:29 +0100 Subject: [PATCH 180/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 8e557a381c..166400cb7f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,7 +53,7 @@ Plugin expects instances with filled dictionary `colorspaceData` on a representa Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace - must be available in used color config +- **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) - **`Arguments`** - special additional command line arguments for `oiiotool` From 0a4cae9db2eaf3beea496e57065240a7b4eec1c1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:42:48 +0100 Subject: [PATCH 181/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 166400cb7f..908191f122 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -54,7 +54,7 @@ Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace OR into display and viewer space could be used. (It is disjunctive: Colorspace & nothing in Display and View or opposite) +- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. - **`Arguments`** - special additional command line arguments for `oiiotool` From 3a0e9dc78ce48bf9d964eed90b5afea24853e8a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 16:43:06 +0100 Subject: [PATCH 182/202] OP-4643 - updates to documentation Co-authored-by: Toke Jepsen --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 908191f122..0a73868d2d 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -55,7 +55,7 @@ Notable parameters: - **`Extension`** - target extension. If left empty, original extension is used. - **`Colorspace`** - target colorspace, which must be available in used color config. - **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. -- **`Arguments`** - special additional command line arguments for `oiiotool` +- **`Arguments`** - special additional command line arguments for `oiiotool`. Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. From 7f8a766c6a294991c379a356e63ab3b2fd9e53a7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:21:20 +0100 Subject: [PATCH 183/202] OP-4643 - updates to documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- website/docs/project_settings/settings_project_global.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 0a73868d2d..9e2ee187cc 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -46,8 +46,8 @@ The **colorspace name** value is a raw string input and no validation is run aft ::: ### Extract OIIO Transcode -There is profile configurable plugin which allows to transcode any incoming representation to one or multiple new representations (configured in `Output Definitions`) with different target colorspaces. -Plugin expects instances with filled dictionary `colorspaceData` on a representation. This data contains information about source colorspace and must be collected for transcoding. +OIIOTools transcoder plugin with configurable output presets. Any incoming representation with `colorspaceData` is convertable to single or multiple representations with different target colorspaces or display and viewer names found in linked **config.ocio** file. + `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: From 559d54c3a1b061f3234b0491f6abbb8b22aee6c8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 184/202] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 29 +--------------------- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++--- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 4d2f72fc41..982cee7a46 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,30 +1114,3 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) - - -def split_cmd_args(in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - Args: - in_args (list): of arguments ['-n', '-d uint10'] - Returns - (list): ['-n', '-d', 'unint10'] - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 5678bdbf065922d1e065782ce419149bdd29cbae Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:02:17 +0100 Subject: [PATCH 185/202] OP-4643 - different splitting for oiio It seems that logic in ExtractReview does different thing. --- openpype/lib/transcoding.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 982cee7a46..376297ff32 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(additional_command_args) + oiio_cmd.extend(split_cmd_args(additional_command_args)) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1114,3 +1114,21 @@ def convert_colorspace( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +def split_cmd_args(in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + Args: + in_args (list): of arguments ['-n', '-d uint10'] + Returns + (list): ['-n', '-d', 'unint10'] + """ + splitted_args = [] + for arg in in_args: + if not arg.strip(): + continue + splitted_args.extend(arg.split(" ")) + return splitted_args From f30b3c52307e4db5e6715a56ba9532c89dcfceb8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 18:14:57 +0100 Subject: [PATCH 186/202] OP-4643 - allow colorspace to be empty and collected from DCC --- openpype/plugins/publish/extract_color_transcode.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..82b92ec93e 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,7 +118,8 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 7ecf6fde48aebc705f13ac965e2a9819239b2c87 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 12:22:53 +0100 Subject: [PATCH 187/202] OP-4643 - fix colorspace from DCC representation["colorspaceData"]["colorspace"] is only input colorspace --- openpype/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 82b92ec93e..456e40008d 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,8 +118,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = (output_def["colorspace"] or - colorspace_data.get("colorspace")) + target_colorspace = output_def["colorspace"] view = output_def["view"] or colorspace_data.get("view") display = (output_def["display"] or colorspace_data.get("display")) From 945f1dfe55ad1f150cf0f71baacaea80857a0e4e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:33:20 +0100 Subject: [PATCH 188/202] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- openpype/lib/transcoding.py | 2 +- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++---- .../schemas/schema_global_publish.json | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 376297ff32..c0bda2aa37 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1052,7 +1052,7 @@ def convert_colorspace( output_path, config_path, source_colorspace, - target_colorspace, + target_colorspace=None, view=None, display=None, additional_command_args=None, diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 456e40008d..b0921688e9 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -118,10 +118,17 @@ class ExtractOIIOTranscode(publish.Extractor): output_name, output_extension) - target_colorspace = output_def["colorspace"] - view = output_def["view"] or colorspace_data.get("view") - display = (output_def["display"] or - colorspace_data.get("display")) + transcoding_type = output_def["transcoding_type"] + + target_colorspace = view = display = None + if transcoding_type == "colorspace": + target_colorspace = (output_def["colorspace"] or + colorspace_data.get("colorspace")) + else: + view = output_def["view"] or colorspace_data.get("view") + display = (output_def["display"] or + colorspace_data.get("display")) + # both could be already collected by DCC, # but could be overwritten if view: diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3e9467af61..76574e8b9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -271,6 +271,15 @@ "label": "Extension", "type": "text" }, + { + "type": "enum", + "key": "transcoding_type", + "label": "Transcoding type", + "enum_items": [ + { "colorspace": "Use Colorspace" }, + { "display": "Use Display&View" } + ] + }, { "key": "colorspace", "label": "Colorspace", From 25fb38bd4c210770c711d725ed1b88f0c1b8731b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:34:03 +0100 Subject: [PATCH 189/202] OP-4643 - added explicit enum for transcoding type As transcoding info (colorspace, display) might be collected from DCC, it must be explicit which should be used. --- .../assets/global_oiio_transcode.png | Bin 29010 -> 17936 bytes .../settings_project_global.md | 5 +++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/website/docs/project_settings/assets/global_oiio_transcode.png b/website/docs/project_settings/assets/global_oiio_transcode.png index 99396d5bb3f16d434a92c6079515128488b82489..d818ecfe19f93366e5f4664734d68c7b448d7b4f 100644 GIT binary patch literal 17936 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 DbV~re literal 29010 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 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9e2ee187cc..6e78ee5d45 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -53,8 +53,9 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre Notable parameters: - **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. -- **`Colorspace`** - target colorspace, which must be available in used color config. -- **`Display & View`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both `Colorspace` and `Display & View` at the same time. +- **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. +- **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). +- **`Display & View`** - display and viewer colorspace. (If `Transcoding type` is `Use Display&View` values in configuration is used OR if empty values collected on instance from DCC). - **`Arguments`** - special additional command line arguments for `oiiotool`. From b8f8fd9a5a5342376b28b18f4a9d394ddf3d6b42 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Feb 2023 17:42:07 +0100 Subject: [PATCH 190/202] OP-4643 - added use case for Maya to documentation --- .../assets/global_oiio_transcode2.png | Bin 0 -> 17960 bytes .../project_settings/settings_project_global.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 website/docs/project_settings/assets/global_oiio_transcode2.png diff --git a/website/docs/project_settings/assets/global_oiio_transcode2.png b/website/docs/project_settings/assets/global_oiio_transcode2.png new file mode 100644 index 0000000000000000000000000000000000000000..906f780830a96b4bc6f26d98484dd3f9cc3885f2 GIT binary patch literal 17960 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 F{{cSxQDp!C literal 0 HcmV?d00001 diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 6e78ee5d45..f58d2c2bf2 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -62,6 +62,9 @@ Notable parameters: Example here describes use case for creation of new color coded review of png image sequence. Original representation's files are kept intact, review is created from transcoded files, but these files are removed in cleanup process. ![global_oiio_transcode](assets/global_oiio_transcode.png) +Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) + ## Profile filters Many of the settings are using a concept of **Profile filters** From 539ba60eb4c21e310e716a806683acbc7a0284a5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Feb 2023 11:05:26 +0100 Subject: [PATCH 191/202] OP-4643 - updates to documentation Co-authored-by: Roy Nieterau --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index f58d2c2bf2..d904080ad1 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -51,7 +51,7 @@ OIIOTools transcoder plugin with configurable output presets. Any incoming repre `oiiotool` is used for transcoding, eg. `oiiotool` must be present in `vendor/bin/oiio` or environment variable `OPENPYPE_OIIO_PATHS` must be provided for custom oiio installation. Notable parameters: -- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation looses its 'review' tag if present. +- **`Delete Original Representation`** - keep or remove original representation. If old representation is kept, but there is new transcoded representation with 'Create review' tag, original representation loses its 'review' tag if present. - **`Extension`** - target extension. If left empty, original extension is used. - **`Transcoding type`** - transcoding into colorspace or into display and viewer space could be used. Cannot use both at the same time. - **`Colorspace`** - target colorspace, which must be available in used color config. (If `Transcoding type` is `Use Colorspace` value in configuration is used OR if empty value collected on instance from DCC). From 64a142ef6482b61eb0967806882ccb6c70ecd91c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 6 Jan 2023 14:03:22 +0100 Subject: [PATCH 192/202] OP-4643 - fix for full file paths --- openpype/plugins/publish/extract_color_transcode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index b0921688e9..99c8c87e51 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -1,7 +1,6 @@ import os import copy import clique - import pyblish.api from openpype.pipeline import publish From d2f8407111905b621e29b27202ef3e59afa63983 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 11:59:13 +0100 Subject: [PATCH 193/202] OP-4643 - fix files to delete --- .../plugins/publish/extract_color_transcode.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 99c8c87e51..61e29697d6 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -222,6 +222,21 @@ class ExtractOIIOTranscode(publish.Extractor): renamed_files.append(file_name) new_repre["files"] = renamed_files + def _rename_in_representation(self, new_repre, files_to_convert, + output_extension): + """Replace old extension with new one everywhere in representation.""" + if new_repre["name"] == new_repre["ext"]: + new_repre["name"] = output_extension + new_repre["ext"] = output_extension + + renamed_files = [] + for file_name in files_to_convert: + file_name, _ = os.path.splitext(file_name) + file_name = '{}.{}'.format(file_name, + output_extension) + renamed_files.append(file_name) + new_repre["files"] = renamed_files + def _translate_to_sequence(self, files_to_convert): """Returns original list or list with filename formatted in single sequence format. From c038fbf884f872ec717588290b9ba9273f219d36 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 27 Jan 2023 13:18:33 +0100 Subject: [PATCH 194/202] OP-4643 - fix no tags in repre --- openpype/plugins/publish/extract_color_transcode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 61e29697d6..aca4adc40c 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -175,6 +175,8 @@ class ExtractOIIOTranscode(publish.Extractor): if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: + if not new_repre.get("tags"): + new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) From 195e9b436047a797bd9c99ac37ba5080690e3943 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Feb 2023 16:13:59 +0100 Subject: [PATCH 195/202] OP-4643 - name of new representation from output definition key --- .../publish/extract_color_transcode.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index aca4adc40c..bd81dd6087 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -225,10 +225,22 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre["files"] = renamed_files def _rename_in_representation(self, new_repre, files_to_convert, - output_extension): - """Replace old extension with new one everywhere in representation.""" - if new_repre["name"] == new_repre["ext"]: - new_repre["name"] = output_extension + output_name, output_extension): + """Replace old extension with new one everywhere in representation. + + Args: + new_repre (dict) + files_to_convert (list): of filenames from repre["files"], + standardized to always list + output_name (str): key of output definition from Settings, + if "" token used, keep original repre name + output_extension (str): extension from output definition + """ + if output_name != "passthrough": + new_repre["name"] = output_name + if not output_extension: + return + new_repre["ext"] = output_extension renamed_files = [] From 4a70ec9c54fbf7caec8ac8bd1f17adecbd9dd6b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Feb 2023 12:07:00 +0100 Subject: [PATCH 196/202] Fix - added missed scopes for Slack bot --- openpype/modules/slack/manifest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 7a65cc5915..233c39fbaf 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,6 +19,8 @@ oauth_config: - chat:write.public - files:write - channels:read + - users:read + - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 7f94f7ef7183a51bdfa1bd40078a9183ee50e1bd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Feb 2023 15:14:14 +0100 Subject: [PATCH 197/202] OP-4643 - allow new repre to stay One might want to delete outputs with 'delete' tag, but repre must stay there at least until extract_review. More universal new tag might be created for this. --- openpype/plugins/publish/extract_color_transcode.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index bd81dd6087..4892a00fbe 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -175,8 +175,6 @@ class ExtractOIIOTranscode(publish.Extractor): if new_repre.get("tags") is None: new_repre["tags"] = [] for tag in output_def["tags"]: - if not new_repre.get("tags"): - new_repre["tags"] = [] if tag not in new_repre["tags"]: new_repre["tags"].append(tag) @@ -192,6 +190,12 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] + # TODO implement better way, for now do not delete new repre + # new repre might have 'delete' tag to removed, but it first must + # be there for review to be created + if "newly_added" in tags: + tags.remove("newly_added") + continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From cce048fd3e0f50441fa9f893dfb9efebe43d95a8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 15 Feb 2023 16:21:25 +0100 Subject: [PATCH 198/202] OP-4642 - refactored newly added representations --- openpype/plugins/publish/extract_color_transcode.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/plugins/publish/extract_color_transcode.py b/openpype/plugins/publish/extract_color_transcode.py index 4892a00fbe..a6fa710425 100644 --- a/openpype/plugins/publish/extract_color_transcode.py +++ b/openpype/plugins/publish/extract_color_transcode.py @@ -190,12 +190,6 @@ class ExtractOIIOTranscode(publish.Extractor): for repre in tuple(instance.data["representations"]): tags = repre.get("tags") or [] - # TODO implement better way, for now do not delete new repre - # new repre might have 'delete' tag to removed, but it first must - # be there for review to be created - if "newly_added" in tags: - tags.remove("newly_added") - continue if "delete" in tags and "thumbnail" not in tags: instance.data["representations"].remove(repre) From 9eaa0d1ff8e0882a2778fce44f09fba3f2cccecd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 12:12:35 +0100 Subject: [PATCH 199/202] OP-4643 - split command line arguments to separate items Reuse existing method from ExtractReview, put it into transcoding.py --- openpype/plugins/publish/extract_review.py | 27 +++------------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0f6dacba18..e80141fc4a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,6 +22,7 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, + split_cmd_args ) @@ -670,7 +671,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) + ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -723,28 +724,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) - def split_ffmpeg_args(self, in_args): - """Makes sure all entered arguments are separated in individual items. - - Split each argument string with " -" to identify if string contains - one or more arguments. - """ - splitted_args = [] - for arg in in_args: - sub_args = arg.split(" -") - if len(sub_args) == 1: - if arg and arg not in splitted_args: - splitted_args.append(arg) - continue - - for idx, arg in enumerate(sub_args): - if idx != 0: - arg = "-" + arg - - if arg and arg not in splitted_args: - splitted_args.append(arg) - return splitted_args - def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -764,7 +743,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = self.split_ffmpeg_args(output_args) + output_args = split_cmd_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"] From 751586fd415ea6aa10327cca8fb57f2f1657b9d7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 13:11:11 +0100 Subject: [PATCH 200/202] Revert "Fix - added missed scopes for Slack bot" This reverts commit 5e0c4a3ab1432e120b8f0c324f899070f1a5f831. --- openpype/modules/slack/manifest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 233c39fbaf..7a65cc5915 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -19,8 +19,6 @@ oauth_config: - chat:write.public - files:write - channels:read - - users:read - - usergroups:read settings: org_deploy_enabled: false socket_mode_enabled: false From 84ccfa60f43c1fa5a575b62c79779d66dfc2951d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 15:06:16 +0100 Subject: [PATCH 201/202] OP-4643 - added documentation --- website/docs/project_settings/settings_project_global.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index d904080ad1..b320b5502f 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -63,7 +63,7 @@ Example here describes use case for creation of new color coded review of png im ![global_oiio_transcode](assets/global_oiio_transcode.png) Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC. -![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png) +![global_oiio_transcode_in_Maya](assets/global_oiio_transcode.png)n ## Profile filters From 72a3572d9527093290ebd600b538e2375e690861 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Feb 2023 17:56:21 +0100 Subject: [PATCH 202/202] Revert "OP-4643 - split command line arguments to separate items" This reverts commit deaad39437501f18fc3ba4be8b1fc5f0ee3be65d. --- openpype/lib/transcoding.py | 3 ++- openpype/plugins/publish/extract_review.py | 27 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index c0bda2aa37..e5e21195e5 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1100,7 +1100,7 @@ def convert_colorspace( raise ValueError("Both screen and display must be set.") if additional_command_args: - oiio_cmd.extend(split_cmd_args(additional_command_args)) + oiio_cmd.extend(additional_command_args) if target_colorspace: oiio_cmd.extend(["--colorconvert", @@ -1132,3 +1132,4 @@ def split_cmd_args(in_args): continue splitted_args.extend(arg.split(" ")) return splitted_args + diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index e80141fc4a..0f6dacba18 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -22,7 +22,6 @@ from openpype.lib.transcoding import ( should_convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_transcode_temp_directory, - split_cmd_args ) @@ -671,7 +670,7 @@ class ExtractReview(pyblish.api.InstancePlugin): res_filters = self.rescaling_filters(temp_data, output_def, new_repre) ffmpeg_video_filters.extend(res_filters) - ffmpeg_input_args = split_cmd_args(ffmpeg_input_args) + ffmpeg_input_args = self.split_ffmpeg_args(ffmpeg_input_args) lut_filters = self.lut_filters(new_repre, instance, ffmpeg_input_args) ffmpeg_video_filters.extend(lut_filters) @@ -724,6 +723,28 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args ) + def split_ffmpeg_args(self, in_args): + """Makes sure all entered arguments are separated in individual items. + + Split each argument string with " -" to identify if string contains + one or more arguments. + """ + splitted_args = [] + for arg in in_args: + sub_args = arg.split(" -") + if len(sub_args) == 1: + if arg and arg not in splitted_args: + splitted_args.append(arg) + continue + + for idx, arg in enumerate(sub_args): + if idx != 0: + arg = "-" + arg + + if arg and arg not in splitted_args: + splitted_args.append(arg) + return splitted_args + def ffmpeg_full_args( self, input_args, video_filters, audio_filters, output_args ): @@ -743,7 +764,7 @@ class ExtractReview(pyblish.api.InstancePlugin): Returns: list: Containing all arguments ready to run in subprocess. """ - output_args = split_cmd_args(output_args) + output_args = self.split_ffmpeg_args(output_args) video_args_dentifiers = ["-vf", "-filter:v"] audio_args_dentifiers = ["-af", "-filter:a"]