mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #4270 from ynput/feature/OP-4406_color-v3-global-color-plugins
This commit is contained in:
commit
36b3ccdd54
14 changed files with 2127 additions and 841 deletions
|
|
@ -1,4 +1,3 @@
|
|||
import enlighten
|
||||
import os
|
||||
import re
|
||||
import urllib
|
||||
|
|
@ -252,6 +251,11 @@ class RemoteFileHandler:
|
|||
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 @@ class RemoteFileHandler:
|
|||
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):
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import nuke
|
|||
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
|
||||
|
|
@ -22,6 +21,10 @@ class NukeRenderLocal(publish.Extractor):
|
|||
families = ["render.local", "prerender.local", "still.local"]
|
||||
|
||||
def process(self, instance):
|
||||
# get colorspace settings data
|
||||
config_data, file_rules = self.get_colorspace_settings(
|
||||
instance.context)
|
||||
|
||||
families = instance.data["families"]
|
||||
|
||||
node = None
|
||||
|
|
@ -67,6 +70,7 @@ class NukeRenderLocal(publish.Extractor):
|
|||
)
|
||||
|
||||
ext = node["file_type"].value()
|
||||
colorspace = node["colorspace"].value()
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
|
@ -90,6 +94,14 @@ class NukeRenderLocal(publish.Extractor):
|
|||
'files': filenames,
|
||||
"stagingDir": out_dir
|
||||
}
|
||||
|
||||
# inject colorspace data
|
||||
self.set_representation_colorspace(
|
||||
repre, instance.context,
|
||||
config_data, file_rules,
|
||||
colorspace=colorspace
|
||||
)
|
||||
|
||||
instance.data["representations"].append(repre)
|
||||
|
||||
self.log.info("Extracted instance '{0}' to: {1}".format(
|
||||
|
|
|
|||
423
openpype/pipeline/colorspace.py
Normal file
423
openpype/pipeline/colorspace.py
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
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(
|
||||
"Not able 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)
|
||||
current_platform = platform.system().lower()
|
||||
|
||||
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
|
||||
|
||||
# get colorspace settings
|
||||
imageio_global, imageio_host = _get_imageio_settings(
|
||||
project_settings, host_name)
|
||||
|
||||
# get config path from either global or host_name
|
||||
config_global = imageio_global["ocio_config"]
|
||||
config_host = imageio_host["ocio_config"]
|
||||
|
||||
# set config path
|
||||
config_path = None
|
||||
if config_global["enabled"]:
|
||||
config_path = config_global["filepath"][current_platform]
|
||||
if config_host["enabled"]:
|
||||
config_path = config_host["filepath"][current_platform]
|
||||
|
||||
if not config_path:
|
||||
return
|
||||
|
||||
formatting_data = deepcopy(anatomy_data)
|
||||
|
||||
# format the path for potential env vars
|
||||
formatting_data.update(dict(**os.environ))
|
||||
|
||||
# format path for anatomy keys
|
||||
formatted_path = StringTemplate(config_path).format(
|
||||
formatting_data)
|
||||
|
||||
abs_path = os.path.abspath(formatted_path)
|
||||
return {
|
||||
"path": os.path.normpath(abs_path),
|
||||
"template": config_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
|
||||
|
|
@ -19,6 +19,7 @@ from .publish_plugins import (
|
|||
RepairContextAction,
|
||||
|
||||
Extractor,
|
||||
ExtractorColormanaged,
|
||||
)
|
||||
|
||||
from .lib import (
|
||||
|
|
@ -63,6 +64,7 @@ __all__ = (
|
|||
"RepairContextAction",
|
||||
|
||||
"Extractor",
|
||||
"ExtractorColormanaged",
|
||||
|
||||
"get_publish_template_name",
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ from .lib import (
|
|||
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 @@ class RepairContextAction(pyblish.api.Action):
|
|||
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,93 @@ class Extractor(pyblish.api.InstancePlugin):
|
|||
"""
|
||||
|
||||
return get_instance_staging_dir(instance)
|
||||
|
||||
|
||||
class ExtractorColormanaged(Extractor):
|
||||
"""Extractor base for color managed image data.
|
||||
|
||||
Class implements a "set_representation_colorspace" function, which is 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):
|
||||
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_
|
||||
)
|
||||
return config_data, file_rules
|
||||
|
||||
def set_representation_colorspace(
|
||||
self, representation, context,
|
||||
config_data, file_rules,
|
||||
colorspace=None
|
||||
):
|
||||
|
||||
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.pop()
|
||||
|
||||
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,
|
||||
"configData": config_data
|
||||
}
|
||||
|
||||
# update data key
|
||||
representation["colorspaceData"] = colorspace_data
|
||||
|
||||
self.log.debug("__ colorspace_data: `{}`".format(
|
||||
pformat(colorspace_data)))
|
||||
|
|
|
|||
49
openpype/plugins/publish/extract_colorspace_data.py
Normal file
49
openpype/plugins/publish/extract_colorspace_data.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
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",
|
||||
"configData": {
|
||||
"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
|
||||
config_data, file_rules = self.get_colorspace_settings(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,
|
||||
config_data, file_rules
|
||||
)
|
||||
|
|
@ -529,6 +529,15 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
|
|||
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
|
||||
|
||||
# optionals
|
||||
# retrieve additional anatomy data from representation if exists
|
||||
for key, anatomy_key in {
|
||||
|
|
@ -726,6 +735,11 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
|
|||
# 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
|
||||
)
|
||||
|
|
|
|||
168
openpype/scripts/ocio_wrapper.py
Normal file
168
openpype/scripts/ocio_wrapper.py
Normal file
|
|
@ -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 <command> *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=<path> --out_path=<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=<path> --out_path=<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()
|
||||
1720
poetry.lock
generated
1720
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.14.2-nightly.2" # OpenPype
|
||||
version = "3.14.8" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
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 = "^3.7"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import re
|
|||
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 @@ class ModuleUnitTest(BaseTest):
|
|||
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 @@ class ModuleUnitTest(BaseTest):
|
|||
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 @@ class ModuleUnitTest(BaseTest):
|
|||
|
||||
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 @@ class ModuleUnitTest(BaseTest):
|
|||
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 @@ class ModuleUnitTest(BaseTest):
|
|||
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 @@ class PublishTest(ModuleUnitTest):
|
|||
|
||||
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 @@ class PublishTest(ModuleUnitTest):
|
|||
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
|
||||
|
|
|
|||
13
tests/unit/openpype/pipeline/lib.py
Normal file
13
tests/unit/openpype/pipeline/lib.py
Normal file
|
|
@ -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
|
||||
203
tests/unit/openpype/pipeline/publish/test_publish_plugins.py
Normal file
203
tests/unit/openpype/pipeline/publish/test_publish_plugins.py
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
|
||||
"""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
|
||||
|
||||
from tests.unit.openpype.pipeline.lib import TestPipeline
|
||||
from openpype.pipeline.publish import publish_plugins
|
||||
from openpype.pipeline import colorspace
|
||||
|
||||
|
||||
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 = [
|
||||
(
|
||||
"1kJ1ZYcf7V7jS8IW2routSYQoGUfUWj4F",
|
||||
"test_pipeline_colorspace.zip",
|
||||
""
|
||||
)
|
||||
]
|
||||
|
||||
@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,
|
||||
"ocio"
|
||||
)
|
||||
dest_path = os.path.join(
|
||||
dest_dir,
|
||||
"config.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,
|
||||
"ocio"
|
||||
)
|
||||
dest_path = os.path.join(
|
||||
dest_dir,
|
||||
"config.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):
|
||||
expected_config_template = (
|
||||
"{root[work]}/{project[name]}/{asset}/ocio/config.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()
|
||||
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.set_representation_colorspace(
|
||||
representation_nuke, context,
|
||||
config_data_nuke, file_rules_nuke
|
||||
)
|
||||
# load plugin function for testing
|
||||
plugin = publish_plugins.ExtractorColormanaged()
|
||||
plugin.set_representation_colorspace(
|
||||
representation_hiero, context,
|
||||
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()
|
||||
185
tests/unit/openpype/pipeline/test_colorspace.py
Normal file
185
tests/unit/openpype/pipeline/test_colorspace.py
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
|
||||
|
||||
"""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 = [
|
||||
(
|
||||
"1kJ1ZYcf7V7jS8IW2routSYQoGUfUWj4F",
|
||||
"test_pipeline_colorspace.zip",
|
||||
""
|
||||
)
|
||||
]
|
||||
|
||||
@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,
|
||||
"ocio"
|
||||
)
|
||||
dest_path = os.path.join(
|
||||
dest_dir,
|
||||
"config.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,
|
||||
"ocio"
|
||||
)
|
||||
dest_path = os.path.join(
|
||||
dest_dir,
|
||||
"config.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]}/ocio/config.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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue