diff --git a/common/openpype_common/distribution/file_handler.py b/common/openpype_common/distribution/file_handler.py index f585c776325..e649f143e9d 100644 --- a/common/openpype_common/distribution/file_handler.py +++ b/common/openpype_common/distribution/file_handler.py @@ -1,4 +1,3 @@ -import enlighten import os import re import urllib @@ -252,6 +251,11 @@ def _get_confirm_token(response): if key.startswith('download_warning'): return value + # handle antivirus warning for big zips + found = re.search("(confirm=)([^&.+])", response.text) + if found: + return found.groups()[1] + return None @staticmethod @@ -259,15 +263,9 @@ def _save_response_content( response_gen, destination, ): with open(destination, "wb") as f: - pbar = enlighten.Counter( - total=None, desc="Save content", units="%", color="green") - progress = 0 for chunk in response_gen: if chunk: # filter out keep-alive new chunks f.write(chunk) - progress += len(chunk) - - pbar.close() @staticmethod def _quota_exceeded(first_chunk): diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 282c714c4cd..0325838e781 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -31,7 +31,6 @@ from openpype.settings import ( get_project_settings, - get_anatomy_settings, get_current_project_settings, ) from openpype.modules import ModulesManager @@ -45,6 +44,9 @@ get_current_project_asset, get_custom_workfile_template_from_session ) +from openpype.pipeline.colorspace import ( + get_imageio_config +) from openpype.pipeline.workfile import BuildWorkfile from . import gizmo_menu @@ -690,14 +692,6 @@ def get_node_path(path, padding=4): def get_nuke_imageio_settings(): - project_imageio = get_project_settings( - Context.project_name)["nuke"]["imageio"] - - # backward compatibility for project started before 3.10 - # those are still having `__legacy__` knob types. - if not project_imageio["enabled"]: - return get_anatomy_settings(Context.project_name)["imageio"]["nuke"] - return get_project_settings(Context.project_name)["nuke"]["imageio"] @@ -2002,59 +1996,55 @@ def set_viewers_colorspace(self, viewer_dict): "Attention! Viewer nodes {} were erased." "It had wrong color profile".format(erased_viewers)) - def set_root_colorspace(self, root_dict): + def set_root_colorspace(self, nuke_colorspace): ''' Adds correct colorspace to root Arguments: - root_dict (dict): adjustmensts from presets + nuke_colorspace (dict): adjustmensts from presets ''' - if not isinstance(root_dict, dict): - msg = "set_root_colorspace(): argument should be dictionary" - log.error(msg) - nuke.message(msg) - - log.debug(">> root_dict: {}".format(root_dict)) + workfile_settings = nuke_colorspace["workfile"] + + # resolve config data if they are enabled in host + config_data = None + if nuke_colorspace.get("ocio_config", {}).get("enabled"): + # switch ocio config to custom config + workfile_settings["OCIO_config"] = "custom" + workfile_settings["colorManagement"] = "OCIO" + + # get resolved ocio config path + config_data = get_imageio_config( + legacy_io.active_project(), "nuke" + ) # first set OCIO if self._root_node["colorManagement"].value() \ - not in str(root_dict["colorManagement"]): + not in str(workfile_settings["colorManagement"]): self._root_node["colorManagement"].setValue( - str(root_dict["colorManagement"])) - log.debug("nuke.root()['{0}'] changed to: {1}".format( - "colorManagement", root_dict["colorManagement"])) - root_dict.pop("colorManagement") + str(workfile_settings["colorManagement"])) + + # we dont need the key anymore + workfile_settings.pop("colorManagement") # second set ocio version if self._root_node["OCIO_config"].value() \ - not in str(root_dict["OCIO_config"]): + not in str(workfile_settings["OCIO_config"]): self._root_node["OCIO_config"].setValue( - str(root_dict["OCIO_config"])) - log.debug("nuke.root()['{0}'] changed to: {1}".format( - "OCIO_config", root_dict["OCIO_config"])) - root_dict.pop("OCIO_config") + str(workfile_settings["OCIO_config"])) - # third set ocio custom path - if root_dict.get("customOCIOConfigPath"): - unresolved_path = root_dict["customOCIOConfigPath"] - ocio_paths = unresolved_path[platform.system().lower()] - - resolved_path = None - for ocio_p in ocio_paths: - resolved_path = str(ocio_p).format(**os.environ) - if not os.path.exists(resolved_path): - continue + # we dont need the key anymore + workfile_settings.pop("OCIO_config") - if resolved_path: - self._root_node["customOCIOConfigPath"].setValue( - str(resolved_path).replace("\\", "/") - ) - log.debug("nuke.root()['{}'] changed to: {}".format( - "customOCIOConfigPath", resolved_path)) - root_dict.pop("customOCIOConfigPath") + # third set ocio custom path + if config_data: + self._root_node["customOCIOConfigPath"].setValue( + str(config_data["path"]).replace("\\", "/") + ) + # backward compatibility, remove in case it exists + workfile_settings.pop("customOCIOConfigPath") # then set the rest - for knob, value in root_dict.items(): + for knob, value in workfile_settings.items(): # skip unfilled ocio config path # it will be dict in value if isinstance(value, dict): @@ -2184,7 +2174,7 @@ def set_colorspace(self): log.info("Setting colorspace to workfile...") try: - self.set_root_colorspace(nuke_colorspace["workfile"]) + self.set_root_colorspace(nuke_colorspace) except AttributeError: msg = "set_colorspace(): missing `workfile` settings in template" nuke.message(msg) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index 81fe8cd528a..2433364f85d 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -7,8 +7,7 @@ from openpype.pipeline import publish -class NukeRenderLocal(publish.Extractor): - # TODO: rewrite docstring to nuke +class NukeRenderLocal(publish.ExtractorColormanaged): """Render the current Nuke composition locally. Extract the result of savers by starting a comp render @@ -69,6 +68,7 @@ def process(self, instance): ) ext = node["file_type"].value() + colorspace = node["colorspace"].value() if "representations" not in instance.data: instance.data["representations"] = [] @@ -92,6 +92,13 @@ def process(self, instance): 'files': filenames, "stagingDir": out_dir } + + # inject colorspace data + self.set_representation_colorspace( + repre, instance.context, + colorspace=colorspace + ) + instance.data["representations"].append(repre) self.log.info("Extracted instance '{0}' to: {1}".format( diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py new file mode 100644 index 00000000000..e7480cf59ec --- /dev/null +++ b/openpype/pipeline/colorspace.py @@ -0,0 +1,468 @@ +from copy import deepcopy +import re +import os +import sys +import json +import platform +import contextlib +import tempfile +from openpype import PACKAGE_DIR +from openpype.settings import get_project_settings +from openpype.lib import ( + StringTemplate, + run_openpype_process, + Logger +) +from openpype.pipeline import Anatomy + +log = Logger.get_logger(__name__) + + +@contextlib.contextmanager +def _make_temp_json_file(): + """Wrapping function for json temp file + """ + try: + # Store dumped json to temporary file + temporary_json_file = tempfile.NamedTemporaryFile( + mode="w", suffix=".json", delete=False + ) + temporary_json_file.close() + temporary_json_filepath = temporary_json_file.name.replace( + "\\", "/" + ) + + yield temporary_json_filepath + + except IOError as _error: + raise IOError( + "Unable to create temp json file: {}".format( + _error + ) + ) + + finally: + # Remove the temporary json + os.remove(temporary_json_filepath) + + +def get_ocio_config_script_path(): + """Get path to ocio wrapper script + + Returns: + str: path string + """ + return os.path.normpath( + os.path.join( + PACKAGE_DIR, + "scripts", + "ocio_wrapper.py" + ) + ) + + +def get_imageio_colorspace_from_filepath( + path, host_name, project_name, + config_data=None, file_rules=None, + project_settings=None, + validate=True +): + """Get colorspace name from filepath + + ImageIO Settings file rules are tested for matching rule. + + Args: + path (str): path string, file rule pattern is tested on it + host_name (str): host name + project_name (str): project name + config_data (dict, optional): config path and template in dict. + Defaults to None. + file_rules (dict, optional): file rule data from settings. + Defaults to None. + project_settings (dict, optional): project settings. Defaults to None. + validate (bool, optional): should resulting colorspace be validated + with config file? Defaults to True. + + Returns: + str: name of colorspace + """ + if not any([config_data, file_rules]): + project_settings = project_settings or get_project_settings( + project_name + ) + config_data = get_imageio_config( + project_name, host_name, project_settings) + file_rules = get_imageio_file_rules( + project_name, host_name, project_settings) + + # match file rule from path + colorspace_name = None + for _frule_name, file_rule in file_rules.items(): + pattern = file_rule["pattern"] + extension = file_rule["ext"] + ext_match = re.match( + r".*(?=.{})".format(extension), path + ) + file_match = re.search( + pattern, path + ) + + if ext_match and file_match: + colorspace_name = file_rule["colorspace"] + + if not colorspace_name: + log.info("No imageio file rule matched input path: '{}'".format( + path + )) + return None + + # validate matching colorspace with config + if validate and config_data: + validate_imageio_colorspace_in_config( + config_data["path"], colorspace_name) + + return colorspace_name + + +def parse_colorspace_from_filepath( + path, host_name, project_name, + config_data=None, + project_settings=None +): + """Parse colorspace name from filepath + + An input path can have colorspace name used as part of name + or as folder name. + + Args: + path (str): path string + host_name (str): host name + project_name (str): project name + config_data (dict, optional): config path and template in dict. + Defaults to None. + project_settings (dict, optional): project settings. Defaults to None. + + Returns: + str: name of colorspace + """ + if not config_data: + project_settings = project_settings or get_project_settings( + project_name + ) + config_data = get_imageio_config( + project_name, host_name, project_settings) + + config_path = config_data["path"] + + # match file rule from path + colorspace_name = None + colorspaces = get_ocio_config_colorspaces(config_path) + for colorspace_key in colorspaces: + # check underscored variant of colorspace name + # since we are reformating it in integrate.py + if colorspace_key.replace(" ", "_") in path: + colorspace_name = colorspace_key + break + if colorspace_key in path: + colorspace_name = colorspace_key + break + + if not colorspace_name: + log.info("No matching colorspace in config '{}' for path: '{}'".format( + config_path, path + )) + return None + + return colorspace_name + + +def validate_imageio_colorspace_in_config(config_path, colorspace_name): + """Validator making sure colorspace name is used in config.ocio + + Args: + config_path (str): path leading to config.ocio file + colorspace_name (str): tested colorspace name + + Raises: + KeyError: missing colorspace name + + Returns: + bool: True if exists + """ + colorspaces = get_ocio_config_colorspaces(config_path) + if colorspace_name not in colorspaces: + raise KeyError( + "Missing colorspace '{}' in config file '{}'".format( + colorspace_name, config_path) + ) + return True + + +def get_ocio_config_colorspaces(config_path): + """Get all colorspace data + + Wrapper function for aggregating all names and its families. + Families can be used for building menu and submenus in gui. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: colorspace and family in couple + """ + if sys.version_info[0] == 2: + return get_colorspace_data_subprocess(config_path) + + from ..scripts.ocio_wrapper import _get_colorspace_data + return _get_colorspace_data(config_path) + + +def get_colorspace_data_subprocess(config_path): + """Get colorspace data via subprocess + + Wrapper for Python 2 hosts. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: colorspace and family in couple + """ + with _make_temp_json_file() as tmp_json_path: + # Prepare subprocess arguments + args = [ + "run", get_ocio_config_script_path(), + "config", "get_colorspace", + "--in_path", config_path, + "--out_path", tmp_json_path + + ] + log.info("Executing: {}".format(" ".join(args))) + + process_kwargs = { + "logger": log, + "env": {} + } + + run_openpype_process(*args, **process_kwargs) + + # return all colorspaces + return_json_data = open(tmp_json_path).read() + return json.loads(return_json_data) + + +def get_ocio_config_views(config_path): + """Get all viewer data + + Wrapper function for aggregating all display and related viewers. + Key can be used for building gui menu with submenus. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: `display/viewer` and viewer data + """ + if sys.version_info[0] == 2: + return get_views_data_subprocess(config_path) + + from ..scripts.ocio_wrapper import _get_views_data + return _get_views_data(config_path) + + +def get_views_data_subprocess(config_path): + """Get viewers data via subprocess + + Wrapper for Python 2 hosts. + + Args: + config_path (str): path leading to config.ocio file + + Returns: + dict: `display/viewer` and viewer data + """ + with _make_temp_json_file() as tmp_json_path: + # Prepare subprocess arguments + args = [ + "run", get_ocio_config_script_path(), + "config", "get_views", + "--in_path", config_path, + "--out_path", tmp_json_path + + ] + log.info("Executing: {}".format(" ".join(args))) + + process_kwargs = { + "logger": log, + "env": {} + } + + run_openpype_process(*args, **process_kwargs) + + # return all colorspaces + return_json_data = open(tmp_json_path).read() + return json.loads(return_json_data) + + +def get_imageio_config( + project_name, host_name, + project_settings=None, + anatomy_data=None, + anatomy=None +): + """Returns config data from settings + + Config path is formatted in `path` key + and original settings input is saved into `template` key. + + Args: + project_name (str): project name + host_name (str): host name + project_settings (dict, optional): project settings. + Defaults to None. + anatomy_data (dict, optional): anatomy formatting data. + Defaults to None. + anatomy (lib.Anatomy, optional): Anatomy object. + Defaults to None. + + Returns: + dict or bool: config path data or None + """ + project_settings = project_settings or get_project_settings(project_name) + anatomy = anatomy or Anatomy(project_name) + + if not anatomy_data: + from openpype.pipeline.context_tools import ( + get_template_data_from_session) + anatomy_data = get_template_data_from_session() + + # add project roots to anatomy data + anatomy_data["root"] = anatomy.roots + anatomy_data["platform"] = platform.system().lower() + + # get colorspace settings + imageio_global, imageio_host = _get_imageio_settings( + project_settings, host_name) + + config_host = imageio_host["ocio_config"] + + if config_host["enabled"]: + config_data = _get_config_data( + config_host["filepath"], anatomy_data + ) + else: + config_data = None + + if not config_data: + # get config path from either global or host_name + config_global = imageio_global["ocio_config"] + config_data = _get_config_data( + config_global["filepath"], anatomy_data + ) + + if not config_data: + raise FileExistsError( + "No OCIO config found in settings. It is " + "either missing or there is typo in path inputs" + ) + + return config_data + + +def _get_config_data(path_list, anatomy_data): + """Return first existing path in path list. + + If template is used in path inputs, + then it is formated by anatomy data + and environment variables + + Args: + path_list (list[str]): list of abs paths + anatomy_data (dict): formating data + + Returns: + dict: config data + """ + formatting_data = deepcopy(anatomy_data) + + # format the path for potential env vars + formatting_data.update(dict(**os.environ)) + + # first try host config paths + for path_ in path_list: + formated_path = _format_path(path_, formatting_data) + + if not os.path.exists(formated_path): + continue + + return { + "path": os.path.normpath(formated_path), + "template": path_ + } + + +def _format_path(tempate_path, formatting_data): + """Single template path formating. + + Args: + tempate_path (str): template string + formatting_data (dict): data to be used for + template formating + + Returns: + str: absolute formated path + """ + # format path for anatomy keys + formatted_path = StringTemplate(tempate_path).format( + formatting_data) + + return os.path.abspath(formatted_path) + + +def get_imageio_file_rules(project_name, host_name, project_settings=None): + """Get ImageIO File rules from project settings + + Args: + project_name (str): project name + host_name (str): host name + project_settings (dict, optional): project settings. + Defaults to None. + + Returns: + dict: file rules data + """ + project_settings = project_settings or get_project_settings(project_name) + + imageio_global, imageio_host = _get_imageio_settings( + project_settings, host_name) + + # get file rules from global and host_name + frules_global = imageio_global["file_rules"] + frules_host = imageio_host["file_rules"] + + # compile file rules dictionary + file_rules = {} + if frules_global["enabled"]: + file_rules.update(frules_global["rules"]) + if frules_host["enabled"]: + file_rules.update(frules_host["rules"]) + + return file_rules + + +def _get_imageio_settings(project_settings, host_name): + """Get ImageIO settings for global and host + + Args: + project_settings (dict): project settings. + Defaults to None. + host_name (str): host name + + Returns: + tuple[dict, dict]: image io settings for global and host + """ + # get image io from global and host_name + imageio_global = project_settings["global"]["imageio"] + imageio_host = project_settings[host_name]["imageio"] + + return imageio_global, imageio_host diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index 04b1a66f3a7..dc6fc0f97a8 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -19,6 +19,7 @@ RepairContextAction, Extractor, + ExtractorColormanaged, ) from .lib import ( @@ -63,6 +64,7 @@ "RepairContextAction", "Extractor", + "ExtractorColormanaged", "get_publish_template_name", diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index 47dfaf6b981..d9145275f7b 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,6 +1,6 @@ import inspect from abc import ABCMeta - +from pprint import pformat import pyblish.api from pyblish.plugin import MetaPlugin, ExplicitMetaPlugin @@ -13,6 +13,12 @@ get_instance_staging_dir, ) +from openpype.pipeline.colorspace import ( + get_imageio_colorspace_from_filepath, + get_imageio_config, + get_imageio_file_rules +) + class AbstractMetaInstancePlugin(ABCMeta, MetaPlugin): pass @@ -250,12 +256,12 @@ def process(self, context, plugin): if not hasattr(plugin, "repair"): raise RuntimeError("Plug-in does not have repair method.") - # Get the errored instances + # Get the failed instances self.log.info("Finding failed instances..") - errored_plugins = get_errored_plugins_from_context(context) + failed_plugins = get_errored_plugins_from_context(context) # Apply pyblish.logic to get the instances for the plug-in - if plugin in errored_plugins: + if plugin in failed_plugins: self.log.info("Attempting fix ...") plugin.repair(context) @@ -280,3 +286,143 @@ def staging_dir(self, instance): """ return get_instance_staging_dir(instance) + + +class ExtractorColormanaged(Extractor): + """Extractor base for color managed image data. + + Each Extractor intended to export pixel data representation + should inherit from this class to allow color managed data. + Class implements "get_colorspace_settings" and + "set_representation_colorspace" functions used + for injecting colorspace data to representation data for farther + integration into db document. + + """ + + allowed_ext = [ + "cin", "dpx", "avi", "dv", "gif", "flv", "mkv", "mov", "mpg", "mpeg", + "mp4", "m4v", "mxf", "iff", "z", "ifl", "jpeg", "jpg", "jfif", "lut", + "1dl", "exr", "pic", "png", "ppm", "pnm", "pgm", "pbm", "rla", "rpf", + "sgi", "rgba", "rgb", "bw", "tga", "tiff", "tif", "img" + ] + + @staticmethod + def get_colorspace_settings(context): + """Retuns solved settings for the host context. + + Args: + context (publish.Context): publishing context + + Returns: + tuple | bool: config, file rules or None + """ + if "imageioSettings" in context.data: + return context.data["imageioSettings"] + + project_name = context.data["projectName"] + host_name = context.data["hostName"] + anatomy_data = context.data["anatomyData"] + project_settings_ = context.data["project_settings"] + + config_data = get_imageio_config( + project_name, host_name, + project_settings=project_settings_, + anatomy_data=anatomy_data + ) + file_rules = get_imageio_file_rules( + project_name, host_name, + project_settings=project_settings_ + ) + + # caching settings for future instance processing + context.data["imageioSettings"] = (config_data, file_rules) + + return config_data, file_rules + + def set_representation_colorspace( + self, representation, context, + colorspace=None, + colorspace_settings=None + ): + """Sets colorspace data to representation. + + Args: + representation (dict): publishing representation + context (publish.Context): publishing context + config_data (dict): host resolved config data + file_rules (dict): host resolved file rules data + colorspace (str, optional): colorspace name. Defaults to None. + colorspace_settings (tuple[dict, dict], optional): + Settings for config_data and file_rules. + Defaults to None. + + Example: + ``` + { + # for other publish plugins and loaders + "colorspace": "linear", + "config": { + # for future references in case need + "path": "/abs/path/to/config.ocio", + # for other plugins within remote publish cases + "template": "{project[root]}/path/to/config.ocio" + } + } + ``` + + """ + if colorspace_settings is None: + colorspace_settings = self.get_colorspace_settings(context) + + # unpack colorspace settings + config_data, file_rules = colorspace_settings + + if not config_data: + # warn in case no colorspace path was defined + self.log.warning("No colorspace management was defined") + return + + self.log.info("Config data is : `{}`".format( + config_data)) + + ext = representation["ext"] + # check extension + self.log.debug("__ ext: `{}`".format(ext)) + if ext.lower() not in self.allowed_ext: + return + + project_name = context.data["projectName"] + host_name = context.data["hostName"] + project_settings = context.data["project_settings"] + + # get one filename + filename = representation["files"] + if isinstance(filename, list): + filename = filename[0] + + self.log.debug("__ filename: `{}`".format( + filename)) + + # get matching colorspace from rules + colorspace = colorspace or get_imageio_colorspace_from_filepath( + filename, host_name, project_name, + config_data=config_data, + file_rules=file_rules, + project_settings=project_settings + ) + self.log.debug("__ colorspace: `{}`".format( + colorspace)) + + # infuse data to representation + if colorspace: + colorspace_data = { + "colorspace": colorspace, + "config": config_data + } + + # update data key + representation["colorspaceData"] = colorspace_data + + self.log.debug("__ colorspace_data: `{}`".format( + pformat(colorspace_data))) diff --git a/openpype/plugins/publish/extract_colorspace_data.py b/openpype/plugins/publish/extract_colorspace_data.py new file mode 100644 index 00000000000..611fb91cbb6 --- /dev/null +++ b/openpype/plugins/publish/extract_colorspace_data.py @@ -0,0 +1,47 @@ +import pyblish.api +from openpype.pipeline import publish + + +class ExtractColorspaceData(publish.ExtractorColormanaged): + """ Inject Colorspace data to available representations. + + Input data: + - context.data[colorspace_config_path]: + for anatomy formatting of possible template tokens in config path + - context.data[colorspace_config_path]: + for resolving project and host related config.ocio + - context.data[colorspace_file_rules]: + for resolving matched file rule from representation file name + and adding it to representation + + Output data: + representation[colorspaceData] = { + "colorspace": "linear", + "config": { + "path": "/abs/path/to/config.ocio", + "template": "{project[root]}/path/to/config.ocio" + } + } + """ + label = "Extract Colorspace data" + order = pyblish.api.ExtractorOrder + 0.49 + + def process(self, instance): + representations = instance.data.get("representations") + if not representations: + self.log.info("No representations at instance : `{}`".format( + instance)) + return + + # get colorspace settings + context = instance.context + + # loop representations + for representation in representations: + # skip if colorspaceData is already at representation + if representation.get("colorspaceData"): + continue + + self.set_representation_colorspace( + representation, context + ) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5f811ce002c..a97ab2acca5 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -534,6 +534,15 @@ def prepare_representation(self, repre, template_data["representation"] = repre["name"] template_data["ext"] = repre["ext"] + # add template data for colorspaceData + if repre.get("colorspaceData"): + colorspace = repre["colorspaceData"]["colorspace"] + # replace spaces with underscores + # pipeline.colorspace.parse_colorspace_from_filepath + # is checking it with underscores too + colorspace = colorspace.replace(" ", "_") + template_data["colorspace"] = colorspace + stagingdir = repre.get("stagingDir") if not stagingdir: # Fall back to instance staging dir if not explicitly @@ -750,6 +759,11 @@ def prepare_representation(self, repre, # and the actual representation entity for the database data = repre.get("data", {}) data.update({"path": published_path, "template": template}) + + # add colorspace data if any exists on representation + if repre.get("colorspaceData"): + data["colorspaceData"] = repre["colorspaceData"] + repre_doc = new_representation_doc( repre["name"], version["_id"], repre_context, data, repre_id ) diff --git a/openpype/scripts/ocio_wrapper.py b/openpype/scripts/ocio_wrapper.py new file mode 100644 index 00000000000..0685b2e52a1 --- /dev/null +++ b/openpype/scripts/ocio_wrapper.py @@ -0,0 +1,168 @@ +"""OpenColorIO Wrapper. + +Only to be interpreted by Python 3. It is run in subprocess in case +Python 2 hosts needs to use it. Or it is used as module for Python 3 +processing. + +Providing functionality: +- get_colorspace - console command - python 2 + - returning all available color spaces + found in input config path. +- _get_colorspace_data - python 3 - module function + - returning all available colorspaces + found in input config path. +- get_views - console command - python 2 + - returning all available viewers + found in input config path. +- _get_views_data - python 3 - module function + - returning all available viewers + found in input config path. +""" + +import click +import json +from pathlib2 import Path +import PyOpenColorIO as ocio + + +@click.group() +def main(): + pass + + +@main.group() +def config(): + """Config related commands group + + Example of use: + > pyton.exe ./ocio_wrapper.py config *args + """ + pass + + +@config.command( + name="get_colorspace", + help=( + "return all colorspaces from config file " + "--path input arg is required" + ) +) +@click.option("--in_path", required=True, + help="path where to read ocio config file", + type=click.Path(exists=True)) +@click.option("--out_path", required=True, + help="path where to write output json file", + type=click.Path()) +def get_colorspace(in_path, out_path): + """Aggregate all colorspace to file. + + Python 2 wrapped console command + + Args: + in_path (str): config file path string + out_path (str): temp json file path string + + Example of use: + > pyton.exe ./ocio_wrapper.py config get_colorspace + --in_path= --out_path= + """ + json_path = Path(out_path) + + out_data = _get_colorspace_data(in_path) + + with open(json_path, "w") as f: + json.dump(out_data, f) + + print(f"Colorspace data are saved to '{json_path}'") + + +def _get_colorspace_data(config_path): + """Return all found colorspace data. + + Args: + config_path (str): path string leading to config.ocio + + Raises: + IOError: Input config does not exist. + + Returns: + dict: aggregated available colorspaces + """ + config_path = Path(config_path) + + if not config_path.is_file(): + raise IOError( + f"Input path `{config_path}` should be `config.ocio` file") + + config = ocio.Config().CreateFromFile(str(config_path)) + + return { + c.getName(): c.getFamily() + for c in config.getColorSpaces() + } + + +@config.command( + name="get_views", + help=( + "return all viewers from config file " + "--path input arg is required" + ) +) +@click.option("--in_path", required=True, + help="path where to read ocio config file", + type=click.Path(exists=True)) +@click.option("--out_path", required=True, + help="path where to write output json file", + type=click.Path()) +def get_views(in_path, out_path): + """Aggregate all viewers to file. + + Python 2 wrapped console command + + Args: + in_path (str): config file path string + out_path (str): temp json file path string + + Example of use: + > pyton.exe ./ocio_wrapper.py config get_views \ + --in_path= --out_path= + """ + json_path = Path(out_path) + + out_data = _get_views_data(in_path) + + with open(json_path, "w") as f: + json.dump(out_data, f) + + print(f"Viewer data are saved to '{json_path}'") + + +def _get_views_data(config_path): + """Return all found viewer data. + + Args: + config_path (str): path string leading to config.ocio + + Raises: + IOError: Input config does not exist. + + Returns: + dict: aggregated available viewers + """ + config_path = Path(config_path) + + if not config_path.is_file(): + raise IOError("Input path should be `config.ocio` file") + + config = ocio.Config().CreateFromFile(str(config_path)) + + return { + f"{d}/{v}": {"display": d, "view": v} + for d in config.getDisplays() + for v in config.getViews(d) + } + + +if __name__ == '__main__': + main() diff --git a/openpype/settings/defaults/project_anatomy/attributes.json b/openpype/settings/defaults/project_anatomy/attributes.json index 983ac603f95..bf8bbef8de1 100644 --- a/openpype/settings/defaults/project_anatomy/attributes.json +++ b/openpype/settings/defaults/project_anatomy/attributes.json @@ -19,8 +19,7 @@ "blender/2-91", "harmony/20", "photoshop/2021", - "aftereffects/2021", - "unreal/4-26" + "aftereffects/2021" ], "tools_env": [], "active": true diff --git a/openpype/settings/defaults/project_settings/aftereffects.json b/openpype/settings/defaults/project_settings/aftereffects.json index 8083aa0972a..e4b957fb85f 100644 --- a/openpype/settings/defaults/project_settings/aftereffects.json +++ b/openpype/settings/defaults/project_settings/aftereffects.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "create": { "RenderCreator": { "defaults": [ diff --git a/openpype/settings/defaults/project_settings/blender.json b/openpype/settings/defaults/project_settings/blender.json index 7acecfaae01..12a322cb983 100644 --- a/openpype/settings/defaults/project_settings/blender.json +++ b/openpype/settings/defaults/project_settings/blender.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "workfile_builder": { "create_first_version": false, "custom_templates": [] diff --git a/openpype/settings/defaults/project_settings/celaction.json b/openpype/settings/defaults/project_settings/celaction.json index dbe5625f06f..ad01e62d958 100644 --- a/openpype/settings/defaults/project_settings/celaction.json +++ b/openpype/settings/defaults/project_settings/celaction.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "publish": { "CollectRenderPath": { "output_extension": "png", diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index 1422a76af37..cbd99c45606 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -1,5 +1,13 @@ { "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + }, "project": { "colourPolicy": "ACES 1.1", "frameDepth": "16-bit fp", diff --git a/openpype/settings/defaults/project_settings/fusion.json b/openpype/settings/defaults/project_settings/fusion.json index 1b4c4c55b55..720178e17a8 100644 --- a/openpype/settings/defaults/project_settings/fusion.json +++ b/openpype/settings/defaults/project_settings/fusion.json @@ -1,5 +1,13 @@ { "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + }, "ocio": { "enabled": false, "configFilePath": { diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 2548a4e33a2..0e078dc157b 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -1,4 +1,22 @@ { + "imageio": { + "ocio_config": { + "filepath": [ + "{OPENPYPE_ROOT}/vendor/bin/ocioconfig/OpenColorIOConfigs/aces_1.2/config.ocio", + "{OPENPYPE_ROOT}/vendor/bin/ocioconfig/OpenColorIOConfigs/nuke-default/config.ocio" + ] + }, + "file_rules": { + "enabled": false, + "rules": { + "example": { + "pattern": ".*(beauty).*", + "colorspace": "ACES - ACEScg", + "ext": "exr" + } + } + } + }, "publish": { "CollectAnatomyInstanceData": { "follow_workfile_version": false @@ -422,7 +440,9 @@ "template": "{family}{Task}" }, { - "families": ["render"], + "families": [ + "render" + ], "hosts": [ "aftereffects" ], diff --git a/openpype/settings/defaults/project_settings/harmony.json b/openpype/settings/defaults/project_settings/harmony.json index d843bc8e709..1f4ea88272a 100644 --- a/openpype/settings/defaults/project_settings/harmony.json +++ b/openpype/settings/defaults/project_settings/harmony.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "load": { "ImageSequenceLoader": { "family": [ diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index d2ba6973054..c6180d0a58c 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -1,5 +1,13 @@ { "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + }, "workfile": { "ocioConfigName": "nuke-default", "ocioconfigpath": { diff --git a/openpype/settings/defaults/project_settings/houdini.json b/openpype/settings/defaults/project_settings/houdini.json index 15179835696..68cc8945fee 100644 --- a/openpype/settings/defaults/project_settings/houdini.json +++ b/openpype/settings/defaults/project_settings/houdini.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "shelves": [], "create": { "CreateArnoldAss": { diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 5f40c2a10c3..d9498c3bc37 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1,5 +1,13 @@ { "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + }, "colorManagementPreference_v2": { "enabled": true, "configFilePath": { @@ -158,24 +166,6 @@ "Main" ] }, - "CreateMultiverseUsd": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateMultiverseUsdComp": { - "enabled": true, - "defaults": [ - "Main" - ] - }, - "CreateMultiverseUsdOver": { - "enabled": true, - "defaults": [ - "Main" - ] - }, "CreateAss": { "enabled": true, "defaults": [ @@ -196,6 +186,24 @@ "maskColor_manager": false, "maskOperator": false }, + "CreateMultiverseUsd": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateMultiverseUsdComp": { + "enabled": true, + "defaults": [ + "Main" + ] + }, + "CreateMultiverseUsdOver": { + "enabled": true, + "defaults": [ + "Main" + ] + }, "CreateAssembly": { "enabled": true, "defaults": [ @@ -1040,4 +1048,4 @@ "ValidateNoAnimation": false } } -} +} \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 5aeca288ad9..cd8ea02272e 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -9,7 +9,14 @@ } }, "imageio": { - "enabled": false, + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + }, "viewer": { "viewerProcess": "sRGB" }, diff --git a/openpype/settings/defaults/project_settings/photoshop.json b/openpype/settings/defaults/project_settings/photoshop.json index fa0dc7b1c4d..cdfab0c4390 100644 --- a/openpype/settings/defaults/project_settings/photoshop.json +++ b/openpype/settings/defaults/project_settings/photoshop.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "create": { "CreateImage": { "defaults": [ diff --git a/openpype/settings/defaults/project_settings/resolve.json b/openpype/settings/defaults/project_settings/resolve.json index b6fbdecc955..66013c5ac79 100644 --- a/openpype/settings/defaults/project_settings/resolve.json +++ b/openpype/settings/defaults/project_settings/resolve.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "create": { "CreateShotClip": { "hierarchy": "{folder}/{sequence}", diff --git a/openpype/settings/defaults/project_settings/traypublisher.json b/openpype/settings/defaults/project_settings/traypublisher.json index e99b96b8c49..8a222a6dd26 100644 --- a/openpype/settings/defaults/project_settings/traypublisher.json +++ b/openpype/settings/defaults/project_settings/traypublisher.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "simple_creators": [ { "family": "workfile", diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json index e03ce320303..5a3e1dc2dfa 100644 --- a/openpype/settings/defaults/project_settings/tvpaint.json +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "stop_timer_on_application_exit": false, "publish": { "CollectRenderScene": { diff --git a/openpype/settings/defaults/project_settings/unreal.json b/openpype/settings/defaults/project_settings/unreal.json index 391e2415a5a..b06bf287145 100644 --- a/openpype/settings/defaults/project_settings/unreal.json +++ b/openpype/settings/defaults/project_settings/unreal.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "level_sequences_for_layouts": false, "delete_unmatched_assets": false, "project_setup": { diff --git a/openpype/settings/defaults/project_settings/webpublisher.json b/openpype/settings/defaults/project_settings/webpublisher.json index 09c7d3ec94b..27eac131b7f 100644 --- a/openpype/settings/defaults/project_settings/webpublisher.json +++ b/openpype/settings/defaults/project_settings/webpublisher.json @@ -1,4 +1,14 @@ { + "imageio": { + "ocio_config": { + "enabled": false, + "filepath": [] + }, + "file_rules": { + "enabled": false, + "rules": {} + } + }, "timeout_profiles": [ { "hosts": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_aftereffects.json b/openpype/settings/entities/schemas/projects_schema/schema_project_aftereffects.json index 1a3eaef540e..8dc83f5506e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_aftereffects.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_aftereffects.json @@ -5,6 +5,23 @@ "label": "AfterEffects", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json b/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json index 4c72ebda2fa..725d9bfb083 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_blender.json @@ -5,6 +5,23 @@ "label": "Blender", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "schema_template", "name": "template_workfile_options", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_celaction.json b/openpype/settings/entities/schemas/projects_schema/schema_project_celaction.json index 15d9350c847..2320d9ae266 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_celaction.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_celaction.json @@ -5,6 +5,23 @@ "label": "CelAction", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 24726f2d07a..0f20c0efbe6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -11,6 +11,14 @@ "label": "Color Management (ImageIO)", "is_group": true, "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + }, { "key": "project", "type": "dict", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json b/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json index 8f98a8173f3..8c62d758159 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_fusion.json @@ -11,6 +11,14 @@ "label": "Color Management (ImageIO)", "collapsible": true, "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + }, { "key": "ocio", "type": "dict", @@ -23,6 +31,10 @@ "key": "enabled", "label": "Set OCIO variable for Fusion" }, + { + "type": "label", + "label": "'configFilePath' will be deprecated.
Please move values to : project_settings/{app}/imageio/ocio_config/filepath." + }, { "type": "path", "key": "configFilePath", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_global.json b/openpype/settings/entities/schemas/projects_schema/schema_project_global.json index a8bce475924..6f31f4f6859 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_global.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_global.json @@ -5,6 +5,34 @@ "label": "Global", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "key": "ocio_config", + "type": "dict", + "label": "OCIO config", + "collapsible": true, + "children": [ + { + "type": "path", + "key": "filepath", + "label": "Config path", + "multiplatform": false, + "multipath": true + } + ] + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "schema", "name": "schema_global_publish" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_harmony.json b/openpype/settings/entities/schemas/projects_schema/schema_project_harmony.json index 311f742f812..e6bf835c9fd 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_harmony.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_harmony.json @@ -5,6 +5,23 @@ "label": "Harmony", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json index 9e18522def0..03bfb56ad14 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json @@ -5,116 +5,128 @@ "label": "Hiero", "is_file": true, "children": [ - { - "key": "imageio", - "type": "dict", - "label": "Color Management (ImageIO)", - "is_group": true, - "collapsible": true, - "children": [ - { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsible": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "ocioConfigName", - "label": "OpenColorIO Config", - "enum_items": [ - { - "nuke-default": "nuke-default" - }, - { - "aces_1.0.3": "aces_1.0.3" - }, - { - "aces_1.1": "aces_1.1" - }, - { - "custom": "custom" - } - ] - }, - { - "type": "path", - "key": "ocioconfigpath", - "label": "Custom OCIO path", - "multiplatform": true, - "multipath": true - }, - { - "type": "text", - "key": "workingSpace", - "label": "Working Space" - }, - { - "type": "text", - "key": "sixteenBitLut", - "label": "16 Bit Files" - }, - { - "type": "text", - "key": "eightBitLut", - "label": "8 Bit Files" - }, - { - "type": "text", - "key": "floatLut", - "label": "Floating Point Files" - }, - { - "type": "text", - "key": "logLut", - "label": "Log Files" - }, - { - "type": "text", - "key": "viewerLut", - "label": "Viewer" - }, - { - "type": "text", - "key": "thumbnailLut", - "label": "Thumbnails" - } - ] - } - ] - }, - { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsible": true, - "children": [ - { - "type": "list", - "key": "inputs", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "regex", - "label": "Regex" - }, - { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - } - ] - } - } - ] - } - ] - }, + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "collapsible": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + }, + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "label", + "label": "'ocioconfigpath' will be deprecated.
Please move values to : project_settings/{app}/imageio/ocio_config/filepath." + }, + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "ocioConfigName", + "label": "OpenColorIO Config", + "enum_items": [ + { + "nuke-default": "nuke-default" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" + } + ] + }, + { + "type": "path", + "key": "ocioconfigpath", + "label": "Custom OCIO path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "workingSpace", + "label": "Working Space" + }, + { + "type": "text", + "key": "sixteenBitLut", + "label": "16 Bit Files" + }, + { + "type": "text", + "key": "eightBitLut", + "label": "8 Bit Files" + }, + { + "type": "text", + "key": "floatLut", + "label": "Floating Point Files" + }, + { + "type": "text", + "key": "logLut", + "label": "Log Files" + }, + { + "type": "text", + "key": "viewerLut", + "label": "Viewer" + }, + { + "type": "text", + "key": "thumbnailLut", + "label": "Thumbnails" + } + ] + } + ] + }, + { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, + { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + }, { "type": "dict", "collapsible": true, @@ -129,102 +141,102 @@ "is_group": true, "children": [ { - "type": "collapsible-wrap", - "label": "Shot Hierarchy And Rename Settings", - "collapsible": false, - "children": [ - { - "type": "text", - "key": "hierarchy", - "label": "Shot parent hierarchy" - }, - { - "type": "boolean", - "key": "clipRename", - "label": "Rename clips" - }, - { - "type": "text", - "key": "clipName", - "label": "Clip name template" - }, - { - "type": "number", - "key": "countFrom", - "label": "Count sequence from" - }, - { - "type": "number", - "key": "countSteps", - "label": "Stepping number" - } - ] + "type": "collapsible-wrap", + "label": "Shot Hierarchy And Rename Settings", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "hierarchy", + "label": "Shot parent hierarchy" + }, + { + "type": "boolean", + "key": "clipRename", + "label": "Rename clips" + }, + { + "type": "text", + "key": "clipName", + "label": "Clip name template" + }, + { + "type": "number", + "key": "countFrom", + "label": "Count sequence from" + }, + { + "type": "number", + "key": "countSteps", + "label": "Stepping number" + } + ] }, { - "type": "collapsible-wrap", - "label": "Shot Template Keywords", - "collapsible": false, - "children": [ - { - "type": "text", - "key": "folder", - "label": "{folder}" - }, - { - "type": "text", - "key": "episode", - "label": "{episode}" - }, - { - "type": "text", - "key": "sequence", - "label": "{sequence}" - }, - { - "type": "text", - "key": "track", - "label": "{track}" - }, - { - "type": "text", - "key": "shot", - "label": "{shot}" - } - ] + "type": "collapsible-wrap", + "label": "Shot Template Keywords", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "folder", + "label": "{folder}" + }, + { + "type": "text", + "key": "episode", + "label": "{episode}" + }, + { + "type": "text", + "key": "sequence", + "label": "{sequence}" + }, + { + "type": "text", + "key": "track", + "label": "{track}" + }, + { + "type": "text", + "key": "shot", + "label": "{shot}" + } + ] }, { - "type": "collapsible-wrap", - "label": "Vertical Synchronization Of Attributes", - "collapsible": false, - "children": [ - { - "type": "boolean", - "key": "vSyncOn", - "label": "Enable Vertical Sync" - } - ] + "type": "collapsible-wrap", + "label": "Vertical Synchronization Of Attributes", + "collapsible": false, + "children": [ + { + "type": "boolean", + "key": "vSyncOn", + "label": "Enable Vertical Sync" + } + ] }, { - "type": "collapsible-wrap", - "label": "Shot Attributes", - "collapsible": false, - "children": [ - { - "type": "number", - "key": "workfileFrameStart", - "label": "Workfiles Start Frame" - }, - { - "type": "number", - "key": "handleStart", - "label": "Handle start (head)" - }, - { - "type": "number", - "key": "handleEnd", - "label": "Handle end (tail)" - } - ] + "type": "collapsible-wrap", + "label": "Shot Attributes", + "collapsible": false, + "children": [ + { + "type": "number", + "key": "workfileFrameStart", + "label": "Workfiles Start Frame" + }, + { + "type": "number", + "key": "handleStart", + "label": "Handle start (head)" + }, + { + "type": "number", + "key": "handleEnd", + "label": "Handle end (tail)" + } + ] } ] } @@ -322,4 +334,4 @@ "name": "schema_scriptsmenu" } ] -} +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_houdini.json b/openpype/settings/entities/schemas/projects_schema/schema_project_houdini.json index 808f1542260..24b06f77db5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_houdini.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_houdini.json @@ -5,6 +5,23 @@ "label": "Houdini", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "schema", "name": "schema_houdini_scriptshelf" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json index b2d79797a34..d83666b5b2c 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -12,6 +12,14 @@ "collapsible": true, "is_group": true, "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + }, { "key": "colorManagementPreference_v2", "type": "dict", @@ -24,6 +32,10 @@ "key": "enabled", "label": "Use Color Management Preference v2" }, + { + "type": "label", + "label": "'configFilePath' will be deprecated.
Please move values to : project_settings/{app}/imageio/ocio_config/filepath." + }, { "type": "path", "key": "configFilePath", @@ -54,6 +66,10 @@ "label": "Color Management Preference (legacy)", "collapsible": true, "children": [ + { + "type": "label", + "label": "'configFilePath' will be deprecated.
Please move values to : project_settings/{app}/imageio/ocio_config/filepath." + }, { "type": "path", "key": "configFilePath", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json b/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json index b768db30ee0..0071e632afb 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json @@ -5,6 +5,23 @@ "label": "Photoshop", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_resolve.json b/openpype/settings/entities/schemas/projects_schema/schema_project_resolve.json index 68e405b7d73..b326f22394e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_resolve.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_resolve.json @@ -5,6 +5,23 @@ "label": "DaVinci Resolve", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json b/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json index faa5033d2a6..2ef1d2a4146 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json @@ -5,6 +5,23 @@ "label": "Tray Publisher", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "list", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json index 61342ef738a..db38c938dc9 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -5,6 +5,23 @@ "label": "TVPaint", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "boolean", "key": "stop_timer_on_application_exit", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_unreal.json b/openpype/settings/entities/schemas/projects_schema/schema_project_unreal.json index 09e5791ac42..8988dd2ff04 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_unreal.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_unreal.json @@ -5,6 +5,23 @@ "label": "Unreal Engine", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "boolean", "key": "level_sequences_for_layouts", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json b/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json index a81a403bcbe..66ccca644d5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json @@ -5,6 +5,23 @@ "label": "Web Publisher", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] + }, { "type": "list", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_imageio_config.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_imageio_config.json new file mode 100644 index 00000000000..e7cff969d33 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_imageio_config.json @@ -0,0 +1,21 @@ +{ + "key": "ocio_config", + "type": "dict", + "label": "OCIO config", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "path", + "key": "filepath", + "label": "Config path", + "multiplatform": false, + "multipath": true + } + ] +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_imageio_file_rules.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_imageio_file_rules.json new file mode 100644 index 00000000000..a171ba1c557 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_imageio_file_rules.json @@ -0,0 +1,41 @@ +{ + "key": "file_rules", + "type": "dict", + "label": "File Rules", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "rules", + "label": "Rules", + "type": "dict-modifiable", + "highlight_content": true, + "collapsible": false, + "object_type": { + "type": "dict", + "children": [ + { + "key": "pattern", + "label": "Regex pattern", + "type": "text" + }, + { + "key": "colorspace", + "label": "Colorspace name", + "type": "text" + }, + { + "key": "ext", + "label": "File extension", + "type": "text" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json index 52db853ef68..1cd6f0e67fc 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json @@ -2,14 +2,20 @@ "key": "imageio", "type": "dict", "label": "Color Management (ImageIO)", - "checkbox_key": "enabled", "collapsible": true, "is_group": true, "children": [ { - "type": "boolean", - "key": "enabled", - "label": "Enabled" + "type": "label", + "label": "'Custom OCIO config path' has deprecated.
If you need to set custom config, just enable and add path into 'OCIO config'.
Anatomy keys are supported.." + }, + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" }, { "key": "viewer", diff --git a/poetry.lock b/poetry.lock index cf780e8dd29..f71611cb6f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3,10 +3,10 @@ [[package]] name = "acre" version = "1.0.0" -description = "" +description = "Lightweight cross-platform environment management Python package that makes it trivial to launch applications in their own configurable working environment." category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7" files = [] develop = false @@ -174,14 +174,14 @@ frozenlist = ">=1.1.0" [[package]] name = "alabaster" -version = "0.7.12" +version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, ] [[package]] @@ -567,63 +567,63 @@ files = [ [[package]] name = "coverage" -version = "7.0.4" +version = "7.0.5" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "coverage-7.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:daf91db39324e9939a9db919ee4fb42a1a23634a056616dae891a030e89f87ba"}, - {file = "coverage-7.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55121fe140d7e42cb970999b93cf1c2b24484ce028b32bbd00238bb25c13e34a"}, - {file = "coverage-7.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c027fbb83a8c78a6e06a0302ea1799fdb70e5cda9845a5e000545b8e2b47ea39"}, - {file = "coverage-7.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf82db5b7f16b51ec32fe0bd2da0805b177c807aa8bfb478c7e6f893418c284"}, - {file = "coverage-7.0.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ba5cc54baf3c322c4388de2a43cc95f7809366f0600e743e5aae8ea9d1038b2"}, - {file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:260854160083f8275a9d9d49a05ab0ffc7a1f08f2ccccbfaec94a18aae9f407c"}, - {file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ea45f0dba5a993e93b158f1a9dcfff2770e3bcabf2b80dbe7aa15dce0bcb3bf3"}, - {file = "coverage-7.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6abc91f6f8b3cc0ae1034e2c03f38769fba1952ab70d0b26953aa01691265c39"}, - {file = "coverage-7.0.4-cp310-cp310-win32.whl", hash = "sha256:053cdc47cae08257051d7e934a0de4d095b60eb8a3024fa9f1b2322fa1547137"}, - {file = "coverage-7.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:1e9e94f2612ee549a4b3ee79cbc61bceed77e69cf38cfa05858bae939a886d16"}, - {file = "coverage-7.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5caa9dd91dcc5f054350dc57a02e053d79633907b9ccffff999568d13dcd19f8"}, - {file = "coverage-7.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:efc200fa75d9634525b40babc7a16342bd21c101db1a58ef84dc14f4bf6ac0fd"}, - {file = "coverage-7.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1791e5f74c5b52f76e83fe9f4bb9571cf76d40ee0c51952ee1e4ee935b7e98b9"}, - {file = "coverage-7.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d9201cfa5a98652b9cef36ab202f17fe3ea83f497b4ba2a8ed39399dfb8fcd4"}, - {file = "coverage-7.0.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22d8ef6865cb6834cab2b72fff20747a55c714b57b675f7e11c9624fe4f7cb45"}, - {file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b84076e3de192fba0f95e279ac017b64c7c6ecd4f09f36f13420f5bed898a9c7"}, - {file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:dcfbf8ffc046f20d75fd775a92c378f6fc7b9bded6c6f2ab88b6b9cb5805a184"}, - {file = "coverage-7.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4665a714af31f160403c2e448fb2fef330719d2e04e836b08d60d612707c1041"}, - {file = "coverage-7.0.4-cp311-cp311-win32.whl", hash = "sha256:2e59aef3fba5758059208c9eff10ae7ded3629e797972746ec33b56844f69411"}, - {file = "coverage-7.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:2b854f7985b48122b6fe346631e86d67b63293f8255cb59a93d79e3d9f1574e3"}, - {file = "coverage-7.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e44b60b0b49aa85d548d392a2dca2c6a581cd4084e72e9e16bd58bd86ec20816"}, - {file = "coverage-7.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2904d7a0388911c61e7e3beefe48c29dfccaba938fc1158f63190101a21e04c2"}, - {file = "coverage-7.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc74b64bfa89e2f862ea45dd6ac1def371d7cc883b76680d20bdd61a6f3daa20"}, - {file = "coverage-7.0.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06046f54e719da21c79f98ecc0962581d1aee0b3798dc6b12b1217da8bf93f4"}, - {file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bc9c77004970a364a1e5454cf7cb884e4277592b959c287689b2a0fd027ef552"}, - {file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0815a09b32384e8ff00a5939ec9cd10efce8742347e019c2daca1a32f5ac2aae"}, - {file = "coverage-7.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a78a80d131c067d67d8a6f9bd3d3f7ea7eac82c1c7259f97d7ab73f723da9d55"}, - {file = "coverage-7.0.4-cp37-cp37m-win32.whl", hash = "sha256:2b5936b624fbe711ed02dfd86edd678822e5ee68da02b6d231e5c01090b64590"}, - {file = "coverage-7.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a63922765ee49d5b4c32afb2cd5516812c8665f3b78e64a0dd005bdfabf991b1"}, - {file = "coverage-7.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d68f2f7bddb3acdd3b36ef7f334b9d14f30b93e094f808fbbd8d288b8f9e2f9b"}, - {file = "coverage-7.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dafdba3b2b9010abab08cb8c0dc6549bfca6e1630fe14d47b01dca00d39e694"}, - {file = "coverage-7.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0322354757b47640535daabd2d56384ff3cad2896248fc84d328c5fad4922d5c"}, - {file = "coverage-7.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e8267466662aff93d66fa72b9591d02122dfc8a729b0a43dd70e0fb07ed9b37"}, - {file = "coverage-7.0.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f684d88eb4924ed0630cf488fd5606e334c6835594bb5fe36b50a509b10383ed"}, - {file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:70c294bb15ba576fb96b580db35895bf03749d683df044212b74e938a7f6821f"}, - {file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:34c0457e1ba450ae8b22dc8ea2fd36ada1010af61291e4c96963cd9d9633366f"}, - {file = "coverage-7.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b75aff2c35ceaa299691e772f7bf7c8aeab25f46acea2be3dd04cccb914a9860"}, - {file = "coverage-7.0.4-cp38-cp38-win32.whl", hash = "sha256:6c5554d55668381e131577f20e8f620d4882b04ad558f7e7f3f1f55b3124c379"}, - {file = "coverage-7.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c82f34fafaf5bc05d222fcf84423d6e156432ca35ca78672d4affd0c09c6ef6c"}, - {file = "coverage-7.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8dfb5fed540f77e814bf4ec79619c241af6b4578fa1093c5e3389bbb7beab3f"}, - {file = "coverage-7.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee32a080bab779b71c4d09a3eb5254bfca43ee88828a683dab27dfe8f582516e"}, - {file = "coverage-7.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dfbee0bf0d633be3a2ab068f5a5731a70adf147d0ba17d9f9932b46c7c5782b"}, - {file = "coverage-7.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32dc010713455ac0fe2fddb0e48aa43875cc7eb7b09768df10bad8ce45f9c430"}, - {file = "coverage-7.0.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cb88a3019ad042eaa69fc7639ef077793fedbf313e89207aa82fefe92c97ebd"}, - {file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:73bc6114aab7753ca784f87bcd3b7613bc797aa255b5bca45e5654070ae9acfb"}, - {file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92f135d370fcd7a6fb9659fa2eb716dd2ca364719cbb1756f74d90a221bca1a7"}, - {file = "coverage-7.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f3d485e6ec6e09857bf2115ece572d666b7c498377d4c70e66bb06c63ed177c2"}, - {file = "coverage-7.0.4-cp39-cp39-win32.whl", hash = "sha256:c58921fcd9914b56444292e7546fe183d079db99528142c809549ddeaeacd8e9"}, - {file = "coverage-7.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:f092d9f2ddaa30235d33335fbdb61eb8f3657af519ef5f9dd6bdae65272def11"}, - {file = "coverage-7.0.4-pp37.pp38.pp39-none-any.whl", hash = "sha256:cb8cfa3bf3a9f18211279458917fef5edeb5e1fdebe2ea8b11969ec2ebe48884"}, - {file = "coverage-7.0.4.tar.gz", hash = "sha256:f6c4ad409a0caf7e2e12e203348b1a9b19c514e7d078520973147bf2d3dcbc6f"}, + {file = "coverage-7.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a7f23bbaeb2a87f90f607730b45564076d870f1fb07b9318d0c21f36871932b"}, + {file = "coverage-7.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c18d47f314b950dbf24a41787ced1474e01ca816011925976d90a88b27c22b89"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef14d75d86f104f03dea66c13188487151760ef25dd6b2dbd541885185f05f40"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66e50680e888840c0995f2ad766e726ce71ca682e3c5f4eee82272c7671d38a2"}, + {file = "coverage-7.0.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9fed35ca8c6e946e877893bbac022e8563b94404a605af1d1e6accc7eb73289"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d8d04e755934195bdc1db45ba9e040b8d20d046d04d6d77e71b3b34a8cc002d0"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e109f1c9a3ece676597831874126555997c48f62bddbcace6ed17be3e372de8"}, + {file = "coverage-7.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a1890fca2962c4f1ad16551d660b46ea77291fba2cc21c024cd527b9d9c8809"}, + {file = "coverage-7.0.5-cp310-cp310-win32.whl", hash = "sha256:be9fcf32c010da0ba40bf4ee01889d6c737658f4ddff160bd7eb9cac8f094b21"}, + {file = "coverage-7.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:cbfcba14a3225b055a28b3199c3d81cd0ab37d2353ffd7f6fd64844cebab31ad"}, + {file = "coverage-7.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30b5fec1d34cc932c1bc04017b538ce16bf84e239378b8f75220478645d11fca"}, + {file = "coverage-7.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1caed2367b32cc80a2b7f58a9f46658218a19c6cfe5bc234021966dc3daa01f0"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d254666d29540a72d17cc0175746cfb03d5123db33e67d1020e42dae611dc196"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19245c249aa711d954623d94f23cc94c0fd65865661f20b7781210cb97c471c0"}, + {file = "coverage-7.0.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b05ed4b35bf6ee790832f68932baf1f00caa32283d66cc4d455c9e9d115aafc"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:29de916ba1099ba2aab76aca101580006adfac5646de9b7c010a0f13867cba45"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e057e74e53db78122a3979f908973e171909a58ac20df05c33998d52e6d35757"}, + {file = "coverage-7.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:411d4ff9d041be08fdfc02adf62e89c735b9468f6d8f6427f8a14b6bb0a85095"}, + {file = "coverage-7.0.5-cp311-cp311-win32.whl", hash = "sha256:52ab14b9e09ce052237dfe12d6892dd39b0401690856bcfe75d5baba4bfe2831"}, + {file = "coverage-7.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:1f66862d3a41674ebd8d1a7b6f5387fe5ce353f8719040a986551a545d7d83ea"}, + {file = "coverage-7.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b69522b168a6b64edf0c33ba53eac491c0a8f5cc94fa4337f9c6f4c8f2f5296c"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436e103950d05b7d7f55e39beeb4d5be298ca3e119e0589c0227e6d0b01ee8c7"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c56bec53d6e3154eaff6ea941226e7bd7cc0d99f9b3756c2520fc7a94e6d96"}, + {file = "coverage-7.0.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a38362528a9115a4e276e65eeabf67dcfaf57698e17ae388599568a78dcb029"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f67472c09a0c7486e27f3275f617c964d25e35727af952869dd496b9b5b7f6a3"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:220e3fa77d14c8a507b2d951e463b57a1f7810a6443a26f9b7591ef39047b1b2"}, + {file = "coverage-7.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecb0f73954892f98611e183f50acdc9e21a4653f294dfbe079da73c6378a6f47"}, + {file = "coverage-7.0.5-cp37-cp37m-win32.whl", hash = "sha256:d8f3e2e0a1d6777e58e834fd5a04657f66affa615dae61dd67c35d1568c38882"}, + {file = "coverage-7.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9e662e6fc4f513b79da5d10a23edd2b87685815b337b1a30cd11307a6679148d"}, + {file = "coverage-7.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:790e4433962c9f454e213b21b0fd4b42310ade9c077e8edcb5113db0818450cb"}, + {file = "coverage-7.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49640bda9bda35b057b0e65b7c43ba706fa2335c9a9896652aebe0fa399e80e6"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d66187792bfe56f8c18ba986a0e4ae44856b1c645336bd2c776e3386da91e1dd"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:276f4cd0001cd83b00817c8db76730938b1ee40f4993b6a905f40a7278103b3a"}, + {file = "coverage-7.0.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95304068686545aa368b35dfda1cdfbbdbe2f6fe43de4a2e9baa8ebd71be46e2"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:17e01dd8666c445025c29684d4aabf5a90dc6ef1ab25328aa52bedaa95b65ad7"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea76dbcad0b7b0deb265d8c36e0801abcddf6cc1395940a24e3595288b405ca0"}, + {file = "coverage-7.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:50a6adc2be8edd7ee67d1abc3cd20678987c7b9d79cd265de55941e3d0d56499"}, + {file = "coverage-7.0.5-cp38-cp38-win32.whl", hash = "sha256:e4ce984133b888cc3a46867c8b4372c7dee9cee300335e2925e197bcd45b9e16"}, + {file = "coverage-7.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4a950f83fd3f9bca23b77442f3a2b2ea4ac900944d8af9993743774c4fdc57af"}, + {file = "coverage-7.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c2155943896ac78b9b0fd910fb381186d0c345911f5333ee46ac44c8f0e43ab"}, + {file = "coverage-7.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54f7e9705e14b2c9f6abdeb127c390f679f6dbe64ba732788d3015f7f76ef637"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee30375b409d9a7ea0f30c50645d436b6f5dfee254edffd27e45a980ad2c7f4"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b78729038abea6a5df0d2708dce21e82073463b2d79d10884d7d591e0f385ded"}, + {file = "coverage-7.0.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13250b1f0bd023e0c9f11838bdeb60214dd5b6aaf8e8d2f110c7e232a1bff83b"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c407b1950b2d2ffa091f4e225ca19a66a9bd81222f27c56bd12658fc5ca1209"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c76a3075e96b9c9ff00df8b5f7f560f5634dffd1658bafb79eb2682867e94f78"}, + {file = "coverage-7.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f26648e1b3b03b6022b48a9b910d0ae209e2d51f50441db5dce5b530fad6d9b1"}, + {file = "coverage-7.0.5-cp39-cp39-win32.whl", hash = "sha256:ba3027deb7abf02859aca49c865ece538aee56dcb4871b4cced23ba4d5088904"}, + {file = "coverage-7.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:949844af60ee96a376aac1ded2a27e134b8c8d35cc006a52903fc06c24a3296f"}, + {file = "coverage-7.0.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:b9727ac4f5cf2cbf87880a63870b5b9730a8ae3a4a360241a0fdaa2f71240ff0"}, + {file = "coverage-7.0.5.tar.gz", hash = "sha256:051afcbd6d2ac39298d62d340f94dbb6a1f31de06dfaf6fcef7b759dd3860c45"}, ] [package.dependencies] @@ -812,22 +812,23 @@ files = [ [[package]] name = "dnspython" -version = "2.2.1" +version = "2.3.0" description = "DNS toolkit" category = "main" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.7,<4.0" files = [ - {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, - {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, + {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, + {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, ] [package.extras] curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -dnssec = ["cryptography (>=2.6,<37.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"] +dnssec = ["cryptography (>=2.6,<40.0)"] +doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.11.0)"] +doq = ["aioquic (>=0.9.20)"] idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.20)"] +trio = ["trio (>=0.14,<0.23)"] wmi = ["wmi (>=1.5.1,<2.0.0)"] [[package]] @@ -1029,13 +1030,13 @@ websocket-client = ">=0.40.0,<1" [[package]] name = "future" -version = "0.18.2" +version = "0.18.3" description = "Clean single-source support for Python 3 and 2" category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ - {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, + {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, ] [[package]] @@ -1208,14 +1209,14 @@ pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0 [[package]] name = "identify" -version = "2.5.12" +version = "2.5.13" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "identify-2.5.12-py2.py3-none-any.whl", hash = "sha256:e8a400c3062d980243d27ce10455a52832205649bbcaf27ffddb3dfaaf477bad"}, - {file = "identify-2.5.12.tar.gz", hash = "sha256:0bc96b09c838310b6fcfcc61f78a981ea07f94836ef6ef553da5bb5d4745d662"}, + {file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"}, + {file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"}, ] [package.extras] @@ -1690,6 +1691,41 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "opencolorio" +version = "2.2.1" +description = "OpenColorIO (OCIO) is a complete color management solution geared towards motion picture production with an emphasis on visual effects and computer animation." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "opencolorio-2.2.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a9feec76e450325f12203264194d905a938d5e7944772b806886f9531e406d42"}, + {file = "opencolorio-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7eeae01328b359408940a1f29d53b15b034755413d95d08781b76084ee14cbb1"}, + {file = "opencolorio-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85b63a9162e99f0f29ef4074017d1b6e8caf59096043fb91cbacfc5bc01fa0b9"}, + {file = "opencolorio-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67d19ea54daff2b209b91981da415aa41ea8e3a60fecd5dd843ae13272d38dcf"}, + {file = "opencolorio-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:da0043a1007d269b5da3c8ca1de8c63926b38bf5e08cfade6cb8f2f5f6b663b9"}, + {file = "opencolorio-2.2.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:62180cec075cae8dff56eeb977132eb9755d7fe312d8d34236cba838cb9314b3"}, + {file = "opencolorio-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:24b7bfc4b77c04845de847373e58232c48838042d5e45e027b8bf64bada988e3"}, + {file = "opencolorio-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41cadab13b18dbedd992df2056c787cf38bf89a5b0903b90f701d5228ac496f9"}, + {file = "opencolorio-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa278dd4414791a5605e685b562b6ad1c729a4a44c1c906151f5bca10c0ff10e"}, + {file = "opencolorio-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:7b44858c26b662ec42b089f8f85ea3aa63aa04e0e58e902a4cbf8cae0fbd4c6c"}, + {file = "opencolorio-2.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:07fce0d36a6041b524b2122b9f55fbd03e029def5a22e93822041b652b60590a"}, + {file = "opencolorio-2.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae043bc588d9ee98f54fe9524481eba5634d6dd70d0c70e1bd242b60a3a81731"}, + {file = "opencolorio-2.2.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a4ad1a4ed5742a7dda41f0548274e8328b2774ce04dfc31fd5dfbacabc4c166"}, + {file = "opencolorio-2.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bd885e34898c204db19a9e6926c551a74bda6d8e7d3ef27596630e3422b99b1"}, + {file = "opencolorio-2.2.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:86ed205bec96fd84e882d431c181560df0cf6f0f73150976303b6f3ff1d9d5ed"}, + {file = "opencolorio-2.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c1bf1c19baa86203e2329194ea837161520dae5c94e4f04b7659e9bfe4f1a6a9"}, + {file = "opencolorio-2.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639f7052da7086d999c0d84e424453fb44abc8f2d22ec8601d20d8ee9d90384b"}, + {file = "opencolorio-2.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7e3208c5c1ac63a6e921398db661fdd9309b17253b285f227818713f3faec92"}, + {file = "opencolorio-2.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:68814600c0d8c07b552e1f1e4e32d45bffba4cb49b41481e5d4dd0bc56a206ea"}, + {file = "opencolorio-2.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:cb5337ac2804dbb90c856b423d2799d3dc35f9c948da25d8e6506d1dd8200df7"}, + {file = "opencolorio-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:425593a96de7927aa7cda065dc3729e881de1d0b72c43e704e02962adb63b4ad"}, + {file = "opencolorio-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8be9f6af01e4c710de4cc03c9b6de04ef0844bf611e9100abf045ec62a4c685a"}, + {file = "opencolorio-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84788002aa28151409f2367a040e9d39ffea0a9129777451bd0c55ac87d9d47"}, + {file = "opencolorio-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:d92802922bc4e2ff3e9a06d44b6055efd1863abb1aaf0243849d35b077b72253"}, + {file = "opencolorio-2.2.1.tar.gz", hash = "sha256:283abb8db5bc18ab9686e08255a9245deaba3d7837be5363b7a69b0902b73a78"}, +] + [[package]] name = "opentimelineio" version = "0.14.1" @@ -1734,7 +1770,7 @@ view = ["PySide2 (>=5.11,<6.0)"] name = "packaging" version = "23.0" description = "Core utilities for Python packages" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1784,18 +1820,18 @@ testing = ["docopt", "pytest (<6.0.0)"] [[package]] name = "patchelf" -version = "0.17.0.0" +version = "0.17.2.0" description = "A small utility to modify the dynamic linker and RPATH of ELF executables." category = "dev" optional = false python-versions = "*" files = [ - {file = "patchelf-0.17.0.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bb675f65ad3d999763e7332a53ec98d5ae186fb057e15f781de0d108e2258f5d"}, - {file = "patchelf-0.17.0.0-py2.py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:634e7b0c389251f2f8ae3f30a0cfb15873048113fcd22ae18ca354a96dc94368"}, - {file = "patchelf-0.17.0.0-py2.py3-none-manylinux_2_17_s390x.manylinux2014_s390x.musllinux_1_1_s390x.whl", hash = "sha256:80008417d50f66a82747a1a851f100c0f2de5f0df7aec9eaca0b479ad10a3caa"}, - {file = "patchelf-0.17.0.0-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.musllinux_1_1_i686.whl", hash = "sha256:f01e680cdfbbcdfd70dc95b23fe19f6c05b76a92961d04bf812ee188b8bd013a"}, - {file = "patchelf-0.17.0.0-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:858447ad58f84818afce32ad870c559fa27c3fe102302e9906d376461055e599"}, - {file = "patchelf-0.17.0.0.tar.gz", hash = "sha256:a90c0244593ad353513b098a3c46a5fd60a71c160b7acf86ed16f1b5cb98878b"}, + {file = "patchelf-0.17.2.0-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:b8d86f32e1414d6964d5d166ddd2cf829d156fba0d28d32a3bd0192f987f4529"}, + {file = "patchelf-0.17.2.0-py2.py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:9233a0f2fc73820c5bd468f27507bdf0c9ac543f07c7f9888bb7cf910b1be22f"}, + {file = "patchelf-0.17.2.0-py2.py3-none-manylinux_2_17_s390x.manylinux2014_s390x.musllinux_1_1_s390x.whl", hash = "sha256:6601d7d831508bcdd3d8ebfa6435c2379bf11e41af2409ae7b88de572926841c"}, + {file = "patchelf-0.17.2.0-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.musllinux_1_1_i686.whl", hash = "sha256:c62a34f0c25e6c2d6ae44389f819a00ccdf3f292ad1b814fbe1cc23cb27023ce"}, + {file = "patchelf-0.17.2.0-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:1b9fd14f300341dc020ae05c49274dd1fa6727eabb4e61dd7fb6fb3600acd26e"}, + {file = "patchelf-0.17.2.0.tar.gz", hash = "sha256:dedf987a83d7f6d6f5512269e57f5feeec36719bd59567173b6d9beabe019efe"}, ] [package.extras] @@ -1831,6 +1867,13 @@ files = [ {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, + {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, + {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, + {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, + {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, + {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, + {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, + {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, @@ -1965,14 +2008,14 @@ virtualenv = ">=20.10.0" [[package]] name = "prefixed" -version = "0.5.0" +version = "0.6.0" description = "Prefixed alternative numeric library" category = "main" optional = false python-versions = "*" files = [ - {file = "prefixed-0.5.0-py2.py3-none-any.whl", hash = "sha256:debab03014863087eb013750a2e71daa5f6a295cee46b44ba1b90d7262c1b92d"}, - {file = "prefixed-0.5.0.tar.gz", hash = "sha256:b134d734136250b17b68eede65a3370fab0134412cb66bc8be3568ff05bdf8e4"}, + {file = "prefixed-0.6.0-py2.py3-none-any.whl", hash = "sha256:5ab094773dc71df68cc78151c81510b9521dcc6b58a4acb78442b127d4e400fa"}, + {file = "prefixed-0.6.0.tar.gz", hash = "sha256:b39fbfac72618fa1eeb5b3fd9ed1341f10dd90df75499cb4c38a6c3ef47cdd94"}, ] [[package]] @@ -2309,7 +2352,7 @@ files = [ cffi = ">=1.4.1" [package.extras] -docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +docs = ["sphinx (>=1.6.5)", "sphinx_rtd_theme"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] @@ -2583,14 +2626,14 @@ files = [ [[package]] name = "pytz" -version = "2022.7" +version = "2022.7.1" description = "World timezone definitions, modern and historical" category = "dev" optional = false python-versions = "*" files = [ - {file = "pytz-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"}, - {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"}, + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] [[package]] @@ -2805,18 +2848,18 @@ files = [ [[package]] name = "setuptools" -version = "65.6.3" +version = "65.7.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"}, - {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"}, + {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, + {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -3139,14 +3182,14 @@ files = [ [[package]] name = "urllib3" -version = "1.26.13" +version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"}, - {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"}, + {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, + {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, ] [package.extras] @@ -3177,14 +3220,14 @@ testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7 [[package]] name = "wcwidth" -version = "0.2.5" +version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" category = "main" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] [[package]] @@ -3419,4 +3462,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "2.0" python-versions = ">=3.9.1,<3.10" -content-hash = "03a7c6c3f5aebd2e3ca4c23f66c8ae1c119cef7e041460f9ec916726df6a569e" +content-hash = "02daca205796a0f29a0d9f50707544e6804f32027eba493cd2aa7f175a00dcea" diff --git a/pyproject.toml b/pyproject.toml index 329e6bb3e74..9adea409f05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.14.2" # OpenPype +version = "3.14.10" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" @@ -70,6 +70,7 @@ requests = "^2.25.1" pysftp = "^0.2.9" dropbox = "^11.20.0" aiohttp-middlewares = "^2.0.0" +opencolorio = "^2.2.0" [tool.poetry.dev-dependencies] flake8 = "^6.0" diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index d804c43219d..2bafa169714 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -14,6 +14,7 @@ from tests.lib.db_handler import DBHandler from common.openpype_common.distribution.file_handler import RemoteFileHandler from openpype.modules import ModulesManager +from openpype.settings import get_project_settings class BaseTest: @@ -28,6 +29,7 @@ class ModuleUnitTest(BaseTest): Implemented fixtures: monkeypatch_session - fixture for env vars with session scope + project_settings - fixture for project settings with session scope download_test_data - tmp folder with extracted data from GDrive env_var - sets env vars from input file db_setup - prepares avalon AND openpype DBs for testing from @@ -59,6 +61,12 @@ def monkeypatch_session(self): yield m m.undo() + @pytest.fixture(scope='module') + def project_settings(self): + yield get_project_settings( + self.PROJECT + ) + @pytest.fixture(scope="module") def download_test_data(self, test_data_folder, persist, request): test_data_folder = test_data_folder or self.TEST_DATA_FOLDER @@ -67,6 +75,7 @@ def download_test_data(self, test_data_folder, persist, request): yield test_data_folder else: tmpdir = tempfile.mkdtemp() + print("Temporary folder created:: {}".format(tmpdir)) for test_file in self.TEST_FILES: file_id, file_name, md5 = test_file @@ -78,7 +87,6 @@ def download_test_data(self, test_data_folder, persist, request): if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS: # noqa: E501 RemoteFileHandler.unzip(os.path.join(tmpdir, file_name)) - print("Temporary folder created:: {}".format(tmpdir)) yield tmpdir persist = (persist or self.PERSIST or @@ -87,6 +95,15 @@ def download_test_data(self, test_data_folder, persist, request): print("Removing {}".format(tmpdir)) shutil.rmtree(tmpdir) + @pytest.fixture(scope="module") + def output_folder_url(self, download_test_data): + """Returns location of published data, cleans it first if exists.""" + path = os.path.join(download_test_data, "output") + if os.path.exists(path): + print("Purging {}".format(path)) + shutil.rmtree(path) + yield path + @pytest.fixture(scope="module") def env_var(self, monkeypatch_session, download_test_data): """Sets temporary env vars from json file.""" @@ -152,14 +169,27 @@ def db_setup(self, download_test_data, env_var, monkeypatch_session, db_handler.teardown(self.TEST_OPENPYPE_NAME) @pytest.fixture(scope="module") - def dbcon(self, db_setup): + def dbcon(self, db_setup, output_folder_url): """Provide test database connection. Database prepared from dumps with 'db_setup' fixture. """ from openpype.pipeline import AvalonMongoDB dbcon = AvalonMongoDB() - dbcon.Session["AVALON_PROJECT"] = self.TEST_PROJECT_NAME + dbcon.Session["AVALON_PROJECT"] = self.PROJECT + dbcon.Session["AVALON_ASSET"] = self.ASSET + dbcon.Session["AVALON_TASK"] = self.TASK + + # set project root to temp folder + platform_str = platform.system().lower() + root_key = "config.roots.work.{}".format(platform_str) + dbcon.update_one( + {"type": "project"}, + {"$set": + { + root_key: output_folder_url + }} + ) yield dbcon @pytest.fixture(scope="module") @@ -228,15 +258,6 @@ def app_name(self, app_variant): yield "{}/{}".format(self.APP_GROUP, app_variant) - @pytest.fixture(scope="module") - def output_folder_url(self, download_test_data): - """Returns location of published data, cleans it first if exists.""" - path = os.path.join(download_test_data, "output") - if os.path.exists(path): - print("Purging {}".format(path)) - shutil.rmtree(path) - yield path - @pytest.fixture(scope="module") def app_args(self, download_test_data): """Returns additional application arguments from a test file. @@ -267,17 +288,6 @@ def app_args(self, download_test_data): def launched_app(self, dbcon, download_test_data, last_workfile_path, startup_scripts, app_args, app_name, output_folder_url): """Launch host app""" - # set publishing folders - platform_str = platform.system().lower() - root_key = "config.roots.work.{}".format(platform_str) - dbcon.update_one( - {"type": "project"}, - {"$set": - { - root_key: output_folder_url - }} - ) - # set schema - for integrate_new from openpype import PACKAGE_DIR # Path to OpenPype's schema diff --git a/tests/unit/openpype/pipeline/lib.py b/tests/unit/openpype/pipeline/lib.py new file mode 100644 index 00000000000..7a920b40aea --- /dev/null +++ b/tests/unit/openpype/pipeline/lib.py @@ -0,0 +1,13 @@ +import pytest +from tests.lib.testing_classes import ModuleUnitTest +from openpype.pipeline import legacy_io + + +class TestPipeline(ModuleUnitTest): + """ Testing Pipeline base class + """ + + @pytest.fixture(scope="module") + def legacy_io(self, dbcon): + legacy_io.Session = dbcon.Session + yield legacy_io.Session diff --git a/tests/unit/openpype/pipeline/publish/test_publish_plugins.py b/tests/unit/openpype/pipeline/publish/test_publish_plugins.py new file mode 100644 index 00000000000..5c2d7b367f6 --- /dev/null +++ b/tests/unit/openpype/pipeline/publish/test_publish_plugins.py @@ -0,0 +1,216 @@ + + +"""Test Publish_plugins pipeline publish modul, tests API methods + + File: + creates temporary directory and downloads .zip file from GDrive + unzips .zip file + uses content of .zip file (MongoDB's dumps) to import to new databases + with use of 'monkeypatch_session' modifies required env vars + temporarily + runs battery of tests checking that site operation for Sync Server + module are working + removes temporary folder + removes temporary databases (?) +""" +import os +import pytest +import shutil +import logging + +from tests.unit.openpype.pipeline.lib import TestPipeline +from openpype.pipeline.publish import publish_plugins +from openpype.pipeline import colorspace + +log = logging.getLogger(__name__) + + +class TestPipelinePublishPlugins(TestPipeline): + """ Testing Pipeline pubish_plugins.py + + Example: + cd to OpenPype repo root dir + poetry run python ./start.py runtests \ + ../tests/unit/openpype/pipeline/publish + """ + + # files are the same as those used in `test_pipeline_colorspace` + TEST_FILES = [ + ( + "1d7t9_cVKeZRVF0ppCHiE5MJTTtTlJgBe", + "test_pipeline_colorspace.zip", + "" + ) + ] + PROJECT = "test_project" + ASSET = "sh0010" + HIERARCHY = "shots/sq010" + TASK = "comp" + + @pytest.fixture(scope="module") + def context(self, legacy_io, project_settings): + class CTX: + data = { + "projectName": legacy_io["AVALON_PROJECT"], + "asset": legacy_io["AVALON_ASSET"], + "hostName": "nuke", + "anatomyData": {}, + "project_settings": project_settings + } + yield CTX + + @pytest.fixture(scope="module") + def config_path_project( + self, + download_test_data, + output_folder_url + ): + src_path = os.path.join( + download_test_data, + "input", + "data", + "configs", + "aces_1.3", + "ayon_aces_config_project.ocio" + ) + dest_dir = os.path.join( + output_folder_url, + self.PROJECT, + "config" + ) + dest_path = os.path.join( + dest_dir, + "aces.ocio" + ) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + + shutil.copyfile(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def config_path_asset( + self, + download_test_data, + output_folder_url + ): + src_path = os.path.join( + download_test_data, + "input", + "data", + "configs", + "aces_1.3", + "ayon_aces_config_asset.ocio" + ) + dest_dir = os.path.join( + output_folder_url, + self.PROJECT, + self.HIERARCHY, + self.ASSET, + "config" + ) + dest_path = os.path.join( + dest_dir, + "aces.ocio" + ) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + + shutil.copyfile(src_path, dest_path) + + yield dest_path + + def test_get_colorspace_settings(self, context, config_path_asset): + expected_config_template = ( + "{root[work]}/{project[name]}" + "/{hierarchy}/{asset}/config/aces.ocio" + ) + expected_file_rules = { + "comp_review": { + "pattern": "renderCompMain.baking_h264", + "colorspace": "Camera Rec.709", + "ext": "mp4" + } + } + + # load plugin function for testing + plugin = publish_plugins.ExtractorColormanaged() + plugin.log = log + config_data, file_rules = plugin.get_colorspace_settings(context) + + assert config_data["template"] == expected_config_template, ( + "Returned config tempate is not " + f"matching {expected_config_template}" + ) + assert file_rules == expected_file_rules, ( + "Returned file rules are not " + f"matching {expected_file_rules}" + ) + + def test_set_representation_colorspace( + self, context, project_settings, + config_path_project, config_path_asset + ): + expected_colorspace_hiero = "sRGB - Texture" + expected_colorspace_nuke = "Camera Rec.709" + + config_data_nuke = colorspace.get_imageio_config( + "test_project", "nuke", project_settings) + file_rules_nuke = colorspace.get_imageio_file_rules( + "test_project", "nuke", project_settings) + + config_data_hiero = colorspace.get_imageio_config( + "test_project", "hiero", project_settings) + file_rules_hiero = colorspace.get_imageio_file_rules( + "test_project", "hiero", project_settings) + + representation_nuke = { + "ext": "mp4", + "files": "this_file_renderCompMain.baking_h264.mp4" + } + representation_hiero = { + "ext": "mp4", + "files": "this_file_renderCompMain_h264burninburnin.mp4" + } + + # load plugin function for testing + plugin = publish_plugins.ExtractorColormanaged() + plugin.log = log + plugin.set_representation_colorspace( + representation_nuke, context, + colorspace_settings=(config_data_nuke, file_rules_nuke) + ) + # load plugin function for testing + plugin = publish_plugins.ExtractorColormanaged() + plugin.log = log + plugin.set_representation_colorspace( + representation_hiero, context, + colorspace_settings=(config_data_hiero, file_rules_hiero) + ) + + colorspace_data_nuke = representation_nuke.get("colorspaceData") + colorspace_data_hiero = representation_hiero.get("colorspaceData") + + assert colorspace_data_nuke, ( + "Colorspace data were not created in prepresentation" + f"matching {representation_nuke}" + ) + assert colorspace_data_hiero, ( + "Colorspace data were not created in prepresentation" + f"matching {representation_hiero}" + ) + + ret_colorspace_nuke = colorspace_data_nuke["colorspace"] + assert ret_colorspace_nuke == expected_colorspace_nuke, ( + "Returned colorspace is not " + f"matching {expected_colorspace_nuke}" + ) + ret_colorspace_hiero = colorspace_data_hiero["colorspace"] + assert ret_colorspace_hiero == expected_colorspace_hiero, ( + "Returned colorspace is not " + f"matching {expected_colorspace_hiero}" + ) + + +test_case = TestPipelinePublishPlugins() diff --git a/tests/unit/openpype/pipeline/test_colorspace.py b/tests/unit/openpype/pipeline/test_colorspace.py new file mode 100644 index 00000000000..d064ca2be49 --- /dev/null +++ b/tests/unit/openpype/pipeline/test_colorspace.py @@ -0,0 +1,189 @@ + + +"""Test Colorspace pipeline modul, tests API methods + + File: + creates temporary directory and downloads .zip file from GDrive + unzips .zip file + uses content of .zip file (MongoDB's dumps) to import to new databases + with use of 'monkeypatch_session' modifies required env vars + temporarily + runs battery of tests checking that site operation for Sync Server + module are working + removes temporary folder + removes temporary databases (?) +""" +import pytest +import shutil +import os + +from tests.unit.openpype.pipeline.lib import TestPipeline +from openpype.pipeline import colorspace + + +class TestPipelineColorspace(TestPipeline): + """ Testing Colorspace + + Example: + cd to OpenPype repo root dir + poetry run python ./start.py runtests ../tests/unit/openpype/pipeline + """ + + TEST_FILES = [ + ( + "1d7t9_cVKeZRVF0ppCHiE5MJTTtTlJgBe", + "test_pipeline_colorspace.zip", + "" + ) + ] + + PROJECT = "test_project" + ASSET = "test_asset" + TASK = "test_task" + + @pytest.fixture(scope="module") + def config_path_project( + self, + download_test_data, + output_folder_url + ): + src_path = os.path.join( + download_test_data, + "input", + "data", + "configs", + "aces_1.3", + "ayon_aces_config_project.ocio" + ) + dest_dir = os.path.join( + output_folder_url, + self.PROJECT, + "config" + ) + dest_path = os.path.join( + dest_dir, + "aces.ocio" + ) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + + shutil.copyfile(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def config_path_asset( + self, + download_test_data, + output_folder_url + ): + src_path = os.path.join( + download_test_data, + "input", + "data", + "configs", + "aces_1.3", + "ayon_aces_config_asset.ocio" + ) + dest_dir = os.path.join( + output_folder_url, + self.PROJECT, + self.ASSET, + "config" + ) + dest_path = os.path.join( + dest_dir, + "aces.ocio" + ) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + + shutil.copyfile(src_path, dest_path) + + yield dest_path + + def test_config_file_project( + self, + legacy_io, + config_path_project, + project_settings + ): + expected_template = "{root[work]}/{project[name]}/config/aces.ocio" + + # get config_data from hiero + # where project level config is defined + config_data = colorspace.get_imageio_config( + "test_project", "hiero", project_settings) + + assert os.path.exists(config_data["path"]), ( + f"Config file \'{config_data['path']}\' doesn't exist" + ) + assert config_data["template"] == expected_template, ( + f"Config template \'{config_data['template']}\' doesn't match " + f"expected tempalte \'{expected_template}\'" + ) + + def test_parse_colorspace_from_filepath( + self, + legacy_io, + config_path_asset, + project_settings + ): + path_1 = "renderCompMain_ACES2065-1.####.exr" + expected_1 = "ACES2065-1" + ret_1 = colorspace.parse_colorspace_from_filepath( + path_1, "nuke", "test_project", project_settings=project_settings + ) + assert ret_1 == expected_1, f"Not matching colorspace {expected_1}" + + path_2 = "renderCompMain_BMDFilm_WideGamut_Gen5.mov" + expected_2 = "BMDFilm WideGamut Gen5" + ret_2 = colorspace.parse_colorspace_from_filepath( + path_2, "nuke", "test_project", project_settings=project_settings + ) + assert ret_2 == expected_2, f"Not matching colorspace {expected_2}" + + def test_get_ocio_config_views_asset(self, config_path_asset): + expected_num_keys = 12 + + ret = colorspace.get_ocio_config_views(config_path_asset) + + assert len(ret) == expected_num_keys, ( + f"Not matching num viewer keys {expected_num_keys}") + + def test_get_ocio_config_views_project(self, config_path_project): + expected_num_keys = 3 + + ret = colorspace.get_ocio_config_views(config_path_project) + + assert len(ret) == expected_num_keys, ( + f"Not matching num viewer keys {expected_num_keys}") + + def test_file_rules(self, project_settings): + expected_nuke = { + "comp_review": { + "pattern": "renderCompMain.baking_h264", + "colorspace": "Camera Rec.709", + "ext": "mp4" + } + } + expected_hiero = { + "comp_review": { + "pattern": "renderCompMain_h264burninburnin", + "colorspace": "sRGB - Texture", + "ext": "mp4" + } + } + + nuke_file_rules = colorspace.get_imageio_file_rules( + "test_project", "nuke", project_settings=project_settings) + assert expected_nuke == nuke_file_rules, ( + f"Not matching file rules {expected_nuke}") + + hiero_file_rules = colorspace.get_imageio_file_rules( + "test_project", "hiero", project_settings=project_settings) + assert expected_hiero == hiero_file_rules, ( + f"Not matching file rules {expected_hiero}") + + +test_case = TestPipelineColorspace() diff --git a/website/docs/dev_colorspace.md b/website/docs/dev_colorspace.md new file mode 100644 index 00000000000..fe9a0ec1e38 --- /dev/null +++ b/website/docs/dev_colorspace.md @@ -0,0 +1,120 @@ +--- +id: dev_colorspace +title: Colorspace Management and Distribution +sidebar_label: Colorspace +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Introduction +Defines the distribution of colors and OCIO config during publishing. Once colorspace data are captured and integrated into representation loaders could use them for loading image and video data with correct colorspace. + +:::warning Color Management (ImageIO) +Adding the `imagio` settings schema is required for any host or module which is processing pixel data. +::: + +## Data model +Published representations that are extracted with color managed data store a **colorspaceData** entry in its data: `representation_doc["data"]["colorspaceData"]`. + +It's up to the Host implementation to pre-configure the application or workfile to have the correct OCIO config applied. +It's up to the Extractors to set these values for the representation during publishing. +It's up to the Loaders to read these values and apply the correct expected color space. + +### Keys +- **colorspace** - string value used in other publish plugins and loaders +- **config** - storing two versions of path. + - **path** - is formated and with baked platform root. It is used for posible need to find out where we were sourcing color config during publishing. + - **template** - unformated tempate resolved from settings. It is used for other plugins targeted to remote publish which could be processed at different platform. + +### Example + { + "colorspace": "linear", + "config": { + "path": "/abs/path/to/config.ocio", + "template": "{project[root]}/path/to/config.ocio" + } + } + + +## How to integrate it into a host +1. The settings for a host should add the `imagio` schema. Ideally near the top of all categories in its `/settings/entities/schemas/system_scheams/host_settings/schema_{host}.json` so it matches the settings layout other hosts. +```json +{ + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "type": "schema", + "name": "schema_imageio_config" + }, + { + "type": "schema", + "name": "schema_imageio_file_rules" + } + + ] +} +``` + +2. Set the OCIO config path for the host to the path returned from `openpype.pipeline.colorspace.get_imageio_config`, for example: + - set the `OCIO` environment variable before launching the host via a prelaunch hook + - or (if the host allows) to set the workfile OCIO config path using the host's API + +3. Each Extractor exporting pixel data (e.g. image or video) has to use parent class `openpype.pipeline.publish.publish_plugins.ExtractorColormanaged` and use `self.set_representation_colorspace` on the representations to be integrated. + +The **set_representation_colorspace** method adds `colorspaceData` to the representation. If the `colorspace` passed is not `None` then it is added directly to the representation with resolved config path otherwise a color space is assumed using the configured file rules. If no file rule matches the `colorspaceData` is **not** added to the representation. + +An example implementation can be found here: `openpype\hosts\nuke\plugins\publish\extract_render_local.py` + + +4. The Loader plug-ins should take into account the `colorspaceData` in the published representation's data to allow the DCC to read in the expected color space. +```python +from openpype.pipeline.colorspace import ( + get_imageio_colorspace_from_filepath, + get_imageio_config, + get_imageio_file_rules +) + +class YourLoader(api.Loader): + def load(self, context, name=None, namespace=None, options=None): + path = self.fname + colorspace_data = context["representation"]["data"].get("colorspaceData", {}) + colorspace = ( + colorspace_data.get("colorspace") + # try to match colorspace from file rules + or self.get_colorspace_from_file_rules(path, context) + ) + + # pseudocode + load_file(path, colorspace=colorspace) + + def get_colorspace_from_file_rules(self, path, context) + project_name = context.data["projectName"] + host_name = context.data["hostName"] + anatomy_data = context.data["anatomyData"] + project_settings_ = context.data["project_settings"] + + config_data = get_imageio_config( + project_name, host_name, + project_settings=project_settings_, + anatomy_data=anatomy_data + ) + file_rules = get_imageio_file_rules( + project_name, host_name, + project_settings=project_settings_ + ) + # get matching colorspace from rules + colorspace = get_imageio_colorspace_from_filepath( + path, host_name, project_name, + config_data=config_data, + file_rules=file_rules, + project_settings=project_settings + ) +``` + +:::warning Loading +A custom OCIO config can be set per asset/shot and thus it can happen the current session you are loading into uses a different config than the original context's **colorspaceData** was published with. It's up the loader's implementation to take that into account and decide what to do if the colorspace differs and or might not exist. +::: \ No newline at end of file diff --git a/website/docs/project_settings/settings_project_global.md b/website/docs/project_settings/settings_project_global.md index 9666c6568ad..c5495b573ec 100644 --- a/website/docs/project_settings/settings_project_global.md +++ b/website/docs/project_settings/settings_project_global.md @@ -13,6 +13,39 @@ Project settings can have project specific values. Each new project is using stu Projects always use default project values unless they have [project override](../admin_settings#project-overrides) (orage colour). Any changes in default project may affect all existing projects. ::: +## Color Management (ImageIO) + +:::info Default OCIO config +OpenPype distributes its own OCIO configs. Those can be found in `{openpype install dir}/{version}/vendor/bin/ocioconfig/OpenColorIOConfigs`. Windows example: `C:\Program Files (x86)\OpenPype\3.14.0\vendor\bin\ocioconfig\OpenColorIOConfigs` +::: + +### Using OCIO config +Global config path is set by default to OpenPype distributed configs. At the moment there are only two - **aces_1.2** and **nuke-default**. Since this path input is not platform specific it is required to use at least an environment variable do platform specific config root directory. Order of paths matter so first path found and existing first served. + +Each OCIO config path input supports formatting using environment variables and [anatomy template keys](../admin_settings_project_anatomy#available-template-keys). The default global OCIO config path is `{OPENPYPE_ROOT}/vendor/bin/ocioconfig/OpenColorIOConfigs/aces_1.2/config.ocio`. + +If the project settings for a particular host has its own OCIO config **enabled** and set to at least one path and the path exists, it overrides the global OCIO config for that host. + +**For example** + +Project nuke-specific OCIO config: `project_settings/nuke/imageio/ocio_config` + +If config path is defined to particular shot target with following path inputs: +1. `{root[work]}/{project[name]}/{hierarchy}/{asset}/config/aces.ocio` +2. `{root[work]}/{project[name]}/{hierarchy}/config/aces.ocio` + +Procedure of resolving path (from above example) will look first into path 1st and if the path is not existing then it will try 2nd and if even that is not existing then it will fall back to global default. + +### Using File rules +File rules are inspired by [OCIO v2 configuration]((https://opencolorio.readthedocs.io/en/latest/guides/authoring/rules.html)). Each rule has a unique name which can be overridden by host-specific _File rules_ (example: `project_settings/nuke/imageio/file_rules/rules`). + +The _input pattern_ matching uses REGEX expression syntax (try [regexr.com](https://regexr.com/)). Matching rules procedure's intention is to be used during publishing or loading of representation. Since the publishing procedure is run before integrator formate publish template path, make sure the pattern is working or any work render path. + +:::warning Colorspace name input +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. +::: + + ## Profile filters Many of the settings are using a concept of **Profile filters** diff --git a/website/sidebars.js b/website/sidebars.js index f2d9ffee061..a70addfcea3 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -158,6 +158,7 @@ module.exports = { "dev_publishing" ] }, - "dev_deadline" + "dev_deadline", + "dev_colorspace" ] };