From 640c24d9b70665d1ecec6cf293759c213eaf4271 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:33:39 +0200 Subject: [PATCH 001/463] add 'parents' to folder template data --- client/ayon_core/pipeline/template_data.py | 13 +++++++------ .../publish/collect_anatomy_instance_data.py | 5 ++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index d5f06d6a59..2c5346f14b 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -87,14 +87,14 @@ def get_folder_template_data(folder_entity, project_name): """ path = folder_entity["path"] - hierarchy_parts = path.split("/") + parents = path.split("/") # Remove empty string from the beginning - hierarchy_parts.pop(0) + parents.pop(0) # Remove last part which is folder name - folder_name = hierarchy_parts.pop(-1) - hierarchy = "/".join(hierarchy_parts) - if hierarchy_parts: - parent_name = hierarchy_parts[-1] + folder_name = parents.pop(-1) + hierarchy = "/".join(parents) + if parents: + parent_name = parents[-1] else: parent_name = project_name @@ -103,6 +103,7 @@ def get_folder_template_data(folder_entity, project_name): "name": folder_name, "type": folder_entity["folderType"], "path": path, + "parents": parents, }, "asset": folder_name, "hierarchy": hierarchy, diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index b6636696c1..6eeca6ad29 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -407,8 +407,10 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): anatomy_data["hierarchy"] = hierarchy parent_name = project_entity["name"] + parents = [] if hierarchy: - parent_name = hierarchy.split("/")[-1] + parents = hierarchy.split("/") + parent_name = parents[-1] folder_name = instance.data["folderPath"].split("/")[-1] anatomy_data.update({ @@ -422,6 +424,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # Using 'Shot' is current default behavior of editorial # (or 'newHierarchyIntegration') publishing. "type": "Shot", + "parents": parents, }, }) From c08d0baa88839020caa7e9372fb286f100d56fc3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:57:55 +0200 Subject: [PATCH 002/463] simplify split --- client/ayon_core/pipeline/template_data.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index 2c5346f14b..c7aa46fd62 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -87,9 +87,8 @@ def get_folder_template_data(folder_entity, project_name): """ path = folder_entity["path"] - parents = path.split("/") - # Remove empty string from the beginning - parents.pop(0) + # Remove empty string from the beginning and split by '/' + parents = path.lstrip("/").split("/") # Remove last part which is folder name folder_name = parents.pop(-1) hierarchy = "/".join(parents) From 282d1720ae958bbe8833ac64d6295e658a28b438 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 12 Sep 2024 16:39:09 +0200 Subject: [PATCH 003/463] Add staging directory functions and configurations - Added functions to handle custom staging directories - Updated imports and removed deprecated code - Created a new module for staging directory handling --- client/ayon_core/pipeline/__init__.py | 27 +-- .../ayon_core/pipeline/publish/constants.py | 1 - client/ayon_core/pipeline/publish/lib.py | 175 ++++++-------- client/ayon_core/pipeline/stagingdir.py | 220 ++++++++++++++++++ client/ayon_core/pipeline/tempdir.py | 90 +++++-- .../publish/collect_custom_staging_dir.py | 76 ------ .../plugins/publish/extract_burnin.py | 11 +- .../publish/extract_color_transcode.py | 15 +- .../plugins/publish/extract_review.py | 7 +- 9 files changed, 396 insertions(+), 226 deletions(-) create mode 100644 client/ayon_core/pipeline/stagingdir.py delete mode 100644 client/ayon_core/plugins/publish/collect_custom_staging_dir.py diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 8fd00ee6b6..d5c3140d37 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -8,6 +8,10 @@ from .constants import ( from .anatomy import Anatomy +from .tempdir import get_temp_dir + +from .stagingdir import get_staging_dir + from .create import ( BaseCreator, Creator, @@ -116,10 +120,12 @@ __all__ = ( "AYON_CONTAINER_ID", "AYON_INSTANCE_ID", "HOST_WORKFILE_EXTENSIONS", - # --- Anatomy --- "Anatomy", - + # --- Temp dir --- + "get_temp_dir", + # --- Staging dir --- + "get_staging_dir", # --- Create --- "BaseCreator", "Creator", @@ -127,42 +133,34 @@ __all__ = ( "HiddenCreator", "CreatedInstance", "CreatorError", - "CreatorError", - # - legacy creation "LegacyCreator", "legacy_create", - "discover_creator_plugins", "discover_legacy_creator_plugins", "register_creator_plugin", "deregister_creator_plugin", "register_creator_plugin_path", "deregister_creator_plugin_path", - # --- Load --- "HeroVersionType", "IncompatibleLoaderError", "LoaderPlugin", "ProductLoaderPlugin", - "discover_loader_plugins", "register_loader_plugin", "deregister_loader_plugin_path", "register_loader_plugin_path", "deregister_loader_plugin", - "load_container", "remove_container", "update_container", "switch_container", - "loaders_from_representation", "get_representation_path", "get_representation_context", "get_repres_contexts", - # --- Publish --- "PublishValidationError", "PublishXmlValidationError", @@ -170,50 +168,41 @@ __all__ = ( "AYONPyblishPluginMixin", "OpenPypePyblishPluginMixin", "OptionalPyblishPluginMixin", - # --- Actions --- "LauncherAction", "InventoryAction", - "discover_launcher_actions", "register_launcher_action", "register_launcher_action_path", - "discover_inventory_actions", "register_inventory_action", "register_inventory_action_path", "deregister_inventory_action", "deregister_inventory_action_path", - # --- Process context --- "install_ayon_plugins", "install_openpype_plugins", "install_host", "uninstall_host", "is_installed", - "register_root", "registered_root", - "register_host", "registered_host", "deregister_host", "get_process_id", - "get_global_context", "get_current_context", "get_current_host_name", "get_current_project_name", "get_current_folder_path", "get_current_task_name", - # Workfile templates "discover_workfile_build_plugins", "register_workfile_build_plugin", "deregister_workfile_build_plugin", "register_workfile_build_plugin_path", "deregister_workfile_build_plugin_path", - # Backwards compatible function names "install", "uninstall", diff --git a/client/ayon_core/pipeline/publish/constants.py b/client/ayon_core/pipeline/publish/constants.py index 38f5ffef3f..5240628365 100644 --- a/client/ayon_core/pipeline/publish/constants.py +++ b/client/ayon_core/pipeline/publish/constants.py @@ -8,4 +8,3 @@ ValidateMeshOrder = pyblish.api.ValidatorOrder + 0.3 DEFAULT_PUBLISH_TEMPLATE = "default" DEFAULT_HERO_PUBLISH_TEMPLATE = "default" -TRANSIENT_DIR_TEMPLATE = "default" diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 8b82622e4c..9cfcd3f71a 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -2,7 +2,6 @@ import os import sys import inspect import copy -import tempfile import xml.etree.ElementTree from typing import Optional, Union, List @@ -18,15 +17,11 @@ from ayon_core.lib import ( ) from ayon_core.settings import get_project_settings from ayon_core.addon import AddonsManager -from ayon_core.pipeline import ( - tempdir, - Anatomy -) +from ayon_core.pipeline import get_staging_dir from ayon_core.pipeline.plugin_discover import DiscoverResult from .constants import ( DEFAULT_PUBLISH_TEMPLATE, DEFAULT_HERO_PUBLISH_TEMPLATE, - TRANSIENT_DIR_TEMPLATE ) @@ -581,58 +576,6 @@ def context_plugin_should_run(plugin, context): return False -def get_instance_staging_dir(instance): - """Unified way how staging dir is stored and created on instances. - - First check if 'stagingDir' is already set in instance data. - In case there already is new tempdir will not be created. - - It also supports `AYON_TMPDIR`, so studio can define own temp - shared repository per project or even per more granular context. - Template formatting is supported also with optional keys. Folder is - created in case it doesn't exists. - - Available anatomy formatting keys: - - root[work | ] - - project[name | code] - - Note: - Staging dir does not have to be necessarily in tempdir so be careful - about its usage. - - Args: - instance (pyblish.lib.Instance): Instance for which we want to get - staging dir. - - Returns: - str: Path to staging dir of instance. - """ - staging_dir = instance.data.get('stagingDir') - if staging_dir: - return staging_dir - - anatomy = instance.context.data.get("anatomy") - - # get customized tempdir path from `AYON_TMPDIR` env var - custom_temp_dir = tempdir.create_custom_tempdir( - anatomy.project_name, anatomy) - - if custom_temp_dir: - staging_dir = os.path.normpath( - tempfile.mkdtemp( - prefix="pyblish_tmp_", - dir=custom_temp_dir - ) - ) - else: - staging_dir = os.path.normpath( - tempfile.mkdtemp(prefix="pyblish_tmp_") - ) - instance.data['stagingDir'] = staging_dir - - return staging_dir - - def get_publish_repre_path(instance, repre, only_published=False): """Get representation path that can be used for integration. @@ -685,6 +628,8 @@ def get_publish_repre_path(instance, repre, only_published=False): return None +# deprecated: backward compatibility only +# TODO: remove in the future def get_custom_staging_dir_info( project_name, host_name, @@ -694,67 +639,85 @@ def get_custom_staging_dir_info( product_name, project_settings=None, anatomy=None, - log=None + log=None, ): - """Checks profiles if context should use special custom dir as staging. + from ayon_core.pipeline.stagingdir import get_staging_dir_config - Args: - project_name (str) - host_name (str) - product_type (str) - task_name (str) - task_type (str) - product_name (str) - project_settings(Dict[str, Any]): Prepared project settings. - anatomy (Dict[str, Any]) - log (Logger) (optional) + tr_data = get_staging_dir_config( + host_name, + project_name, + task_type, + task_name, + product_type, + product_name, + project_settings=project_settings, + anatomy=anatomy, + log=log, + ) + + if not tr_data: + return None, None + + return tr_data["template"], tr_data["persistence"] + + +def get_instance_staging_dir(instance): + """Unified way how staging dir is stored and created on instances. + + First check if 'stagingDir' is already set in instance data. + In case there already is new tempdir will not be created. Returns: - (tuple) - Raises: - ValueError - if misconfigured template should be used + str: Path to staging dir """ - settings = project_settings or get_project_settings(project_name) - custom_staging_dir_profiles = (settings["core"] - ["tools"] - ["publish"] - ["custom_staging_dir_profiles"]) - if not custom_staging_dir_profiles: - return None, None + staging_dir = instance.data.get("stagingDir") - if not log: - log = Logger.get_logger("get_custom_staging_dir_info") + if staging_dir: + return staging_dir - filtering_criteria = { - "hosts": host_name, - "families": product_type, - "task_names": task_name, - "task_types": task_type, - "subsets": product_name - } - profile = filter_profiles(custom_staging_dir_profiles, - filtering_criteria, - logger=log) + anatomy_data = instance.data["anatomyData"] + formatting_data = copy.deepcopy(anatomy_data) - if not profile or not profile["active"]: - return None, None + product_type = instance.data["productType"] + product_name = instance.data["productName"] - if not anatomy: - anatomy = Anatomy(project_name) + # context data based variables + project_entity = instance.context.data["projectEntity"] + folder_entity = instance.context.data["folderEntity"] + task_entity = instance.context.data["taskEntity"] + host_name = instance.context.data["hostName"] + project_settings = instance.context.data["project_settings"] + anatomy = instance.context.data["anatomy"] + current_file = instance.context.data.get("currentFile") - template_name = profile["template_name"] or TRANSIENT_DIR_TEMPLATE + # add current file as workfile name into formatting data + if current_file: + workfile = os.path.basename(current_file) + workfile_name, _ = os.path.splitext(workfile) + formatting_data["workfile_name"] = workfile_name - custom_staging_dir = anatomy.get_template_item( - "staging", template_name, "directory", default=None + dir_data = get_staging_dir( + host_name, + project_entity, + folder_entity, + task_entity, + product_type, + product_name, + anatomy, + project_settings=project_settings, + formatting_data=formatting_data, ) - if custom_staging_dir is None: - raise ValueError(( - "Anatomy of project \"{}\" does not have set" - " \"{}\" template key!" - ).format(project_name, template_name)) - is_persistent = profile["custom_staging_dir_persistent"] - return custom_staging_dir.template, is_persistent + staging_dir_path = dir_data["stagingDir"] + + # TODO: not sure if this is necessary + # path might be already created by get_staging_dir + if not os.path.exists(staging_dir_path): + os.makedirs(staging_dir_path) + + instance.data.update(dir_data) + + return staging_dir_path def get_published_workfile_instance(context): diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py new file mode 100644 index 0000000000..e8fa1c4853 --- /dev/null +++ b/client/ayon_core/pipeline/stagingdir.py @@ -0,0 +1,220 @@ +from ayon_core.lib import Logger, filter_profiles, StringTemplate +from ayon_core.settings import get_project_settings +from .anatomy import Anatomy +from .tempdir import get_temp_dir +from ayon_core.pipeline.template_data import get_template_data + + +STAGING_DIR_TEMPLATES = "staging" + + +def get_staging_dir_config( + host_name, + project_name, + task_type, + task_name, + product_type, + product_name, + project_settings=None, + anatomy=None, + log=None, +): + """Get matching staging dir profile. + + Args: + host_name (str): Name of host. + project_name (str): Name of project. + task_type (str): Type of task. + task_name (str): Name of task. + product_type (str): Type of product. + product_name (str): Name of product. + project_settings(Dict[str, Any]): Prepared project settings. + anatomy (Dict[str, Any]) + log (Optional[logging.Logger]) + + Returns: + Dict or None: Data with directory template and is_persistent or None + Raises: + ValueError - if misconfigured template should be used + """ + settings = project_settings or get_project_settings(project_name) + + staging_dir_profiles = settings["core"]["tools"]["publish"][ + "custom_staging_dir_profiles" + ] + + if not staging_dir_profiles: + return None + + if not log: + log = Logger.get_logger("get_staging_dir_config") + + filtering_criteria = { + "hosts": host_name, + "task_types": task_type, + "task_names": task_name, + "product_types": product_type, + "product_names": product_name, + } + profile = filter_profiles( + staging_dir_profiles, filtering_criteria, logger=log) + + if not profile or not profile["active"]: + return None + + if not anatomy: + anatomy = Anatomy(project_name) + + # get template from template name + template_name = profile["template_name"] + _validate_template_name(project_name, template_name, anatomy) + + template = anatomy.templates[STAGING_DIR_TEMPLATES][template_name] + + if not template: + # template should always be found either from anatomy or from profile + raise ValueError( + "Staging dir profile is misconfigured! " + "No template was found for profile! " + "Check your project settings at: " + "'ayon+settings://core/tools/publish/custom_staging_dir_profiles'" + ) + + data_persistence = profile["custom_staging_dir_persistent"] + + return {"template": template, "persistence": data_persistence} + + +def _validate_template_name(project_name, template_name, anatomy): + """Check that staging dir section with appropriate template exist. + + Raises: + ValueError - if misconfigured template + """ + # TODO: only for backward compatibility of anatomy for older projects + if STAGING_DIR_TEMPLATES not in anatomy.templates: + raise ValueError( + ( + 'Anatomy of project "{}" does not have set' ' "{}" template section!' + ).format(project_name, template_name) + ) + + if template_name not in anatomy.templates[STAGING_DIR_TEMPLATES]: + raise ValueError( + ( + 'Anatomy of project "{}" does not have set' + ' "{}" template key at Staging Dir section!' + ).format(project_name, template_name) + ) + + +def get_staging_dir( + host_name, + project_entity, + folder_entity, + task_entity, + product_type, + product_name, + anatomy, + project_settings=None, + **kwargs +): + """Get staging dir data. + + If `force_temp` is set, staging dir will be created as tempdir. + If `always_get_some_dir` is set, staging dir will be created as tempdir if + no staging dir profile is found. + If `prefix` or `suffix` is not set, default values will be used. + + Arguments: + host_name (str): Name of host. + project_entity (Dict[str, Any]): Project entity. + folder_entity (Dict[str, Any]): Folder entity. + task_entity (Dict[str, Any]): Task entity. + product_type (str): Type of product. + product_name (str): Name of product. + anatomy (ayon_core.pipeline.Anatomy): Anatomy object. + project_settings (Optional[Dict[str, Any]]): Prepared project settings. + **kwargs: Arbitrary keyword arguments. See below. + + Keyword Arguments: + force_temp (bool): If True, staging dir will be created as tempdir. + always_return_path (bool): If True, staging dir will be created as + tempdir if no staging dir profile is found. + prefix (str): Prefix for staging dir. + suffix (str): Suffix for staging dir. + formatting_data (Dict[str, Any]): Data for formatting staging dir + template. + + Returns: + Dict[str, Any]: Staging dir data + """ + + log = kwargs.get("log") or Logger.get_logger("get_staging_dir") + always_return_path = kwargs.get("always_return_path") + + # make sure always_return_path is set to true by default + if always_return_path is None: + always_return_path = True + + if kwargs.get("force_temp"): + return get_temp_dir( + project_name=project_entity["name"], + anatomy=anatomy, + prefix=kwargs.get("prefix"), + suffix=kwargs.get("suffix"), + ) + + # making fewer queries to database + ctx_data = get_template_data( + project_entity, folder_entity, task_entity, host_name + ) + # add roots to ctx_data + ctx_data["root"] = anatomy.roots + + # add additional data + ctx_data.update({ + "product": { + "type": product_type, + "name": product_name + }, + "host": host_name, + }) + + # add additional data from kwargs + if kwargs.get("formatting_data"): + ctx_data.update(kwargs.get("formatting_data")) + + # get staging dir config + staging_dir_config = get_staging_dir_config( + host_name, + project_entity["name"], + task_entity["type"], + task_entity["name"], + product_type, + product_name, + project_settings=project_settings, + anatomy=anatomy, + log=log, + ) + + # if no preset matching and always_get_some_dir is set, return tempdir + if not staging_dir_config and always_return_path: + return { + "stagingDir": get_temp_dir( + project_name=project_name, + anatomy=anatomy, + prefix=kwargs.get("prefix"), + suffix=kwargs.get("suffix"), + ), + "stagingDir_persistent": False, + } + elif not staging_dir_config: + return None + + return { + "stagingDir": StringTemplate.format_template( + staging_dir_config["template"], ctx_data + ), + "stagingDir_persistent": staging_dir_config["persistence"], + } diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index 29d4659393..a6328135ee 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -3,11 +3,80 @@ Temporary folder operations """ import os +import tempfile +from pathlib import Path from ayon_core.lib import StringTemplate from ayon_core.pipeline import Anatomy -def create_custom_tempdir(project_name, anatomy=None): +def get_temp_dir( + project_name=None, anatomy=None, prefix=None, suffix=None, make_local=False +): + """Get temporary dir path. + + If `make_local` is set, tempdir will be created in local tempdir. + If `anatomy` is not set, default anatomy will be used. + If `prefix` or `suffix` is not set, default values will be used. + + It also supports `OPENPYPE_TMPDIR`, so studio can define own temp + shared repository per project or even per more granular context. + Template formatting is supported also with optional keys. Folder is + created in case it doesn't exists. + + Available anatomy formatting keys: + - root[work | ] + - project[name | code] + + Note: + Staging dir does not have to be necessarily in tempdir so be careful + about its usage. + + Args: + project_name (str)[optional]: Name of project. + anatomy (openpype.pipeline.Anatomy)[optional]: Anatomy object. + make_local (bool)[optional]: If True, temp dir will be created in + local tempdir. + suffix (str)[optional]: Suffix for tempdir. + prefix (str)[optional]: Prefix for tempdir. + + Returns: + str: Path to staging dir of instance. + """ + prefix = prefix or "ay_tmp_" + suffix = suffix or "" + + if make_local: + return _create_local_staging_dir(prefix, suffix) + + # make sure anatomy is set + if not anatomy: + anatomy = Anatomy(project_name) + + # get customized tempdir path from `OPENPYPE_TMPDIR` env var + custom_temp_dir = _create_custom_tempdir(anatomy.project_name, anatomy) + + return _create_local_staging_dir(prefix, suffix, custom_temp_dir) + + +def _create_local_staging_dir(prefix, suffix, dir=None): + """Create local staging dir + + Args: + prefix (str): prefix for tempdir + suffix (str): suffix for tempdir + + Returns: + str: path to tempdir + """ + # use pathlib for creating tempdir + staging_dir = Path(tempfile.mkdtemp( + prefix=prefix, suffix=suffix, dir=dir + )) + + return staging_dir.as_posix() + + +def _create_custom_tempdir(project_name, anatomy=None): """ Create custom tempdir Template path formatting is supporting: @@ -38,7 +107,7 @@ def create_custom_tempdir(project_name, anatomy=None): if anatomy is None: anatomy = Anatomy(project_name) # create base formate data - data = { + template_formatting_data = { "root": anatomy.roots, "project": { "name": anatomy.project_name, @@ -47,19 +116,14 @@ def create_custom_tempdir(project_name, anatomy=None): } # path is anatomy template custom_tempdir = StringTemplate.format_template( - env_tmpdir, data).normalized() + env_tmpdir, template_formatting_data) + + custom_tempdir_path = Path(custom_tempdir) else: # path is absolute - custom_tempdir = env_tmpdir + custom_tempdir_path = Path(env_tmpdir) - # create the dir path if it doesn't exists - if not os.path.exists(custom_tempdir): - try: - # create it if it doesn't exists - os.makedirs(custom_tempdir) - except IOError as error: - raise IOError( - "Path couldn't be created: {}".format(error)) + custom_tempdir_path.mkdir(parents=True, exist_ok=True) - return custom_tempdir + return custom_tempdir_path.as_posix() diff --git a/client/ayon_core/plugins/publish/collect_custom_staging_dir.py b/client/ayon_core/plugins/publish/collect_custom_staging_dir.py deleted file mode 100644 index 49c3a98dd2..0000000000 --- a/client/ayon_core/plugins/publish/collect_custom_staging_dir.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -Requires: - anatomy - - -Provides: - instance.data -> stagingDir (folder path) - -> stagingDir_persistent (bool) -""" -import copy -import os.path - -import pyblish.api - -from ayon_core.pipeline.publish.lib import get_custom_staging_dir_info - - -class CollectCustomStagingDir(pyblish.api.InstancePlugin): - """Looks through profiles if stagingDir should be persistent and in special - location. - - Transient staging dir could be useful in specific use cases where is - desirable to have temporary renders in specific, persistent folders, could - be on disks optimized for speed for example. - - It is studio responsibility to clean up obsolete folders with data. - - Location of the folder is configured in `project_anatomy/templates/others`. - ('transient' key is expected, with 'folder' key) - - Which family/task type/product is applicable is configured in: - `project_settings/global/tools/publish/custom_staging_dir_profiles` - - """ - label = "Collect Custom Staging Directory" - order = pyblish.api.CollectorOrder + 0.4990 - - template_key = "transient" - - def process(self, instance): - product_type = instance.data["productType"] - product_name = instance.data["productName"] - host_name = instance.context.data["hostName"] - project_name = instance.context.data["projectName"] - project_settings = instance.context.data["project_settings"] - anatomy = instance.context.data["anatomy"] - task = instance.data["anatomyData"].get("task", {}) - - transient_tml, is_persistent = get_custom_staging_dir_info( - project_name, - host_name, - product_type, - product_name, - task.get("name"), - task.get("type"), - project_settings=project_settings, - anatomy=anatomy, - log=self.log) - - if transient_tml: - anatomy_data = copy.deepcopy(instance.data["anatomyData"]) - anatomy_data["root"] = anatomy.roots - scene_name = instance.context.data.get("currentFile") - if scene_name: - anatomy_data["scene_name"] = os.path.basename(scene_name) - transient_dir = transient_tml.format(**anatomy_data) - instance.data["stagingDir"] = transient_dir - - instance.data["stagingDir_persistent"] = is_persistent - result_str = "Adding '{}' as".format(transient_dir) - else: - result_str = "Not adding" - - self.log.debug("{} custom staging dir for instance with '{}'".format( - result_str, product_type - )) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 58a032a030..72578d9dc0 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -9,11 +9,13 @@ import clique import pyblish.api from ayon_core import resources, AYON_CORE_ROOT -from ayon_core.pipeline import publish +from ayon_core.pipeline import ( + publish, + get_temp_dir +) from ayon_core.lib import ( run_ayon_launcher_process, - get_transcode_temp_directory, convert_input_paths_for_ffmpeg, should_convert_for_ffmpeg ) @@ -250,7 +252,10 @@ class ExtractBurnin(publish.Extractor): # - change staging dir of source representation # - must be set back after output definitions processing if do_convert: - new_staging_dir = get_transcode_temp_directory() + new_staging_dir = get_temp_dir( + project_name=instance.context.data["projectName"], + make_local=True, + ) repre["stagingDir"] = new_staging_dir convert_input_paths_for_ffmpeg( diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index a28a761e7e..ba173867f8 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -3,15 +3,15 @@ import copy import clique import pyblish.api -from ayon_core.pipeline import publish +from ayon_core.pipeline import ( + publish, + get_temp_dir +) from ayon_core.lib import ( - is_oiio_supported, ) - from ayon_core.lib.transcoding import ( convert_colorspace, - get_transcode_temp_directory, ) from ayon_core.lib.profiles_filtering import filter_profiles @@ -104,7 +104,10 @@ class ExtractOIIOTranscode(publish.Extractor): new_repre = copy.deepcopy(repre) original_staging_dir = new_repre["stagingDir"] - new_staging_dir = get_transcode_temp_directory() + new_staging_dir = get_temp_dir( + project_name=instance.context.data["projectName"], + make_local=True, + ) new_repre["stagingDir"] = new_staging_dir if isinstance(new_repre["files"], list): @@ -254,7 +257,7 @@ class ExtractOIIOTranscode(publish.Extractor): (list) of [file.1001-1010#.exr] or [fileA.exr, fileB.exr] """ pattern = [clique.PATTERNS["frames"]] - collections, remainder = clique.assemble( + collections, _ = clique.assemble( files_to_convert, patterns=pattern, assume_padded_when_ambiguous=True) diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 06b451bfbe..26cd2ef0b2 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -22,8 +22,8 @@ from ayon_core.lib.transcoding import ( should_convert_for_ffmpeg, get_review_layer_name, convert_input_paths_for_ffmpeg, - get_transcode_temp_directory, ) +from ayon_core.pipeline import get_temp_dir from ayon_core.pipeline.publish import ( KnownPublishError, get_publish_instance_label, @@ -310,7 +310,10 @@ class ExtractReview(pyblish.api.InstancePlugin): # - change staging dir of source representation # - must be set back after output definitions processing if do_convert: - new_staging_dir = get_transcode_temp_directory() + new_staging_dir = get_temp_dir( + project_name=instance.context.data["projectName"], + make_local=True, + ) repre["stagingDir"] = new_staging_dir convert_input_paths_for_ffmpeg( From 8b1674619ce7004069bed2fa10d8af39ffac3cb6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 12 Sep 2024 16:40:49 +0200 Subject: [PATCH 004/463] Add staging directory functionality and a new plugin for managing staging directories in the pipeline. - Added import statement for 'os' in creator_plugins.py - Implemented method 'apply_staging_dir' to apply staging directory with persistence to instance's transient data in creator_plugins.py - Updated comments and added TODOs related to staging directories in various files - Created a new plugin 'CollectManagedStagingDir' to manage staging directories in publish/lib.py --- .../pipeline/create/creator_plugins.py | 55 +++++++++++++++++++ client/ayon_core/pipeline/publish/lib.py | 2 +- .../publish/collect_managed_staging_dir.py | 43 +++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/plugins/publish/collect_managed_staging_dir.py diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 61c10ee736..1360a74519 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import os import copy import collections from typing import TYPE_CHECKING, Optional @@ -14,6 +15,7 @@ from ayon_core.pipeline.plugin_discover import ( deregister_plugin, deregister_plugin_path ) +from ayon_core.pipeline import get_staging_dir from .constants import DEFAULT_VARIANT_VALUE from .product_name import get_product_name @@ -782,6 +784,59 @@ class Creator(BaseCreator): """ return self.pre_create_attr_defs + def apply_staging_dir(self, instance): + """Apply staging dir with persistence to instance's transient data. + + Method is called on instance creation and on instance update. + + Args: + instance (CreatedInstance): Instance for which should be staging + dir applied. + + Returns: + str: Path to staging dir. + """ + create_ctx = self.create_context + product_name = instance.get("productName") + product_type = instance.get("productType") + folder_path = instance.get("folderPath") + if not any([product_name, folder_path]): + return None + + version = instance.get("version") + if version is not None: + formatting_data = {"version": version} + + staging_dir_data = get_staging_dir( + create_ctx.host_name, + create_ctx.get_current_project_entity(), + create_ctx.get_current_folder_entity(), + create_ctx.get_current_task_entity(), + product_type, + product_name, + create_ctx.get_current_project_anatomy(), + create_ctx.get_current_project_settings(), + always_return_path=False, + log=self.log, + formatting_data=formatting_data, + ) + + if not staging_dir_data: + return None + + staging_dir_path = staging_dir_data["stagingDir"] + + # TODO: not sure if this is necessary + # path might be already created by get_staging_dir + if not os.path.exists(staging_dir_path): + os.makedirs(staging_dir_path) + + instance.transient_data.update(staging_dir_data) + + self.log.info(f"Applied staging dir to instance: {staging_dir_path}") + + return staging_dir_path + class HiddenCreator(BaseCreator): @abstractmethod diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 9cfcd3f71a..714794e8f8 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -628,7 +628,7 @@ def get_publish_repre_path(instance, repre, only_published=False): return None -# deprecated: backward compatibility only +# deprecated: backward compatibility only (2024-09-12) # TODO: remove in the future def get_custom_staging_dir_info( project_name, diff --git a/client/ayon_core/plugins/publish/collect_managed_staging_dir.py b/client/ayon_core/plugins/publish/collect_managed_staging_dir.py new file mode 100644 index 0000000000..ca6d5161c1 --- /dev/null +++ b/client/ayon_core/plugins/publish/collect_managed_staging_dir.py @@ -0,0 +1,43 @@ +""" +Requires: + anatomy + + +Provides: + instance.data -> stagingDir (folder path) + -> stagingDir_persistent (bool) +""" + +import pyblish.api + +from ayon_core.pipeline.publish import get_instance_staging_dir + + +class CollectManagedStagingDir(pyblish.api.InstancePlugin): + """Apply matching Staging Dir profile to a instance. + + Apply Staging dir via profiles could be useful in specific use cases + where is desirable to have temporary renders in specific, + persistent folders, could be on disks optimized for speed for example. + + It is studio's responsibility to clean up obsolete folders with data. + + Location of the folder is configured in: + `ayon+anatomy://_/templates/staging`. + + Which family/task type/subset is applicable is configured in: + `ayon+settings://core/tools/publish/custom_staging_dir_profiles` + """ + + label = "Collect Managed Staging Directory" + order = pyblish.api.CollectorOrder + 0.4990 + + def process(self, instance): + + staging_dir_path = get_instance_staging_dir(instance) + persistance = instance.data.get("stagingDir_persistent", False) + + self.log.info(( + f"Instance staging dir was set to `{staging_dir_path}` " + f"and persistence is set to `{persistance}`" + )) From 9e57f74b5c354a3d864eed71f545a5a7f93cc001 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 13 Sep 2024 16:20:46 +0200 Subject: [PATCH 005/463] Update variable names for clarity and consistency. - Renamed variables for better understanding and uniformity - Improved readability by using more descriptive names --- client/ayon_core/pipeline/publish/lib.py | 6 +++--- client/ayon_core/pipeline/stagingdir.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 714794e8f8..fb4db6ddf1 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -696,7 +696,7 @@ def get_instance_staging_dir(instance): workfile_name, _ = os.path.splitext(workfile) formatting_data["workfile_name"] = workfile_name - dir_data = get_staging_dir( + staging_dir_data = get_staging_dir( host_name, project_entity, folder_entity, @@ -708,14 +708,14 @@ def get_instance_staging_dir(instance): formatting_data=formatting_data, ) - staging_dir_path = dir_data["stagingDir"] + staging_dir_path = staging_dir_data["stagingDir"] # TODO: not sure if this is necessary # path might be already created by get_staging_dir if not os.path.exists(staging_dir_path): os.makedirs(staging_dir_path) - instance.data.update(dir_data) + instance.data.update(staging_dir_data) return staging_dir_path diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index e8fa1c4853..5ab9596528 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -202,7 +202,7 @@ def get_staging_dir( if not staging_dir_config and always_return_path: return { "stagingDir": get_temp_dir( - project_name=project_name, + project_name=project_entity["name"], anatomy=anatomy, prefix=kwargs.get("prefix"), suffix=kwargs.get("suffix"), From 2f6ca5a2385bd3073961cf49bca220e9d3fb88b9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 18 Oct 2024 18:35:27 +0200 Subject: [PATCH 006/463] Implemented explicit frames for simple files representations --- .../pipeline/farm/pyblish_functions.py | 79 ++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 98951b2766..5908644dca 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -7,7 +7,7 @@ from copy import deepcopy import attr import ayon_api import clique -from ayon_core.lib import Logger +from ayon_core.lib import Logger, collect_frames from ayon_core.pipeline import get_current_project_name, get_representation_path from ayon_core.pipeline.create import get_product_name from ayon_core.pipeline.farm.patterning import match_aov_pattern @@ -295,11 +295,17 @@ def _add_review_families(families): return families -def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, - skip_integration_repre_list, - do_not_add_review, - context, - color_managed_plugin): +def prepare_representations( + skeleton_data, + exp_files, + anatomy, + aov_filter, + skip_integration_repre_list, + do_not_add_review, + context, + color_managed_plugin, + frames_to_render +): """Create representations for file sequences. This will return representations of expected files if they are not @@ -315,6 +321,8 @@ def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, skip_integration_repre_list (list): exclude specific extensions, do_not_add_review (bool): explicitly skip review color_managed_plugin (publish.ColormanagedPyblishPluginMixin) + frames_to_render (str): implicit or explicit range of frames to render + this value is sent to Deadline in JobInfo.Frames Returns: list of representations @@ -325,6 +333,8 @@ def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, log = Logger.get_logger("farm_publishing") + frames_to_render = _get_real_frames_to_render(frames_to_render) + # create representation for every collected sequence for collection in collections: ext = collection.tail.lstrip(".") @@ -361,18 +371,21 @@ def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, " This may cause issues on farm." ).format(staging)) - frame_start = int(skeleton_data.get("frameStartHandle")) + frame_start = int(frames_to_render[0]) + frame_end = int(frames_to_render[-1]) if skeleton_data.get("slate"): frame_start -= 1 + files = _get_real_files_to_rendered(collection, frames_to_render) + # explicitly disable review by user preview = preview and not do_not_add_review rep = { "name": ext, "ext": ext, - "files": [os.path.basename(f) for f in list(collection)], + "files": files, "frameStart": frame_start, - "frameEnd": int(skeleton_data.get("frameEndHandle")), + "frameEnd": frame_end, # If expectedFile are absolute, we need only filenames "stagingDir": staging, "fps": skeleton_data.get("fps"), @@ -413,10 +426,13 @@ def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, " This may cause issues on farm." ).format(staging)) + files = _get_real_files_to_rendered( + [os.path.basename(remainder)], frames_to_render) + rep = { "name": ext, "ext": ext, - "files": os.path.basename(remainder), + "files": files[0], "stagingDir": staging, } @@ -453,6 +469,49 @@ def prepare_representations(skeleton_data, exp_files, anatomy, aov_filter, return representations +def _get_real_frames_to_render(frames): + """Returns list of frames that should be rendered. + + Artists could want to selectively render only particular frames + """ + frames_to_render = [] + for frame in frames.split(","): + if "-" in frame: + splitted = frame.split("-") + frames_to_render.extend(range(int(splitted[0]), int(splitted[1]))) + else: + frames_to_render.append(frame) + return [str(frame_to_render) for frame_to_render in frames_to_render] + + +def _get_real_files_to_rendered(collection, frames_to_render): + """Use expected files based on real frames_to_render. + + Artists might explicitly set frames they want to render via Publisher UI. + This uses this value to filter out files + Args: + frames_to_render (list): of str '1001' + """ + files = [os.path.basename(f) for f in list(collection)] + file_name, extracted_frame = list(collect_frames(files).items())[0] + if extracted_frame: + found_frame_pattern_length = len(extracted_frame) + normalized_frames_to_render = set() + for frame_to_render in frames_to_render: + normalized_frames_to_render.add( + str(frame_to_render).zfill(found_frame_pattern_length) + ) + + filtered_files = [] + for file_name in files: + if any(frame in file_name + for frame in normalized_frames_to_render): + filtered_files.append(file_name) + + files = filtered_files + return files + + def create_instances_for_aov(instance, skeleton, aov_filter, skip_integration_repre_list, do_not_add_review): From 2b5ab5439aa7d33b5c70ca11ce476cadae81ba0f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Oct 2024 13:41:20 +0200 Subject: [PATCH 007/463] returning empty lines --- client/ayon_core/pipeline/__init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index d5c3140d37..505c847c36 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -120,12 +120,16 @@ __all__ = ( "AYON_CONTAINER_ID", "AYON_INSTANCE_ID", "HOST_WORKFILE_EXTENSIONS", + # --- Anatomy --- "Anatomy", + # --- Temp dir --- "get_temp_dir", + # --- Staging dir --- "get_staging_dir", + # --- Create --- "BaseCreator", "Creator", @@ -134,6 +138,7 @@ __all__ = ( "CreatedInstance", "CreatorError", "CreatorError", + # - legacy creation "LegacyCreator", "legacy_create", @@ -143,6 +148,7 @@ __all__ = ( "deregister_creator_plugin", "register_creator_plugin_path", "deregister_creator_plugin_path", + # --- Load --- "HeroVersionType", "IncompatibleLoaderError", @@ -161,6 +167,7 @@ __all__ = ( "get_representation_path", "get_representation_context", "get_repres_contexts", + # --- Publish --- "PublishValidationError", "PublishXmlValidationError", @@ -168,6 +175,7 @@ __all__ = ( "AYONPyblishPluginMixin", "OpenPypePyblishPluginMixin", "OptionalPyblishPluginMixin", + # --- Actions --- "LauncherAction", "InventoryAction", @@ -179,6 +187,7 @@ __all__ = ( "register_inventory_action_path", "deregister_inventory_action", "deregister_inventory_action_path", + # --- Process context --- "install_ayon_plugins", "install_openpype_plugins", @@ -197,12 +206,14 @@ __all__ = ( "get_current_project_name", "get_current_folder_path", "get_current_task_name", + # Workfile templates "discover_workfile_build_plugins", "register_workfile_build_plugin", "deregister_workfile_build_plugin", "register_workfile_build_plugin_path", "deregister_workfile_build_plugin_path", + # Backwards compatible function names "install", "uninstall", From 2b765954a3451bc6cba358584b64fed9e8f633e6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Oct 2024 13:54:05 +0200 Subject: [PATCH 008/463] returning empty lines --- client/ayon_core/pipeline/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index 505c847c36..ea8b1617c6 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -137,11 +137,13 @@ __all__ = ( "HiddenCreator", "CreatedInstance", "CreatorError", + "CreatorError", # - legacy creation "LegacyCreator", "legacy_create", + "discover_creator_plugins", "discover_legacy_creator_plugins", "register_creator_plugin", @@ -154,15 +156,18 @@ __all__ = ( "IncompatibleLoaderError", "LoaderPlugin", "ProductLoaderPlugin", + "discover_loader_plugins", "register_loader_plugin", "deregister_loader_plugin_path", "register_loader_plugin_path", "deregister_loader_plugin", + "load_container", "remove_container", "update_container", "switch_container", + "loaders_from_representation", "get_representation_path", "get_representation_context", @@ -179,9 +184,11 @@ __all__ = ( # --- Actions --- "LauncherAction", "InventoryAction", + "discover_launcher_actions", "register_launcher_action", "register_launcher_action_path", + "discover_inventory_actions", "register_inventory_action", "register_inventory_action_path", @@ -194,12 +201,15 @@ __all__ = ( "install_host", "uninstall_host", "is_installed", + "register_root", "registered_root", + "register_host", "registered_host", "deregister_host", "get_process_id", + "get_global_context", "get_current_context", "get_current_host_name", From 396af0cf8610c2d2991c8a7842f164dcc49d98f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 24 Oct 2024 13:52:43 +0200 Subject: [PATCH 009/463] Update client/ayon_core/pipeline/create/creator_plugins.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/create/creator_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 1360a74519..32ac2bd61f 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -800,7 +800,7 @@ class Creator(BaseCreator): product_name = instance.get("productName") product_type = instance.get("productType") folder_path = instance.get("folderPath") - if not any([product_name, folder_path]): + if not product_name or not folder_path: return None version = instance.get("version") From 9a860785bbce917cc2064db74a018ab012922d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 24 Oct 2024 13:52:57 +0200 Subject: [PATCH 010/463] Update client/ayon_core/pipeline/create/creator_plugins.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/create/creator_plugins.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 32ac2bd61f..0de7707e38 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -828,8 +828,7 @@ class Creator(BaseCreator): # TODO: not sure if this is necessary # path might be already created by get_staging_dir - if not os.path.exists(staging_dir_path): - os.makedirs(staging_dir_path) + os.makedirs(staging_dir_path, exist_ok=True) instance.transient_data.update(staging_dir_data) From bd03634ed1a3237e49f1d3307a96722d0960b2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 24 Oct 2024 13:53:38 +0200 Subject: [PATCH 011/463] Update client/ayon_core/pipeline/publish/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/publish/lib.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index fb4db6ddf1..ba56c38c78 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -642,7 +642,15 @@ def get_custom_staging_dir_info( log=None, ): from ayon_core.pipeline.stagingdir import get_staging_dir_config - + warnings.warn( + ( + "Function 'get_custom_staging_dir_info' in" + " 'ayon_core.pipeline.publish' is deprecated. Please use" + " 'get_custom_staging_dir_info'" + " in 'ayon_core.pipeline.stagingdir'." + ), + DeprecationWarning, + ) tr_data = get_staging_dir_config( host_name, project_name, From fedf8e60c7b33f5873538773561269e686ee81b4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Oct 2024 13:56:45 +0200 Subject: [PATCH 012/463] Add warnings module for future use Imported the 'warnings' module for potential future usage in the codebase. --- client/ayon_core/pipeline/publish/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index ba56c38c78..657af9570b 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -2,6 +2,7 @@ import os import sys import inspect import copy +import warnings import xml.etree.ElementTree from typing import Optional, Union, List From ea23f355f6dcb24f2dcb2806878afa27ddd11907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 24 Oct 2024 14:00:36 +0200 Subject: [PATCH 013/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/publish/lib.py | 3 +-- client/ayon_core/pipeline/stagingdir.py | 20 +++++++------------- client/ayon_core/pipeline/tempdir.py | 15 ++++++--------- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 657af9570b..6a31da82b2 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -721,8 +721,7 @@ def get_instance_staging_dir(instance): # TODO: not sure if this is necessary # path might be already created by get_staging_dir - if not os.path.exists(staging_dir_path): - os.makedirs(staging_dir_path) + os.makedirs(staging_dir_path, exist_ok=True) instance.data.update(staging_dir_data) diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index 5ab9596528..d0172c4848 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -1,9 +1,9 @@ from ayon_core.lib import Logger, filter_profiles, StringTemplate from ayon_core.settings import get_project_settings -from .anatomy import Anatomy -from .tempdir import get_temp_dir from ayon_core.pipeline.template_data import get_template_data +from .anatomy import Anatomy +from .tempdir import get_temp_dir STAGING_DIR_TEMPLATES = "staging" @@ -34,8 +34,10 @@ def get_staging_dir_config( Returns: Dict or None: Data with directory template and is_persistent or None + Raises: ValueError - if misconfigured template should be used + """ settings = project_settings or get_project_settings(project_name) @@ -91,14 +93,6 @@ def _validate_template_name(project_name, template_name, anatomy): Raises: ValueError - if misconfigured template """ - # TODO: only for backward compatibility of anatomy for older projects - if STAGING_DIR_TEMPLATES not in anatomy.templates: - raise ValueError( - ( - 'Anatomy of project "{}" does not have set' ' "{}" template section!' - ).format(project_name, template_name) - ) - if template_name not in anatomy.templates[STAGING_DIR_TEMPLATES]: raise ValueError( ( @@ -147,9 +141,9 @@ def get_staging_dir( template. Returns: - Dict[str, Any]: Staging dir data - """ + Optional[Dict[str, Any]]: Staging dir data + """ log = kwargs.get("log") or Logger.get_logger("get_staging_dir") always_return_path = kwargs.get("always_return_path") @@ -209,7 +203,7 @@ def get_staging_dir( ), "stagingDir_persistent": False, } - elif not staging_dir_config: + if not staging_dir_config: return None return { diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index a6328135ee..448e774e7c 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -23,24 +23,21 @@ def get_temp_dir( Template formatting is supported also with optional keys. Folder is created in case it doesn't exists. - Available anatomy formatting keys: - - root[work | ] - - project[name | code] - Note: Staging dir does not have to be necessarily in tempdir so be careful about its usage. Args: - project_name (str)[optional]: Name of project. - anatomy (openpype.pipeline.Anatomy)[optional]: Anatomy object. - make_local (bool)[optional]: If True, temp dir will be created in + project_name (str): Name of project. + anatomy (Optional[Anatomy]): Project Anatomy object. + suffix (Optional[str]): Suffix for tempdir. + prefix (Optional[str]): Prefix for tempdir. + make_local (Optional[bool]): If True, temp dir will be created in local tempdir. - suffix (str)[optional]: Suffix for tempdir. - prefix (str)[optional]: Prefix for tempdir. Returns: str: Path to staging dir of instance. + """ prefix = prefix or "ay_tmp_" suffix = suffix or "" From ea4ec677cac7a73f14225722f0dbc9c17328ff6a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Oct 2024 14:40:30 +0200 Subject: [PATCH 014/463] reviewer suggestions for changes - Renamed function `get_staging_dir` to `get_staging_dir_info` for clarity. - Updated variable names in multiple files to use `template_data` instead of `formatting_data`. - Adjusted function parameters in the staging directory module for consistency and added new optional parameters. - Improved logging by passing logger instances instead of creating new loggers within functions. --- client/ayon_core/pipeline/__init__.py | 4 +- .../pipeline/create/creator_plugins.py | 18 ++++----- client/ayon_core/pipeline/publish/lib.py | 17 ++++----- client/ayon_core/pipeline/stagingdir.py | 34 ++++++++++------- client/ayon_core/pipeline/tempdir.py | 37 ++++++++----------- 5 files changed, 54 insertions(+), 56 deletions(-) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index ea8b1617c6..4060501a92 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -10,7 +10,7 @@ from .anatomy import Anatomy from .tempdir import get_temp_dir -from .stagingdir import get_staging_dir +from .stagingdir import get_staging_dir_info from .create import ( BaseCreator, @@ -128,7 +128,7 @@ __all__ = ( "get_temp_dir", # --- Staging dir --- - "get_staging_dir", + "get_staging_dir_info", # --- Create --- "BaseCreator", diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 0de7707e38..124395ae16 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -15,7 +15,7 @@ from ayon_core.pipeline.plugin_discover import ( deregister_plugin, deregister_plugin_path ) -from ayon_core.pipeline import get_staging_dir +from ayon_core.pipeline import get_staging_dir_info from .constants import DEFAULT_VARIANT_VALUE from .product_name import get_product_name @@ -805,9 +805,9 @@ class Creator(BaseCreator): version = instance.get("version") if version is not None: - formatting_data = {"version": version} + template_data = {"version": version} - staging_dir_data = get_staging_dir( + staging_dir_info = get_staging_dir_info( create_ctx.host_name, create_ctx.get_current_project_entity(), create_ctx.get_current_folder_entity(), @@ -817,20 +817,20 @@ class Creator(BaseCreator): create_ctx.get_current_project_anatomy(), create_ctx.get_current_project_settings(), always_return_path=False, - log=self.log, - formatting_data=formatting_data, + logger=self.log, + template_data=template_data, ) - if not staging_dir_data: + if not staging_dir_info: return None - staging_dir_path = staging_dir_data["stagingDir"] + staging_dir_path = staging_dir_info["stagingDir"] # TODO: not sure if this is necessary - # path might be already created by get_staging_dir + # path might be already created by get_staging_dir_info os.makedirs(staging_dir_path, exist_ok=True) - instance.transient_data.update(staging_dir_data) + instance.transient_data.update(staging_dir_info) self.log.info(f"Applied staging dir to instance: {staging_dir_path}") diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 6a31da82b2..0f3a7c1d45 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -18,7 +18,7 @@ from ayon_core.lib import ( ) from ayon_core.settings import get_project_settings from ayon_core.addon import AddonsManager -from ayon_core.pipeline import get_staging_dir +from ayon_core.pipeline import get_staging_dir_info from ayon_core.pipeline.plugin_discover import DiscoverResult from .constants import ( DEFAULT_PUBLISH_TEMPLATE, @@ -685,7 +685,7 @@ def get_instance_staging_dir(instance): return staging_dir anatomy_data = instance.data["anatomyData"] - formatting_data = copy.deepcopy(anatomy_data) + template_data = copy.deepcopy(anatomy_data) product_type = instance.data["productType"] product_name = instance.data["productName"] @@ -703,9 +703,9 @@ def get_instance_staging_dir(instance): if current_file: workfile = os.path.basename(current_file) workfile_name, _ = os.path.splitext(workfile) - formatting_data["workfile_name"] = workfile_name + template_data["workfile_name"] = workfile_name - staging_dir_data = get_staging_dir( + staging_dir_info = get_staging_dir_info( host_name, project_entity, folder_entity, @@ -714,16 +714,15 @@ def get_instance_staging_dir(instance): product_name, anatomy, project_settings=project_settings, - formatting_data=formatting_data, + template_data=template_data, ) - staging_dir_path = staging_dir_data["stagingDir"] + staging_dir_path = staging_dir_info["stagingDir"] - # TODO: not sure if this is necessary - # path might be already created by get_staging_dir + # path might be already created by get_staging_dir_info os.makedirs(staging_dir_path, exist_ok=True) - instance.data.update(staging_dir_data) + instance.data.update(staging_dir_info) return staging_dir_path diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index d0172c4848..e9d425cf28 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -102,7 +102,7 @@ def _validate_template_name(project_name, template_name, anatomy): ) -def get_staging_dir( +def get_staging_dir_info( host_name, project_entity, folder_entity, @@ -111,9 +111,13 @@ def get_staging_dir( product_name, anatomy, project_settings=None, + template_data=None, + always_return_path=None, + force_tmp_dir=None, + logger=None, **kwargs ): - """Get staging dir data. + """Get staging dir info data. If `force_temp` is set, staging dir will be created as tempdir. If `always_get_some_dir` is set, staging dir will be created as tempdir if @@ -129,29 +133,31 @@ def get_staging_dir( product_name (str): Name of product. anatomy (ayon_core.pipeline.Anatomy): Anatomy object. project_settings (Optional[Dict[str, Any]]): Prepared project settings. + template_data (Optional[Dict[str, Any]]): Data for formatting staging + dir template. + always_return_path (Optional[bool]): If True, staging dir will be + created as tempdir if no staging dir profile is found. Input value + False will return None if no staging dir profile is found. + force_tmp_dir (Optional[bool]): If True, staging dir will be created as + tempdir. + logger (Optional[logging.Logger]): Logger instance. **kwargs: Arbitrary keyword arguments. See below. Keyword Arguments: - force_temp (bool): If True, staging dir will be created as tempdir. - always_return_path (bool): If True, staging dir will be created as - tempdir if no staging dir profile is found. prefix (str): Prefix for staging dir. suffix (str): Suffix for staging dir. - formatting_data (Dict[str, Any]): Data for formatting staging dir - template. Returns: - Optional[Dict[str, Any]]: Staging dir data + Optional[Dict[str, Any]]: Staging dir info data """ - log = kwargs.get("log") or Logger.get_logger("get_staging_dir") - always_return_path = kwargs.get("always_return_path") + log = logger or Logger.get_logger("get_staging_dir_info") # make sure always_return_path is set to true by default if always_return_path is None: always_return_path = True - if kwargs.get("force_temp"): + if force_tmp_dir: return get_temp_dir( project_name=project_entity["name"], anatomy=anatomy, @@ -175,9 +181,9 @@ def get_staging_dir( "host": host_name, }) - # add additional data from kwargs - if kwargs.get("formatting_data"): - ctx_data.update(kwargs.get("formatting_data")) + # add additional template formatting data + if template_data: + ctx_data.update(template_data) # get staging dir config staging_dir_config = get_staging_dir_config( diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index 448e774e7c..440ed882aa 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -10,29 +10,25 @@ from ayon_core.pipeline import Anatomy def get_temp_dir( - project_name=None, anatomy=None, prefix=None, suffix=None, make_local=False + project_name, anatomy=None, prefix=None, suffix=None, use_local_temp=False ): """Get temporary dir path. - If `make_local` is set, tempdir will be created in local tempdir. + If `use_local_temp` is set, tempdir will be created in local tempdir. If `anatomy` is not set, default anatomy will be used. If `prefix` or `suffix` is not set, default values will be used. - It also supports `OPENPYPE_TMPDIR`, so studio can define own temp + It also supports `AYON_TMPDIR`, so studio can define own temp shared repository per project or even per more granular context. Template formatting is supported also with optional keys. Folder is created in case it doesn't exists. - Note: - Staging dir does not have to be necessarily in tempdir so be careful - about its usage. - Args: project_name (str): Name of project. anatomy (Optional[Anatomy]): Project Anatomy object. suffix (Optional[str]): Suffix for tempdir. prefix (Optional[str]): Prefix for tempdir. - make_local (Optional[bool]): If True, temp dir will be created in + use_local_temp (Optional[bool]): If True, temp dir will be created in local tempdir. Returns: @@ -42,7 +38,7 @@ def get_temp_dir( prefix = prefix or "ay_tmp_" suffix = suffix or "" - if make_local: + if use_local_temp: return _create_local_staging_dir(prefix, suffix) # make sure anatomy is set @@ -55,19 +51,20 @@ def get_temp_dir( return _create_local_staging_dir(prefix, suffix, custom_temp_dir) -def _create_local_staging_dir(prefix, suffix, dir=None): +def _create_local_staging_dir(prefix, suffix, dirpath=None): """Create local staging dir Args: prefix (str): prefix for tempdir suffix (str): suffix for tempdir + dirpath (Optional[str]): path to tempdir Returns: str: path to tempdir """ # use pathlib for creating tempdir staging_dir = Path(tempfile.mkdtemp( - prefix=prefix, suffix=suffix, dir=dir + prefix=prefix, suffix=suffix, dir=dirpath )) return staging_dir.as_posix() @@ -89,31 +86,27 @@ def _create_custom_tempdir(project_name, anatomy=None): Returns: str | None: formatted path or None """ - env_tmpdir = os.getenv("AYON_TMPDIR") + env_tmpdir = os.getenv( + "AYON_TMPDIR", + ) if not env_tmpdir: - env_tmpdir = os.getenv("OPENPYPE_TMPDIR") - if not env_tmpdir: - return - print( - "DEPRECATION WARNING: Used 'OPENPYPE_TMPDIR' environment" - " variable. Please use 'AYON_TMPDIR' instead." - ) + return None custom_tempdir = None if "{" in env_tmpdir: if anatomy is None: anatomy = Anatomy(project_name) # create base formate data - template_formatting_data = { + template_data = { "root": anatomy.roots, "project": { "name": anatomy.project_name, "code": anatomy.project_code, - } + }, } # path is anatomy template custom_tempdir = StringTemplate.format_template( - env_tmpdir, template_formatting_data) + env_tmpdir, template_data) custom_tempdir_path = Path(custom_tempdir) From 1eb09045832f29024b0b2dba00adc6d71ace132f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Oct 2024 14:48:47 +0200 Subject: [PATCH 015/463] Remove unnecessary root addition in get_staging_dir_info function. The code changes remove the unnecessary addition of roots to ctx_data in the get_staging_dir_info function. --- client/ayon_core/pipeline/stagingdir.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index e9d425cf28..818acef36a 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -169,8 +169,6 @@ def get_staging_dir_info( ctx_data = get_template_data( project_entity, folder_entity, task_entity, host_name ) - # add roots to ctx_data - ctx_data["root"] = anatomy.roots # add additional data ctx_data.update({ @@ -178,7 +176,7 @@ def get_staging_dir_info( "type": product_type, "name": product_name }, - "host": host_name, + "root": anatomy.roots }) # add additional template formatting data From fa9af2f8ded7e9c89519cfde2f01e04a3dd8b58a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Oct 2024 15:06:11 +0200 Subject: [PATCH 016/463] Refactor Creator class method to handle missing info better. - Updated return type in docstring - Added a comment for clarity - Removed unnecessary return statements --- client/ayon_core/pipeline/create/creator_plugins.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 801feedd3d..4cbf432efd 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -843,14 +843,16 @@ class Creator(BaseCreator): dir applied. Returns: - str: Path to staging dir. + Optional[str]: Staging dir path or None if not applied. """ create_ctx = self.create_context product_name = instance.get("productName") product_type = instance.get("productType") folder_path = instance.get("folderPath") + + # this can only work if product name and folder path are available if not product_name or not folder_path: - return None + return version = instance.get("version") if version is not None: @@ -871,7 +873,7 @@ class Creator(BaseCreator): ) if not staging_dir_info: - return None + return staging_dir_path = staging_dir_info["stagingDir"] From 8208bef6f31933a1b0aef5151dc2b957e712fe03 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 25 Oct 2024 23:06:12 +0200 Subject: [PATCH 017/463] Allow to target not only `productType` by default with attributes, but also by `families` data on created instance --- client/ayon_core/pipeline/publish/publish_plugins.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index d2c70894cc..6a2f4c0279 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -205,9 +205,9 @@ class AYONPyblishPluginMixin: if not cls.__instanceEnabled__: return False - for _ in pyblish.logic.plugins_by_families( - [cls], [instance.product_type] - ): + families = [instance.product_type] + families.extend(instance.data.get("families", [])) + for _ in pyblish.logic.plugins_by_families([cls], families): return True return False From 1790f078ffa18cf02788114419f85c9e6226d7d0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 26 Oct 2024 19:13:43 +0200 Subject: [PATCH 018/463] Draft to set defaults using `profiles` --- .../extract_usd_layer_contributions.py | 91 ++++++++++++------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py index 180cb8bbf1..6f08df790f 100644 --- a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py +++ b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py @@ -14,7 +14,8 @@ from ayon_core.lib import ( BoolDef, UISeparatorDef, UILabelDef, - EnumDef + EnumDef, + filter_profiles ) try: from ayon_core.pipeline.usdlib import ( @@ -463,6 +464,59 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, if not cls.instance_matches_plugin_families(instance): return [] + # Set default target layer based on product type + # TODO: Define profiles in settings + profiles = [ + { + "productType": "model", + "contribution_layer": "model", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "productType": "look", + "contribution_layer": "look", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "productType": "groom", + "contribution_layer": "groom", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "productType": "rig", + "contribution_layer": "rig", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdShot" + }, + { + "productType": "usd", + "contribution_layer": "assembly", + "contribution_apply_as_variant": False, + "contribution_target_product": "usdShot" + }, + ] + profile = filter_profiles(profiles, { + "productType": instance.data["productType"] + }) + if not profile: + profile = {} + + # Define defaults + default_contribution_layer = profile.get( + "contribution_layer", None) + default_apply_as_variant = profile.get( + "contribution_apply_as_variant", False) + default_target_product = profile.get( + "contribution_target_product", "usdAsset") + default_init_as = ( + "asset" + if profile.get("contribution_target_product") == "usdAsset" + else "shot") + init_as_visible = False + # Attributes logic publish_attributes = instance["publish_attributes"].get( cls.__name__, {}) @@ -495,7 +549,7 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "the contribution itself will be added to the " "department layer." ), - default="usdAsset", + default=default_target_product, visible=visible), EnumDef("contribution_target_product_init", label="Initialize as", @@ -507,8 +561,8 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "setting will do nothing." ), items=["asset", "shot"], - default="asset", - visible=visible), + default=default_init_as, + visible=visible and init_as_visible), # Asset layer, e.g. model.usd, look.usd, rig.usd EnumDef("contribution_layer", @@ -520,7 +574,7 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "the list) will contribute as a stronger opinion." ), items=list(cls.contribution_layers.keys()), - default="model", + default=default_contribution_layer, visible=visible), BoolDef("contribution_apply_as_variant", label="Add as variant", @@ -532,7 +586,7 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "appended to as a sublayer to the department layer " "instead." ), - default=True, + default=default_apply_as_variant, visible=visible), TextDef("contribution_variant_set_name", label="Variant Set Name", @@ -588,31 +642,6 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, instance.set_publish_plugin_attr_defs(cls.__name__, new_attrs) -class CollectUSDLayerContributionsHoudiniLook(CollectUSDLayerContributions): - """ - This is solely here to expose the attribute definitions for the - Houdini "look" family. - """ - # TODO: Improve how this is built for the look family - hosts = ["houdini"] - families = ["look"] - label = CollectUSDLayerContributions.label + " (Look)" - - @classmethod - def get_attr_defs_for_instance(cls, create_context, instance): - # Filtering of instance, if needed, can be customized - if not cls.instance_matches_plugin_families(instance): - return [] - - defs = super().get_attr_defs_for_instance(create_context, instance) - - # Update default for department layer to look - layer_def = next(d for d in defs if d.key == "contribution_layer") - layer_def.default = "look" - - return defs - - class ValidateUSDDependencies(pyblish.api.InstancePlugin): families = ["usdLayer"] From 8a074daa2bb226de239d6d8291069c8796398086 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 4 Nov 2024 16:56:41 +0100 Subject: [PATCH 019/463] Fix single frame render --- client/ayon_core/pipeline/farm/pyblish_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 5908644dca..c70967dfc1 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -478,7 +478,8 @@ def _get_real_frames_to_render(frames): for frame in frames.split(","): if "-" in frame: splitted = frame.split("-") - frames_to_render.extend(range(int(splitted[0]), int(splitted[1]))) + frames_to_render.extend( + range(int(splitted[0]), int(splitted[1])+1)) else: frames_to_render.append(frame) return [str(frame_to_render) for frame_to_render in frames_to_render] From 3a8a9ec4831be2b72d6ae3f7a2ba35fc20a32482 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:25:20 +0100 Subject: [PATCH 020/463] implemented logic to revert to default values --- client/ayon_core/tools/publisher/abstract.py | 17 ++++ client/ayon_core/tools/publisher/control.py | 12 +++ .../tools/publisher/models/create.py | 98 ++++++++++++------- 3 files changed, 93 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/tools/publisher/abstract.py b/client/ayon_core/tools/publisher/abstract.py index 7fad2b8176..4ed91813d3 100644 --- a/client/ayon_core/tools/publisher/abstract.py +++ b/client/ayon_core/tools/publisher/abstract.py @@ -375,6 +375,14 @@ class AbstractPublisherFrontend(AbstractPublisherCommon): ): pass + @abstractmethod + def revert_instances_create_attr_values( + self, + instance_ids: List["Union[str, None]"], + key: str, + ): + pass + @abstractmethod def get_publish_attribute_definitions( self, @@ -397,6 +405,15 @@ class AbstractPublisherFrontend(AbstractPublisherCommon): ): pass + @abstractmethod + def revert_instances_publish_attr_values( + self, + instance_ids: List["Union[str, None]"], + plugin_name: str, + key: str, + ): + pass + @abstractmethod def get_product_name( self, diff --git a/client/ayon_core/tools/publisher/control.py b/client/ayon_core/tools/publisher/control.py index 347755d557..98fdda08cf 100644 --- a/client/ayon_core/tools/publisher/control.py +++ b/client/ayon_core/tools/publisher/control.py @@ -412,6 +412,11 @@ class PublisherController( instance_ids, key, value ) + def revert_instances_create_attr_values(self, instance_ids, key): + self._create_model.revert_instances_create_attr_values( + instance_ids, key + ) + def get_publish_attribute_definitions(self, instance_ids, include_context): """Collect publish attribute definitions for passed instances. @@ -432,6 +437,13 @@ class PublisherController( instance_ids, plugin_name, key, value ) + def revert_instances_publish_attr_values( + self, instance_ids, plugin_name, key + ): + return self._create_model.revert_instances_publish_attr_values( + instance_ids, plugin_name, key + ) + def get_product_name( self, creator_identifier, diff --git a/client/ayon_core/tools/publisher/models/create.py b/client/ayon_core/tools/publisher/models/create.py index 8763d79712..ca26749b65 100644 --- a/client/ayon_core/tools/publisher/models/create.py +++ b/client/ayon_core/tools/publisher/models/create.py @@ -40,6 +40,7 @@ from ayon_core.tools.publisher.abstract import ( ) CREATE_EVENT_SOURCE = "publisher.create.model" +_DEFAULT_VALUE = object() class CreatorType: @@ -752,20 +753,12 @@ class CreateModel: self._remove_instances_from_context(instance_ids) def set_instances_create_attr_values(self, instance_ids, key, value): - with self._create_context.bulk_value_changes(CREATE_EVENT_SOURCE): - for instance_id in instance_ids: - instance = self._get_instance_by_id(instance_id) - creator_attributes = instance["creator_attributes"] - attr_def = creator_attributes.get_attr_def(key) - if ( - attr_def is None - or not attr_def.is_value_def - or not attr_def.visible - or not attr_def.enabled - or not attr_def.is_value_valid(value) - ): - continue - creator_attributes[key] = value + self._set_instances_create_attr_values(instance_ids, key, value) + + def revert_instances_create_attr_values(self, instance_ids, key): + self._set_instances_create_attr_values( + instance_ids, key, _DEFAULT_VALUE + ) def get_creator_attribute_definitions( self, instance_ids: List[str] @@ -816,28 +809,18 @@ class CreateModel: return output def set_instances_publish_attr_values( - self, instance_ids, plugin_name, key, value + self, instance_ids, plugin_name, key, value ): - with self._create_context.bulk_value_changes(CREATE_EVENT_SOURCE): - for instance_id in instance_ids: - if instance_id is None: - instance = self._create_context - else: - instance = self._get_instance_by_id(instance_id) - plugin_val = instance.publish_attributes[plugin_name] - attr_def = plugin_val.get_attr_def(key) - # Ignore if attribute is not available or enabled/visible - # on the instance, or the value is not valid for definition - if ( - attr_def is None - or not attr_def.is_value_def - or not attr_def.visible - or not attr_def.enabled - or not attr_def.is_value_valid(value) - ): - continue + self._set_instances_publish_attr_values( + instance_ids, plugin_name, key, value + ) - plugin_val[key] = value + def revert_instances_publish_attr_values( + self, instance_ids, plugin_name, key + ): + self._set_instances_publish_attr_values( + instance_ids, plugin_name, key, _DEFAULT_VALUE + ) def get_publish_attribute_definitions( self, @@ -1064,6 +1047,53 @@ class CreateModel: CreatorItem.from_creator(creator) ) + def _set_instances_create_attr_values(self, instance_ids, key, value): + with self._create_context.bulk_value_changes(CREATE_EVENT_SOURCE): + for instance_id in instance_ids: + instance = self._get_instance_by_id(instance_id) + creator_attributes = instance["creator_attributes"] + attr_def = creator_attributes.get_attr_def(key) + if ( + attr_def is None + or not attr_def.is_value_def + or not attr_def.visible + or not attr_def.enabled + ): + continue + + if value is _DEFAULT_VALUE: + creator_attributes[key] = attr_def.default + + elif attr_def.is_value_valid(value): + creator_attributes[key] = value + + def _set_instances_publish_attr_values( + self, instance_ids, plugin_name, key, value + ): + with self._create_context.bulk_value_changes(CREATE_EVENT_SOURCE): + for instance_id in instance_ids: + if instance_id is None: + instance = self._create_context + else: + instance = self._get_instance_by_id(instance_id) + plugin_val = instance.publish_attributes[plugin_name] + attr_def = plugin_val.get_attr_def(key) + # Ignore if attribute is not available or enabled/visible + # on the instance, or the value is not valid for definition + if ( + attr_def is None + or not attr_def.is_value_def + or not attr_def.visible + or not attr_def.enabled + ): + continue + + if value is _DEFAULT_VALUE: + plugin_val[key] = attr_def.default + + elif attr_def.is_value_valid(value): + plugin_val[key] = value + def _cc_added_instance(self, event): instance_ids = { instance.id From 56a07fe9183eaa33ad202261afb2d880499d2805 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:28:30 +0100 Subject: [PATCH 021/463] added 'AttributeDefinitionsLabel' helper label widget --- .../ayon_core/tools/attribute_defs/widgets.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 026aea00ad..e1977cca2c 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -20,11 +20,14 @@ from ayon_core.tools.utils import ( FocusSpinBox, FocusDoubleSpinBox, MultiSelectionComboBox, + set_style_property, ) from ayon_core.tools.utils import NiceCheckbox from .files_widget import FilesWidget +_REVERT_TO_DEFAULT_LABEL = "Revert to default" + def create_widget_for_attr_def(attr_def, parent=None): widget = _create_widget_for_attr_def(attr_def, parent) @@ -74,6 +77,52 @@ def _create_widget_for_attr_def(attr_def, parent=None): )) +class AttributeDefinitionsLabel(QtWidgets.QLabel): + """Label related to value attribute definition. + + Label is used to show attribute definition label and to show if value + is overridden. + + Label can be right-clicked to revert value to default. + """ + revert_to_default_requested = QtCore.Signal(str) + + def __init__( + self, + attr_id: str, + label: str, + parent: QtWidgets.QWidget, + ): + super().__init__(label, parent) + + self._attr_id = attr_id + self._overridden = False + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + + self.customContextMenuRequested.connect(self._on_context_menu) + + def set_overridden(self, overridden: bool): + if self._overridden == overridden: + return + self._overridden = overridden + set_style_property( + self, + "overridden", + "1" if overridden else "" + ) + + def _on_context_menu(self, point: QtCore.QPoint): + menu = QtWidgets.QMenu(self) + action = QtWidgets.QAction(menu) + action.setText(_REVERT_TO_DEFAULT_LABEL) + action.triggered.connect(self._request_revert_to_default) + menu.addAction(action) + menu.exec_(self.mapToGlobal(point)) + + def _request_revert_to_default(self): + self.revert_to_default_requested.emit(self._attr_id) + + class AttributeDefinitionsWidget(QtWidgets.QWidget): """Create widgets for attribute definitions in grid layout. From 33a5195b7156e444139232b55545058f34e173bc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:28:46 +0100 Subject: [PATCH 022/463] added 'AttributeDefinitionsLabel' to init --- client/ayon_core/tools/attribute_defs/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/attribute_defs/__init__.py b/client/ayon_core/tools/attribute_defs/__init__.py index f991fdec3d..7f6cbb41be 100644 --- a/client/ayon_core/tools/attribute_defs/__init__.py +++ b/client/ayon_core/tools/attribute_defs/__init__.py @@ -1,6 +1,7 @@ from .widgets import ( create_widget_for_attr_def, AttributeDefinitionsWidget, + AttributeDefinitionsLabel, ) from .dialog import ( @@ -11,6 +12,7 @@ from .dialog import ( __all__ = ( "create_widget_for_attr_def", "AttributeDefinitionsWidget", + "AttributeDefinitionsLabel", "AttributeDefinitionsDialog", ) From 7d0f1f97e4ded1cc2a855e7db51fcceb2affc560 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:29:48 +0100 Subject: [PATCH 023/463] use new label in product attributes --- .../publisher/widgets/product_attributes.py | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/product_attributes.py b/client/ayon_core/tools/publisher/widgets/product_attributes.py index b0b2640640..07cbfb1907 100644 --- a/client/ayon_core/tools/publisher/widgets/product_attributes.py +++ b/client/ayon_core/tools/publisher/widgets/product_attributes.py @@ -4,8 +4,10 @@ from typing import Dict, List, Any from qtpy import QtWidgets, QtCore from ayon_core.lib.attribute_definitions import AbstractAttrDef, UnknownDef -from ayon_core.tools.utils import set_style_property -from ayon_core.tools.attribute_defs import create_widget_for_attr_def +from ayon_core.tools.attribute_defs import ( + create_widget_for_attr_def, + AttributeDefinitionsLabel, +) from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend from ayon_core.tools.publisher.constants import ( INPUTS_LAYOUT_HSPACING, @@ -16,14 +18,6 @@ if typing.TYPE_CHECKING: from typing import Union -def _set_label_overriden(label: QtWidgets.QLabel, overriden: bool): - set_style_property( - label, - "overriden", - "1" if overriden else "" - ) - - class _CreateAttrDefInfo: """Helper class to store information about create attribute definition.""" def __init__( @@ -31,12 +25,14 @@ class _CreateAttrDefInfo: attr_def: AbstractAttrDef, instance_ids: List["Union[str, None]"], defaults: List[Any], - label_widget: "Union[None, QtWidgets.QLabel]", + label_widget: "Union[AttributeDefinitionsLabel, None]", ): self.attr_def: AbstractAttrDef = attr_def self.instance_ids: List["Union[str, None]"] = instance_ids self.defaults: List[Any] = defaults - self.label_widget: "Union[None, QtWidgets.QLabel]" = label_widget + self.label_widget: "Union[AttributeDefinitionsLabel, None]" = ( + label_widget + ) class _PublishAttrDefInfo: @@ -47,13 +43,15 @@ class _PublishAttrDefInfo: plugin_name: str, instance_ids: List["Union[str, None]"], defaults: List[Any], - label_widget: "Union[None, QtWidgets.QLabel]", + label_widget: "Union[AttributeDefinitionsLabel, None]", ): self.attr_def: AbstractAttrDef = attr_def self.plugin_name: str = plugin_name self.instance_ids: List["Union[str, None]"] = instance_ids self.defaults: List[Any] = defaults - self.label_widget: "Union[None, QtWidgets.QLabel]" = label_widget + self.label_widget: "Union[AttributeDefinitionsLabel, None]" = ( + label_widget + ) class CreatorAttrsWidget(QtWidgets.QWidget): @@ -187,7 +185,9 @@ class CreatorAttrsWidget(QtWidgets.QWidget): label = attr_def.label or attr_def.key if label: - label_widget = QtWidgets.QLabel(label, self) + label_widget = AttributeDefinitionsLabel( + attr_def.id, label, self + ) tooltip = attr_def.tooltip if tooltip: label_widget.setToolTip(tooltip) @@ -202,7 +202,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget): if not attr_def.is_label_horizontal: row += 1 attr_def_info.label_widget = label_widget - _set_label_overriden(label_widget, is_overriden) + label_widget.set_overridden(is_overriden) content_layout.addWidget( widget, row, col_num, 1, expand_cols @@ -237,7 +237,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget): if attr_def_info.label_widget is not None: defaults = attr_def_info.defaults is_overriden = len(defaults) != 1 or value not in defaults - _set_label_overriden(attr_def_info.label_widget, is_overriden) + attr_def_info.label_widget.set_overridden(is_overriden) self._controller.set_instances_create_attr_values( attr_def_info.instance_ids, @@ -367,7 +367,9 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): if attr_def.is_value_def: label = attr_def.label or attr_def.key if label: - label_widget = QtWidgets.QLabel(label, content_widget) + label_widget = AttributeDefinitionsLabel( + attr_def.id, label, content_widget + ) tooltip = attr_def.tooltip if tooltip: label_widget.setToolTip(tooltip) @@ -423,7 +425,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): widget.set_value(values[0]) if label_widget is not None: - _set_label_overriden(label_widget, is_overriden) + label_widget.set_overridden(is_overriden) self._scroll_area.setWidget(content_widget) self._content_widget = content_widget @@ -436,7 +438,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): if attr_def_info.label_widget is not None: defaults = attr_def_info.defaults is_overriden = len(defaults) != 1 or value not in defaults - _set_label_overriden(attr_def_info.label_widget, is_overriden) + attr_def_info.label_widget.set_overridden(is_overriden) self._controller.set_instances_publish_attr_values( attr_def_info.instance_ids, From 5569c95aefb02dec9ef1760fa6dd7a0eb497707b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:29:56 +0100 Subject: [PATCH 024/463] change style of label --- client/ayon_core/style/style.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index 0d1d4f710e..bd96a3aeed 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -44,10 +44,6 @@ QLabel { background: transparent; } -QLabel[overriden="1"] { - color: {color:font-overridden}; -} - /* Inputs */ QAbstractSpinBox, QLineEdit, QPlainTextEdit, QTextEdit { border: 1px solid {color:border}; @@ -1589,6 +1585,10 @@ CreateNextPageOverlay { } /* Attribute Definition widgets */ +AttributeDefinitionsLabel[overridden="1"] { + color: {color:font-overridden}; +} + AttributeDefinitionsWidget QAbstractSpinBox, QLineEdit, QPlainTextEdit, QTextEdit { padding: 1px; } From 9de6a74789a4d8ba92b66806b1005e37e3a977b7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:31:23 +0100 Subject: [PATCH 025/463] base attribute widget can handle reset to default logic --- .../ayon_core/tools/attribute_defs/widgets.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index e1977cca2c..09637a9696 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -241,11 +241,18 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): class _BaseAttrDefWidget(QtWidgets.QWidget): # Type 'object' may not work with older PySide versions value_changed = QtCore.Signal(object, str) + revert_to_default_requested = QtCore.Signal(str) - def __init__(self, attr_def, parent): - super(_BaseAttrDefWidget, self).__init__(parent) + def __init__( + self, + attr_def: AbstractAttrDef, + parent: "Union[QtWidgets.QWidget, None]", + handle_revert_to_default: Optional[bool] = True, + ): + super().__init__(parent) - self.attr_def = attr_def + self.attr_def: AbstractAttrDef = attr_def + self._handle_revert_to_default: bool = handle_revert_to_default main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) @@ -254,6 +261,15 @@ class _BaseAttrDefWidget(QtWidgets.QWidget): self._ui_init() + def revert_to_default_value(self): + if not self.attr_def.is_value_def: + return + + if self._handle_revert_to_default: + self.set_value(self.attr_def.default) + else: + self.revert_to_default_requested.emit(self.attr_def.id) + def _ui_init(self): raise NotImplementedError( "Method '_ui_init' is not implemented. {}".format( From bbe1d9e3fd1b8e6ca284614ccf5e6ee5d212efd5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:32:19 +0100 Subject: [PATCH 026/463] 'create_widget_for_attr_def' can pass in all init args --- .../ayon_core/tools/attribute_defs/widgets.py | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 09637a9696..d3f51a196c 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -1,4 +1,6 @@ import copy +import typing +from typing import Optional from qtpy import QtWidgets, QtCore @@ -26,11 +28,20 @@ from ayon_core.tools.utils import NiceCheckbox from .files_widget import FilesWidget +if typing.TYPE_CHECKING: + from typing import Union + _REVERT_TO_DEFAULT_LABEL = "Revert to default" -def create_widget_for_attr_def(attr_def, parent=None): - widget = _create_widget_for_attr_def(attr_def, parent) +def create_widget_for_attr_def( + attr_def: AbstractAttrDef, + parent: Optional[QtWidgets.QWidget] = None, + handle_revert_to_default: Optional[bool] = True, +): + widget = _create_widget_for_attr_def( + attr_def, parent, handle_revert_to_default + ) if not attr_def.visible: widget.setVisible(False) @@ -39,42 +50,50 @@ def create_widget_for_attr_def(attr_def, parent=None): return widget -def _create_widget_for_attr_def(attr_def, parent=None): +def _create_widget_for_attr_def( + attr_def: AbstractAttrDef, + parent: "Union[QtWidgets.QWidget, None]", + handle_revert_to_default: bool, +): if not isinstance(attr_def, AbstractAttrDef): raise TypeError("Unexpected type \"{}\" expected \"{}\"".format( str(type(attr_def)), AbstractAttrDef )) + cls = None if isinstance(attr_def, NumberDef): - return NumberAttrWidget(attr_def, parent) + cls = NumberAttrWidget - if isinstance(attr_def, TextDef): - return TextAttrWidget(attr_def, parent) + elif isinstance(attr_def, TextDef): + cls = TextAttrWidget - if isinstance(attr_def, EnumDef): - return EnumAttrWidget(attr_def, parent) + elif isinstance(attr_def, EnumDef): + cls = EnumAttrWidget - if isinstance(attr_def, BoolDef): - return BoolAttrWidget(attr_def, parent) + elif isinstance(attr_def, BoolDef): + cls = BoolAttrWidget - if isinstance(attr_def, UnknownDef): - return UnknownAttrWidget(attr_def, parent) + elif isinstance(attr_def, UnknownDef): + cls = UnknownAttrWidget - if isinstance(attr_def, HiddenDef): - return HiddenAttrWidget(attr_def, parent) + elif isinstance(attr_def, HiddenDef): + cls = HiddenAttrWidget - if isinstance(attr_def, FileDef): - return FileAttrWidget(attr_def, parent) + elif isinstance(attr_def, FileDef): + cls = FileAttrWidget - if isinstance(attr_def, UISeparatorDef): - return SeparatorAttrWidget(attr_def, parent) + elif isinstance(attr_def, UISeparatorDef): + cls = SeparatorAttrWidget - if isinstance(attr_def, UILabelDef): - return LabelAttrWidget(attr_def, parent) + elif isinstance(attr_def, UILabelDef): + cls = LabelAttrWidget - raise ValueError("Unknown attribute definition \"{}\"".format( - str(type(attr_def)) - )) + if cls is None: + raise ValueError("Unknown attribute definition \"{}\"".format( + str(type(attr_def)) + )) + + return cls(attr_def, parent, handle_revert_to_default) class AttributeDefinitionsLabel(QtWidgets.QLabel): From c23cf6746d8014f46921fcc610d894b36f027d43 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:32:58 +0100 Subject: [PATCH 027/463] 'AttributeDefinitionsWidget' shows overriden values on labels --- .../ayon_core/tools/attribute_defs/widgets.py | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index d3f51a196c..94121e51bc 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -151,16 +151,18 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): """ def __init__(self, attr_defs=None, parent=None): - super(AttributeDefinitionsWidget, self).__init__(parent) + super().__init__(parent) - self._widgets = [] + self._widgets_by_id = {} + self._labels_by_id = {} self._current_keys = set() self.set_attr_defs(attr_defs) def clear_attr_defs(self): """Remove all existing widgets and reset layout if needed.""" - self._widgets = [] + self._widgets_by_id = {} + self._labels_by_id = {} self._current_keys = set() layout = self.layout() @@ -202,6 +204,7 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): self._current_keys.add(attr_def.key) widget = create_widget_for_attr_def(attr_def, self) self._widgets.append(widget) + self._widgets_by_id[attr_def.id] = widget if not attr_def.visible: continue @@ -213,7 +216,13 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): col_num = 2 - expand_cols if attr_def.is_value_def and attr_def.label: - label_widget = QtWidgets.QLabel(attr_def.label, self) + label_widget = AttributeDefinitionsLabel( + attr_def.id, attr_def.label, self + ) + label_widget.revert_to_default_requested.connect( + self._on_revert_request + ) + self._labels_by_id[attr_def.id] = label_widget tooltip = attr_def.tooltip if tooltip: label_widget.setToolTip(tooltip) @@ -228,6 +237,9 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): if not attr_def.is_label_horizontal: row += 1 + if attr_def.is_value_def: + widget.value_changed.connect(self._on_value_change) + layout.addWidget( widget, row, col_num, 1, expand_cols ) @@ -236,7 +248,7 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): def set_value(self, value): new_value = copy.deepcopy(value) unused_keys = set(new_value.keys()) - for widget in self._widgets: + for widget in self._widgets_by_id.values(): attr_def = widget.attr_def if attr_def.key not in new_value: continue @@ -249,13 +261,26 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): def current_value(self): output = {} - for widget in self._widgets: + for widget in self._widgets_by_id.values(): attr_def = widget.attr_def if not isinstance(attr_def, UIDef): output[attr_def.key] = widget.current_value() return output + def _on_revert_request(self, attr_id): + widget = self._widgets_by_id.get(attr_id) + if widget is not None: + widget.set_value(widget.attr_def.default) + + def _on_value_change(self, value, attr_id): + widget = self._widgets_by_id.get(attr_id) + if widget is None: + return + label = self._labels_by_id.get(attr_id) + if label is not None: + label.set_overridden(value != widget.attr_def.default) + class _BaseAttrDefWidget(QtWidgets.QWidget): # Type 'object' may not work with older PySide versions From 47973464fdd7fd923a208d8b44b78da15bcd69f8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:33:38 +0100 Subject: [PATCH 028/463] remoe python 2 super calls --- client/ayon_core/tools/attribute_defs/widgets.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 94121e51bc..118f4b5f64 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -364,7 +364,7 @@ class ClickableLineEdit(QtWidgets.QLineEdit): clicked = QtCore.Signal() def __init__(self, text, parent): - super(ClickableLineEdit, self).__init__(parent) + super().__init__(parent) self.setText(text) self.setReadOnly(True) @@ -373,7 +373,7 @@ class ClickableLineEdit(QtWidgets.QLineEdit): def mousePressEvent(self, event): if event.button() == QtCore.Qt.LeftButton: self._mouse_pressed = True - super(ClickableLineEdit, self).mousePressEvent(event) + super().mousePressEvent(event) def mouseReleaseEvent(self, event): if self._mouse_pressed: @@ -381,7 +381,7 @@ class ClickableLineEdit(QtWidgets.QLineEdit): if self.rect().contains(event.pos()): self.clicked.emit() - super(ClickableLineEdit, self).mouseReleaseEvent(event) + super().mouseReleaseEvent(event) class NumberAttrWidget(_BaseAttrDefWidget): @@ -596,7 +596,7 @@ class BoolAttrWidget(_BaseAttrDefWidget): class EnumAttrWidget(_BaseAttrDefWidget): def __init__(self, *args, **kwargs): self._multivalue = False - super(EnumAttrWidget, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) @property def multiselection(self): @@ -723,7 +723,7 @@ class HiddenAttrWidget(_BaseAttrDefWidget): def setVisible(self, visible): if visible: visible = False - super(HiddenAttrWidget, self).setVisible(visible) + super().setVisible(visible) def current_value(self): if self._multivalue: From b5d018c071341474e91c97f60b389a21e45f30b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:34:41 +0100 Subject: [PATCH 029/463] publisher does handle revert to default --- .../publisher/widgets/product_attributes.py | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/product_attributes.py b/client/ayon_core/tools/publisher/widgets/product_attributes.py index 07cbfb1907..cb165d1be7 100644 --- a/client/ayon_core/tools/publisher/widgets/product_attributes.py +++ b/client/ayon_core/tools/publisher/widgets/product_attributes.py @@ -141,7 +141,9 @@ class CreatorAttrsWidget(QtWidgets.QWidget): row = 0 for attr_def, info_by_id in result: - widget = create_widget_for_attr_def(attr_def, content_widget) + widget = create_widget_for_attr_def( + attr_def, content_widget, handle_revert_to_default=False + ) default_values = [] if attr_def.is_value_def: values = [] @@ -161,6 +163,9 @@ class CreatorAttrsWidget(QtWidgets.QWidget): widget.set_value(values, True) widget.value_changed.connect(self._input_value_changed) + widget.revert_to_default_requested.connect( + self._on_request_revert_to_default + ) attr_def_info = _CreateAttrDefInfo( attr_def, list(info_by_id), default_values, None ) @@ -203,6 +208,9 @@ class CreatorAttrsWidget(QtWidgets.QWidget): row += 1 attr_def_info.label_widget = label_widget label_widget.set_overridden(is_overriden) + label_widget.revert_to_default_requested.connect( + self._on_request_revert_to_default + ) content_layout.addWidget( widget, row, col_num, 1, expand_cols @@ -245,6 +253,15 @@ class CreatorAttrsWidget(QtWidgets.QWidget): value ) + def _on_request_revert_to_default(self, attr_id): + attr_def_info = self._attr_def_info_by_id.get(attr_id) + if attr_def_info is None: + return + self._controller.revert_instances_create_attr_values( + attr_def_info.instance_ids, + attr_def_info.attr_def.key, + ) + class PublishPluginAttrsWidget(QtWidgets.QWidget): """Widget showing publish plugin attributes for selected instances. @@ -346,7 +363,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): for plugin_name, attr_defs, plugin_values in result: for attr_def in attr_defs: widget = create_widget_for_attr_def( - attr_def, content_widget + attr_def, content_widget, handle_revert_to_default=False ) visible_widget = attr_def.visible # Hide unknown values of publish plugins @@ -370,6 +387,9 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): label_widget = AttributeDefinitionsLabel( attr_def.id, label, content_widget ) + label_widget.revert_to_default_requested.connect( + self._on_request_revert_to_default + ) tooltip = attr_def.tooltip if tooltip: label_widget.setToolTip(tooltip) @@ -392,6 +412,9 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): continue widget.value_changed.connect(self._input_value_changed) + widget.revert_to_default_requested.connect( + self._on_request_revert_to_default + ) instance_ids = [] values = [] @@ -447,6 +470,17 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): value ) + def _on_request_revert_to_default(self, attr_id): + attr_def_info = self._attr_def_info_by_id.get(attr_id) + if attr_def_info is None: + return + + self._controller.revert_instances_publish_attr_values( + attr_def_info.instance_ids, + attr_def_info.plugin_name, + attr_def_info.attr_def.key, + ) + def _on_instance_attr_defs_change(self, event): for instance_id in event.data: if ( From 2d51436da71fb9b6b95409779bc1f33715e837af Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:46:35 +0100 Subject: [PATCH 030/463] refresh content --- client/ayon_core/tools/publisher/widgets/product_attributes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/product_attributes.py b/client/ayon_core/tools/publisher/widgets/product_attributes.py index cb165d1be7..3ff295c986 100644 --- a/client/ayon_core/tools/publisher/widgets/product_attributes.py +++ b/client/ayon_core/tools/publisher/widgets/product_attributes.py @@ -261,6 +261,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget): attr_def_info.instance_ids, attr_def_info.attr_def.key, ) + self._refresh_content() class PublishPluginAttrsWidget(QtWidgets.QWidget): @@ -480,6 +481,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): attr_def_info.plugin_name, attr_def_info.attr_def.key, ) + self._refresh_content() def _on_instance_attr_defs_change(self, event): for instance_id in event.data: From 9be42980bdb46578b6a04a7424d1a04b165e507e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:58:21 +0100 Subject: [PATCH 031/463] implemented request restart logic for most of widgets --- .../tools/attribute_defs/_constants.py | 1 + .../ayon_core/tools/attribute_defs/widgets.py | 58 ++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 client/ayon_core/tools/attribute_defs/_constants.py diff --git a/client/ayon_core/tools/attribute_defs/_constants.py b/client/ayon_core/tools/attribute_defs/_constants.py new file mode 100644 index 0000000000..b58a05bac6 --- /dev/null +++ b/client/ayon_core/tools/attribute_defs/_constants.py @@ -0,0 +1 @@ +REVERT_TO_DEFAULT_LABEL = "Revert to default" diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 118f4b5f64..03482c1006 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -26,13 +26,12 @@ from ayon_core.tools.utils import ( ) from ayon_core.tools.utils import NiceCheckbox +from ._constants import REVERT_TO_DEFAULT_LABEL from .files_widget import FilesWidget if typing.TYPE_CHECKING: from typing import Union -_REVERT_TO_DEFAULT_LABEL = "Revert to default" - def create_widget_for_attr_def( attr_def: AbstractAttrDef, @@ -133,7 +132,7 @@ class AttributeDefinitionsLabel(QtWidgets.QLabel): def _on_context_menu(self, point: QtCore.QPoint): menu = QtWidgets.QMenu(self) action = QtWidgets.QAction(menu) - action.setText(_REVERT_TO_DEFAULT_LABEL) + action.setText(REVERT_TO_DEFAULT_LABEL) action.triggered.connect(self._request_revert_to_default) menu.addAction(action) menu.exec_(self.mapToGlobal(point)) @@ -393,6 +392,9 @@ class NumberAttrWidget(_BaseAttrDefWidget): else: input_widget = FocusSpinBox(self) + # Override context menu event to add revert to default action + input_widget.contextMenuEvent = self._input_widget_context_event + if self.attr_def.tooltip: input_widget.setToolTip(self.attr_def.tooltip) @@ -430,6 +432,16 @@ class NumberAttrWidget(_BaseAttrDefWidget): self._set_multiselection_visible(True) return False + def _input_widget_context_event(self, event): + line_edit = self._input_widget.lineEdit() + menu = line_edit.createStandardContextMenu() + menu.setAttribute(QtCore.Qt.WA_DeleteOnClose) + action = QtWidgets.QAction(menu) + action.setText(REVERT_TO_DEFAULT_LABEL) + action.triggered.connect(self.revert_to_default_value) + menu.addAction(action) + menu.popup(event.globalPos()) + def current_value(self): return self._input_widget.value() @@ -495,6 +507,9 @@ class TextAttrWidget(_BaseAttrDefWidget): else: input_widget = QtWidgets.QLineEdit(self) + # Override context menu event to add revert to default action + input_widget.contextMenuEvent = self._input_widget_context_event + if ( self.attr_def.placeholder and hasattr(input_widget, "setPlaceholderText") @@ -516,6 +531,15 @@ class TextAttrWidget(_BaseAttrDefWidget): self.main_layout.addWidget(input_widget, 0) + def _input_widget_context_event(self, event): + menu = self._input_widget.createStandardContextMenu() + menu.setAttribute(QtCore.Qt.WA_DeleteOnClose) + action = QtWidgets.QAction(menu) + action.setText(REVERT_TO_DEFAULT_LABEL) + action.triggered.connect(self.revert_to_default_value) + menu.addAction(action) + menu.popup(event.globalPos()) + def _on_value_change(self): if self.multiline: new_value = self._input_widget.toPlainText() @@ -568,6 +592,20 @@ class BoolAttrWidget(_BaseAttrDefWidget): self.main_layout.addWidget(input_widget, 0) self.main_layout.addStretch(1) + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self._on_context_menu) + + def _on_context_menu(self, pos): + self._menu = QtWidgets.QMenu(self) + + action = QtWidgets.QAction(self._menu) + action.setText(REVERT_TO_DEFAULT_LABEL) + action.triggered.connect(self.revert_to_default_value) + self._menu.addAction(action) + + global_pos = self.mapToGlobal(pos) + self._menu.exec_(global_pos) + def _on_value_change(self): new_value = self._input_widget.isChecked() self.value_changed.emit(new_value, self.attr_def.id) @@ -631,6 +669,20 @@ class EnumAttrWidget(_BaseAttrDefWidget): self.main_layout.addWidget(input_widget, 0) + input_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + input_widget.customContextMenuRequested.connect(self._on_context_menu) + + def _on_context_menu(self, pos): + menu = QtWidgets.QMenu(self) + + action = QtWidgets.QAction(menu) + action.setText(REVERT_TO_DEFAULT_LABEL) + action.triggered.connect(self.revert_to_default_value) + menu.addAction(action) + + global_pos = self.mapToGlobal(pos) + menu.exec_(global_pos) + def _on_value_change(self): new_value = self.current_value() if self._multivalue: From cc45af7a96023b4ee9d39e81968bf0cce2290508 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:58:55 +0100 Subject: [PATCH 032/463] implemented request revert on files widget --- .../tools/attribute_defs/files_widget.py | 72 ++++++++++++------- .../ayon_core/tools/attribute_defs/widgets.py | 15 ++++ 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index 95091bed5a..46399c5fce 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -17,6 +17,8 @@ from ayon_core.tools.utils import ( PixmapLabel ) +from ._constants import REVERT_TO_DEFAULT_LABEL + ITEM_ID_ROLE = QtCore.Qt.UserRole + 1 ITEM_LABEL_ROLE = QtCore.Qt.UserRole + 2 ITEM_ICON_ROLE = QtCore.Qt.UserRole + 3 @@ -598,7 +600,7 @@ class FilesView(QtWidgets.QListView): """View showing instances and their groups.""" remove_requested = QtCore.Signal() - context_menu_requested = QtCore.Signal(QtCore.QPoint) + context_menu_requested = QtCore.Signal(QtCore.QPoint, bool) def __init__(self, *args, **kwargs): super(FilesView, self).__init__(*args, **kwargs) @@ -690,9 +692,8 @@ class FilesView(QtWidgets.QListView): def _on_context_menu_request(self, pos): index = self.indexAt(pos) - if index.isValid(): - point = self.viewport().mapToGlobal(pos) - self.context_menu_requested.emit(point) + point = self.viewport().mapToGlobal(pos) + self.context_menu_requested.emit(point, index.isValid()) def _on_selection_change(self): self._remove_btn.setEnabled(self.has_selected_item_ids()) @@ -721,27 +722,34 @@ class FilesView(QtWidgets.QListView): class FilesWidget(QtWidgets.QFrame): value_changed = QtCore.Signal() + revert_requested = QtCore.Signal() def __init__(self, single_item, allow_sequences, extensions_label, parent): - super(FilesWidget, self).__init__(parent) + super().__init__(parent) self.setAcceptDrops(True) + wrapper_widget = QtWidgets.QWidget(self) + empty_widget = DropEmpty( - single_item, allow_sequences, extensions_label, self + single_item, allow_sequences, extensions_label, wrapper_widget ) files_model = FilesModel(single_item, allow_sequences) files_proxy_model = FilesProxyModel() files_proxy_model.setSourceModel(files_model) - files_view = FilesView(self) + files_view = FilesView(wrapper_widget) files_view.setModel(files_proxy_model) - layout = QtWidgets.QStackedLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setStackingMode(QtWidgets.QStackedLayout.StackAll) - layout.addWidget(empty_widget) - layout.addWidget(files_view) - layout.setCurrentWidget(empty_widget) + wrapper_layout = QtWidgets.QStackedLayout(wrapper_widget) + wrapper_layout.setContentsMargins(0, 0, 0, 0) + wrapper_layout.setStackingMode(QtWidgets.QStackedLayout.StackAll) + wrapper_layout.addWidget(empty_widget) + wrapper_layout.addWidget(files_view) + wrapper_layout.setCurrentWidget(empty_widget) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(wrapper_widget, 1) files_proxy_model.rowsInserted.connect(self._on_rows_inserted) files_proxy_model.rowsRemoved.connect(self._on_rows_removed) @@ -761,7 +769,11 @@ class FilesWidget(QtWidgets.QFrame): self._widgets_by_id = {} - self._layout = layout + self._wrapper_widget = wrapper_widget + self._wrapper_layout = wrapper_layout + + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self._on_context_menu) def _set_multivalue(self, multivalue): if self._multivalue is multivalue: @@ -770,7 +782,7 @@ class FilesWidget(QtWidgets.QFrame): self._files_view.set_multivalue(multivalue) self._files_model.set_multivalue(multivalue) self._files_proxy_model.set_multivalue(multivalue) - self.setEnabled(not multivalue) + self._wrapper_widget.setEnabled(not multivalue) def set_value(self, value, multivalue): self._in_set_value = True @@ -888,22 +900,28 @@ class FilesWidget(QtWidgets.QFrame): if items_to_delete: self._remove_item_by_ids(items_to_delete) - def _on_context_menu_requested(self, pos): - if self._multivalue: - return + def _on_context_menu(self, pos): + self._on_context_menu_requested(pos, False) + def _on_context_menu_requested(self, pos, valid_index): menu = QtWidgets.QMenu(self._files_view) + if valid_index and not self._multivalue: + if self._files_view.has_selected_sequence(): + split_action = QtWidgets.QAction("Split sequence", menu) + split_action.triggered.connect(self._on_split_request) + menu.addAction(split_action) - if self._files_view.has_selected_sequence(): - split_action = QtWidgets.QAction("Split sequence", menu) - split_action.triggered.connect(self._on_split_request) - menu.addAction(split_action) + remove_action = QtWidgets.QAction("Remove", menu) + remove_action.triggered.connect(self._on_remove_requested) + menu.addAction(remove_action) - remove_action = QtWidgets.QAction("Remove", menu) - remove_action.triggered.connect(self._on_remove_requested) - menu.addAction(remove_action) + if not valid_index: + revert_action = QtWidgets.QAction(REVERT_TO_DEFAULT_LABEL, menu) + revert_action.triggered.connect(self.revert_requested) + menu.addAction(revert_action) - menu.popup(pos) + if menu.actions(): + menu.popup(pos) def dragEnterEvent(self, event): if self._multivalue: @@ -1011,5 +1029,5 @@ class FilesWidget(QtWidgets.QFrame): current_widget = self._files_view else: current_widget = self._empty_widget - self._layout.setCurrentWidget(current_widget) + self._wrapper_layout.setCurrentWidget(current_widget) self._files_view.update_remove_btn_visibility() diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 03482c1006..22f4bfe535 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -811,10 +811,25 @@ class FileAttrWidget(_BaseAttrDefWidget): self.main_layout.addWidget(input_widget, 0) + input_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + input_widget.customContextMenuRequested.connect(self._on_context_menu) + input_widget.revert_requested.connect(self.revert_to_default_value) + def _on_value_change(self): new_value = self.current_value() self.value_changed.emit(new_value, self.attr_def.id) + def _on_context_menu(self, pos): + menu = QtWidgets.QMenu(self) + + action = QtWidgets.QAction(menu) + action.setText(REVERT_TO_DEFAULT_LABEL) + action.triggered.connect(self.revert_to_default_value) + menu.addAction(action) + + global_pos = self.mapToGlobal(pos) + menu.exec_(global_pos) + def current_value(self): return self._input_widget.current_value() From 53a839b34fcf04669e094e728448e95ba792d4f4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:15:09 +0100 Subject: [PATCH 033/463] fix condition triggering refresh of values in UI --- .../ayon_core/tools/publisher/widgets/product_attributes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/product_attributes.py b/client/ayon_core/tools/publisher/widgets/product_attributes.py index 3ff295c986..2b9f316d41 100644 --- a/client/ayon_core/tools/publisher/widgets/product_attributes.py +++ b/client/ayon_core/tools/publisher/widgets/product_attributes.py @@ -232,7 +232,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget): for instance_id, changes in event["instance_changes"].items(): if ( instance_id in self._current_instance_ids - and "creator_attributes" not in changes + and "creator_attributes" in changes ): self._refresh_content() break @@ -498,7 +498,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget): for instance_id, changes in event["instance_changes"].items(): if ( instance_id in self._current_instance_ids - and "publish_attributes" not in changes + and "publish_attributes" in changes ): self._refresh_content() break From 569ce30b9672c77e6d553a03098160c0c13e166c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:38:44 +0100 Subject: [PATCH 034/463] pass all required arguments to FileDefItem --- client/ayon_core/lib/attribute_definitions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 34956fd33f..789c878d40 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -966,7 +966,9 @@ class FileDef(AbstractAttrDef): FileDefItem.from_dict(default) elif isinstance(default, str): - default = FileDefItem.from_paths([default.strip()])[0] + default = FileDefItem.from_paths( + [default.strip()], allow_sequences + )[0] else: raise TypeError(( @@ -1044,7 +1046,9 @@ class FileDef(AbstractAttrDef): pass if string_paths: - file_items = FileDefItem.from_paths(string_paths) + file_items = FileDefItem.from_paths( + string_paths, self.allow_sequences + ) dict_items.extend([ file_item.to_dict() for file_item in file_items From 521d8ed9ec87df5487480ebbfdfac5b31f7dfab4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:28:07 +0100 Subject: [PATCH 035/463] move register functions below classes --- client/ayon_core/lib/attribute_definitions.py | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 34956fd33f..e4e998189d 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -15,67 +15,6 @@ import clique _attr_defs_by_type = {} -def register_attr_def_class(cls): - """Register attribute definition. - - Currently registered definitions are used to deserialize data to objects. - - Attrs: - cls (AbstractAttrDef): Non-abstract class to be registered with unique - 'type' attribute. - - Raises: - KeyError: When type was already registered. - """ - - if cls.type in _attr_defs_by_type: - raise KeyError("Type \"{}\" was already registered".format(cls.type)) - _attr_defs_by_type[cls.type] = cls - - -def get_attributes_keys(attribute_definitions): - """Collect keys from list of attribute definitions. - - Args: - attribute_definitions (List[AbstractAttrDef]): Objects of attribute - definitions. - - Returns: - Set[str]: Keys that will be created using passed attribute definitions. - """ - - keys = set() - if not attribute_definitions: - return keys - - for attribute_def in attribute_definitions: - if not isinstance(attribute_def, UIDef): - keys.add(attribute_def.key) - return keys - - -def get_default_values(attribute_definitions): - """Receive default values for attribute definitions. - - Args: - attribute_definitions (List[AbstractAttrDef]): Attribute definitions - for which default values should be collected. - - Returns: - Dict[str, Any]: Default values for passed attribute definitions. - """ - - output = {} - if not attribute_definitions: - return output - - for attr_def in attribute_definitions: - # Skip UI definitions - if not isinstance(attr_def, UIDef): - output[attr_def.key] = attr_def.default - return output - - class AbstractAttrDefMeta(ABCMeta): """Metaclass to validate the existence of 'key' attribute. @@ -1062,6 +1001,67 @@ class FileDef(AbstractAttrDef): return [] +def register_attr_def_class(cls): + """Register attribute definition. + + Currently registered definitions are used to deserialize data to objects. + + Attrs: + cls (AbstractAttrDef): Non-abstract class to be registered with unique + 'type' attribute. + + Raises: + KeyError: When type was already registered. + """ + + if cls.type in _attr_defs_by_type: + raise KeyError("Type \"{}\" was already registered".format(cls.type)) + _attr_defs_by_type[cls.type] = cls + + +def get_attributes_keys(attribute_definitions): + """Collect keys from list of attribute definitions. + + Args: + attribute_definitions (List[AbstractAttrDef]): Objects of attribute + definitions. + + Returns: + Set[str]: Keys that will be created using passed attribute definitions. + """ + + keys = set() + if not attribute_definitions: + return keys + + for attribute_def in attribute_definitions: + if not isinstance(attribute_def, UIDef): + keys.add(attribute_def.key) + return keys + + +def get_default_values(attribute_definitions): + """Receive default values for attribute definitions. + + Args: + attribute_definitions (List[AbstractAttrDef]): Attribute definitions + for which default values should be collected. + + Returns: + Dict[str, Any]: Default values for passed attribute definitions. + """ + + output = {} + if not attribute_definitions: + return output + + for attr_def in attribute_definitions: + # Skip UI definitions + if not isinstance(attr_def, UIDef): + output[attr_def.key] = attr_def.default + return output + + def serialize_attr_def(attr_def): """Serialize attribute definition to data. From 9d629eca2fd87903afd28d2998c4522f8be67fd2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:30:55 +0100 Subject: [PATCH 036/463] added helper type definitions --- client/ayon_core/lib/attribute_definitions.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e4e998189d..76abe5fe4d 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -6,14 +6,33 @@ import json import copy import warnings from abc import ABCMeta, abstractmethod -from typing import Any, Optional +import typing +from typing import Any, Optional, List, TypedDict import clique +if typing.TYPE_CHECKING: + from typing import Union # Global variable which store attribute definitions by type # - default types are registered on import _attr_defs_by_type = {} +# Type hint helpers +IntFloatType = "Union[int, float]" + + +class EnumItemDict(TypedDict): + label: str + value: Any + + +class FileDefItemDict(TypedDict): + directory: str + filenames: List[str] + frames: Optional[List[int]] + template: Optional[str] + is_sequence: Optional[bool] + class AbstractAttrDefMeta(ABCMeta): """Metaclass to validate the existence of 'key' attribute. From 443ebf8523adbbee9112c5f4aa26980f6c3122ff Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:43:46 +0100 Subject: [PATCH 037/463] added most of typehints --- client/ayon_core/lib/attribute_definitions.py | 177 ++++++++++++------ 1 file changed, 117 insertions(+), 60 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 76abe5fe4d..82c7ab9cb1 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -7,12 +7,14 @@ import copy import warnings from abc import ABCMeta, abstractmethod import typing -from typing import Any, Optional, List, TypedDict +from typing import ( + Any, Optional, List, Set, Dict, Iterable, TypedDict, TypeVar, +) import clique if typing.TYPE_CHECKING: - from typing import Union + from typing import Self, Union, Pattern # Global variable which store attribute definitions by type # - default types are registered on import _attr_defs_by_type = {} @@ -51,8 +53,12 @@ class AbstractAttrDefMeta(ABCMeta): def _convert_reversed_attr( - main_value, depr_value, main_label, depr_label, default -): + main_value: Any, + depr_value: Any, + main_label: str, + depr_label: str, + default: Any, +) -> Any: if main_value is not None and depr_value is not None: if main_value == depr_value: print( @@ -141,7 +147,7 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): def id(self) -> str: return self._id - def clone(self): + def clone(self) -> "Self": data = self.serialize() data.pop("type") return self.deserialize(data) @@ -214,7 +220,7 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): pass @abstractmethod - def convert_value(self, value): + def convert_value(self, value: Any) -> Any: """Convert value to a valid one. Convert passed value to a valid type. Use default if value can't be @@ -223,7 +229,7 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): pass - def serialize(self): + def serialize(self) -> Dict[str, Any]: """Serialize object to data so it's possible to recreate it. Returns: @@ -246,7 +252,7 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): return data @classmethod - def deserialize(cls, data): + def deserialize(cls, data: Dict[str, Any]) -> "Self": """Recreate object from data. Data can be received using 'serialize' method. @@ -257,7 +263,7 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): return cls(**data) - def _def_type_compare(self, other: "AbstractAttrDef") -> bool: + def _def_type_compare(self, other: "Self") -> bool: return True @@ -268,13 +274,19 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): class UIDef(AbstractAttrDef): is_value_def = False - def __init__(self, key=None, default=None, *args, **kwargs): + def __init__( + self, + key: Optional[str] = None, + default: Optional[Any] = None, + *args, + **kwargs + ): super().__init__(key, default, *args, **kwargs) def is_value_valid(self, value: Any) -> bool: return True - def convert_value(self, value): + def convert_value(self, value: Any) -> Any: return value @@ -305,14 +317,14 @@ class UnknownDef(AbstractAttrDef): type = "unknown" - def __init__(self, key, default=None, **kwargs): + def __init__(self, key: str, default: Optional[Any] = None, **kwargs): kwargs["default"] = default super().__init__(key, **kwargs) def is_value_valid(self, value: Any) -> bool: return True - def convert_value(self, value): + def convert_value(self, value: Any) -> Any: return value @@ -327,7 +339,7 @@ class HiddenDef(AbstractAttrDef): type = "hidden" - def __init__(self, key, default=None, **kwargs): + def __init__(self, key: str, default: Optional[Any] = None, **kwargs): kwargs["default"] = default kwargs["visible"] = False super().__init__(key, **kwargs) @@ -335,7 +347,7 @@ class HiddenDef(AbstractAttrDef): def is_value_valid(self, value: Any) -> bool: return True - def convert_value(self, value): + def convert_value(self, value: Any) -> Any: return value @@ -360,7 +372,12 @@ class NumberDef(AbstractAttrDef): ] def __init__( - self, key, minimum=None, maximum=None, decimals=None, default=None, + self, + key: str, + minimum: Optional[IntFloatType] = None, + maximum: Optional[IntFloatType] = None, + decimals: Optional[int] = None, + default: Optional[IntFloatType] = None, **kwargs ): minimum = 0 if minimum is None else minimum @@ -386,9 +403,9 @@ class NumberDef(AbstractAttrDef): super().__init__(key, default=default, **kwargs) - self.minimum = minimum - self.maximum = maximum - self.decimals = 0 if decimals is None else decimals + self.minimum: IntFloatType = minimum + self.maximum: IntFloatType = maximum + self.decimals: int = 0 if decimals is None else decimals def is_value_valid(self, value: Any) -> bool: if self.decimals == 0: @@ -400,7 +417,7 @@ class NumberDef(AbstractAttrDef): return False return True - def convert_value(self, value): + def convert_value(self, value: Any) -> IntFloatType: if isinstance(value, str): try: value = float(value) @@ -444,7 +461,12 @@ class TextDef(AbstractAttrDef): ] def __init__( - self, key, multiline=None, regex=None, placeholder=None, default=None, + self, + key: str, + multiline: Optional[bool] = None, + regex: Optional[str] = None, + placeholder: Optional[str] = None, + default: Optional[str] = None, **kwargs ): if default is None: @@ -463,9 +485,9 @@ class TextDef(AbstractAttrDef): if isinstance(regex, str): regex = re.compile(regex) - self.multiline = multiline - self.placeholder = placeholder - self.regex = regex + self.multiline: bool = multiline + self.placeholder: Optional[str] = placeholder + self.regex: Optional["Pattern"] = regex def is_value_valid(self, value: Any) -> bool: if not isinstance(value, str): @@ -474,12 +496,12 @@ class TextDef(AbstractAttrDef): return False return True - def convert_value(self, value): + def convert_value(self, value: Any) -> str: if isinstance(value, str): return value return self.default - def serialize(self): + def serialize(self) -> Dict[str, Any]: data = super().serialize() regex = None if self.regex is not None: @@ -503,8 +525,9 @@ class EnumDef(AbstractAttrDef): is enabled. Args: - items (Union[list[str], list[dict[str, Any]]): Items definition that - can be converted using 'prepare_enum_items'. + key (str): Key under which value is stored. + items (Union[Dict[Any, str], List[Any], List[EnumItemDict]]): Items + definition that can be converted using 'prepare_enum_items'. default (Optional[Any]): Default value. Must be one key(value) from passed items or list of values for multiselection. multiselection (Optional[bool]): If True, multiselection is allowed. @@ -514,7 +537,12 @@ class EnumDef(AbstractAttrDef): type = "enum" def __init__( - self, key, items, default=None, multiselection=False, **kwargs + self, + key: str, + items: "Union[Dict[Any, str], List[Any], List[EnumItemDict]]", + default: "Union[str, List[Any]]" = None, + multiselection: Optional[bool] = False, + **kwargs ): if not items: raise ValueError(( @@ -525,6 +553,9 @@ class EnumDef(AbstractAttrDef): items = self.prepare_enum_items(items) item_values = [item["value"] for item in items] item_values_set = set(item_values) + if multiselection is None: + multiselection = False + if multiselection: if default is None: default = [] @@ -535,9 +566,9 @@ class EnumDef(AbstractAttrDef): super().__init__(key, default=default, **kwargs) - self.items = items - self._item_values = item_values_set - self.multiselection = multiselection + self.items: List[EnumItemDict] = items + self._item_values: Set[Any] = item_values_set + self.multiselection: bool = multiselection def convert_value(self, value): if not self.multiselection: @@ -567,7 +598,7 @@ class EnumDef(AbstractAttrDef): return data @staticmethod - def prepare_enum_items(items): + def prepare_enum_items(items) -> List[EnumItemDict]: """Convert items to unified structure. Output is a list where each item is dictionary with 'value' @@ -583,13 +614,13 @@ class EnumDef(AbstractAttrDef): ``` Args: - items (Union[Dict[str, Any], List[Any], List[Dict[str, Any]]): The + items (Union[Dict[Any, str], List[Any], List[EnumItemDict]]): The items to convert. Returns: - List[Dict[str, Any]]: Unified structure of items. - """ + List[EnumItemDict]: Unified structure of items. + """ output = [] if isinstance(items, dict): for value, label in items.items(): @@ -644,7 +675,7 @@ class BoolDef(AbstractAttrDef): type = "bool" - def __init__(self, key, default=None, **kwargs): + def __init__(self, key: str, default: Optional[bool] = None, **kwargs): if default is None: default = False super().__init__(key, default=default, **kwargs) @@ -652,7 +683,7 @@ class BoolDef(AbstractAttrDef): def is_value_valid(self, value: Any) -> bool: return isinstance(value, bool) - def convert_value(self, value): + def convert_value(self, value: Any) -> bool: if isinstance(value, bool): return value return self.default @@ -660,7 +691,11 @@ class BoolDef(AbstractAttrDef): class FileDefItem: def __init__( - self, directory, filenames, frames=None, template=None + self, + directory: str, + filenames: List[str], + frames: Optional[List[int]] = None, + template: Optional[str] = None, ): self.directory = directory @@ -689,7 +724,7 @@ class FileDefItem: ) @property - def label(self): + def label(self) -> Optional[str]: if self.is_empty: return None @@ -732,7 +767,7 @@ class FileDefItem: filename_template, ",".join(ranges) ) - def split_sequence(self): + def split_sequence(self) -> List["Self"]: if not self.is_sequence: raise ValueError("Cannot split single file item") @@ -743,7 +778,7 @@ class FileDefItem: return self.from_paths(paths, False) @property - def ext(self): + def ext(self) -> Optional[str]: if self.is_empty: return None _, ext = os.path.splitext(self.filenames[0]) @@ -752,14 +787,14 @@ class FileDefItem: return None @property - def lower_ext(self): + def lower_ext(self) -> Optional[str]: ext = self.ext if ext is not None: return ext.lower() return ext @property - def is_dir(self): + def is_dir(self) -> bool: if self.is_empty: return False @@ -768,10 +803,15 @@ class FileDefItem: return False return True - def set_directory(self, directory): + def set_directory(self, directory: str): self.directory = directory - def set_filenames(self, filenames, frames=None, template=None): + def set_filenames( + self, + filenames: List[str], + frames: Optional[List[int]] = None, + template: Optional[str] = None, + ): if frames is None: frames = [] is_sequence = False @@ -788,11 +828,15 @@ class FileDefItem: self.is_sequence = is_sequence @classmethod - def create_empty_item(cls): + def create_empty_item(cls) -> "Self": return cls("", "") @classmethod - def from_value(cls, value, allow_sequences): + def from_value( + cls, + value: "Union[List[FileDefItemDict], FileDefItemDict]", + allow_sequences: bool, + ) -> List["Self"]: """Convert passed value to FileDefItem objects. Returns: @@ -830,7 +874,7 @@ class FileDefItem: return output @classmethod - def from_dict(cls, data): + def from_dict(cls, data: FileDefItemDict) -> "Self": return cls( data["directory"], data["filenames"], @@ -839,7 +883,11 @@ class FileDefItem: ) @classmethod - def from_paths(cls, paths, allow_sequences): + def from_paths( + cls, + paths: List[str], + allow_sequences: bool, + ) -> List["Self"]: filenames_by_dir = collections.defaultdict(list) for path in paths: normalized = os.path.normpath(path) @@ -868,7 +916,7 @@ class FileDefItem: return output - def to_dict(self): + def to_dict(self) -> FileDefItemDict: output = { "is_sequence": self.is_sequence, "directory": self.directory, @@ -906,8 +954,15 @@ class FileDef(AbstractAttrDef): ] def __init__( - self, key, single_item=True, folders=None, extensions=None, - allow_sequences=True, extensions_label=None, default=None, **kwargs + self, + key: str, + single_item: Optional[bool] = True, + folders: Optional[bool] = None, + extensions: Optional[Iterable[str]] = None, + allow_sequences: Optional[bool] = True, + extensions_label: Optional[str] = None, + default: Optional["Union[FileDefItemDict, List[str]]"] = None, + **kwargs ): if folders is None and extensions is None: folders = True @@ -943,14 +998,14 @@ class FileDef(AbstractAttrDef): if is_label_horizontal is None: kwargs["is_label_horizontal"] = False - self.single_item = single_item - self.folders = folders - self.extensions = set(extensions) - self.allow_sequences = allow_sequences - self.extensions_label = extensions_label + self.single_item: bool = single_item + self.folders: bool = folders + self.extensions: Set[str] = set(extensions) + self.allow_sequences: bool = allow_sequences + self.extensions_label: Optional[str] = extensions_label super().__init__(key, default=default, **kwargs) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not super().__eq__(other): return False @@ -984,7 +1039,9 @@ class FileDef(AbstractAttrDef): return False return True - def convert_value(self, value): + def convert_value( + self, value: Any + ) -> "Union[FileDefItemDict, List[FileDefItemDict]]": if isinstance(value, (str, dict)): value = [value] From 586d29f219f76572ffea9c431fe9f197cd0a2907 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:44:32 +0100 Subject: [PATCH 038/463] define 'EnumItemsInputType' for EnumDef input items --- client/ayon_core/lib/attribute_definitions.py | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 82c7ab9cb1..bf47b7617b 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -8,13 +8,22 @@ import warnings from abc import ABCMeta, abstractmethod import typing from typing import ( - Any, Optional, List, Set, Dict, Iterable, TypedDict, TypeVar, + Any, + Optional, + Tuple, + List, + Set, + Dict, + Iterable, + TypedDict, + TypeVar, ) import clique if typing.TYPE_CHECKING: from typing import Self, Union, Pattern + # Global variable which store attribute definitions by type # - default types are registered on import _attr_defs_by_type = {} @@ -28,6 +37,9 @@ class EnumItemDict(TypedDict): value: Any +EnumItemsInputType = "Union[Dict[Any, str], List[Tuple[Any, str]], List[Any], List[EnumItemDict]]" # noqa: E501 + + class FileDefItemDict(TypedDict): directory: str filenames: List[str] @@ -526,8 +538,8 @@ class EnumDef(AbstractAttrDef): Args: key (str): Key under which value is stored. - items (Union[Dict[Any, str], List[Any], List[EnumItemDict]]): Items - definition that can be converted using 'prepare_enum_items'. + items (EnumItemsInputType): Items definition that can be converted + using 'prepare_enum_items'. default (Optional[Any]): Default value. Must be one key(value) from passed items or list of values for multiselection. multiselection (Optional[bool]): If True, multiselection is allowed. @@ -539,7 +551,7 @@ class EnumDef(AbstractAttrDef): def __init__( self, key: str, - items: "Union[Dict[Any, str], List[Any], List[EnumItemDict]]", + items: EnumItemsInputType, default: "Union[str, List[Any]]" = None, multiselection: Optional[bool] = False, **kwargs @@ -598,7 +610,7 @@ class EnumDef(AbstractAttrDef): return data @staticmethod - def prepare_enum_items(items) -> List[EnumItemDict]: + def prepare_enum_items(items: EnumItemsInputType) -> List[EnumItemDict]: """Convert items to unified structure. Output is a list where each item is dictionary with 'value' @@ -614,8 +626,7 @@ class EnumDef(AbstractAttrDef): ``` Args: - items (Union[Dict[Any, str], List[Any], List[EnumItemDict]]): The - items to convert. + items (EnumItemsInputType): The items to convert. Returns: List[EnumItemDict]: Unified structure of items. From b2a9277267a36fbbad093a5fc94b5f33e286de0b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:55:48 +0100 Subject: [PATCH 039/463] define 'AttrDefType' --- client/ayon_core/lib/attribute_definitions.py | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index bf47b7617b..836d6c7463 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -279,6 +279,8 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): return True +AttrDefType = TypeVar("AttrDefType", bound=AbstractAttrDef) + # ----------------------------------------- # UI attribute definitions won't hold value # ----------------------------------------- @@ -1088,13 +1090,13 @@ class FileDef(AbstractAttrDef): return [] -def register_attr_def_class(cls): +def register_attr_def_class(cls: AttrDefType): """Register attribute definition. Currently registered definitions are used to deserialize data to objects. Attrs: - cls (AbstractAttrDef): Non-abstract class to be registered with unique + cls (AttrDefType): Non-abstract class to be registered with unique 'type' attribute. Raises: @@ -1106,11 +1108,13 @@ def register_attr_def_class(cls): _attr_defs_by_type[cls.type] = cls -def get_attributes_keys(attribute_definitions): +def get_attributes_keys( + attribute_definitions: List[AttrDefType] +) -> Set[str]: """Collect keys from list of attribute definitions. Args: - attribute_definitions (List[AbstractAttrDef]): Objects of attribute + attribute_definitions (List[AttrDefType]): Objects of attribute definitions. Returns: @@ -1127,11 +1131,13 @@ def get_attributes_keys(attribute_definitions): return keys -def get_default_values(attribute_definitions): +def get_default_values( + attribute_definitions: List[AttrDefType] +) -> Dict[str, Any]: """Receive default values for attribute definitions. Args: - attribute_definitions (List[AbstractAttrDef]): Attribute definitions + attribute_definitions (List[AttrDefType]): Attribute definitions for which default values should be collected. Returns: @@ -1149,11 +1155,11 @@ def get_default_values(attribute_definitions): return output -def serialize_attr_def(attr_def): +def serialize_attr_def(attr_def: AttrDefType) -> Dict[str, Any]: """Serialize attribute definition to data. Args: - attr_def (AbstractAttrDef): Attribute definition to serialize. + attr_def (AttrDefType): Attribute definition to serialize. Returns: Dict[str, Any]: Serialized data. @@ -1162,11 +1168,13 @@ def serialize_attr_def(attr_def): return attr_def.serialize() -def serialize_attr_defs(attr_defs): +def serialize_attr_defs( + attr_defs: List[AttrDefType] +) -> List[Dict[str, Any]]: """Serialize attribute definitions to data. Args: - attr_defs (List[AbstractAttrDef]): Attribute definitions to serialize. + attr_defs (List[AttrDefType]): Attribute definitions to serialize. Returns: List[Dict[str, Any]]: Serialized data. @@ -1178,7 +1186,7 @@ def serialize_attr_defs(attr_defs): ] -def deserialize_attr_def(attr_def_data): +def deserialize_attr_def(attr_def_data: Dict[str, Any]) -> AttrDefType: """Deserialize attribute definition from data. Args: @@ -1191,7 +1199,9 @@ def deserialize_attr_def(attr_def_data): return cls.deserialize(attr_def_data) -def deserialize_attr_defs(attr_defs_data): +def deserialize_attr_defs( + attr_defs_data: List[Dict[str, Any]] +) -> List[AttrDefType]: """Deserialize attribute definitions. Args: From 341dc04cabd6982b636689cd63e83f3a9f0b3a5e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:55:59 +0100 Subject: [PATCH 040/463] change formatting of docstrings --- client/ayon_core/lib/attribute_definitions.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 836d6c7463..9e1a92b18e 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -52,8 +52,8 @@ class AbstractAttrDefMeta(ABCMeta): """Metaclass to validate the existence of 'key' attribute. Each object of `AbstractAttrDef` must have defined 'key' attribute. - """ + """ def __call__(cls, *args, **kwargs): obj = super(AbstractAttrDefMeta, cls).__call__(*args, **kwargs) init_class = getattr(obj, "__init__class__", None) @@ -116,8 +116,8 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): enabled (Optional[bool]): Item is enabled (for UI purposes). hidden (Optional[bool]): DEPRECATED: Use 'visible' instead. disabled (Optional[bool]): DEPRECATED: Use 'enabled' instead. - """ + """ type_attributes = [] is_value_def = True @@ -227,8 +227,8 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): Returns: str: Type of attribute definition. - """ + """ pass @abstractmethod @@ -237,8 +237,8 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): Convert passed value to a valid type. Use default if value can't be converted. - """ + """ pass def serialize(self) -> Dict[str, Any]: @@ -247,8 +247,8 @@ class AbstractAttrDef(metaclass=AbstractAttrDefMeta): Returns: Dict[str, Any]: Serialized object that can be passed to 'deserialize' method. - """ + """ data = { "type": self.type, "key": self.key, @@ -327,8 +327,8 @@ class UnknownDef(AbstractAttrDef): This attribute can be used to keep existing data unchanged but does not have known definition of type. - """ + """ type = "unknown" def __init__(self, key: str, default: Optional[Any] = None, **kwargs): @@ -349,8 +349,8 @@ class HiddenDef(AbstractAttrDef): to other attributes (e.g. in multi-page UIs). Keep in mind the value should be possible to parse by json parser. - """ + """ type = "hidden" def __init__(self, key: str, default: Optional[Any] = None, **kwargs): @@ -376,8 +376,8 @@ class NumberDef(AbstractAttrDef): maximum(int, float): Maximum possible value. decimals(int): Maximum decimal points of value. default(int, float): Default value for conversion. - """ + """ type = "number" type_attributes = [ "minimum", @@ -466,8 +466,8 @@ class TextDef(AbstractAttrDef): regex(str, re.Pattern): Regex validation. placeholder(str): UI placeholder for attribute. default(str, None): Default value. Empty string used when not defined. - """ + """ type = "text" type_attributes = [ "multiline", @@ -546,8 +546,8 @@ class EnumDef(AbstractAttrDef): passed items or list of values for multiselection. multiselection (Optional[bool]): If True, multiselection is allowed. Output is list of selected items. - """ + """ type = "enum" def __init__( @@ -684,8 +684,8 @@ class BoolDef(AbstractAttrDef): Args: default(bool): Default value. Set to `False` if not defined. - """ + """ type = "bool" def __init__(self, key: str, default: Optional[bool] = None, **kwargs): @@ -854,8 +854,8 @@ class FileDefItem: Returns: list: Created FileDefItem objects. - """ + """ # Convert single item to iterable if not isinstance(value, (list, tuple, set)): value = [value] @@ -1101,8 +1101,8 @@ def register_attr_def_class(cls: AttrDefType): Raises: KeyError: When type was already registered. - """ + """ if cls.type in _attr_defs_by_type: raise KeyError("Type \"{}\" was already registered".format(cls.type)) _attr_defs_by_type[cls.type] = cls @@ -1119,8 +1119,8 @@ def get_attributes_keys( Returns: Set[str]: Keys that will be created using passed attribute definitions. - """ + """ keys = set() if not attribute_definitions: return keys @@ -1142,8 +1142,8 @@ def get_default_values( Returns: Dict[str, Any]: Default values for passed attribute definitions. - """ + """ output = {} if not attribute_definitions: return output @@ -1163,8 +1163,8 @@ def serialize_attr_def(attr_def: AttrDefType) -> Dict[str, Any]: Returns: Dict[str, Any]: Serialized data. - """ + """ return attr_def.serialize() @@ -1178,8 +1178,8 @@ def serialize_attr_defs( Returns: List[Dict[str, Any]]: Serialized data. - """ + """ return [ serialize_attr_def(attr_def) for attr_def in attr_defs @@ -1192,8 +1192,8 @@ def deserialize_attr_def(attr_def_data: Dict[str, Any]) -> AttrDefType: Args: attr_def_data (Dict[str, Any]): Attribute definition data to deserialize. - """ + """ attr_type = attr_def_data.pop("type") cls = _attr_defs_by_type[attr_type] return cls.deserialize(attr_def_data) @@ -1206,8 +1206,8 @@ def deserialize_attr_defs( Args: List[Dict[str, Any]]: List of attribute definitions. - """ + """ return [ deserialize_attr_def(attr_def_data) for attr_def_data in attr_defs_data From 683bc0e39a303189149ea86f3db9747e9cb0a498 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:51:54 +0100 Subject: [PATCH 041/463] fix import --- client/ayon_core/lib/attribute_definitions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 9e1a92b18e..68c84276cb 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -10,7 +10,6 @@ import typing from typing import ( Any, Optional, - Tuple, List, Set, Dict, @@ -22,7 +21,7 @@ from typing import ( import clique if typing.TYPE_CHECKING: - from typing import Self, Union, Pattern + from typing import Tuple, Self, Union, Pattern # Global variable which store attribute definitions by type # - default types are registered on import From 07bbe08c76e58f835c1892af63395979dcfbf26b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:08:08 +0100 Subject: [PATCH 042/463] remove 'Tuple' import Looks like the import is not needed even if the typehint is used for 'EnumItemsInputType'? --- client/ayon_core/lib/attribute_definitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index 68c84276cb..e841a4b230 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -21,7 +21,7 @@ from typing import ( import clique if typing.TYPE_CHECKING: - from typing import Tuple, Self, Union, Pattern + from typing import Self, Union, Pattern # Global variable which store attribute definitions by type # - default types are registered on import From 68db3d9c117df46aaf883a344aef61d26752aa22 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Nov 2024 15:48:19 +0100 Subject: [PATCH 043/463] Add logic to extract colorspace from metadata if available. - Extract colorspace from media metadata for review clips. - Update instance data with the extracted colorspace information. --- .../plugins/publish/collect_otio_review.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 69cf9199e7..04422391c5 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -95,9 +95,46 @@ class CollectOtioReview(pyblish.api.InstancePlugin): instance.data["label"] = label + " (review)" instance.data["families"] += ["review", "ftrack"] instance.data["otioReviewClips"] = otio_review_clips + self.log.info( "Creating review track: {}".format(otio_review_clips)) + # get colorspace from metadata if available + if len(otio_review_clips) >= 1 and any( + # lets make sure any clip with media reference is found + ( + clip + for clip in otio_review_clips + if isinstance(clip, otio.schema.Clip) + and clip.media_reference + ) + ): + # get metadata from first clip + # get colorspace from metadata if available + # check if resolution is the same as source + r_otio_cl = next( + ( + clip + for clip in otio_review_clips + if isinstance(clip, otio.schema.Clip) + and clip.media_reference + ), + None, + ) + + # get metadata from first clip with media reference + media_ref = r_otio_cl.media_reference + media_metadata = media_ref.metadata + + # TODO: we might need some alternative method since + # native OTIO exports do not support ayon metadata + if review_colorspace := media_metadata.get( + "ayon.source.colorspace" + ): + instance.data["reviewColorspace"] = review_colorspace + self.log.info( + "Review colorspace: {}".format(review_colorspace)) + self.log.debug( "_ instance.data: {}".format(pformat(instance.data))) self.log.debug( From 83f28bf184bfa514294133f01787a47758ca610d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Nov 2024 15:48:30 +0100 Subject: [PATCH 044/463] Refactor plugin to include Colormanaged mixin The code changes refactor the plugin to include a Colormanaged mixin for managing colorspace data in representations. The mixin is added to the existing plugin class. --- .../plugins/publish/collect_otio_subset_resources.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index 37a5e87a7a..c142036b83 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -10,12 +10,16 @@ import os import clique import pyblish.api +from ayon_core.pipeline import publish from ayon_core.pipeline.publish import ( get_publish_template_name ) -class CollectOtioSubsetResources(pyblish.api.InstancePlugin): +class CollectOtioSubsetResources( + pyblish.api.InstancePlugin, + publish.ColormanagedPyblishPluginMixin +): """Get Resources for a product version""" label = "Collect OTIO Subset Resources" @@ -190,9 +194,13 @@ class CollectOtioSubsetResources(pyblish.api.InstancePlugin): instance.data["originalDirname"] = self.staging_dir if repre: + colorspace = instance.data.get("colorspace") + # add colorspace data to representation + self.set_representation_colorspace( + repre, instance.context, colorspace) + # add representation to instance data instance.data["representations"].append(repre) - self.log.debug(">>>>>>>> {}".format(repre)) self.log.debug(instance.data) From 6a635b9d5e0852a77bd2bfaaa28b3ec6d1e8b4d0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Nov 2024 15:49:03 +0100 Subject: [PATCH 045/463] Update color transcoding process with debug log messages. - Add debug logs for files to convert, transcoded file, and input path. --- client/ayon_core/plugins/publish/extract_color_transcode.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 3e54d324e3..e7e0c982eb 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -5,7 +5,6 @@ import pyblish.api from ayon_core.pipeline import publish from ayon_core.lib import ( - is_oiio_supported, ) @@ -154,12 +153,15 @@ class ExtractOIIOTranscode(publish.Extractor): files_to_convert = self._translate_to_sequence( files_to_convert) + self.log.debug("Files to convert: {}".format(files_to_convert)) for file_name in files_to_convert: + self.log.debug("Transcoding file: `{}`".format(file_name)) input_path = os.path.join(original_staging_dir, file_name) output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) + self.log.debug("Ynput path: `{}`".format(input_path)) convert_colorspace( input_path, output_path, From 3a71bbca295d8d2b9d7ab452ac1b3b8f3f26037c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Nov 2024 15:49:13 +0100 Subject: [PATCH 046/463] Add colorspace data extraction to representation loop Extracts colorspace data from instance data and sets it in the representation loop for processing. --- client/ayon_core/plugins/publish/extract_colorspace_data.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_colorspace_data.py b/client/ayon_core/plugins/publish/extract_colorspace_data.py index 7da4890748..d68ad4d80d 100644 --- a/client/ayon_core/plugins/publish/extract_colorspace_data.py +++ b/client/ayon_core/plugins/publish/extract_colorspace_data.py @@ -37,6 +37,9 @@ class ExtractColorspaceData(publish.Extractor, # get colorspace settings context = instance.context + # colorspace name could be kept in instance.data + colorspace = instance.data.get("colorspace") + # loop representations for representation in representations: # skip if colorspaceData is already at representation @@ -44,5 +47,5 @@ class ExtractColorspaceData(publish.Extractor, continue self.set_representation_colorspace( - representation, context + representation, context, colorspace) ) From 0ff9ae65d8843afa0179277c2c3342fab465cec9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Nov 2024 15:50:38 +0100 Subject: [PATCH 047/463] Refactor ExtractOTIOReview class inheritance and add colorspace handling - Refactored class inheritance for ExtractOTIOReview - Added handling for colorspace data in representation creation --- .../plugins/publish/extract_otio_review.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index faba9fd36d..2c6472f8a4 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -26,7 +26,10 @@ from ayon_core.lib import ( from ayon_core.pipeline import publish -class ExtractOTIOReview(publish.Extractor): +class ExtractOTIOReview( + publish.Extractor, + publish.ColormanagedPyblishPluginMixin +): """ Extract OTIO timeline into one concuted image sequence file. @@ -78,7 +81,9 @@ class ExtractOTIOReview(publish.Extractor): self.used_frames = [] self.workfile_start = int(instance.data.get( "workfileFrameStart", 1001)) - handle_start - self.padding = len(str(self.workfile_start)) + # NOTE: padding has to be converted from + # end frame since start could be lower then 1000 + self.padding = len(str(instance.data.get("frameEnd", 1001))) self.used_frames.append(self.workfile_start) self.to_width = instance.data.get( "resolutionWidth") or self.to_width @@ -86,8 +91,10 @@ class ExtractOTIOReview(publish.Extractor): "resolutionHeight") or self.to_height # skip instance if no reviewable data available - if (not isinstance(otio_review_clips[0], otio.schema.Clip)) \ - and (len(otio_review_clips) == 1): + if ( + not isinstance(otio_review_clips[0], otio.schema.Clip) + and len(otio_review_clips) == 1 + ): self.log.warning( "Instance `{}` has nothing to process".format(instance)) return @@ -168,7 +175,7 @@ class ExtractOTIOReview(publish.Extractor): start -= clip_handle_start duration += clip_handle_start elif len(otio_review_clips) > 1 \ - and (index == len(otio_review_clips) - 1): + and (index == len(otio_review_clips) - 1): # more clips | last clip reframing with handle duration += clip_handle_end elif len(otio_review_clips) == 1: @@ -263,6 +270,13 @@ class ExtractOTIOReview(publish.Extractor): # creating and registering representation representation = self._create_representation(start, duration) + + # add colorspace data to representation + if colorspace := instance.data.get("reviewColorspace"): + self.set_representation_colorspace( + representation, instance.context, colorspace + ) + instance.data["representations"].append(representation) self.log.info("Adding representation: {}".format(representation)) From e0e541b24a01110846ff57b359a66ed8b60af81c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 6 Nov 2024 15:57:24 +0100 Subject: [PATCH 048/463] Refactor colorspace extraction logic - Removed unnecessary closing parenthesis in colorspace extraction method. --- client/ayon_core/plugins/publish/extract_colorspace_data.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_colorspace_data.py b/client/ayon_core/plugins/publish/extract_colorspace_data.py index d68ad4d80d..0ffa0f3035 100644 --- a/client/ayon_core/plugins/publish/extract_colorspace_data.py +++ b/client/ayon_core/plugins/publish/extract_colorspace_data.py @@ -48,4 +48,3 @@ class ExtractColorspaceData(publish.Extractor, self.set_representation_colorspace( representation, context, colorspace) - ) From 46c6511c500804c6d690aaab02ac6c02bdf22b5d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 7 Nov 2024 09:25:22 +0100 Subject: [PATCH 049/463] Refactor debug log in color transcoding function Removed unnecessary debug log statement from color transcoding function. --- client/ayon_core/plugins/publish/extract_color_transcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index e7e0c982eb..56d5d33ea4 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -161,7 +161,7 @@ class ExtractOIIOTranscode(publish.Extractor): output_path = self._get_output_file_path(input_path, new_staging_dir, output_extension) - self.log.debug("Ynput path: `{}`".format(input_path)) + convert_colorspace( input_path, output_path, From ed9b8fe430e1d5c21a3946f548b57c3f8b1d056f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:08:04 +0100 Subject: [PATCH 050/463] moved TypedDict to typecheck imports --- client/ayon_core/lib/attribute_definitions.py | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e841a4b230..02d468f1bb 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -14,14 +14,35 @@ from typing import ( Set, Dict, Iterable, - TypedDict, TypeVar, ) import clique if typing.TYPE_CHECKING: - from typing import Self, Union, Pattern + from typing import Self, Tuple, Union, TypedDict, Pattern + + + class EnumItemDict(TypedDict): + label: str + value: Any + + + EnumItemsInputType = Union[ + Dict[Any, str], + List[Tuple[Any, str]], + List[Any], + List[EnumItemDict] + ] + + + class FileDefItemDict(TypedDict): + directory: str + filenames: List[str] + frames: Optional[List[int]] + template: Optional[str] + is_sequence: Optional[bool] + # Global variable which store attribute definitions by type # - default types are registered on import @@ -31,22 +52,6 @@ _attr_defs_by_type = {} IntFloatType = "Union[int, float]" -class EnumItemDict(TypedDict): - label: str - value: Any - - -EnumItemsInputType = "Union[Dict[Any, str], List[Tuple[Any, str]], List[Any], List[EnumItemDict]]" # noqa: E501 - - -class FileDefItemDict(TypedDict): - directory: str - filenames: List[str] - frames: Optional[List[int]] - template: Optional[str] - is_sequence: Optional[bool] - - class AbstractAttrDefMeta(ABCMeta): """Metaclass to validate the existence of 'key' attribute. @@ -552,7 +557,7 @@ class EnumDef(AbstractAttrDef): def __init__( self, key: str, - items: EnumItemsInputType, + items: "EnumItemsInputType", default: "Union[str, List[Any]]" = None, multiselection: Optional[bool] = False, **kwargs @@ -579,7 +584,7 @@ class EnumDef(AbstractAttrDef): super().__init__(key, default=default, **kwargs) - self.items: List[EnumItemDict] = items + self.items: List["EnumItemDict"] = items self._item_values: Set[Any] = item_values_set self.multiselection: bool = multiselection @@ -611,7 +616,7 @@ class EnumDef(AbstractAttrDef): return data @staticmethod - def prepare_enum_items(items: EnumItemsInputType) -> List[EnumItemDict]: + def prepare_enum_items(items: "EnumItemsInputType") -> List["EnumItemDict"]: """Convert items to unified structure. Output is a list where each item is dictionary with 'value' @@ -886,7 +891,7 @@ class FileDefItem: return output @classmethod - def from_dict(cls, data: FileDefItemDict) -> "Self": + def from_dict(cls, data: "FileDefItemDict") -> "Self": return cls( data["directory"], data["filenames"], @@ -928,7 +933,7 @@ class FileDefItem: return output - def to_dict(self) -> FileDefItemDict: + def to_dict(self) -> "FileDefItemDict": output = { "is_sequence": self.is_sequence, "directory": self.directory, From ad25aa7b525276da52bf80e2686a0482954a1bec Mon Sep 17 00:00:00 2001 From: Joseph HENRY Date: Thu, 7 Nov 2024 12:47:24 +0100 Subject: [PATCH 051/463] Use open -R for opening explorer on MacOS --- client/ayon_core/plugins/actions/open_file_explorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/actions/open_file_explorer.py b/client/ayon_core/plugins/actions/open_file_explorer.py index 50a3107444..e96392ec00 100644 --- a/client/ayon_core/plugins/actions/open_file_explorer.py +++ b/client/ayon_core/plugins/actions/open_file_explorer.py @@ -99,7 +99,7 @@ class OpenTaskPath(LauncherAction): if platform_name == "windows": args = ["start", path] elif platform_name == "darwin": - args = ["open", "-na", path] + args = ["open", "-R", path] elif platform_name == "linux": args = ["xdg-open", path] else: From 7d23e1ac3fc29a8e8bc99ef94283f62f2b9f746f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 7 Nov 2024 16:36:47 +0100 Subject: [PATCH 052/463] Fix support for scriptsmenu running commands in Qt6 (e.g. PySide6 in Maya 2025) --- client/ayon_core/vendor/python/scriptsmenu/action.py | 9 +++++---- .../ayon_core/vendor/python/scriptsmenu/launchformaya.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/vendor/python/scriptsmenu/action.py b/client/ayon_core/vendor/python/scriptsmenu/action.py index 49b08788f9..3ba281fed7 100644 --- a/client/ayon_core/vendor/python/scriptsmenu/action.py +++ b/client/ayon_core/vendor/python/scriptsmenu/action.py @@ -1,6 +1,6 @@ import os -from qtpy import QtWidgets +from qtpy import QtWidgets, QT6 class Action(QtWidgets.QAction): @@ -112,20 +112,21 @@ module.{module_name}()""" Run the command of the instance or copy the command to the active shelf based on the current modifiers. - If callbacks have been registered with fouind modifier integer the + If callbacks have been registered with found modifier integer the function will trigger all callbacks. When a callback function returns a non zero integer it will not execute the action's command - """ # get the current application and its linked keyboard modifiers app = QtWidgets.QApplication.instance() modifiers = app.keyboardModifiers() + if not QT6: + modifiers = int(modifiers) # If the menu has a callback registered for the current modifier # we run the callback instead of the action itself. registered = self._root.registered_callbacks - callbacks = registered.get(int(modifiers), []) + callbacks = registered.get(modifiers, []) for callback in callbacks: signal = callback(self) if signal != 0: diff --git a/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py b/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py index 496278ac6f..a5503bc63e 100644 --- a/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py +++ b/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py @@ -4,7 +4,7 @@ import maya.cmds as cmds import maya.mel as mel import scriptsmenu -from qtpy import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets, QT6 log = logging.getLogger(__name__) @@ -130,7 +130,7 @@ def main(title="Scripts", parent=None, objectName=None): # Register control + shift callback to add to shelf (maya behavior) modifiers = QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier - if int(cmds.about(version=True)) < 2025: + if not QT6: modifiers = int(modifiers) menu.register_callback(modifiers, to_shelf) From 87bb613b751ea508ae54a9813bf6eb5852ca5b6b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 7 Nov 2024 17:38:00 +0100 Subject: [PATCH 053/463] Added optionality to new argument in method signature --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c70967dfc1..e9f179c668 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -304,7 +304,7 @@ def prepare_representations( do_not_add_review, context, color_managed_plugin, - frames_to_render + frames_to_render=None ): """Create representations for file sequences. From 2337d116d54eaabfd73b81f9f45c1865e124a65a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:46:53 +0100 Subject: [PATCH 054/463] change is_latest based on version item --- client/ayon_core/tools/sceneinventory/model.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index b7f79986ac..9b1e75a0d1 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -194,14 +194,14 @@ class InventoryModel(QtGui.QStandardItemModel): group_items = [] for repre_id, container_items in items_by_repre_id.items(): repre_info = repre_info_by_id[repre_id] - version_label = "N/A" version_color = None - is_latest = False - is_hero = False - status_name = None if not repre_info.is_valid: + version_label = "N/A" group_name = "< Entity N/A >" item_icon = invalid_item_icon + is_latest = False + is_hero = False + status_name = None else: group_name = "{}_{}: ({})".format( @@ -217,6 +217,7 @@ class InventoryModel(QtGui.QStandardItemModel): version_item = version_items[repre_info.version_id] version_label = format_version(version_item.version) is_hero = version_item.version < 0 + is_latest = version_item.is_latest if not version_item.is_latest: version_color = self.OUTDATED_COLOR status_name = version_item.status From 8a7239fc0511c19bfb1f1a0bf0e01d18b9026fa8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:47:04 +0100 Subject: [PATCH 055/463] remove unncessary line --- client/ayon_core/tools/sceneinventory/models/containers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 871455c96b..4f3ddf1ded 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -383,7 +383,6 @@ class ContainersModel: container_items_by_id[item.item_id] = item container_items.append(item) - self._containers_by_id = containers_by_id self._container_items_by_id = container_items_by_id self._items_cache = container_items From 1de069c324a8d49c7e1424fe7d0fd95539238145 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:47:15 +0100 Subject: [PATCH 056/463] remove unnessary conversion --- client/ayon_core/tools/sceneinventory/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 9b1e75a0d1..bdcd183c99 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -426,7 +426,7 @@ class FilterProxyModel(QtCore.QSortFilterProxyModel): state = bool(state) if state != self._filter_outdated: - self._filter_outdated = bool(state) + self._filter_outdated = state self.invalidateFilter() def set_hierarchy_view(self, state): From 749984c0bff74c4491a7d6e853afa70906b1e984 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 8 Nov 2024 13:10:44 +0100 Subject: [PATCH 057/463] Fix loader load option box widgets --- client/ayon_core/tools/attribute_defs/widgets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 22f4bfe535..93f63730f5 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -202,7 +202,6 @@ class AttributeDefinitionsWidget(QtWidgets.QWidget): self._current_keys.add(attr_def.key) widget = create_widget_for_attr_def(attr_def, self) - self._widgets.append(widget) self._widgets_by_id[attr_def.id] = widget if not attr_def.visible: From 20206a3cf3444c3a74b8f3aa046985def1cbfa38 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:57:00 +0100 Subject: [PATCH 058/463] check executable name before killing the process --- client/ayon_core/tools/tray/lib.py | 103 ++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/tools/tray/lib.py b/client/ayon_core/tools/tray/lib.py index 39fcc2cdd3..94550775e6 100644 --- a/client/ayon_core/tools/tray/lib.py +++ b/client/ayon_core/tools/tray/lib.py @@ -3,12 +3,9 @@ import sys import json import hashlib import platform -import subprocess -import csv import time import signal -import locale -from typing import Optional, Dict, Tuple, Any +from typing import Optional, List, Dict, Tuple, Any import requests from ayon_api.utils import get_default_settings_variant @@ -53,15 +50,99 @@ def _get_server_and_variant( return server_url, variant +def _windows_get_pid_args(pid: int) -> Optional[List[str]]: + import ctypes + from ctypes import wintypes + + # Define constants + PROCESS_COMMANDLINE_INFO = 60 + STATUS_NOT_FOUND = 0xC0000225 + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + + # Define the UNICODE_STRING structure + class UNICODE_STRING(ctypes.Structure): + _fields_ = [ + ("Length", wintypes.USHORT), + ("MaximumLength", wintypes.USHORT), + ("Buffer", wintypes.LPWSTR) + ] + + shell32 = ctypes.WinDLL("shell32", use_last_error=True) + + CommandLineToArgvW = shell32.CommandLineToArgvW + CommandLineToArgvW.argtypes = [ + wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_int) + ] + CommandLineToArgvW.restype = ctypes.POINTER(wintypes.LPWSTR) + + output = None + # Open the process + handle = ctypes.windll.kernel32.OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION, False, pid + ) + if not handle: + return output + + try: + buffer_len = wintypes.ULONG() + # Get the right buffer size first + status = ctypes.windll.ntdll.NtQueryInformationProcess( + handle, + PROCESS_COMMANDLINE_INFO, + ctypes.c_void_p(None), + 0, + ctypes.byref(buffer_len) + ) + + if status == STATUS_NOT_FOUND: + return output + + # Create buffer with collected size + buffer = ctypes.create_string_buffer(buffer_len.value) + + # Get the command line + status = ctypes.windll.ntdll.NtQueryInformationProcess( + handle, + PROCESS_COMMANDLINE_INFO, + buffer, + buffer_len, + ctypes.byref(buffer_len) + ) + if status: + return output + # Build the string + tmp = ctypes.cast(buffer, ctypes.POINTER(UNICODE_STRING)).contents + size = tmp.Length // 2 + 1 + cmdline_buffer = ctypes.create_unicode_buffer(size) + ctypes.cdll.msvcrt.wcscpy(cmdline_buffer, tmp.Buffer) + + args_len = ctypes.c_int() + args = CommandLineToArgvW( + cmdline_buffer, ctypes.byref(args_len) + ) + output = [args[idx] for idx in range(args_len.value)] + ctypes.windll.kernel32.LocalFree(args) + + finally: + ctypes.windll.kernel32.CloseHandle(handle) + return output def _windows_pid_is_running(pid: int) -> bool: - args = ["tasklist.exe", "/fo", "csv", "/fi", f"PID eq {pid}"] - output = subprocess.check_output(args) - encoding = locale.getpreferredencoding() - csv_content = csv.DictReader(output.decode(encoding).splitlines()) - # if "PID" not in csv_content.fieldnames: - # return False - for _ in csv_content: + args = _windows_get_pid_args(pid) + if not args: + return False + executable_path = args[0] + + filename = os.path.basename(executable_path).lower() + if "ayon" in filename: return True + + # Try to handle tray running from code + # - this might be potential danger that kills other python process running + # 'start.py' script (low chance, but still) + if "python" in filename and len(args) > 1: + script_filename = os.path.basename(args[1].lower()) + if script_filename == "start.py": + return True return False From 41db386f23f4ec18e870abe1817d0f71eb8fc775 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:59:05 +0100 Subject: [PATCH 059/463] add empty lines --- client/ayon_core/tools/tray/lib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/tray/lib.py b/client/ayon_core/tools/tray/lib.py index 94550775e6..13ee1eea5c 100644 --- a/client/ayon_core/tools/tray/lib.py +++ b/client/ayon_core/tools/tray/lib.py @@ -126,6 +126,8 @@ def _windows_get_pid_args(pid: int) -> Optional[List[str]]: finally: ctypes.windll.kernel32.CloseHandle(handle) return output + + def _windows_pid_is_running(pid: int) -> bool: args = _windows_get_pid_args(pid) if not args: From 262cc0e7bb117516b1c1a3a7ef3b71d0508c8adf Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 8 Nov 2024 18:07:10 +0000 Subject: [PATCH 060/463] [Automated] Add generated package files to main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 2b2af81e18..74f64e7944 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.6+dev" +__version__ = "1.0.7" diff --git a/package.py b/package.py index 59f0e82be0..c3fc02b625 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.6+dev" +version = "1.0.7" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index ca626eff00..12a68630e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.6+dev" +version = "1.0.7" description = "" authors = ["Ynput Team "] readme = "README.md" From 7ae9b1815378352ed86f7b0dee251d58995bf11a Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 8 Nov 2024 18:07:45 +0000 Subject: [PATCH 061/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 74f64e7944..3a5b63785d 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.7" +__version__ = "1.0.7+dev" diff --git a/package.py b/package.py index c3fc02b625..ef2f3822eb 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.7" +version = "1.0.7+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 12a68630e2..78a3021b30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.7" +version = "1.0.7+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From aaadaffabe5aeb033f3b1f7e0fe3341c69356564 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 11 Nov 2024 16:57:41 +0800 Subject: [PATCH 062/463] refactoring the load container so that it can load the library project --- client/ayon_core/pipeline/load/utils.py | 4 +- .../ayon_core/tools/sceneinventory/control.py | 7 +- .../ayon_core/tools/sceneinventory/model.py | 2 +- .../tools/sceneinventory/models/containers.py | 131 +++++++++++------- client/ayon_core/tools/sceneinventory/view.py | 9 +- 5 files changed, 97 insertions(+), 56 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index ee2c1af07f..6f69651a8f 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -465,7 +465,7 @@ def update_container(container, version=-1): from ayon_core.pipeline import get_current_project_name # Compute the different version from 'representation' - project_name = get_current_project_name() + project_name = container.get("project_name", get_current_project_name()) repre_id = container["representation"] if not _is_valid_representation_id(repre_id): raise ValueError( @@ -588,7 +588,7 @@ def switch_container(container, representation, loader_plugin=None): ) # Get the new representation to switch to - project_name = get_current_project_name() + project_name = container.get("project_name", get_current_project_name()) context = get_representation_context( project_name, representation["id"] diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index b890462506..8ce3a1bb7a 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -4,7 +4,7 @@ from ayon_core.lib.events import QueuedEventSystem from ayon_core.host import HostBase from ayon_core.pipeline import ( registered_host, - get_current_context, + get_current_context ) from ayon_core.tools.common_models import HierarchyModel, ProjectsModel @@ -110,8 +110,9 @@ class SceneInventoryController: representation_ids ) - def get_version_items(self, product_ids): - return self._containers_model.get_version_items(product_ids) + def get_version_items(self, product_ids, representation_ids): + return self._containers_model.get_version_items( + product_ids, representation_ids) # Site Sync methods def is_sitesync_enabled(self): diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index b7f79986ac..9d1202a906 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -150,7 +150,7 @@ class InventoryModel(QtGui.QStandardItemModel): if repre_info.is_valid } version_items_by_product_id = self._controller.get_version_items( - product_ids + product_ids, repre_id ) # SiteSync addon information progress_by_id = self._controller.get_representations_site_progress( diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 871455c96b..693a5948c9 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -6,6 +6,7 @@ from ayon_api.graphql import GraphQlQuery from ayon_core.host import ILoadHost from ayon_core.tools.common_models.projects import StatusStates +from ayon_core.pipeline.context_tools import get_current_project_name # --- Implementation that should be in ayon-python-api --- @@ -93,13 +94,15 @@ class ContainerItem: loader_name, namespace, object_name, - item_id + item_id, + project_name ): self.representation_id = representation_id self.loader_name = loader_name self.object_name = object_name self.namespace = namespace self.item_id = item_id + self.project_name = project_name @classmethod def from_container_data(cls, container): @@ -109,6 +112,8 @@ class ContainerItem: namespace=container["namespace"], object_name=container["objectName"], item_id=uuid.uuid4().hex, + project_name=container.get( + "project_name", get_current_project_name()) ) @@ -222,6 +227,9 @@ class ContainersModel: def get_representation_info_items(self, representation_ids): output = {} missing_repre_ids = set() + missing_repre_ids_by_project = {} + containers = self._controller.get_containers() + for repre_id in representation_ids: try: uuid.UUID(repre_id) @@ -229,54 +237,60 @@ class ContainersModel: output[repre_id] = RepresentationInfo.new_invalid() continue + project_name = self._find_project_name(containers, repre_id) + if project_name is None: + project_name = self._controller.get_current_project_name() + repre_info = self._repre_info_by_id.get(repre_id) if repre_info is None: missing_repre_ids.add(repre_id) + missing_repre_ids_by_project.update({project_name: repre_id}) else: output[repre_id] = repre_info if not missing_repre_ids: return output - project_name = self._controller.get_current_project_name() - repre_hierarchy_by_id = get_representations_hierarchy( - project_name, missing_repre_ids - ) - for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): - kwargs = { - "folder_id": None, - "folder_path": None, - "product_id": None, - "product_name": None, - "product_type": None, - "product_group": None, - "version_id": None, - "representation_name": None, - } - folder = repre_hierarchy.folder - product = repre_hierarchy.product - version = repre_hierarchy.version - repre = repre_hierarchy.representation - if folder: - kwargs["folder_id"] = folder["id"] - kwargs["folder_path"] = folder["path"] - if product: - group = product["attrib"]["productGroup"] - kwargs["product_id"] = product["id"] - kwargs["product_name"] = product["name"] - kwargs["product_type"] = product["productType"] - kwargs["product_group"] = group - if version: - kwargs["version_id"] = version["id"] - if repre: - kwargs["representation_name"] = repre["name"] + for project_name, missing_ids in missing_repre_ids_by_project.items(): + repre_hierarchy_by_id = get_representations_hierarchy( + project_name, {missing_ids} + ) + for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): + kwargs = { + "folder_id": None, + "folder_path": None, + "product_id": None, + "product_name": None, + "product_type": None, + "product_group": None, + "version_id": None, + "representation_name": None, + } + folder = repre_hierarchy.folder + product = repre_hierarchy.product + version = repre_hierarchy.version + repre = repre_hierarchy.representation + if folder: + kwargs["folder_id"] = folder["id"] + kwargs["folder_path"] = folder["path"] + if product: + group = product["attrib"]["productGroup"] + kwargs["product_id"] = product["id"] + kwargs["product_name"] = product["name"] + kwargs["product_type"] = product["productType"] + kwargs["product_group"] = group + if version: + kwargs["version_id"] = version["id"] + if repre: + kwargs["representation_name"] = repre["name"] - repre_info = RepresentationInfo(**kwargs) - self._repre_info_by_id[repre_id] = repre_info - output[repre_id] = repre_info + repre_info = RepresentationInfo(**kwargs) + self._repre_info_by_id[repre_id] = repre_info + output[repre_id] = repre_info return output - def get_version_items(self, product_ids): + def get_version_items(self, product_ids, representation_ids): + project_ids_by_project_names = {} if not product_ids: return {} @@ -293,20 +307,37 @@ class ContainersModel: def version_sorted(entity): return entity["version"] + containers = self.get_containers() + for repre_id in representation_ids: + project_name = self._find_project_name(containers, repre_id) + if project_name is None: + project_name = self._controller.get_current_project_name() + repre_hierarchy_by_id = get_representations_hierarchy( + project_name, {repre_id} + ) + product_ids_list = set() + for repre_hierarchy in repre_hierarchy_by_id.values(): + product = repre_hierarchy.product + product_id = product["id"] + if product_id not in missing_ids: + continue + product_ids_list.add(product_id) + project_ids_by_project_names.update({project_name: product_ids_list}) - project_name = self._controller.get_current_project_name() version_entities_by_product_id = { product_id: [] for product_id in missing_ids } - - version_entities = list(ayon_api.get_versions( - project_name, - product_ids=missing_ids, - fields={"id", "version", "productId", "status"} - )) - version_entities.sort(key=version_sorted) - for version_entity in version_entities: + version_entities_list = [] + for project_name, missing_product_ids in project_ids_by_project_names.items(): + version_entities = list(ayon_api.get_versions( + project_name, + product_ids=missing_product_ids, + fields={"id", "version", "productId", "status"} + )) + version_entities_list.extend(version_entities) + version_entities_list.sort(key=version_sorted) + for version_entity in version_entities_list: product_id = version_entity["productId"] version_entities_by_product_id[product_id].append( version_entity @@ -337,12 +368,18 @@ class ContainersModel: self._version_items_by_product_id[product_id] = ( version_items_by_id ) - return { product_id: dict(self._version_items_by_product_id[product_id]) for product_id in product_ids } + def _find_project_name(self, containers, representation_id): + # Function to find the project name by representation + for container in containers: + if container.get('representation') == representation_id: + return container.get('project_name', get_current_project_name()) + return None + def _update_cache(self): if self._items_cache is not None: return diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 22ba15fda8..5fc2113824 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -228,7 +228,10 @@ class SceneInventoryView(QtWidgets.QTreeView): return version_items_by_product_id = self._controller.get_version_items( - product_ids + product_ids, { + container_item.representation_id + for container_item in container_items_by_id.values() + } ) has_outdated = False has_loaded_hero_versions = False @@ -751,7 +754,7 @@ class SceneInventoryView(QtWidgets.QTreeView): active_version_id = active_repre_info.version_id active_product_id = active_repre_info.product_id version_items_by_product_id = self._controller.get_version_items( - product_ids + product_ids, repre_ids ) version_items = list( version_items_by_product_id[active_product_id].values() @@ -943,7 +946,7 @@ class SceneInventoryView(QtWidgets.QTreeView): if repre_info.is_valid } version_items_by_product_id = self._controller.get_version_items( - product_ids + product_ids, repre_ids ) update_containers = [] From dcb838e1454523b3cbee00f5fde54fdc0d36fb58 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 11 Nov 2024 19:17:00 +0800 Subject: [PATCH 063/463] resolve the project root during updating container --- client/ayon_core/pipeline/load/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index 6f69651a8f..a6c5f0ce1f 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -542,9 +542,6 @@ def update_container(container, version=-1): ) ) - path = get_representation_path(new_representation) - if not path or not os.path.exists(path): - raise ValueError("Path {} doesn't exist".format(path)) project_entity = ayon_api.get_project(project_name) context = { "project": project_entity, @@ -553,6 +550,9 @@ def update_container(container, version=-1): "version": new_version, "representation": new_representation, } + path = get_representation_path_from_context(context) + if not path or not os.path.exists(path): + raise ValueError("Path {} doesn't exist".format(path)) return Loader().update(container, context) From a7908a46e94cf22ca7b42fa5f2dab2657a3e8f13 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 11 Nov 2024 12:50:05 +0100 Subject: [PATCH 064/463] Update handling of missing otioReviewClips data - Handle case where otioReviewClips is missing by logging a message. --- client/ayon_core/plugins/publish/extract_otio_review.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index 2c6472f8a4..b222c6efc3 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -74,7 +74,10 @@ class ExtractOTIOReview( # TODO: what if handles are different in `versionData`? handle_start = instance.data["handleStart"] handle_end = instance.data["handleEnd"] - otio_review_clips = instance.data["otioReviewClips"] + otio_review_clips = instance.data.get("otioReviewClips") + + if otio_review_clips is None: + self.log.info(f"Instance `{instance}` has no otioReviewClips") # add plugin wide attributes self.representation_files = [] From 3da898b3440b5fd9ba75887205e033cc834685a2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 11 Nov 2024 15:37:59 +0100 Subject: [PATCH 065/463] Update client/ayon_core/pipeline/publish/publish_plugins.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/publish/publish_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 6a2f4c0279..57215eff68 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -206,7 +206,7 @@ class AYONPyblishPluginMixin: return False families = [instance.product_type] - families.extend(instance.data.get("families", [])) + families.extend(instance.get("families", [])) for _ in pyblish.logic.plugins_by_families([cls], families): return True return False From a6729802dc6c0f1cd26dbf9447536d947797f906 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:58:47 +0100 Subject: [PATCH 066/463] make sure version combobox has no focus policy --- client/ayon_core/tools/loader/ui/products_delegates.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/loader/ui/products_delegates.py b/client/ayon_core/tools/loader/ui/products_delegates.py index 9753da37af..fba9b5b3ca 100644 --- a/client/ayon_core/tools/loader/ui/products_delegates.py +++ b/client/ayon_core/tools/loader/ui/products_delegates.py @@ -222,6 +222,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): editor = VersionComboBox(product_id, parent) editor.setProperty("itemId", item_id) + editor.setFocusPolicy(QtCore.Qt.NoFocus) editor.value_changed.connect(self._on_editor_change) editor.destroyed.connect(self._on_destroy) From a089b17f2ffc673830520d95976a57148905a965 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:20:21 +0100 Subject: [PATCH 067/463] added '__required_keys' to CreatedInstance --- client/ayon_core/pipeline/create/structures.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index ba4a373597..fdd41b7255 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -434,6 +434,13 @@ class CreatedInstance: "creator_attributes", "publish_attributes" ) + # Keys that can be changed, but should not be removed from instance + __required_keys = { + "folderPath": None, + "task": None, + "productName": None, + "active": True, + } def __init__( self, @@ -515,6 +522,9 @@ class CreatedInstance: if data: self._data.update(data) + for key, default in self.__required_keys.items(): + self._data.setdefault(key, default) + if not self._data.get("instance_id"): self._data["instance_id"] = str(uuid4()) @@ -567,6 +577,8 @@ class CreatedInstance: has_key = key in self._data output = self._data.pop(key, *args, **kwargs) if has_key: + if key in self.__required_keys: + self._data[key] = self.__required_keys[key] self._create_context.instance_values_changed( self.id, {key: None} ) From 2cf62f0bb455c401897f85f641774e70770ca1fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:20:35 +0100 Subject: [PATCH 068/463] fix product type key in immutable keys --- client/ayon_core/pipeline/create/structures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index fdd41b7255..a1a4d5f8ef 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -429,7 +429,7 @@ class CreatedInstance: __immutable_keys = ( "id", "instance_id", - "product_type", + "productType", "creator_identifier", "creator_attributes", "publish_attributes" From b79e0189a073b579f9e45a0496ec6f87cbb3a617 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:43:17 +0100 Subject: [PATCH 069/463] Use N/A label if is not available --- client/ayon_core/tools/publisher/models/create.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/models/create.py b/client/ayon_core/tools/publisher/models/create.py index ca26749b65..9644af43e0 100644 --- a/client/ayon_core/tools/publisher/models/create.py +++ b/client/ayon_core/tools/publisher/models/create.py @@ -296,7 +296,7 @@ class InstanceItem: return InstanceItem( instance.id, instance.creator_identifier, - instance.label, + instance.label or "N/A", instance.group_label, instance.product_type, instance.product_name, From 735409f9acb945c3af3cf61c9f2d35a2ce51de1e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 12 Nov 2024 20:13:49 +0800 Subject: [PATCH 070/463] do not get the container item from self.get_container --- .../ayon_core/tools/sceneinventory/model.py | 5 +- .../tools/sceneinventory/models/containers.py | 75 ++++++++----------- client/ayon_core/tools/sceneinventory/view.py | 22 +++--- 3 files changed, 48 insertions(+), 54 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 3857ea1700..687d130f04 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -130,6 +130,7 @@ class InventoryModel(QtGui.QStandardItemModel): self._clear_items() items_by_repre_id = {} + project_names = set() for container_item in container_items: # if ( # selected is not None @@ -137,8 +138,10 @@ class InventoryModel(QtGui.QStandardItemModel): # ): # continue repre_id = container_item.representation_id + project_name = container_item.project_name items = items_by_repre_id.setdefault(repre_id, []) items.append(container_item) + project_names.add(project_name) repre_id = set(items_by_repre_id.keys()) repre_info_by_id = self._controller.get_representation_info_items( @@ -150,7 +153,7 @@ class InventoryModel(QtGui.QStandardItemModel): if repre_info.is_valid } version_items_by_product_id = self._controller.get_version_items( - product_ids, repre_id + product_ids, project_names ) # SiteSync addon information progress_by_id = self._controller.get_representations_site_progress( diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 8a4beed52c..f5618d9f35 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -6,7 +6,6 @@ from ayon_api.graphql import GraphQlQuery from ayon_core.host import ILoadHost from ayon_core.tools.common_models.projects import StatusStates -from ayon_core.pipeline.context_tools import get_current_project_name # --- Implementation that should be in ayon-python-api --- @@ -112,8 +111,7 @@ class ContainerItem: namespace=container["namespace"], object_name=container["objectName"], item_id=uuid.uuid4().hex, - project_name=container.get( - "project_name", get_current_project_name()) + project_name=container.get("project_name", None) ) @@ -194,15 +192,21 @@ class ContainersModel: self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} + self._container_items_by_project = {} + self._project_name_by_repre_id = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} + self._product_id_by_project = {} def reset(self): self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} + self._container_items_by_project = {} + self._project_name_by_repre_id = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} + self._product_id_by_project = {} def get_containers(self): self._update_cache() @@ -226,10 +230,8 @@ class ContainersModel: def get_representation_info_items(self, representation_ids): output = {} - missing_repre_ids = set() missing_repre_ids_by_project = {} - containers = self._controller.get_containers() - + current_project_name = self._controller.get_current_project_name() for repre_id in representation_ids: try: uuid.UUID(repre_id) @@ -237,23 +239,23 @@ class ContainersModel: output[repre_id] = RepresentationInfo.new_invalid() continue - project_name = self._find_project_name(containers, repre_id) + project_name = self._project_name_by_repre_id.get(repre_id) if project_name is None: - project_name = self._controller.get_current_project_name() - + project_name = current_project_name repre_info = self._repre_info_by_id.get(repre_id) if repre_info is None: - missing_repre_ids.add(repre_id) - missing_repre_ids_by_project.update({project_name: repre_id}) + missing_repre_ids_by_project.setdefault( + project_name, set() + ).add(repre_id) else: output[repre_id] = repre_info - if not missing_repre_ids: + if not missing_repre_ids_by_project: return output for project_name, missing_ids in missing_repre_ids_by_project.items(): repre_hierarchy_by_id = get_representations_hierarchy( - project_name, {missing_ids} + project_name, missing_ids ) for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): kwargs = { @@ -286,19 +288,23 @@ class ContainersModel: repre_info = RepresentationInfo(**kwargs) self._repre_info_by_id[repre_id] = repre_info + self._product_id_by_project[project_name] = repre_info.product_id output[repre_id] = repre_info return output - def get_version_items(self, product_ids, representation_ids): - project_ids_by_project_names = {} + def get_version_items(self, product_ids, project_names): if not product_ids: return {} - missing_ids = { product_id for product_id in product_ids if product_id not in self._version_items_by_product_id } + + product_ids_by_project = { + project_name: self._product_id_by_project.get(project_name) + for project_name in project_names + } if missing_ids: status_items_by_name = { status_item.name: status_item @@ -307,34 +313,20 @@ class ContainersModel: def version_sorted(entity): return entity["version"] - containers = self.get_containers() - for repre_id in representation_ids: - project_name = self._find_project_name(containers, repre_id) - if project_name is None: - project_name = self._controller.get_current_project_name() - repre_hierarchy_by_id = get_representations_hierarchy( - project_name, {repre_id} - ) - product_ids_list = set() - for repre_hierarchy in repre_hierarchy_by_id.values(): - product = repre_hierarchy.product - product_id = product["id"] - if product_id not in missing_ids: - continue - product_ids_list.add(product_id) - project_ids_by_project_names.update({project_name: product_ids_list}) - + version_entities_list = [] version_entities_by_product_id = { product_id: [] for product_id in missing_ids } - version_entities_list = [] - for project_name, missing_product_ids in project_ids_by_project_names.items(): + for project_name, product_id in product_ids_by_project.items(): + if product_id not in missing_ids: + continue version_entities = list(ayon_api.get_versions( project_name, - product_ids=missing_product_ids, + product_ids={product_id}, fields={"id", "version", "productId", "status"} )) + version_entities_list.extend(version_entities) version_entities_list.sort(key=version_sorted) for version_entity in version_entities_list: @@ -342,7 +334,6 @@ class ContainersModel: version_entities_by_product_id[product_id].append( version_entity ) - for product_id, version_entities in ( version_entities_by_product_id.items() ): @@ -373,13 +364,6 @@ class ContainersModel: for product_id in product_ids } - def _find_project_name(self, containers, representation_id): - # Function to find the project name by representation - for container in containers: - if container.get('representation') == representation_id: - return container.get('project_name', get_current_project_name()) - return None - def _update_cache(self): if self._items_cache is not None: return @@ -395,6 +379,7 @@ class ContainersModel: container_items = [] containers_by_id = {} container_items_by_id = {} + project_name_by_repre_id = {} invalid_ids_mapping = {} for container in containers: try: @@ -418,8 +403,10 @@ class ContainersModel: containers_by_id[item.item_id] = container container_items_by_id[item.item_id] = item + project_name_by_repre_id[item.representation_id] = item.project_name container_items.append(item) self._containers_by_id = containers_by_id self._container_items_by_id = container_items_by_id + self._project_name_by_repre_id = project_name_by_repre_id self._items_cache = container_items diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 5fc2113824..c5a25fa6dc 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -208,6 +208,7 @@ class SceneInventoryView(QtWidgets.QTreeView): filtered_items = [] product_ids = set() version_ids = set() + project_names = set() for container_item in container_items_by_id.values(): repre_id = container_item.representation_id repre_info = repre_info_by_id.get(repre_id) @@ -215,6 +216,7 @@ class SceneInventoryView(QtWidgets.QTreeView): filtered_items.append(container_item) version_ids.add(repre_info.version_id) product_ids.add(repre_info.product_id) + project_names.add(container_item.project_name) # remove remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR) @@ -228,11 +230,7 @@ class SceneInventoryView(QtWidgets.QTreeView): return version_items_by_product_id = self._controller.get_version_items( - product_ids, { - container_item.representation_id - for container_item in container_items_by_id.values() - } - ) + product_ids, project_names) has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False @@ -742,6 +740,10 @@ class SceneInventoryView(QtWidgets.QTreeView): container_item.representation_id for container_item in container_items_by_id.values() } + project_names = { + container_item.project_name + for container_item in container_items_by_id.values() + } repre_info_by_id = self._controller.get_representation_info_items( repre_ids ) @@ -754,8 +756,7 @@ class SceneInventoryView(QtWidgets.QTreeView): active_version_id = active_repre_info.version_id active_product_id = active_repre_info.product_id version_items_by_product_id = self._controller.get_version_items( - product_ids, repre_ids - ) + product_ids, project_names) version_items = list( version_items_by_product_id[active_product_id].values() ) @@ -937,6 +938,10 @@ class SceneInventoryView(QtWidgets.QTreeView): container_item.representation_id for container_item in containers_items_by_id.values() } + project_names = { + container_item.project_name + for container_item in containers_items_by_id.values() + } repre_info_by_id = self._controller.get_representation_info_items( repre_ids ) @@ -946,8 +951,7 @@ class SceneInventoryView(QtWidgets.QTreeView): if repre_info.is_valid } version_items_by_product_id = self._controller.get_version_items( - product_ids, repre_ids - ) + product_ids, project_names) update_containers = [] update_versions = [] From 235949b867aff1f5caf5139e60ec4ca136ae2d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 12 Nov 2024 15:58:08 +0100 Subject: [PATCH 071/463] Update client/ayon_core/plugins/publish/collect_otio_review.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/collect_otio_review.py | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 04422391c5..4708b0a97c 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -100,37 +100,33 @@ class CollectOtioReview(pyblish.api.InstancePlugin): "Creating review track: {}".format(otio_review_clips)) # get colorspace from metadata if available - if len(otio_review_clips) >= 1 and any( - # lets make sure any clip with media reference is found + # get metadata from first clip with media reference + r_otio_cl = next( ( clip for clip in otio_review_clips - if isinstance(clip, otio.schema.Clip) - and clip.media_reference - ) - ): - # get metadata from first clip - # get colorspace from metadata if available - # check if resolution is the same as source - r_otio_cl = next( - ( - clip - for clip in otio_review_clips - if isinstance(clip, otio.schema.Clip) + if ( + isinstance(clip, otio.schema.Clip) and clip.media_reference - ), - None, - ) - - # get metadata from first clip with media reference + ) + ), + None + ) + if r_otio_cl is not None: media_ref = r_otio_cl.media_reference media_metadata = media_ref.metadata # TODO: we might need some alternative method since # native OTIO exports do not support ayon metadata - if review_colorspace := media_metadata.get( + review_colorspace = media_metadata.get( "ayon.source.colorspace" - ): + ) + if review_colorspace is None: + # Backwards compatibility for older scenes + review_colorspace = media_metadata.get( + "openpype.source.colourtransform" + ) + if review_colorspace: instance.data["reviewColorspace"] = review_colorspace self.log.info( "Review colorspace: {}".format(review_colorspace)) From 863c6f51871f089acfba690ee58ac4af95a14a21 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 12 Nov 2024 16:04:25 -0500 Subject: [PATCH 072/463] Allow CSV ingest to create new shots. --- .../plugins/publish/collect_hierarchy.py | 18 +++++++++++------- .../publish/extract_hierarchy_to_ayon.py | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 2ae3cc67f3..e4b4dd408f 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -13,8 +13,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): label = "Collect Hierarchy" order = pyblish.api.CollectorOrder - 0.076 - families = ["shot"] - hosts = ["resolve", "hiero", "flame"] + families = ["shot", "csv_ingest_shot"] + hosts = ["resolve", "hiero", "flame", "traypublisher"] def process(self, context): project_name = context.data["projectName"] @@ -38,8 +38,9 @@ class CollectHierarchy(pyblish.api.ContextPlugin): ): continue - # exclude if not masterLayer True - if not instance.data.get("heroTrack"): + # exclude if not CSV ingest shot and not masterLayer True + if ("csv_ingest_shot" not in families and + not instance.data.get("heroTrack")): continue shot_data = { @@ -49,7 +50,10 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "folder_type": "Shot", "tasks": instance.data.get("tasks") or {}, "comments": instance.data.get("comments", []), - "attributes": { + } + + if "csv_ingest_shot" not in families: + shot_data["attributes"] = { "handleStart": instance.data["handleStart"], "handleEnd": instance.data["handleEnd"], "frameStart": instance.data["frameStart"], @@ -60,8 +64,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "resolutionWidth": instance.data["resolutionWidth"], "resolutionHeight": instance.data["resolutionHeight"], "pixelAspect": instance.data["pixelAspect"], - }, - } + } + # Split by '/' for AYON where asset is a path name = instance.data["folderPath"].split("/")[-1] actual = {name: shot_data} diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index a169affc66..390ce36126 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -22,7 +22,7 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): order = pyblish.api.ExtractorOrder - 0.01 label = "Extract Hierarchy To AYON" - families = ["clip", "shot"] + families = ["clip", "shot", "csv_ingest_shot"] def process(self, context): if not context.data.get("hierarchyContext"): From f6547264fbcbd293036a4e23059e2774114c441c Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 12 Nov 2024 16:30:55 -0500 Subject: [PATCH 073/463] Fix lint. --- client/ayon_core/plugins/publish/collect_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index e4b4dd408f..3340430345 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -64,7 +64,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "resolutionWidth": instance.data["resolutionWidth"], "resolutionHeight": instance.data["resolutionHeight"], "pixelAspect": instance.data["pixelAspect"], - } + } # Split by '/' for AYON where asset is a path name = instance.data["folderPath"].split("/")[-1] From bccd8d813c7b22c57abd3b8b25ad6d19e55a6911 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 13 Nov 2024 10:51:16 +0000 Subject: [PATCH 074/463] [Automated] Add generated package files to main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 3a5b63785d..7702eb67ad 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.7+dev" +__version__ = "1.0.8" diff --git a/package.py b/package.py index ef2f3822eb..bd61438898 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.7+dev" +version = "1.0.8" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 78a3021b30..236a7ddc6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.7+dev" +version = "1.0.8" description = "" authors = ["Ynput Team "] readme = "README.md" From a181fc897d16db9563fca73473eddee590cdd427 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 13 Nov 2024 10:51:57 +0000 Subject: [PATCH 075/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 7702eb67ad..63f7de04dc 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.8" +__version__ = "1.0.8+dev" diff --git a/package.py b/package.py index bd61438898..bbfcc51019 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.8" +version = "1.0.8+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 236a7ddc6c..e29aa08c6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.8" +version = "1.0.8+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 0bec953dec2d786afb09a420e86f83980ddb19a1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 14 Nov 2024 22:46:43 +0800 Subject: [PATCH 076/463] query the product id per project --- .../ayon_core/tools/sceneinventory/control.py | 4 +- .../ayon_core/tools/sceneinventory/model.py | 21 +++-- .../tools/sceneinventory/models/containers.py | 30 +++---- client/ayon_core/tools/sceneinventory/view.py | 86 ++++++++++++------- 4 files changed, 85 insertions(+), 56 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 8ce3a1bb7a..640911df80 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -110,9 +110,9 @@ class SceneInventoryController: representation_ids ) - def get_version_items(self, product_ids, representation_ids): + def get_version_items(self, project_name, product_ids): return self._containers_model.get_version_items( - product_ids, representation_ids) + project_name, product_ids) # Site Sync methods def is_sitesync_enabled(self): diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 687d130f04..162a0d4b71 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -152,9 +152,20 @@ class InventoryModel(QtGui.QStandardItemModel): for repre_info in repre_info_by_id.values() if repre_info.is_valid } - version_items_by_product_id = self._controller.get_version_items( - product_ids, project_names - ) + + project_products = {project_name: set() for project_name in project_names} + for representation_id, items in items_by_repre_id.items(): + repre_info = repre_info_by_id.get(representation_id) + if repre_info and repre_info.is_valid: + product_id = repre_info.product_id + for item in items: + project_name = item.project_name + project_products[project_name].add(product_id) + version_items_by_product_id = {} + for project_name, product_ids in project_products.items(): + version_items_by_product_id.update(self._controller.get_version_items( + project_name, product_ids + )) # SiteSync addon information progress_by_id = self._controller.get_representations_site_progress( repre_id @@ -236,7 +247,6 @@ class InventoryModel(QtGui.QStandardItemModel): for container_item in container_items: object_name = container_item.object_name or "" unique_name = repre_name + object_name - item = QtGui.QStandardItem() item.setColumnCount(root_item.columnCount()) item.setData(container_item.namespace, QtCore.Qt.DisplayRole) @@ -251,7 +261,6 @@ class InventoryModel(QtGui.QStandardItemModel): item.setData(True, IS_CONTAINER_ITEM_ROLE) item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) container_model_items.append(item) - if not container_model_items: continue @@ -290,7 +299,7 @@ class InventoryModel(QtGui.QStandardItemModel): group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE) group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE) group_item.setData(False, IS_CONTAINER_ITEM_ROLE) - + print(group_item) if version_color is not None: group_item.setData(version_color, VERSION_COLOR_ROLE) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index f5618d9f35..4592b489e1 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -249,7 +249,6 @@ class ContainersModel: ).add(repre_id) else: output[repre_id] = repre_info - if not missing_repre_ids_by_project: return output @@ -292,7 +291,7 @@ class ContainersModel: output[repre_id] = repre_info return output - def get_version_items(self, product_ids, project_names): + def get_version_items(self, project_name, product_ids): if not product_ids: return {} missing_ids = { @@ -301,10 +300,7 @@ class ContainersModel: if product_id not in self._version_items_by_product_id } - product_ids_by_project = { - project_name: self._product_id_by_project.get(project_name) - for project_name in project_names - } + current_product_id = self._product_id_by_project.get(project_name) if missing_ids: status_items_by_name = { status_item.name: status_item @@ -313,24 +309,22 @@ class ContainersModel: def version_sorted(entity): return entity["version"] - version_entities_list = [] + if current_product_id not in missing_ids: + return version_entities_by_product_id = { product_id: [] for product_id in missing_ids } - for project_name, product_id in product_ids_by_project.items(): + version_entities = list(ayon_api.get_versions( + project_name, + product_ids={current_product_id}, + fields={"id", "version", "productId", "status"} + )) + version_entities.sort(key=version_sorted) + for version_entity in version_entities: + product_id = version_entity["productId"] if product_id not in missing_ids: continue - version_entities = list(ayon_api.get_versions( - project_name, - product_ids={product_id}, - fields={"id", "version", "productId", "status"} - )) - - version_entities_list.extend(version_entities) - version_entities_list.sort(key=version_sorted) - for version_entity in version_entities_list: - product_id = version_entity["productId"] version_entities_by_product_id[product_id].append( version_entity ) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index c5a25fa6dc..12a7ab2285 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -206,18 +206,20 @@ class SceneInventoryView(QtWidgets.QTreeView): # Exclude items that are "NOT FOUND" since setting versions, updating # and removal won't work for those items. filtered_items = [] - product_ids = set() + project_products = {} version_ids = set() - project_names = set() for container_item in container_items_by_id.values(): repre_id = container_item.representation_id + project_name = container_item.project_name repre_info = repre_info_by_id.get(repre_id) if repre_info and repre_info.is_valid: filtered_items.append(container_item) version_ids.add(repre_info.version_id) - product_ids.add(repre_info.product_id) - project_names.add(container_item.project_name) - + product_id = repre_info.product_id + if project_name not in project_products: + project_products[project_name] = set() + project_products[project_name].add(product_id) + print("p_products", project_products) # remove remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR) remove_action = QtWidgets.QAction(remove_icon, "Remove items", menu) @@ -228,9 +230,12 @@ class SceneInventoryView(QtWidgets.QTreeView): # Keep remove action for invalid items menu.addAction(remove_action) return - - version_items_by_product_id = self._controller.get_version_items( - product_ids, project_names) + version_items_by_product_id = {} + for project_name, product_ids in project_products.items(): + version_items_by_product_id.update( + self._controller.get_version_items( + project_name, product_ids) + ) has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False @@ -736,14 +741,11 @@ class SceneInventoryView(QtWidgets.QTreeView): container_items_by_id = self._controller.get_container_items_by_id( item_ids ) + print(container_items_by_id, "container") repre_ids = { container_item.representation_id for container_item in container_items_by_id.values() } - project_names = { - container_item.project_name - for container_item in container_items_by_id.values() - } repre_info_by_id = self._controller.get_representation_info_items( repre_ids ) @@ -752,11 +754,25 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_info.product_id for repre_info in repre_info_by_id.values() } + project_products = {} + for container_item in container_items_by_id.values(): + repre_id = container_item.representation_id + project_name = container_item.project_name + repre_info = repre_info_by_id.get(repre_id) + if repre_info and repre_info.is_valid: + if project_name not in project_products: + project_products[project_name] = set() + product_id = repre_info.product_id + project_products[project_name].add(product_id) + print("proj_product", project_products) active_repre_info = repre_info_by_id[active_repre_id] active_version_id = active_repre_info.version_id active_product_id = active_repre_info.product_id - version_items_by_product_id = self._controller.get_version_items( - product_ids, project_names) + version_items_by_product_id = {} + for project_name, product_ids in project_products.items(): + version_items_by_product_id.update( + self._controller.get_version_items( + project_name, product_ids)) version_items = list( version_items_by_product_id[active_product_id].values() ) @@ -931,27 +947,37 @@ class SceneInventoryView(QtWidgets.QTreeView): self._update_containers_to_version(item_ids, version=-1) def _on_switch_to_versioned(self, item_ids): - containers_items_by_id = self._controller.get_container_items_by_id( - item_ids - ) + # Get container items by ID + containers_items_by_id = self._controller.get_container_items_by_id(item_ids) repre_ids = { container_item.representation_id for container_item in containers_items_by_id.values() } - project_names = { - container_item.project_name - for container_item in containers_items_by_id.values() + # Extract project names and their corresponding representation IDs + project_name_to_repre_ids = {} + for container_item in containers_items_by_id.values(): + project_name = container_item.project_name + repre_id = container_item.representation_id + if project_name not in project_name_to_repre_ids: + project_name_to_repre_ids[project_name] = set() + project_name_to_repre_ids[project_name].add(repre_id) + + # Get representation info items by ID + repre_info_by_id = self._controller.get_representation_info_items(repre_ids) + + # Create a dictionary to map project names to sets of product IDs + project_products = { + project_name: set() for project_name in project_name_to_repre_ids.keys() } - repre_info_by_id = self._controller.get_representation_info_items( - repre_ids - ) - product_ids = { - repre_info.product_id - for repre_info in repre_info_by_id.values() - if repre_info.is_valid - } - version_items_by_product_id = self._controller.get_version_items( - product_ids, project_names) + + print("project_products", project_products) + version_items_by_product_id = {} + for project_name, product_ids in project_name_to_repre_ids.items(): + version_items_by_product_id.update( + self._controller.get_version_items( + project_name, product_ids + ) + ) update_containers = [] update_versions = [] From 574ea3580da0ad9531c77e9297660c35e5e20f5b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 15 Nov 2024 21:58:59 +0800 Subject: [PATCH 077/463] codes clean up & make sure it supports mulitple asset loading per project --- .../ayon_core/tools/sceneinventory/control.py | 4 +- .../ayon_core/tools/sceneinventory/model.py | 45 +++--- .../tools/sceneinventory/models/containers.py | 34 ++--- client/ayon_core/tools/sceneinventory/view.py | 138 +++++++++++------- 4 files changed, 127 insertions(+), 94 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 640911df80..310e41b117 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -105,9 +105,9 @@ class SceneInventoryController: def get_container_items_by_id(self, item_ids): return self._containers_model.get_container_items_by_id(item_ids) - def get_representation_info_items(self, representation_ids): + def get_representation_info_items(self, project_name, representation_ids): return self._containers_model.get_representation_info_items( - representation_ids + project_name, representation_ids ) def get_version_items(self, project_name, product_ids): diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 162a0d4b71..a37e3a2b40 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -129,43 +129,54 @@ class InventoryModel(QtGui.QStandardItemModel): self._clear_items() - items_by_repre_id = {} - project_names = set() + items_by_repre_id = collections.defaultdict(list) + repre_ids_by_project = collections.defaultdict(set) for container_item in container_items: # if ( # selected is not None # and container_item.item_id not in selected # ): # continue + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) repre_id = container_item.representation_id - project_name = container_item.project_name - items = items_by_repre_id.setdefault(repre_id, []) - items.append(container_item) - project_names.add(project_name) + items_by_repre_id[repre_id].append(container_item) + repre_ids_by_project[project_name].add(repre_id) repre_id = set(items_by_repre_id.keys()) - repre_info_by_id = self._controller.get_representation_info_items( - repre_id - ) + repre_info_by_id = {} + for project_name, repre_ids in repre_ids_by_project.items(): + repre_info = self._controller.get_representation_info_items( + project_name, repre_ids + ) + repre_info_by_id.update(repre_info) product_ids = { repre_info.product_id for repre_info in repre_info_by_id.values() if repre_info.is_valid } - project_products = {project_name: set() for project_name in project_names} - for representation_id, items in items_by_repre_id.items(): + project_products = collections.defaultdict(set) + for container_item in container_items: + representation_id = container_item.representation_id + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) repre_info = repre_info_by_id.get(representation_id) if repre_info and repre_info.is_valid: product_id = repre_info.product_id - for item in items: - project_name = item.project_name - project_products[project_name].add(product_id) + project_products[project_name].add(product_id) + version_items_by_product_id = {} for project_name, product_ids in project_products.items(): - version_items_by_product_id.update(self._controller.get_version_items( + version_items = self._controller.get_version_items( project_name, product_ids - )) + ) + version_items_by_product_id.update(version_items) + # SiteSync addon information progress_by_id = self._controller.get_representations_site_progress( repre_id @@ -299,7 +310,7 @@ class InventoryModel(QtGui.QStandardItemModel): group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE) group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE) group_item.setData(False, IS_CONTAINER_ITEM_ROLE) - print(group_item) + if version_color is not None: group_item.setData(version_color, VERSION_COLOR_ROLE) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 4592b489e1..52b568af03 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -193,20 +193,18 @@ class ContainersModel: self._containers_by_id = {} self._container_items_by_id = {} self._container_items_by_project = {} - self._project_name_by_repre_id = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} - self._product_id_by_project = {} + self._product_ids_by_project = {} def reset(self): self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} self._container_items_by_project = {} - self._project_name_by_repre_id = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} - self._product_id_by_project = {} + self._product_ids_by_project = {} def get_containers(self): self._update_cache() @@ -228,20 +226,17 @@ class ContainersModel: for item_id in item_ids } - def get_representation_info_items(self, representation_ids): + def get_representation_info_items(self, project_name, representation_ids): output = {} missing_repre_ids_by_project = {} - current_project_name = self._controller.get_current_project_name() + if project_name is None: + project_name = self._controller.get_current_project_name() for repre_id in representation_ids: try: uuid.UUID(repre_id) except ValueError: output[repre_id] = RepresentationInfo.new_invalid() continue - - project_name = self._project_name_by_repre_id.get(repre_id) - if project_name is None: - project_name = current_project_name repre_info = self._repre_info_by_id.get(repre_id) if repre_info is None: missing_repre_ids_by_project.setdefault( @@ -256,6 +251,7 @@ class ContainersModel: repre_hierarchy_by_id = get_representations_hierarchy( project_name, missing_ids ) + self._product_ids_by_project[project_name] = set() for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): kwargs = { "folder_id": None, @@ -287,20 +283,22 @@ class ContainersModel: repre_info = RepresentationInfo(**kwargs) self._repre_info_by_id[repre_id] = repre_info - self._product_id_by_project[project_name] = repre_info.product_id + self._product_ids_by_project[project_name].add( + repre_info.product_id) output[repre_id] = repre_info return output def get_version_items(self, project_name, product_ids): if not product_ids: return {} + if project_name is None: + project_name = self._controller.get_current_project_name() missing_ids = { product_id for product_id in product_ids if product_id not in self._version_items_by_product_id } - - current_product_id = self._product_id_by_project.get(project_name) + current_product_ids = self._product_ids_by_project.get(project_name) if missing_ids: status_items_by_name = { status_item.name: status_item @@ -309,22 +307,19 @@ class ContainersModel: def version_sorted(entity): return entity["version"] - if current_product_id not in missing_ids: - return + current_missing_ids = current_product_ids.intersection(missing_ids) version_entities_by_product_id = { product_id: [] - for product_id in missing_ids + for product_id in current_missing_ids } version_entities = list(ayon_api.get_versions( project_name, - product_ids={current_product_id}, + product_ids=current_missing_ids, fields={"id", "version", "productId", "status"} )) version_entities.sort(key=version_sorted) for version_entity in version_entities: product_id = version_entity["productId"] - if product_id not in missing_ids: - continue version_entities_by_product_id[product_id].append( version_entity ) @@ -402,5 +397,4 @@ class ContainersModel: self._containers_by_id = containers_by_id self._container_items_by_id = container_items_by_id - self._project_name_by_repre_id = project_name_by_repre_id self._items_cache = container_items diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 12a7ab2285..1e0b96570e 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -192,11 +192,20 @@ class SceneInventoryView(QtWidgets.QTreeView): container_item = container_items_by_id[item_id] active_repre_id = container_item.representation_id break + repre_ids_by_project = collections.defaultdict(set) + for container_item in container_items_by_id.values(): + repre_id = container_item.representation_id + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) + repre_ids_by_project[project_name].add(repre_id) + repre_info_by_id = {} + for project_name, repre_ids in repre_ids_by_project.items(): + repre_info = self._controller.get_representation_info_items( + project_name, repre_ids) + repre_info_by_id.update(repre_info) - repre_info_by_id = self._controller.get_representation_info_items({ - container_item.representation_id - for container_item in container_items_by_id.values() - }) valid_repre_ids = { repre_id for repre_id, repre_info in repre_info_by_id.items() @@ -206,20 +215,20 @@ class SceneInventoryView(QtWidgets.QTreeView): # Exclude items that are "NOT FOUND" since setting versions, updating # and removal won't work for those items. filtered_items = [] - project_products = {} + product_ids_by_project = collections.defaultdict(set) version_ids = set() for container_item in container_items_by_id.values(): repre_id = container_item.representation_id - project_name = container_item.project_name + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) repre_info = repre_info_by_id.get(repre_id) if repre_info and repre_info.is_valid: filtered_items.append(container_item) version_ids.add(repre_info.version_id) product_id = repre_info.product_id - if project_name not in project_products: - project_products[project_name] = set() - project_products[project_name].add(product_id) - print("p_products", project_products) + product_ids_by_project[project_name].add(product_id) # remove remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR) remove_action = QtWidgets.QAction(remove_icon, "Remove items", menu) @@ -231,11 +240,12 @@ class SceneInventoryView(QtWidgets.QTreeView): menu.addAction(remove_action) return version_items_by_product_id = {} - for project_name, product_ids in project_products.items(): - version_items_by_product_id.update( - self._controller.get_version_items( - project_name, product_ids) + for project_name, product_ids in product_ids_by_project.items(): + version_items = self._controller.get_version_items( + project_name, product_ids ) + version_items_by_product_id.update(version_items +) has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False @@ -741,38 +751,47 @@ class SceneInventoryView(QtWidgets.QTreeView): container_items_by_id = self._controller.get_container_items_by_id( item_ids ) - print(container_items_by_id, "container") - repre_ids = { - container_item.representation_id - for container_item in container_items_by_id.values() - } - repre_info_by_id = self._controller.get_representation_info_items( - repre_ids - ) + repre_ids_by_project = collections.defaultdict(set) + for container_item in container_items_by_id.values(): + repre_id = container_item.representation_id + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) + repre_ids_by_project[project_name].add(repre_id) + repre_info_by_id = {} + for project_name, repre_ids in repre_ids_by_project.items(): + repre_info = self._controller.get_representation_info_items( + project_name, repre_ids + ) + repre_info_by_id.update(repre_info) product_ids = { repre_info.product_id for repre_info in repre_info_by_id.values() } - project_products = {} + product_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): repre_id = container_item.representation_id - project_name = container_item.project_name + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) repre_info = repre_info_by_id.get(repre_id) - if repre_info and repre_info.is_valid: - if project_name not in project_products: - project_products[project_name] = set() - product_id = repre_info.product_id - project_products[project_name].add(product_id) - print("proj_product", project_products) + if not repre_info or not repre_info.is_valid: + continue + product_ids_by_project[project_name].add( + repre_info.product_id + ) active_repre_info = repre_info_by_id[active_repre_id] active_version_id = active_repre_info.version_id active_product_id = active_repre_info.product_id version_items_by_product_id = {} - for project_name, product_ids in project_products.items(): - version_items_by_product_id.update( - self._controller.get_version_items( - project_name, product_ids)) + for project_name, project_product_ids in product_ids_by_project.items(): + version_items = self._controller.get_version_items( + project_name, project_product_ids + ) + version_items_by_product_id.update(version_items) version_items = list( version_items_by_product_id[active_product_id].values() ) @@ -949,35 +968,44 @@ class SceneInventoryView(QtWidgets.QTreeView): def _on_switch_to_versioned(self, item_ids): # Get container items by ID containers_items_by_id = self._controller.get_container_items_by_id(item_ids) - repre_ids = { - container_item.representation_id - for container_item in containers_items_by_id.values() - } # Extract project names and their corresponding representation IDs - project_name_to_repre_ids = {} + repre_ids_by_project = collections.defaultdict(set) for container_item in containers_items_by_id.values(): project_name = container_item.project_name + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() + ) repre_id = container_item.representation_id - if project_name not in project_name_to_repre_ids: - project_name_to_repre_ids[project_name] = set() - project_name_to_repre_ids[project_name].add(repre_id) + repre_ids_by_project[project_name].add(repre_id) # Get representation info items by ID - repre_info_by_id = self._controller.get_representation_info_items(repre_ids) + repre_info_by_id = {} + for project_name, repre_ids in repre_ids_by_project.items(): + repre_info = self._controller.get_representation_info_items( + project_name, repre_ids) + repre_info_by_id.update(repre_info) - # Create a dictionary to map project names to sets of product IDs - project_products = { - project_name: set() for project_name in project_name_to_repre_ids.keys() - } - - print("project_products", project_products) - version_items_by_product_id = {} - for project_name, product_ids in project_name_to_repre_ids.items(): - version_items_by_product_id.update( - self._controller.get_version_items( - project_name, product_ids - ) + product_ids_by_project = collections.defaultdict(set) + for container_item in containers_items_by_id.values(): + repre_id = container_item.representation_id + project_name = ( + container_item.project_name or + self._controller.get_current_project_name() ) + repre_info = repre_info_by_id.get(repre_id) + if not repre_info or not repre_info.is_valid: + continue + product_ids_by_project[project_name].add( + repre_info.product_id + ) + + version_items_by_product_id = {} + for project_name, product_ids in product_ids_by_project.items(): + version_items = self._controller.get_version_items( + project_name, product_ids + ) + version_items_by_product_id.update(version_items) update_containers = [] update_versions = [] From 8965a8859435473c0171053cc2908dd011c05ad8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 18 Nov 2024 17:59:20 +0800 Subject: [PATCH 078/463] clean up code and add project name row into scene inventory --- .../ayon_core/tools/sceneinventory/model.py | 8 +- .../tools/sceneinventory/models/containers.py | 87 +++++++++---------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index a37e3a2b40..03627e60b9 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -36,6 +36,7 @@ REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 23 # This value hold unique value of container that should be used to identify # containers inbetween refresh. ITEM_UNIQUE_NAME_ROLE = QtCore.Qt.UserRole + 24 +PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 25 class InventoryModel(QtGui.QStandardItemModel): @@ -52,6 +53,7 @@ class InventoryModel(QtGui.QStandardItemModel): "Object name", "Active site", "Remote site", + "Project" ] name_col = column_labels.index("Name") version_col = column_labels.index("Version") @@ -63,6 +65,7 @@ class InventoryModel(QtGui.QStandardItemModel): object_name_col = column_labels.index("Object name") active_site_col = column_labels.index("Active site") remote_site_col = column_labels.index("Remote site") + project_col = column_labels.index("Project") display_role_by_column = { name_col: QtCore.Qt.DisplayRole, version_col: VERSION_LABEL_ROLE, @@ -72,6 +75,7 @@ class InventoryModel(QtGui.QStandardItemModel): product_group_col: PRODUCT_GROUP_NAME_ROLE, loader_col: LOADER_NAME_ROLE, object_name_col: OBJECT_NAME_ROLE, + project_col: PROJECT_NAME_ROLE, active_site_col: ACTIVE_SITE_PROGRESS_ROLE, remote_site_col: REMOTE_SITE_PROGRESS_ROLE, } @@ -85,7 +89,7 @@ class InventoryModel(QtGui.QStandardItemModel): foreground_role_by_column = { name_col: NAME_COLOR_ROLE, version_col: VERSION_COLOR_ROLE, - status_col: STATUS_COLOR_ROLE + status_col: STATUS_COLOR_ROLE, } width_by_column = { name_col: 250, @@ -95,6 +99,7 @@ class InventoryModel(QtGui.QStandardItemModel): product_type_col: 150, product_group_col: 120, loader_col: 150, + project_col: 150, } OUTDATED_COLOR = QtGui.QColor(235, 30, 30) @@ -269,6 +274,7 @@ class InventoryModel(QtGui.QStandardItemModel): item.setData(version_label, VERSION_LABEL_ROLE) item.setData(container_item.loader_name, LOADER_NAME_ROLE) item.setData(container_item.object_name, OBJECT_NAME_ROLE) + item.setData(container_item.project_name, PROJECT_NAME_ROLE) item.setData(True, IS_CONTAINER_ITEM_ROLE) item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) container_model_items.append(item) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 52b568af03..b8b9aa400a 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -228,9 +228,7 @@ class ContainersModel: def get_representation_info_items(self, project_name, representation_ids): output = {} - missing_repre_ids_by_project = {} - if project_name is None: - project_name = self._controller.get_current_project_name() + missing_repre_ids = set() for repre_id in representation_ids: try: uuid.UUID(repre_id) @@ -239,60 +237,55 @@ class ContainersModel: continue repre_info = self._repre_info_by_id.get(repre_id) if repre_info is None: - missing_repre_ids_by_project.setdefault( - project_name, set() - ).add(repre_id) + missing_repre_ids.add(repre_id) else: output[repre_id] = repre_info - if not missing_repre_ids_by_project: + if not missing_repre_ids: return output - for project_name, missing_ids in missing_repre_ids_by_project.items(): - repre_hierarchy_by_id = get_representations_hierarchy( - project_name, missing_ids - ) - self._product_ids_by_project[project_name] = set() - for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): - kwargs = { - "folder_id": None, - "folder_path": None, - "product_id": None, - "product_name": None, - "product_type": None, - "product_group": None, - "version_id": None, - "representation_name": None, - } - folder = repre_hierarchy.folder - product = repre_hierarchy.product - version = repre_hierarchy.version - repre = repre_hierarchy.representation - if folder: - kwargs["folder_id"] = folder["id"] - kwargs["folder_path"] = folder["path"] - if product: - group = product["attrib"]["productGroup"] - kwargs["product_id"] = product["id"] - kwargs["product_name"] = product["name"] - kwargs["product_type"] = product["productType"] - kwargs["product_group"] = group - if version: - kwargs["version_id"] = version["id"] - if repre: - kwargs["representation_name"] = repre["name"] + repre_hierarchy_by_id = get_representations_hierarchy( + project_name, missing_repre_ids + ) + self._product_ids_by_project[project_name] = set() + for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): + kwargs = { + "folder_id": None, + "folder_path": None, + "product_id": None, + "product_name": None, + "product_type": None, + "product_group": None, + "version_id": None, + "representation_name": None, + } + folder = repre_hierarchy.folder + product = repre_hierarchy.product + version = repre_hierarchy.version + repre = repre_hierarchy.representation + if folder: + kwargs["folder_id"] = folder["id"] + kwargs["folder_path"] = folder["path"] + if product: + group = product["attrib"]["productGroup"] + kwargs["product_id"] = product["id"] + kwargs["product_name"] = product["name"] + kwargs["product_type"] = product["productType"] + kwargs["product_group"] = group + if version: + kwargs["version_id"] = version["id"] + if repre: + kwargs["representation_name"] = repre["name"] - repre_info = RepresentationInfo(**kwargs) - self._repre_info_by_id[repre_id] = repre_info - self._product_ids_by_project[project_name].add( - repre_info.product_id) - output[repre_id] = repre_info + repre_info = RepresentationInfo(**kwargs) + self._repre_info_by_id[repre_id] = repre_info + self._product_ids_by_project[project_name].add( + repre_info.product_id) + output[repre_id] = repre_info return output def get_version_items(self, project_name, product_ids): if not product_ids: return {} - if project_name is None: - project_name = self._controller.get_current_project_name() missing_ids = { product_id for product_id in product_ids From 0866c002019834e6a4aeda22becb9e5c208f4515 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 18 Nov 2024 19:18:50 +0800 Subject: [PATCH 079/463] code tweaks - kuba's comment --- .../ayon_core/tools/sceneinventory/model.py | 12 ++------ .../tools/sceneinventory/models/containers.py | 5 +++- client/ayon_core/tools/sceneinventory/view.py | 29 ++++--------------- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 03627e60b9..5f9e6bb77c 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -53,7 +53,7 @@ class InventoryModel(QtGui.QStandardItemModel): "Object name", "Active site", "Remote site", - "Project" + "Project", ] name_col = column_labels.index("Name") version_col = column_labels.index("Version") @@ -142,10 +142,7 @@ class InventoryModel(QtGui.QStandardItemModel): # and container_item.item_id not in selected # ): # continue - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_id = container_item.representation_id items_by_repre_id[repre_id].append(container_item) repre_ids_by_project[project_name].add(repre_id) @@ -166,10 +163,7 @@ class InventoryModel(QtGui.QStandardItemModel): project_products = collections.defaultdict(set) for container_item in container_items: representation_id = container_item.representation_id - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_info = repre_info_by_id.get(representation_id) if repre_info and repre_info.is_valid: product_id = repre_info.product_id diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index b8b9aa400a..c12a05fd99 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -5,6 +5,7 @@ import ayon_api from ayon_api.graphql import GraphQlQuery from ayon_core.host import ILoadHost +from ayon_core.pipeline import get_current_project_name from ayon_core.tools.common_models.projects import StatusStates @@ -111,7 +112,9 @@ class ContainerItem: namespace=container["namespace"], object_name=container["objectName"], item_id=uuid.uuid4().hex, - project_name=container.get("project_name", None) + project_name=container.get( + "project_name", get_current_project_name() + ) ) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 1e0b96570e..a049fd1e0b 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -195,10 +195,7 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): repre_id = container_item.representation_id - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_ids_by_project[project_name].add(repre_id) repre_info_by_id = {} for project_name, repre_ids in repre_ids_by_project.items(): @@ -219,10 +216,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_ids = set() for container_item in container_items_by_id.values(): repre_id = container_item.representation_id - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_info = repre_info_by_id.get(repre_id) if repre_info and repre_info.is_valid: filtered_items.append(container_item) @@ -754,10 +748,7 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): repre_id = container_item.representation_id - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_ids_by_project[project_name].add(repre_id) repre_info_by_id = {} for project_name, repre_ids in repre_ids_by_project.items(): @@ -773,10 +764,7 @@ class SceneInventoryView(QtWidgets.QTreeView): product_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): repre_id = container_item.representation_id - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_info = repre_info_by_id.get(repre_id) if not repre_info or not repre_info.is_valid: continue @@ -972,10 +960,6 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_ids_by_project = collections.defaultdict(set) for container_item in containers_items_by_id.values(): project_name = container_item.project_name - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) repre_id = container_item.representation_id repre_ids_by_project[project_name].add(repre_id) @@ -989,10 +973,7 @@ class SceneInventoryView(QtWidgets.QTreeView): product_ids_by_project = collections.defaultdict(set) for container_item in containers_items_by_id.values(): repre_id = container_item.representation_id - project_name = ( - container_item.project_name or - self._controller.get_current_project_name() - ) + project_name = container_item.project_name repre_info = repre_info_by_id.get(repre_id) if not repre_info or not repre_info.is_valid: continue From d2229fbb156bd9f74fc31b0a1c30d1b648f6d727 Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Mon, 18 Nov 2024 08:16:02 -0500 Subject: [PATCH 080/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/collect_hierarchy.py | 11 +++++++---- .../plugins/publish/extract_hierarchy_to_ayon.py | 1 - 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 3340430345..531b6a1d76 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -13,7 +13,6 @@ class CollectHierarchy(pyblish.api.ContextPlugin): label = "Collect Hierarchy" order = pyblish.api.CollectorOrder - 0.076 - families = ["shot", "csv_ingest_shot"] hosts = ["resolve", "hiero", "flame", "traypublisher"] def process(self, context): @@ -38,9 +37,12 @@ class CollectHierarchy(pyblish.api.ContextPlugin): ): continue - # exclude if not CSV ingest shot and not masterLayer True - if ("csv_ingest_shot" not in families and - not instance.data.get("heroTrack")): + # Skip if is not a hero track + # - skip check for traypubliser CSV ingest + if ( + not instance.data.get("heroTrack") + and "csv_ingest_shot" not in families + ): continue shot_data = { @@ -52,6 +54,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "comments": instance.data.get("comments", []), } + # TODO Fill in reason why we don't set attributes for csv_ingest_shot if "csv_ingest_shot" not in families: shot_data["attributes"] = { "handleStart": instance.data["handleStart"], diff --git a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py index 390ce36126..25467fd94f 100644 --- a/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py +++ b/client/ayon_core/plugins/publish/extract_hierarchy_to_ayon.py @@ -22,7 +22,6 @@ class ExtractHierarchyToAYON(pyblish.api.ContextPlugin): order = pyblish.api.ExtractorOrder - 0.01 label = "Extract Hierarchy To AYON" - families = ["clip", "shot", "csv_ingest_shot"] def process(self, context): if not context.data.get("hierarchyContext"): From 004e9626ee5c9e3899bf72fee0af4c88e2b75b8a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 19 Nov 2024 00:19:48 +0800 Subject: [PATCH 081/463] big roy comment - refactoring the dict per repre_id per project --- client/ayon_core/tools/sceneinventory/model.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 5f9e6bb77c..7630e9ee45 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -135,7 +135,8 @@ class InventoryModel(QtGui.QStandardItemModel): self._clear_items() items_by_repre_id = collections.defaultdict(list) - repre_ids_by_project = collections.defaultdict(set) + item_by_repre_id_by_project_id = collections.defaultdict( + lambda: collections.defaultdict(set)) for container_item in container_items: # if ( # selected is not None @@ -145,20 +146,17 @@ class InventoryModel(QtGui.QStandardItemModel): project_name = container_item.project_name repre_id = container_item.representation_id items_by_repre_id[repre_id].append(container_item) - repre_ids_by_project[project_name].add(repre_id) + item_by_repre_id_by_project_id[project_name][repre_id].add(container_item) - repre_id = set(items_by_repre_id.keys()) repre_info_by_id = {} - for project_name, repre_ids in repre_ids_by_project.items(): + repre_id = set() + for project_name, repre_ids in item_by_repre_id_by_project_id.items(): + repre_ids = set(items_by_repre_id.keys()) repre_info = self._controller.get_representation_info_items( project_name, repre_ids ) repre_info_by_id.update(repre_info) - product_ids = { - repre_info.product_id - for repre_info in repre_info_by_id.values() - if repre_info.is_valid - } + repre_id.update(repre_ids) project_products = collections.defaultdict(set) for container_item in container_items: From 4a4377b489c643f6a4a60edebfcf7b9c59c4079b Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 18 Nov 2024 13:38:21 -0500 Subject: [PATCH 082/463] Rework to avoid csv_ingest_shot family. --- .../plugins/publish/collect_hierarchy.py | 51 ++++++++++--------- .../publish/collect_otio_frame_ranges.py | 4 ++ 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 531b6a1d76..5e3be3d86d 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -31,18 +31,14 @@ class CollectHierarchy(pyblish.api.ContextPlugin): product_type = instance.data["productType"] families = instance.data["families"] - # exclude other families then self.families with intersection - if not set(self.families).intersection( - set(families + [product_type]) - ): + # exclude other families then "shot" with intersection + if "shot" not in (families + [product_type]): + self.log.debug("Skipping not a shot: {}".format(families)) continue # Skip if is not a hero track - # - skip check for traypubliser CSV ingest - if ( - not instance.data.get("heroTrack") - and "csv_ingest_shot" not in families - ): + if not instance.data.get("heroTrack"): + self.log.debug("Skipping not a shot from hero track") continue shot_data = { @@ -54,20 +50,29 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "comments": instance.data.get("comments", []), } - # TODO Fill in reason why we don't set attributes for csv_ingest_shot - if "csv_ingest_shot" not in families: - shot_data["attributes"] = { - "handleStart": instance.data["handleStart"], - "handleEnd": instance.data["handleEnd"], - "frameStart": instance.data["frameStart"], - "frameEnd": instance.data["frameEnd"], - "clipIn": instance.data["clipIn"], - "clipOut": instance.data["clipOut"], - "fps": instance.data["fps"], - "resolutionWidth": instance.data["resolutionWidth"], - "resolutionHeight": instance.data["resolutionHeight"], - "pixelAspect": instance.data["pixelAspect"], - } + shot_data["attributes"] = {} + SHOT_ATTRS = ( + "handleStart", + "handleEnd", + "frameStart", + "frameEnd", + "clipIn", + "clipOut", + "fps", + "resolutionWidth", + "resolutionHeight", + "pixelAspect", + ) + for shot_attr in SHOT_ATTRS: + if shot_attr not in instance.data: + # Shot attribute might not be defined (e.g. CSV ingest) + self.log.debug( + "%s shot attribute is not defined for instance.", + shot_attr + ) + continue + + shot_data["attributes"][shot_attr] = instance.data[shot_attr] # Split by '/' for AYON where asset is a path name = instance.data["folderPath"].split("/")[-1] diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index d1c8d03212..62b4cefec6 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -29,6 +29,10 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): otio_range_with_handles ) + if not instance.data.get("otioClip"): + self.log.debug("Skipping collect OTIO frame range.") + return + # get basic variables otio_clip = instance.data["otioClip"] workfile_start = instance.data["workfileFrameStart"] From ff56393da87def3555cbebacb9d6907e27c2f523 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 18 Nov 2024 13:40:09 -0500 Subject: [PATCH 083/463] Fix linting. --- client/ayon_core/plugins/publish/collect_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 5e3be3d86d..4c606fdc10 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -61,7 +61,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "fps", "resolutionWidth", "resolutionHeight", - "pixelAspect", + "pixelAspect", ) for shot_attr in SHOT_ATTRS: if shot_attr not in instance.data: From f003d8af1d654460ab802a7b1a604f092a6d163d Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Mon, 18 Nov 2024 15:29:40 -0500 Subject: [PATCH 084/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/publish/lib.py | 28 +++++++++--------------- client/ayon_core/pipeline/stagingdir.py | 15 ++++++++----- client/ayon_core/pipeline/tempdir.py | 4 +--- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 67a65aec09..3fad15f1a2 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -687,33 +687,25 @@ def get_instance_staging_dir(instance): anatomy_data = instance.data["anatomyData"] template_data = copy.deepcopy(anatomy_data) - product_type = instance.data["productType"] - product_name = instance.data["productName"] - # context data based variables - project_entity = instance.context.data["projectEntity"] - folder_entity = instance.context.data["folderEntity"] - task_entity = instance.context.data["taskEntity"] - host_name = instance.context.data["hostName"] - project_settings = instance.context.data["project_settings"] - anatomy = instance.context.data["anatomy"] - current_file = instance.context.data.get("currentFile") + context = instance.context # add current file as workfile name into formatting data + current_file = context.data.get("currentFile") if current_file: workfile = os.path.basename(current_file) workfile_name, _ = os.path.splitext(workfile) template_data["workfile_name"] = workfile_name staging_dir_info = get_staging_dir_info( - host_name, - project_entity, - folder_entity, - task_entity, - product_type, - product_name, - anatomy, - project_settings=project_settings, + context.data["hostName"], + context.data["projectEntity"], + instance.data.get("folderEntity"), + instance.data.get("taskEntity"), + instance.data["productType"], + instance.data["productName"], + anatomy=context.data["anatomy"], + project_settings=context.data["project_settings"], template_data=template_data, ) diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index 818acef36a..1c658ac817 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -9,12 +9,12 @@ STAGING_DIR_TEMPLATES = "staging" def get_staging_dir_config( - host_name, project_name, task_type, task_name, product_type, product_name, + host_name, project_settings=None, anatomy=None, log=None, @@ -24,8 +24,8 @@ def get_staging_dir_config( Args: host_name (str): Name of host. project_name (str): Name of project. - task_type (str): Type of task. - task_name (str): Name of task. + task_type (Optional[str]): Type of task. + task_name (Optional[str]): Name of task. product_type (str): Type of product. product_name (str): Name of product. project_settings(Dict[str, Any]): Prepared project settings. @@ -103,13 +103,13 @@ def _validate_template_name(project_name, template_name, anatomy): def get_staging_dir_info( - host_name, project_entity, folder_entity, task_entity, product_type, product_name, - anatomy, + host_name, + anatomy=None, project_settings=None, template_data=None, always_return_path=None, @@ -157,6 +157,11 @@ def get_staging_dir_info( if always_return_path is None: always_return_path = True + if anatomy is None: + anatomy = Anatomy( + project_entity["name"], project_entity=project_entity + ) + if force_tmp_dir: return get_temp_dir( project_name=project_entity["name"], diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index 8a9334ecc2..b5f4a31ee7 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -86,9 +86,7 @@ def _create_custom_tempdir(project_name, anatomy=None): Returns: str | None: formatted path or None """ - env_tmpdir = os.getenv( - "AYON_TMPDIR", - ) + env_tmpdir = os.getenv("AYON_TMPDIR") if not env_tmpdir: return From e96133b34892bf0529f0bcc81e56822d5177ffbc Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 18 Nov 2024 16:34:31 -0500 Subject: [PATCH 085/463] Address feedback from PR. --- .../pipeline/create/creator_plugins.py | 2 +- client/ayon_core/pipeline/publish/lib.py | 1 + client/ayon_core/pipeline/stagingdir.py | 54 +++++++++---------- client/ayon_core/pipeline/tempdir.py | 11 ++-- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 4cbf432efd..667f70c27d 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -873,7 +873,7 @@ class Creator(BaseCreator): ) if not staging_dir_info: - return + return None staging_dir_path = staging_dir_info["stagingDir"] diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 3fad15f1a2..8d56deec04 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -707,6 +707,7 @@ def get_instance_staging_dir(instance): anatomy=context.data["anatomy"], project_settings=context.data["project_settings"], template_data=template_data, + always_return_path=True, ) staging_dir_path = staging_dir_info["stagingDir"] diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index 1c658ac817..07ef122337 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -77,7 +77,7 @@ def get_staging_dir_config( # template should always be found either from anatomy or from profile raise ValueError( "Staging dir profile is misconfigured! " - "No template was found for profile! " + f"No template was found for profile: {profile}! " "Check your project settings at: " "'ayon+settings://core/tools/publish/custom_staging_dir_profiles'" ) @@ -112,10 +112,11 @@ def get_staging_dir_info( anatomy=None, project_settings=None, template_data=None, - always_return_path=None, - force_tmp_dir=None, + always_return_path=True, + force_tmp_dir=False, logger=None, - **kwargs + prefix=None, + suffix=None, ): """Get staging dir info data. @@ -141,11 +142,8 @@ def get_staging_dir_info( force_tmp_dir (Optional[bool]): If True, staging dir will be created as tempdir. logger (Optional[logging.Logger]): Logger instance. - **kwargs: Arbitrary keyword arguments. See below. - - Keyword Arguments: - prefix (str): Prefix for staging dir. - suffix (str): Suffix for staging dir. + prefix (Optional[str]) Optional prefix for staging dir name. + suffix (Optional[str]): Optional suffix for staging dir name. Returns: Optional[Dict[str, Any]]: Staging dir info data @@ -153,10 +151,6 @@ def get_staging_dir_info( """ log = logger or Logger.get_logger("get_staging_dir_info") - # make sure always_return_path is set to true by default - if always_return_path is None: - always_return_path = True - if anatomy is None: anatomy = Anatomy( project_entity["name"], project_entity=project_entity @@ -166,11 +160,11 @@ def get_staging_dir_info( return get_temp_dir( project_name=project_entity["name"], anatomy=anatomy, - prefix=kwargs.get("prefix"), - suffix=kwargs.get("suffix"), + prefix=prefix, + suffix=suffix, ) - # making fewer queries to database + # making few queries to database ctx_data = get_template_data( project_entity, folder_entity, task_entity, host_name ) @@ -192,8 +186,8 @@ def get_staging_dir_info( staging_dir_config = get_staging_dir_config( host_name, project_entity["name"], - task_entity["type"], - task_entity["name"], + task_entity.get("type"), + task_entity.get("name"), product_type, product_name, project_settings=project_settings, @@ -201,19 +195,19 @@ def get_staging_dir_info( log=log, ) - # if no preset matching and always_get_some_dir is set, return tempdir - if not staging_dir_config and always_return_path: - return { - "stagingDir": get_temp_dir( - project_name=project_entity["name"], - anatomy=anatomy, - prefix=kwargs.get("prefix"), - suffix=kwargs.get("suffix"), - ), - "stagingDir_persistent": False, - } if not staging_dir_config: - return None + if always_return_path: # no config found but force an output + return { + "stagingDir": get_temp_dir( + project_name=project_entity["name"], + anatomy=anatomy, + prefix=kwargs.get("prefix"), + suffix=kwargs.get("suffix"), + ), + "stagingDir_persistent": False, + } + else: + return None return { "stagingDir": StringTemplate.format_template( diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index b5f4a31ee7..af2ff44a8f 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -5,6 +5,7 @@ Temporary folder operations import os import tempfile from pathlib import Path + from ayon_core.lib import StringTemplate from ayon_core.pipeline import Anatomy @@ -48,7 +49,7 @@ def get_temp_dir( # get customized tempdir path from `OPENPYPE_TMPDIR` env var custom_temp_dir = _create_custom_tempdir(anatomy.project_name, anatomy) - return _create_local_staging_dir(prefix, suffix, custom_temp_dir) + return _create_local_staging_dir(prefix, suffix, dirpath=custom_temp_dir) def _create_local_staging_dir(prefix, suffix, dirpath=None): @@ -70,7 +71,7 @@ def _create_local_staging_dir(prefix, suffix, dirpath=None): return staging_dir.as_posix() -def _create_custom_tempdir(project_name, anatomy=None): +def _create_custom_tempdir(project_name, anatomy): """ Create custom tempdir Template path formatting is supporting: @@ -81,19 +82,17 @@ def _create_custom_tempdir(project_name, anatomy=None): Args: project_name (str): project name - anatomy (ayon_core.pipeline.Anatomy)[optional]: Anatomy object + anatomy (ayon_core.pipeline.Anatomy): Anatomy object Returns: str | None: formatted path or None """ env_tmpdir = os.getenv("AYON_TMPDIR") if not env_tmpdir: - return + return None custom_tempdir = None if "{" in env_tmpdir: - if anatomy is None: - anatomy = Anatomy(project_name) # create base formate data template_data = { "root": anatomy.roots, From 7375538f22d84b8a4493fc84f77cfd4d12cd8fbc Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 18 Nov 2024 16:43:31 -0500 Subject: [PATCH 086/463] Fix lint. --- client/ayon_core/pipeline/stagingdir.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index 07ef122337..2dd5c2f3eb 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -201,8 +201,8 @@ def get_staging_dir_info( "stagingDir": get_temp_dir( project_name=project_entity["name"], anatomy=anatomy, - prefix=kwargs.get("prefix"), - suffix=kwargs.get("suffix"), + prefix=prefix, + suffix=suffix, ), "stagingDir_persistent": False, } From 3078ba2a239c9780bf784d07849d42c2e02ea0c5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 19 Nov 2024 15:45:27 +0800 Subject: [PATCH 087/463] kuba's comment - add current project name as argument in the get_container_data and make sure the scene inventory won't show Entity N/A as name and version --- client/ayon_core/tools/sceneinventory/control.py | 2 +- client/ayon_core/tools/sceneinventory/model.py | 16 +++++++++------- .../tools/sceneinventory/models/containers.py | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 310e41b117..8c02881b82 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -4,7 +4,7 @@ from ayon_core.lib.events import QueuedEventSystem from ayon_core.host import HostBase from ayon_core.pipeline import ( registered_host, - get_current_context + get_current_context, ) from ayon_core.tools.common_models import HierarchyModel, ProjectsModel diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 7630e9ee45..5f9e6bb77c 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -135,8 +135,7 @@ class InventoryModel(QtGui.QStandardItemModel): self._clear_items() items_by_repre_id = collections.defaultdict(list) - item_by_repre_id_by_project_id = collections.defaultdict( - lambda: collections.defaultdict(set)) + repre_ids_by_project = collections.defaultdict(set) for container_item in container_items: # if ( # selected is not None @@ -146,17 +145,20 @@ class InventoryModel(QtGui.QStandardItemModel): project_name = container_item.project_name repre_id = container_item.representation_id items_by_repre_id[repre_id].append(container_item) - item_by_repre_id_by_project_id[project_name][repre_id].add(container_item) + repre_ids_by_project[project_name].add(repre_id) + repre_id = set(items_by_repre_id.keys()) repre_info_by_id = {} - repre_id = set() - for project_name, repre_ids in item_by_repre_id_by_project_id.items(): - repre_ids = set(items_by_repre_id.keys()) + for project_name, repre_ids in repre_ids_by_project.items(): repre_info = self._controller.get_representation_info_items( project_name, repre_ids ) repre_info_by_id.update(repre_info) - repre_id.update(repre_ids) + product_ids = { + repre_info.product_id + for repre_info in repre_info_by_id.values() + if repre_info.is_valid + } project_products = collections.defaultdict(set) for container_item in container_items: diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index c12a05fd99..e135cb0031 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -5,7 +5,6 @@ import ayon_api from ayon_api.graphql import GraphQlQuery from ayon_core.host import ILoadHost -from ayon_core.pipeline import get_current_project_name from ayon_core.tools.common_models.projects import StatusStates @@ -105,7 +104,7 @@ class ContainerItem: self.project_name = project_name @classmethod - def from_container_data(cls, container): + def from_container_data(cls, current_project_name, container): return cls( representation_id=container["representation"], loader_name=container["loader"], @@ -113,7 +112,7 @@ class ContainerItem: object_name=container["objectName"], item_id=uuid.uuid4().hex, project_name=container.get( - "project_name", get_current_project_name() + "project_name", current_project_name ) ) @@ -368,7 +367,8 @@ class ContainersModel: invalid_ids_mapping = {} for container in containers: try: - item = ContainerItem.from_container_data(container) + current_project_name = self._controller.get_current_project_name() + item = ContainerItem.from_container_data(current_project_name, container) repre_id = item.representation_id try: uuid.UUID(repre_id) From 83cc964ca041a95d6af426c39ec7b92ee7706bc5 Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Tue, 19 Nov 2024 08:07:52 -0500 Subject: [PATCH 088/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/collect_hierarchy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 4c606fdc10..39501a9ed5 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -64,7 +64,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): "pixelAspect", ) for shot_attr in SHOT_ATTRS: - if shot_attr not in instance.data: + attr_value = instance.data.get(shot_attr) + if attr_value is None: # Shot attribute might not be defined (e.g. CSV ingest) self.log.debug( "%s shot attribute is not defined for instance.", @@ -72,7 +73,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): ) continue - shot_data["attributes"][shot_attr] = instance.data[shot_attr] + shot_data["attributes"][shot_attr] = attr_value # Split by '/' for AYON where asset is a path name = instance.data["folderPath"].split("/")[-1] From 70a38a6b1a3025ad653c2338dccfb30bdc9e0249 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 19 Nov 2024 08:22:13 -0500 Subject: [PATCH 089/463] Fix linting. --- client/ayon_core/plugins/publish/collect_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 39501a9ed5..cae89bd6bf 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -65,7 +65,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): ) for shot_attr in SHOT_ATTRS: attr_value = instance.data.get(shot_attr) - if attr_value is None: + if attr_value is None: # Shot attribute might not be defined (e.g. CSV ingest) self.log.debug( "%s shot attribute is not defined for instance.", From 26e5c2f52b05fd09c681305ffc151a3863aafbc5 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 19 Nov 2024 09:28:16 -0500 Subject: [PATCH 090/463] Adjust folder type creation in collect_hierarchy. --- client/ayon_core/plugins/publish/collect_hierarchy.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index cae89bd6bf..00f5c06c0b 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -43,9 +43,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): shot_data = { "entity_type": "folder", - # WARNING Default folder type is hardcoded - # suppose that all instances are Shots - "folder_type": "Shot", + # WARNING unless overwritten, default folder type is hardcoded to shot + "folder_type": instance.data.get("folder_type") or "Shot", "tasks": instance.data.get("tasks") or {}, "comments": instance.data.get("comments", []), } From b8ba7f47b0dee929fd1259e49fc791fa13ec3ed3 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 19 Nov 2024 16:49:08 -0500 Subject: [PATCH 091/463] Fix staging data computation. --- client/ayon_core/pipeline/create/creator_plugins.py | 8 +++++--- client/ayon_core/pipeline/publish/lib.py | 4 ++-- client/ayon_core/pipeline/stagingdir.py | 6 +++--- .../plugins/publish/collect_managed_staging_dir.py | 4 ++++ client/ayon_core/plugins/publish/extract_burnin.py | 2 +- .../ayon_core/plugins/publish/extract_color_transcode.py | 2 +- client/ayon_core/plugins/publish/extract_review.py | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 667f70c27d..93e1f6f5cb 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -857,16 +857,18 @@ class Creator(BaseCreator): version = instance.get("version") if version is not None: template_data = {"version": version} + else: + template_data = {} staging_dir_info = get_staging_dir_info( - create_ctx.host_name, create_ctx.get_current_project_entity(), create_ctx.get_current_folder_entity(), create_ctx.get_current_task_entity(), product_type, product_name, - create_ctx.get_current_project_anatomy(), - create_ctx.get_current_project_settings(), + create_ctx.host_name, + anatomy=create_ctx.get_current_project_anatomy(), + project_settings=create_ctx.get_current_project_settings(), always_return_path=False, logger=self.log, template_data=template_data, diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 8d56deec04..4c36f473d1 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -653,12 +653,12 @@ def get_custom_staging_dir_info( DeprecationWarning, ) tr_data = get_staging_dir_config( - host_name, project_name, task_type, task_name, product_type, product_name, + host_name, project_settings=project_settings, anatomy=anatomy, log=log, @@ -698,12 +698,12 @@ def get_instance_staging_dir(instance): template_data["workfile_name"] = workfile_name staging_dir_info = get_staging_dir_info( - context.data["hostName"], context.data["projectEntity"], instance.data.get("folderEntity"), instance.data.get("taskEntity"), instance.data["productType"], instance.data["productName"], + context.data["hostName"], anatomy=context.data["anatomy"], project_settings=context.data["project_settings"], template_data=template_data, diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index 2dd5c2f3eb..c7cc95ff55 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -184,12 +184,12 @@ def get_staging_dir_info( # get staging dir config staging_dir_config = get_staging_dir_config( - host_name, project_entity["name"], - task_entity.get("type"), + task_entity.get("taskType"), task_entity.get("name"), product_type, product_name, + host_name, project_settings=project_settings, anatomy=anatomy, log=log, @@ -211,7 +211,7 @@ def get_staging_dir_info( return { "stagingDir": StringTemplate.format_template( - staging_dir_config["template"], ctx_data + staging_dir_config["template"]["directory"], ctx_data ), "stagingDir_persistent": staging_dir_config["persistence"], } diff --git a/client/ayon_core/plugins/publish/collect_managed_staging_dir.py b/client/ayon_core/plugins/publish/collect_managed_staging_dir.py index ca6d5161c1..1034b9a716 100644 --- a/client/ayon_core/plugins/publish/collect_managed_staging_dir.py +++ b/client/ayon_core/plugins/publish/collect_managed_staging_dir.py @@ -33,7 +33,11 @@ class CollectManagedStagingDir(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.4990 def process(self, instance): + """ Collect the staging data and stores it to the instance. + Args: + instance (object): The instance to inspect. + """ staging_dir_path = get_instance_staging_dir(instance) persistance = instance.data.get("stagingDir_persistent", False) diff --git a/client/ayon_core/plugins/publish/extract_burnin.py b/client/ayon_core/plugins/publish/extract_burnin.py index 3eb4254a5e..8e8764fc33 100644 --- a/client/ayon_core/plugins/publish/extract_burnin.py +++ b/client/ayon_core/plugins/publish/extract_burnin.py @@ -254,7 +254,7 @@ class ExtractBurnin(publish.Extractor): if do_convert: new_staging_dir = get_temp_dir( project_name=instance.context.data["projectName"], - make_local=True, + use_local_temp=True, ) repre["stagingDir"] = new_staging_dir diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 4f0053c426..3c11a016ec 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -106,7 +106,7 @@ class ExtractOIIOTranscode(publish.Extractor): original_staging_dir = new_repre["stagingDir"] new_staging_dir = get_temp_dir( project_name=instance.context.data["projectName"], - make_local=True, + use_local_temp=True, ) new_repre["stagingDir"] = new_staging_dir diff --git a/client/ayon_core/plugins/publish/extract_review.py b/client/ayon_core/plugins/publish/extract_review.py index 26cd2ef0b2..7c38b0453b 100644 --- a/client/ayon_core/plugins/publish/extract_review.py +++ b/client/ayon_core/plugins/publish/extract_review.py @@ -312,7 +312,7 @@ class ExtractReview(pyblish.api.InstancePlugin): if do_convert: new_staging_dir = get_temp_dir( project_name=instance.context.data["projectName"], - make_local=True, + use_local_temp=True, ) repre["stagingDir"] = new_staging_dir From d9c1a299b97ff8a4c5c6612a2644d41e7d564a40 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Nov 2024 17:23:36 +0800 Subject: [PATCH 092/463] loading the asset per repre_id and per project --- .../ayon_core/tools/sceneinventory/model.py | 246 +++++++++--------- .../tools/sceneinventory/models/containers.py | 22 +- 2 files changed, 127 insertions(+), 141 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 5f9e6bb77c..29818e387f 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -131,50 +131,39 @@ class InventoryModel(QtGui.QStandardItemModel): """Refresh the model""" # for debugging or testing, injecting items from outside container_items = self._controller.get_container_items() - self._clear_items() - - items_by_repre_id = collections.defaultdict(list) - repre_ids_by_project = collections.defaultdict(set) - for container_item in container_items: + repre_id = set() + repre_id_by_project_id = collections.defaultdict(set) + version_items_by_product_id = collections.defaultdict(dict) + repre_info_by_id_by_project = collections.defaultdict(list) + item_by_repre_id_by_project_id = collections.defaultdict( + lambda: collections.defaultdict(set)) + for project_name, container_item in container_items.items(): # if ( # selected is not None # and container_item.item_id not in selected # ): # continue - project_name = container_item.project_name - repre_id = container_item.representation_id - items_by_repre_id[repre_id].append(container_item) - repre_ids_by_project[project_name].add(repre_id) - - repre_id = set(items_by_repre_id.keys()) - repre_info_by_id = {} - for project_name, repre_ids in repre_ids_by_project.items(): + for item in container_item.values(): + representation_id = item.representation_id + if item.project_name != project_name: + continue + repre_id.add(representation_id) + item_by_repre_id_by_project_id[project_name][representation_id].add(item) repre_info = self._controller.get_representation_info_items( - project_name, repre_ids + project_name, repre_id ) - repre_info_by_id.update(repre_info) - product_ids = { - repre_info.product_id - for repre_info in repre_info_by_id.values() - if repre_info.is_valid - } + repre_info_by_id_by_project[project_name] = repre_info - project_products = collections.defaultdict(set) - for container_item in container_items: - representation_id = container_item.representation_id - project_name = container_item.project_name - repre_info = repre_info_by_id.get(representation_id) - if repre_info and repre_info.is_valid: - product_id = repre_info.product_id - project_products[project_name].add(product_id) - - version_items_by_product_id = {} - for project_name, product_ids in project_products.items(): + product_ids = { + repre_info.product_id + for repre_info in repre_info.values() + if repre_info.is_valid + } version_items = self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id.update(version_items) + version_items_by_product_id[project_name] = version_items # SiteSync addon information progress_by_id = self._controller.get_representations_site_progress( @@ -216,112 +205,113 @@ class InventoryModel(QtGui.QStandardItemModel): root_item = self.invisibleRootItem() group_items = [] - for repre_id, container_items in items_by_repre_id.items(): - repre_info = repre_info_by_id[repre_id] - version_color = None - if not repre_info.is_valid: - version_label = "N/A" - group_name = "< Entity N/A >" - item_icon = invalid_item_icon - is_latest = False - is_hero = False - status_name = None + for project_name, items_by_repre_id in item_by_repre_id_by_project_id.items(): + for repre_id, container_items in items_by_repre_id.items(): + repre_info = repre_info_by_id_by_project[project_name][repre_id] + version_color = None + if not repre_info.is_valid: + version_label = "N/A" + group_name = "< Entity N/A >" + item_icon = invalid_item_icon + is_latest = False + is_hero = False + status_name = None - else: - group_name = "{}_{}: ({})".format( - repre_info.folder_path.rsplit("/")[-1], - repre_info.product_name, - repre_info.representation_name + else: + group_name = "{}_{}: ({})".format( + repre_info.folder_path.rsplit("/")[-1], + repre_info.product_name, + repre_info.representation_name + ) + item_icon = valid_item_icon + + version_items = ( + version_items_by_product_id[project_name][repre_info.product_id] + ) + version_item = version_items[repre_info.version_id] + version_label = format_version(version_item.version) + is_hero = version_item.version < 0 + is_latest = version_item.is_latest + if not version_item.is_latest: + version_color = self.OUTDATED_COLOR + status_name = version_item.status + + status_color, status_short, status_icon = self._get_status_data( + status_name ) - item_icon = valid_item_icon - version_items = ( - version_items_by_product_id[repre_info.product_id] + repre_name = ( + repre_info.representation_name or "" ) - version_item = version_items[repre_info.version_id] - version_label = format_version(version_item.version) - is_hero = version_item.version < 0 - is_latest = version_item.is_latest - if not version_item.is_latest: - version_color = self.OUTDATED_COLOR - status_name = version_item.status + container_model_items = [] + for container_item in container_items: + object_name = container_item.object_name or "" + unique_name = repre_name + object_name + item = QtGui.QStandardItem() + item.setColumnCount(root_item.columnCount()) + item.setData(container_item.namespace, QtCore.Qt.DisplayRole) + item.setData(self.GRAYOUT_COLOR, NAME_COLOR_ROLE) + item.setData(self.GRAYOUT_COLOR, VERSION_COLOR_ROLE) + item.setData(item_icon, QtCore.Qt.DecorationRole) + item.setData(repre_info.product_id, PRODUCT_ID_ROLE) + item.setData(container_item.item_id, ITEM_ID_ROLE) + item.setData(version_label, VERSION_LABEL_ROLE) + item.setData(container_item.loader_name, LOADER_NAME_ROLE) + item.setData(container_item.object_name, OBJECT_NAME_ROLE) + item.setData(container_item.project_name, PROJECT_NAME_ROLE) + item.setData(True, IS_CONTAINER_ITEM_ROLE) + item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) + container_model_items.append(item) + if not container_model_items: + continue - status_color, status_short, status_icon = self._get_status_data( - status_name - ) + progress = progress_by_id[repre_id] + active_site_progress = "{}%".format( + max(progress["active_site"], 0) * 100 + ) + remote_site_progress = "{}%".format( + max(progress["remote_site"], 0) * 100 + ) - repre_name = ( - repre_info.representation_name or "" - ) - container_model_items = [] - for container_item in container_items: - object_name = container_item.object_name or "" - unique_name = repre_name + object_name - item = QtGui.QStandardItem() - item.setColumnCount(root_item.columnCount()) - item.setData(container_item.namespace, QtCore.Qt.DisplayRole) - item.setData(self.GRAYOUT_COLOR, NAME_COLOR_ROLE) - item.setData(self.GRAYOUT_COLOR, VERSION_COLOR_ROLE) - item.setData(item_icon, QtCore.Qt.DecorationRole) - item.setData(repre_info.product_id, PRODUCT_ID_ROLE) - item.setData(container_item.item_id, ITEM_ID_ROLE) - item.setData(version_label, VERSION_LABEL_ROLE) - item.setData(container_item.loader_name, LOADER_NAME_ROLE) - item.setData(container_item.object_name, OBJECT_NAME_ROLE) - item.setData(container_item.project_name, PROJECT_NAME_ROLE) - item.setData(True, IS_CONTAINER_ITEM_ROLE) - item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) - container_model_items.append(item) - if not container_model_items: - continue + group_item = QtGui.QStandardItem() + group_item.setColumnCount(root_item.columnCount()) + group_item.setData(group_name, QtCore.Qt.DisplayRole) + group_item.setData(group_name, ITEM_UNIQUE_NAME_ROLE) + group_item.setData(group_item_icon, QtCore.Qt.DecorationRole) + group_item.setData(group_item_font, QtCore.Qt.FontRole) + group_item.setData(repre_info.product_id, PRODUCT_ID_ROLE) + group_item.setData(repre_info.product_type, PRODUCT_TYPE_ROLE) + group_item.setData(product_type_icon, PRODUCT_TYPE_ICON_ROLE) + group_item.setData(is_latest, VERSION_IS_LATEST_ROLE) + group_item.setData(is_hero, VERSION_IS_HERO_ROLE) + group_item.setData(version_label, VERSION_LABEL_ROLE) + group_item.setData(len(container_items), COUNT_ROLE) + group_item.setData(status_name, STATUS_NAME_ROLE) + group_item.setData(status_short, STATUS_SHORT_ROLE) + group_item.setData(status_color, STATUS_COLOR_ROLE) + group_item.setData(status_icon, STATUS_ICON_ROLE) - progress = progress_by_id[repre_id] - active_site_progress = "{}%".format( - max(progress["active_site"], 0) * 100 - ) - remote_site_progress = "{}%".format( - max(progress["remote_site"], 0) * 100 - ) - - group_item = QtGui.QStandardItem() - group_item.setColumnCount(root_item.columnCount()) - group_item.setData(group_name, QtCore.Qt.DisplayRole) - group_item.setData(group_name, ITEM_UNIQUE_NAME_ROLE) - group_item.setData(group_item_icon, QtCore.Qt.DecorationRole) - group_item.setData(group_item_font, QtCore.Qt.FontRole) - group_item.setData(repre_info.product_id, PRODUCT_ID_ROLE) - group_item.setData(repre_info.product_type, PRODUCT_TYPE_ROLE) - group_item.setData(product_type_icon, PRODUCT_TYPE_ICON_ROLE) - group_item.setData(is_latest, VERSION_IS_LATEST_ROLE) - group_item.setData(is_hero, VERSION_IS_HERO_ROLE) - group_item.setData(version_label, VERSION_LABEL_ROLE) - group_item.setData(len(container_items), COUNT_ROLE) - group_item.setData(status_name, STATUS_NAME_ROLE) - group_item.setData(status_short, STATUS_SHORT_ROLE) - group_item.setData(status_color, STATUS_COLOR_ROLE) - group_item.setData(status_icon, STATUS_ICON_ROLE) - - group_item.setData( - active_site_progress, ACTIVE_SITE_PROGRESS_ROLE - ) - group_item.setData( - remote_site_progress, REMOTE_SITE_PROGRESS_ROLE - ) - group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE) - group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE) - group_item.setData(False, IS_CONTAINER_ITEM_ROLE) - - if version_color is not None: - group_item.setData(version_color, VERSION_COLOR_ROLE) - - if repre_info.product_group: group_item.setData( - repre_info.product_group, PRODUCT_GROUP_NAME_ROLE + active_site_progress, ACTIVE_SITE_PROGRESS_ROLE ) - group_item.setData(group_icon, PRODUCT_GROUP_ICON_ROLE) + group_item.setData( + remote_site_progress, REMOTE_SITE_PROGRESS_ROLE + ) + group_item.setData(active_site_icon, ACTIVE_SITE_ICON_ROLE) + group_item.setData(remote_site_icon, REMOTE_SITE_ICON_ROLE) + group_item.setData(False, IS_CONTAINER_ITEM_ROLE) - group_item.appendRows(container_model_items) - group_items.append(group_item) + if version_color is not None: + group_item.setData(version_color, VERSION_COLOR_ROLE) + + if repre_info.product_group: + group_item.setData( + repre_info.product_group, PRODUCT_GROUP_NAME_ROLE + ) + group_item.setData(group_icon, PRODUCT_GROUP_ICON_ROLE) + + group_item.appendRows(container_model_items) + group_items.append(group_item) if group_items: root_item.appendRows(group_items) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index e135cb0031..ca67fb59f9 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -191,7 +191,7 @@ class VersionItem: class ContainersModel: def __init__(self, controller): self._controller = controller - self._items_cache = None + self._project_cache = None self._containers_by_id = {} self._container_items_by_id = {} self._container_items_by_project = {} @@ -200,7 +200,7 @@ class ContainersModel: self._product_ids_by_project = {} def reset(self): - self._items_cache = None + self._project_cache = None self._containers_by_id = {} self._container_items_by_id = {} self._container_items_by_project = {} @@ -220,7 +220,7 @@ class ContainersModel: def get_container_items(self): self._update_cache() - return list(self._items_cache) + return self._project_cache def get_container_items_by_id(self, item_ids): return { @@ -248,7 +248,6 @@ class ContainersModel: repre_hierarchy_by_id = get_representations_hierarchy( project_name, missing_repre_ids ) - self._product_ids_by_project[project_name] = set() for repre_id, repre_hierarchy in repre_hierarchy_by_id.items(): kwargs = { "folder_id": None, @@ -280,8 +279,6 @@ class ContainersModel: repre_info = RepresentationInfo(**kwargs) self._repre_info_by_id[repre_id] = repre_info - self._product_ids_by_project[project_name].add( - repre_info.product_id) output[repre_id] = repre_info return output @@ -293,7 +290,6 @@ class ContainersModel: for product_id in product_ids if product_id not in self._version_items_by_product_id } - current_product_ids = self._product_ids_by_project.get(project_name) if missing_ids: status_items_by_name = { status_item.name: status_item @@ -302,14 +298,13 @@ class ContainersModel: def version_sorted(entity): return entity["version"] - current_missing_ids = current_product_ids.intersection(missing_ids) version_entities_by_product_id = { product_id: [] - for product_id in current_missing_ids + for product_id in missing_ids } version_entities = list(ayon_api.get_versions( project_name, - product_ids=current_missing_ids, + product_ids=missing_ids, fields={"id", "version", "productId", "status"} )) version_entities.sort(key=version_sorted) @@ -349,7 +344,7 @@ class ContainersModel: } def _update_cache(self): - if self._items_cache is not None: + if self._project_cache is not None: return host = self._controller.get_host() @@ -363,7 +358,7 @@ class ContainersModel: container_items = [] containers_by_id = {} container_items_by_id = {} - project_name_by_repre_id = {} + project_cache = collections.defaultdict(dict) invalid_ids_mapping = {} for container in containers: try: @@ -388,9 +383,10 @@ class ContainersModel: containers_by_id[item.item_id] = container container_items_by_id[item.item_id] = item - project_name_by_repre_id[item.representation_id] = item.project_name + project_cache[item.project_name] = container_items_by_id container_items.append(item) self._containers_by_id = containers_by_id self._container_items_by_id = container_items_by_id self._items_cache = container_items + self._project_cache = project_cache From 7a224914ea7dc59bcfa158cd6ead2404c4074a9e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Nov 2024 17:43:17 +0800 Subject: [PATCH 093/463] remove unused variable --- client/ayon_core/tools/sceneinventory/model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 29818e387f..640a8017ab 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -133,7 +133,6 @@ class InventoryModel(QtGui.QStandardItemModel): container_items = self._controller.get_container_items() self._clear_items() repre_id = set() - repre_id_by_project_id = collections.defaultdict(set) version_items_by_product_id = collections.defaultdict(dict) repre_info_by_id_by_project = collections.defaultdict(list) item_by_repre_id_by_project_id = collections.defaultdict( From 582dce426fb2005fe8878e69a5f191a87ba4e073 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:35:24 +0100 Subject: [PATCH 094/463] Fix typo --- client/ayon_core/tools/creator/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/creator/widgets.py b/client/ayon_core/tools/creator/widgets.py index 53a2ee1080..09f4e1fa32 100644 --- a/client/ayon_core/tools/creator/widgets.py +++ b/client/ayon_core/tools/creator/widgets.py @@ -104,7 +104,7 @@ class ProductNameValidator(RegularExpressionValidatorClass): def validate(self, text, pos): results = super(ProductNameValidator, self).validate(text, pos) - if results[0] == self.Invalid: + if results[0] == self.invalid: self.invalid.emit(self.invalid_chars(text)) return results From 5ccdfc258a97e519e97cac5ac0d254678f27f1aa Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:36:21 +0100 Subject: [PATCH 095/463] Fix plugins returning empty list --- client/ayon_core/tools/pyblish_pype/model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/tools/pyblish_pype/model.py b/client/ayon_core/tools/pyblish_pype/model.py index 3a402f386e..7c242c817a 100644 --- a/client/ayon_core/tools/pyblish_pype/model.py +++ b/client/ayon_core/tools/pyblish_pype/model.py @@ -780,6 +780,8 @@ class InstanceModel(QtGui.QStandardItemModel): def update_with_result(self, result): instance = result["instance"] + if isinstance(instance, list): + instance = instance.pop() if instance else None if instance is None: instance_id = self.controller.context.id else: @@ -976,6 +978,8 @@ class TerminalModel(QtGui.QStandardItemModel): prepared_records = [] instance_name = None instance = result["instance"] + if isinstance(instance, list): + instance = instance.pop() if instance else None if instance is not None: instance_name = instance.data["name"] From c56cd07e67b22b4e34cf6c246229150799fa5ab0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:47:37 +0100 Subject: [PATCH 096/463] Provided backward compatibility for prepare_representations --- client/ayon_core/pipeline/farm/pyblish_functions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e9f179c668..e236ec6c3d 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -333,7 +333,13 @@ def prepare_representations( log = Logger.get_logger("farm_publishing") - frames_to_render = _get_real_frames_to_render(frames_to_render) + if frames_to_render is not None: + frames_to_render = _get_real_frames_to_render(frames_to_render) + else: + # Backwards compatibility for older logic + frame_start = int(skeleton_data.get("frameStartHandle")) + frame_end = int(skeleton_data.get("frameEndHandle")) + frames_to_render = list(range(frame_start, frame_end + 1)) # create representation for every collected sequence for collection in collections: From c2716872d43d20f1e7de051375244893cd192d38 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:49:24 +0100 Subject: [PATCH 097/463] Do not convert to str unnecessary Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e9f179c668..aa69633a22 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -482,7 +482,8 @@ def _get_real_frames_to_render(frames): range(int(splitted[0]), int(splitted[1])+1)) else: frames_to_render.append(frame) - return [str(frame_to_render) for frame_to_render in frames_to_render] + frames_to_render.sort() + return frames_to_render def _get_real_files_to_rendered(collection, frames_to_render): From de88260ddac22419b3a87606aaae08bfc9ec4e09 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:50:23 +0100 Subject: [PATCH 098/463] frames_to_render are now list of integers Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index aa69633a22..876a5b504f 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -481,7 +481,7 @@ def _get_real_frames_to_render(frames): frames_to_render.extend( range(int(splitted[0]), int(splitted[1])+1)) else: - frames_to_render.append(frame) + frames_to_render.append(int(frame)) frames_to_render.sort() return frames_to_render From 630d2d49130a9cea142aea999203fc00106a269d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:50:36 +0100 Subject: [PATCH 099/463] frames_to_render are now list of integers Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 876a5b504f..6740950d78 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -371,8 +371,8 @@ def prepare_representations( " This may cause issues on farm." ).format(staging)) - frame_start = int(frames_to_render[0]) - frame_end = int(frames_to_render[-1]) + frame_start = frames_to_render[0] + frame_end = frames_to_render[-1] if skeleton_data.get("slate"): frame_start -= 1 From 5f3175258725853011ab4b9eb2c58c1fc6959eda Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:50:59 +0100 Subject: [PATCH 100/463] Used comprehension Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 6740950d78..09df371371 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -498,11 +498,10 @@ def _get_real_files_to_rendered(collection, frames_to_render): file_name, extracted_frame = list(collect_frames(files).items())[0] if extracted_frame: found_frame_pattern_length = len(extracted_frame) - normalized_frames_to_render = set() - for frame_to_render in frames_to_render: - normalized_frames_to_render.add( - str(frame_to_render).zfill(found_frame_pattern_length) - ) + normalized_frames_to_render = { + str(frame_to_render).zfill(found_frame_pattern_length) + for frame_to_render in frames_to_render + } filtered_files = [] for file_name in files: From fd20885ac26366b996c2b14d102cbefdc3a75341 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:51:12 +0100 Subject: [PATCH 101/463] Used comprehension Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../ayon_core/pipeline/farm/pyblish_functions.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 09df371371..4ba088f92c 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -503,13 +503,14 @@ def _get_real_files_to_rendered(collection, frames_to_render): for frame_to_render in frames_to_render } - filtered_files = [] - for file_name in files: - if any(frame in file_name - for frame in normalized_frames_to_render): - filtered_files.append(file_name) - - files = filtered_files + files = [ + filename + for filename in files + if any( + frame in filename + for frame in normalized_frames_to_render + ) + ] return files From 80ab628d44b083f168e1a7e822f4f1145d40145b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 12:52:51 +0100 Subject: [PATCH 102/463] Changed condition to bail early --- .../pipeline/farm/pyblish_functions.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e236ec6c3d..37a018e116 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -501,21 +501,23 @@ def _get_real_files_to_rendered(collection, frames_to_render): """ files = [os.path.basename(f) for f in list(collection)] file_name, extracted_frame = list(collect_frames(files).items())[0] - if extracted_frame: - found_frame_pattern_length = len(extracted_frame) - normalized_frames_to_render = set() - for frame_to_render in frames_to_render: - normalized_frames_to_render.add( - str(frame_to_render).zfill(found_frame_pattern_length) - ) + if not extracted_frame: + return files - filtered_files = [] - for file_name in files: - if any(frame in file_name - for frame in normalized_frames_to_render): - filtered_files.append(file_name) + found_frame_pattern_length = len(extracted_frame) + normalized_frames_to_render = set() + for frame_to_render in frames_to_render: + normalized_frames_to_render.add( + str(frame_to_render).zfill(found_frame_pattern_length) + ) - files = filtered_files + filtered_files = [] + for file_name in files: + if any(frame in file_name + for frame in normalized_frames_to_render): + filtered_files.append(file_name) + + files = filtered_files return files From 0c5777910a7e96acde12cdf11b7df86d67e6a5e2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 14:26:07 +0100 Subject: [PATCH 103/463] Revert "Fix plugins returning empty list" This reverts commit 5ccdfc258a97e519e97cac5ac0d254678f27f1aa. --- client/ayon_core/tools/pyblish_pype/model.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/ayon_core/tools/pyblish_pype/model.py b/client/ayon_core/tools/pyblish_pype/model.py index 7c242c817a..3a402f386e 100644 --- a/client/ayon_core/tools/pyblish_pype/model.py +++ b/client/ayon_core/tools/pyblish_pype/model.py @@ -780,8 +780,6 @@ class InstanceModel(QtGui.QStandardItemModel): def update_with_result(self, result): instance = result["instance"] - if isinstance(instance, list): - instance = instance.pop() if instance else None if instance is None: instance_id = self.controller.context.id else: @@ -978,8 +976,6 @@ class TerminalModel(QtGui.QStandardItemModel): prepared_records = [] instance_name = None instance = result["instance"] - if isinstance(instance, list): - instance = instance.pop() if instance else None if instance is not None: instance_name = instance.data["name"] From 962df74e640f9661489b2ac6433a55d913fcd07d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 14:27:06 +0100 Subject: [PATCH 104/463] Better RegularExpressionValidatorClass.Invalid used --- client/ayon_core/tools/creator/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/creator/widgets.py b/client/ayon_core/tools/creator/widgets.py index 09f4e1fa32..96ce899881 100644 --- a/client/ayon_core/tools/creator/widgets.py +++ b/client/ayon_core/tools/creator/widgets.py @@ -104,7 +104,7 @@ class ProductNameValidator(RegularExpressionValidatorClass): def validate(self, text, pos): results = super(ProductNameValidator, self).validate(text, pos) - if results[0] == self.invalid: + if results[0] == RegularExpressionValidatorClass.Invalid: self.invalid.emit(self.invalid_chars(text)) return results From 63592f9e2bb799350338e5f29e39cdc59bd28077 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 14:32:54 +0100 Subject: [PATCH 105/463] Used comprehension Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e1d83a175e..e3470f4c41 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -507,11 +507,10 @@ def _get_real_files_to_rendered(collection, frames_to_render): return files found_frame_pattern_length = len(extracted_frame) - normalized_frames_to_render = set() - for frame_to_render in frames_to_render: - normalized_frames_to_render.add( - str(frame_to_render).zfill(found_frame_pattern_length) - ) + normalized_frames_to_render = { + str(frame_to_render).zfill(found_frame_pattern_length) + for frame_to_render in frames_to_render + } filtered_files = [] for file_name in files: From 112c4bdc0eec32f7d140964a911d88c5926a27e7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Nov 2024 14:33:08 +0100 Subject: [PATCH 106/463] Used comprehension Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../ayon_core/pipeline/farm/pyblish_functions.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e3470f4c41..16364a17ee 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -512,14 +512,14 @@ def _get_real_files_to_rendered(collection, frames_to_render): for frame_to_render in frames_to_render } - filtered_files = [] - for file_name in files: - if any(frame in file_name - for frame in normalized_frames_to_render): - filtered_files.append(file_name) - - files = filtered_files - return files + return [ + file_name + for file_name in files + if any( + frame in file_name + for frame in normalized_frames_to_render + ) + ] def create_instances_for_aov(instance, skeleton, aov_filter, From c1b83046b7d7bfc3d0284f1134d4966bebda872a Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 21 Nov 2024 13:33:12 +0000 Subject: [PATCH 107/463] [Automated] Add generated package files to main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 63f7de04dc..e75a940be9 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.8+dev" +__version__ = "1.0.9" diff --git a/package.py b/package.py index bbfcc51019..c4da1ded1e 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.8+dev" +version = "1.0.9" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index e29aa08c6d..1a7882bdb5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.8+dev" +version = "1.0.9" description = "" authors = ["Ynput Team "] readme = "README.md" From 8bfcd92f1c7383bd069c7e259f3ebcdbeb8cab41 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 21 Nov 2024 13:33:50 +0000 Subject: [PATCH 108/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index e75a940be9..ab8c9424fa 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.9" +__version__ = "1.0.9+dev" diff --git a/package.py b/package.py index c4da1ded1e..b90db4cde4 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.9" +version = "1.0.9+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 1a7882bdb5..f2d09d925d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.9" +version = "1.0.9+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From ddf0a2b00a6cad3677924ec25ef6341daeb92001 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:00:43 +0100 Subject: [PATCH 109/463] force to use older opencolorio than 2.4.0 --- client/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pyproject.toml b/client/pyproject.toml index a0be9605b6..20b51ff219 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -15,6 +15,6 @@ qtawesome = "0.7.3" aiohttp-middlewares = "^2.0.0" Click = "^8" OpenTimelineIO = "0.16.0" -opencolorio = "^2.3.2" +opencolorio = "<2.4.0" Pillow = "9.5.0" websocket-client = ">=0.40.0,<2" From 5b35547072b89f25c4c53d41612fa7d5b27b1d6f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:10:57 +0100 Subject: [PATCH 110/463] fix syntax of version requirement --- client/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/pyproject.toml b/client/pyproject.toml index 20b51ff219..edf7f57317 100644 --- a/client/pyproject.toml +++ b/client/pyproject.toml @@ -15,6 +15,6 @@ qtawesome = "0.7.3" aiohttp-middlewares = "^2.0.0" Click = "^8" OpenTimelineIO = "0.16.0" -opencolorio = "<2.4.0" +opencolorio = "^2.3.2,<2.4.0" Pillow = "9.5.0" websocket-client = ">=0.40.0,<2" From a70135bb5156fc3883047561edcda702f81f8731 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Nov 2024 22:35:25 +0800 Subject: [PATCH 111/463] implement switch and set version by repre_id/product_id per project --- .../ayon_core/tools/sceneinventory/model.py | 20 +- .../tools/sceneinventory/models/containers.py | 11 +- client/ayon_core/tools/sceneinventory/view.py | 190 +++++++++--------- 3 files changed, 113 insertions(+), 108 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 640a8017ab..75af957cfa 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -133,24 +133,26 @@ class InventoryModel(QtGui.QStandardItemModel): container_items = self._controller.get_container_items() self._clear_items() repre_id = set() + repre_ids_by_project = collections.defaultdict(set) version_items_by_product_id = collections.defaultdict(dict) - repre_info_by_id_by_project = collections.defaultdict(list) + repre_info_by_id_by_project = collections.defaultdict(dict) item_by_repre_id_by_project_id = collections.defaultdict( lambda: collections.defaultdict(set)) - for project_name, container_item in container_items.items(): + for container_item in container_items: # if ( # selected is not None # and container_item.item_id not in selected # ): # continue - for item in container_item.values(): - representation_id = item.representation_id - if item.project_name != project_name: - continue - repre_id.add(representation_id) - item_by_repre_id_by_project_id[project_name][representation_id].add(item) + project_name = container_item.project_name + representation_id = container_item.representation_id + repre_id.add(representation_id) + repre_ids_by_project[project_name].add(representation_id) + item_by_repre_id_by_project_id[project_name][representation_id].add(container_item) + + for project_name, representation_ids in repre_ids_by_project.items(): repre_info = self._controller.get_representation_info_items( - project_name, repre_id + project_name, representation_ids ) repre_info_by_id_by_project[project_name] = repre_info diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index ca67fb59f9..dc41bdc8fa 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -191,7 +191,7 @@ class VersionItem: class ContainersModel: def __init__(self, controller): self._controller = controller - self._project_cache = None + self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} self._container_items_by_project = {} @@ -200,7 +200,7 @@ class ContainersModel: self._product_ids_by_project = {} def reset(self): - self._project_cache = None + self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} self._container_items_by_project = {} @@ -220,7 +220,7 @@ class ContainersModel: def get_container_items(self): self._update_cache() - return self._project_cache + return list(self._items_cache) def get_container_items_by_id(self, item_ids): return { @@ -344,7 +344,7 @@ class ContainersModel: } def _update_cache(self): - if self._project_cache is not None: + if self._items_cache is not None: return host = self._controller.get_host() @@ -358,7 +358,6 @@ class ContainersModel: container_items = [] containers_by_id = {} container_items_by_id = {} - project_cache = collections.defaultdict(dict) invalid_ids_mapping = {} for container in containers: try: @@ -383,10 +382,8 @@ class ContainersModel: containers_by_id[item.item_id] = container container_items_by_id[item.item_id] = item - project_cache[item.project_name] = container_items_by_id container_items.append(item) self._containers_by_id = containers_by_id self._container_items_by_id = container_items_by_id self._items_cache = container_items - self._project_cache = project_cache diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index a049fd1e0b..24e0195e31 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -197,26 +197,29 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_id = container_item.representation_id project_name = container_item.project_name repre_ids_by_project[project_name].add(repre_id) - repre_info_by_id = {} + + repre_info_by_project = collections.defaultdict(dict) for project_name, repre_ids in repre_ids_by_project.items(): repre_info = self._controller.get_representation_info_items( project_name, repre_ids) - repre_info_by_id.update(repre_info) - - valid_repre_ids = { - repre_id - for repre_id, repre_info in repre_info_by_id.items() - if repre_info.is_valid - } - + repre_info_by_project[project_name].update(repre_info) # Exclude items that are "NOT FOUND" since setting versions, updating # and removal won't work for those items. filtered_items = [] - product_ids_by_project = collections.defaultdict(set) version_ids = set() + valid_repre_ids = set() + product_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): - repre_id = container_item.representation_id project_name = container_item.project_name + repre_info_by_id = repre_info_by_project.get(project_name) + repre_id = container_item.representation_id + all_valid_repre_ids = { + repre_id + for repre_id, repre_info in repre_info_by_id.items() + if repre_info.is_valid + } + valid_repre_ids.update(all_valid_repre_ids) + repre_info = repre_info_by_id.get(repre_id) if repre_info and repre_info.is_valid: filtered_items.append(container_item) @@ -233,47 +236,47 @@ class SceneInventoryView(QtWidgets.QTreeView): # Keep remove action for invalid items menu.addAction(remove_action) return - version_items_by_product_id = {} + version_items_by_product_id_by_project = collections.defaultdict(dict) for project_name, product_ids in product_ids_by_project.items(): version_items = self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id.update(version_items -) + version_items_by_product_id_by_project[project_name] = version_items has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False has_outdated_approved = False last_version_by_product_id = {} - for product_id, version_items_by_id in ( - version_items_by_product_id.items() - ): - _has_outdated_approved = False - _last_approved_version_item = None - for version_item in version_items_by_id.values(): - if version_item.is_hero: - has_available_hero_version = True - - elif version_item.is_last_approved: - _last_approved_version_item = version_item - _has_outdated_approved = True - - if version_item.version_id not in version_ids: - continue - - if version_item.is_hero: - has_loaded_hero_versions = True - elif not version_item.is_latest: - has_outdated = True - - if ( - _has_outdated_approved - and _last_approved_version_item is not None + for version_items_by_product_id in version_items_by_product_id_by_project.values(): + for product_id, version_items_by_id in ( + version_items_by_product_id.items() ): - last_version_by_product_id[product_id] = ( - _last_approved_version_item - ) - has_outdated_approved = True + _has_outdated_approved = False + _last_approved_version_item = None + for version_item in version_items_by_id.values(): + if version_item.is_hero: + has_available_hero_version = True + + elif version_item.is_last_approved: + _last_approved_version_item = version_item + _has_outdated_approved = True + + if version_item.version_id not in version_ids: + continue + + if version_item.is_hero: + has_loaded_hero_versions = True + elif not version_item.is_latest: + has_outdated = True + + if ( + _has_outdated_approved + and _last_approved_version_item is not None + ): + last_version_by_product_id[product_id] = ( + _last_approved_version_item + ) + has_outdated_approved = True switch_to_versioned = None if has_loaded_hero_versions: @@ -294,8 +297,9 @@ class SceneInventoryView(QtWidgets.QTreeView): approved_version_by_item_id = {} if has_outdated_approved: for container_item in container_items_by_id.values(): + project_name = container_item.project_name repre_id = container_item.representation_id - repre_info = repre_info_by_id.get(repre_id) + repre_info = repre_info_by_project[project_name][repre_id] if not repre_info or not repre_info.is_valid: continue version_item = last_version_by_product_id.get( @@ -750,54 +754,53 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_id = container_item.representation_id project_name = container_item.project_name repre_ids_by_project[project_name].add(repre_id) - repre_info_by_id = {} + + versions = set() + repre_info_by_project = collections.defaultdict(dict) + version_items_by_product_id_by_project = collections.defaultdict(dict) for project_name, repre_ids in repre_ids_by_project.items(): repre_info = self._controller.get_representation_info_items( project_name, repre_ids ) - repre_info_by_id.update(repre_info) + repre_info_by_project[project_name].update(repre_info) - product_ids = { - repre_info.product_id - for repre_info in repre_info_by_id.values() - } - product_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): - repre_id = container_item.representation_id project_name = container_item.project_name + repre_info_by_id = repre_info_by_project.get(project_name) + repre_id = container_item.representation_id repre_info = repre_info_by_id.get(repre_id) - if not repre_info or not repre_info.is_valid: - continue - product_ids_by_project[project_name].add( + product_ids = { repre_info.product_id - ) - active_repre_info = repre_info_by_id[active_repre_id] - active_version_id = active_repre_info.version_id - active_product_id = active_repre_info.product_id - version_items_by_product_id = {} - for project_name, project_product_ids in product_ids_by_project.items(): + for repre_info in repre_info_by_id.values() + if repre_info.is_valid + } version_items = self._controller.get_version_items( - project_name, project_product_ids + project_name, product_ids ) - version_items_by_product_id.update(version_items) - version_items = list( - version_items_by_product_id[active_product_id].values() - ) - versions = {version_item.version for version_item in version_items} + version_items_by_product_id_by_project[project_name] = version_items + active_repre_info = repre_info_by_id[active_repre_id] + active_version_id = active_repre_info.version_id + active_product_id = active_repre_info.product_id + version_items = list( + version_items_by_product_id_by_project[project_name][active_product_id].values() + ) + all_versions = {version_item.version for version_item in version_items} + versions.update(all_versions) product_ids_by_version = collections.defaultdict(set) - for version_items_by_id in version_items_by_product_id.values(): - for version_item in version_items_by_id.values(): - version = version_item.version - _prod_version = version - if _prod_version < 0: - _prod_version = -1 - product_ids_by_version[_prod_version].add( - version_item.product_id - ) - if version in versions: - continue - versions.add(version) - version_items.append(version_item) + for version_items_by_product_id in version_items_by_product_id_by_project.values(): + for version_items_by_id in version_items_by_product_id.values(): + for version_item in version_items_by_id.values(): + version = version_item.version + _prod_version = version + if _prod_version < 0: + _prod_version = -1 + product_ids_by_version[_prod_version].add( + version_item.product_id + ) + if version in versions: + continue + versions.add(version) + version_items.append(version_item) def version_sorter(item): hero_value = 0 @@ -862,8 +865,9 @@ class SceneInventoryView(QtWidgets.QTreeView): filtered_item_ids = set() for container_item in container_items_by_id.values(): + project_name = container_item.project_name repre_id = container_item.representation_id - repre_info = repre_info_by_id[repre_id] + repre_info = repre_info_by_project[project_name][repre_id] if repre_info.product_id in product_ids: filtered_item_ids.add(container_item.item_id) @@ -964,37 +968,39 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_ids_by_project[project_name].add(repre_id) # Get representation info items by ID - repre_info_by_id = {} + repre_info_by_project = collections.defaultdict(dict) for project_name, repre_ids in repre_ids_by_project.items(): repre_info = self._controller.get_representation_info_items( project_name, repre_ids) - repre_info_by_id.update(repre_info) + repre_info_by_project[project_name].update(repre_info) product_ids_by_project = collections.defaultdict(set) + version_items_by_product_id_by_project = collections.defaultdict(dict) for container_item in containers_items_by_id.values(): - repre_id = container_item.representation_id project_name = container_item.project_name + repre_info_by_id = repre_info_by_project.get(project_name) + repre_id = container_item.representation_id repre_info = repre_info_by_id.get(repre_id) - if not repre_info or not repre_info.is_valid: - continue - product_ids_by_project[project_name].add( + product_ids = { repre_info.product_id - ) - - version_items_by_product_id = {} - for project_name, product_ids in product_ids_by_project.items(): + for repre_info in repre_info_by_id.values() + if repre_info.is_valid + } version_items = self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id.update(version_items) + version_items_by_product_id_by_project[project_name] = version_items update_containers = [] update_versions = [] for item_id, container_item in containers_items_by_id.items(): repre_id = container_item.representation_id + project_name = container_item.project_name repre_info = repre_info_by_id[repre_id] product_id = repre_info.product_id - version_items_id = version_items_by_product_id[product_id] + version_items_id = ( + version_items_by_product_id_by_project[project_name][product_id] + ) version_item = version_items_id.get(repre_info.version_id, {}) if not version_item or not version_item.is_hero: continue From 77e5317ee3bae6b2aa792a9525cc3433e24ddeca Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Nov 2024 22:37:55 +0800 Subject: [PATCH 112/463] remove unused variable --- client/ayon_core/tools/sceneinventory/view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 24e0195e31..025bff6e9f 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -974,7 +974,6 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name, repre_ids) repre_info_by_project[project_name].update(repre_info) - product_ids_by_project = collections.defaultdict(set) version_items_by_product_id_by_project = collections.defaultdict(dict) for container_item in containers_items_by_id.values(): project_name = container_item.project_name From 26251bb9b4ebae496ab7aab0a7a3e0a1a95c1611 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:29:31 +0100 Subject: [PATCH 113/463] simplified parsing of template --- client/ayon_core/lib/path_templates.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index dc88ec956b..1a99ae459d 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -1,6 +1,7 @@ import os import re import numbers +from string import Formatter KEY_PATTERN = re.compile(r"(\{.*?[^{0]*\})") KEY_PADDING_PATTERN = re.compile(r"([^:]+)\S+[><]\S+") @@ -48,16 +49,16 @@ class StringTemplate: self._template = template parts = [] - last_end_idx = 0 - for item in KEY_PATTERN.finditer(template): - start, end = item.span() - if start > last_end_idx: - parts.append(template[last_end_idx:start]) - parts.append(FormattingPart(template[start:end])) - last_end_idx = end + formatter = Formatter() - if last_end_idx < len(template): - parts.append(template[last_end_idx:len(template)]) + for item in formatter.parse(template): + literal_text, field_name, format_spec, conversion = item + if literal_text: + parts.append(literal_text) + if field_name: + parts.append( + FormattingPart(field_name, format_spec, conversion) + ) new_parts = [] for part in parts: From e4875cc5096a5381861b45f45c28eb6c2a68215e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:37:34 +0100 Subject: [PATCH 114/463] fill FormattingPart init --- client/ayon_core/lib/path_templates.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index 1a99ae459d..3871b97849 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -437,8 +437,21 @@ class FormattingPart: Args: template(str): String containing the formatting key. """ - def __init__(self, template): - self._template = template + def __init__(self, field_name, format_spec, conversion): + format_spec_v = "" + if format_spec: + format_spec_v = f":{format_spec}" + conversion_v = "" + if conversion: + conversion_v = f"!{conversion}" + + self._field_name = field_name + self._format_spec = format_spec_v + self._conversion = conversion_v + + template_base = f"{field_name}{format_spec_v}{conversion_v}" + self._template_base = template_base + self._template = f"{{{template_base}}}" @property def template(self): From d15148f001f17f4488119c402a54005be34e0d38 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:38:49 +0100 Subject: [PATCH 115/463] support list in StringTemplate --- client/ayon_core/lib/path_templates.py | 122 +++++++++++++++++-------- 1 file changed, 82 insertions(+), 40 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index 3871b97849..9b545f2851 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -1,10 +1,10 @@ import os import re +import copy import numbers +from typing import List from string import Formatter -KEY_PATTERN = re.compile(r"(\{.*?[^{0]*\})") -KEY_PADDING_PATTERN = re.compile(r"([^:]+)\S+[><]\S+") SUB_DICT_PATTERN = re.compile(r"([^\[\]]+)") OPTIONAL_PATTERN = re.compile(r"(<.*?[^{0]*>)[^0-9]*?") @@ -369,11 +369,10 @@ class TemplatePartResult: @staticmethod def split_keys_to_subdicts(values): output = {} + formatter = Formatter() for key, value in values.items(): - key_padding = list(KEY_PADDING_PATTERN.findall(key)) - if key_padding: - key = key_padding[0] - key_subdict = list(SUB_DICT_PATTERN.findall(key)) + _, field_name, _, _ = next(formatter.parse(f"{{{key}}}")) + key_subdict = list(SUB_DICT_PATTERN.findall(field_name)) data = output last_key = key_subdict.pop(-1) for subkey in key_subdict: @@ -502,6 +501,16 @@ class FormattingPart: return False return not queue + @staticmethod + def keys_to_template_base(keys: List[str]): + if not keys: + return None + # Create copy of keys + keys = list(keys) + template_base = keys.pop(0) + joined_keys = "".join([f"[{key}]" for key in keys]) + return f"{template_base}{joined_keys}" + def format(self, data, result): """Format the formattings string. @@ -509,7 +518,7 @@ class FormattingPart: data(dict): Data that should be used for formatting. result(TemplatePartResult): Object where result is stored. """ - key = self.template[1:-1] + key = self._template_base if key in result.realy_used_values: result.add_output(result.realy_used_values[key]) return result @@ -521,17 +530,38 @@ class FormattingPart: return result # check if key expects subdictionary keys (e.g. project[name]) - existence_check = key - key_padding = list(KEY_PADDING_PATTERN.findall(existence_check)) - if key_padding: - existence_check = key_padding[0] - key_subdict = list(SUB_DICT_PATTERN.findall(existence_check)) + key_subdict = list(SUB_DICT_PATTERN.findall(self._field_name)) value = data missing_key = False invalid_type = False used_keys = [] + keys_to_value = None + used_value = None + for sub_key in key_subdict: + if isinstance(value, list): + if not sub_key.lstrip("-").isdigit(): + invalid_type = True + break + sub_key = int(sub_key) + if sub_key < 0: + sub_key = len(value) + sub_key + + invalid = 0 > sub_key < len(data) + if invalid: + used_keys.append(sub_key) + missing_key = True + break + + used_keys.append(sub_key) + if keys_to_value is None: + keys_to_value = list(used_keys) + keys_to_value.pop(-1) + used_value = copy.deepcopy(value) + value = value[sub_key] + continue + if ( value is None or (hasattr(value, "items") and sub_key not in value) @@ -547,45 +577,57 @@ class FormattingPart: used_keys.append(sub_key) value = value.get(sub_key) - if missing_key or invalid_type: - if len(used_keys) == 0: - invalid_key = key_subdict[0] - else: - invalid_key = used_keys[0] - for idx, sub_key in enumerate(used_keys): - if idx == 0: - continue - invalid_key += "[{0}]".format(sub_key) + field_name = key_subdict[0] + if used_keys: + field_name = self.keys_to_template_base(used_keys) + if missing_key or invalid_type: if missing_key: - result.add_missing_key(invalid_key) + result.add_missing_key(field_name) elif invalid_type: - result.add_invalid_type(invalid_key, value) + result.add_invalid_type(field_name, value) result.add_output(self.template) return result - if self.validate_value_type(value): - fill_data = {} - first_value = True - for used_key in reversed(used_keys): - if first_value: - first_value = False - fill_data[used_key] = value - else: - _fill_data = {used_key: fill_data} - fill_data = _fill_data - - formatted_value = self.template.format(**fill_data) - result.add_realy_used_value(key, formatted_value) - result.add_used_value(existence_check, formatted_value) - result.add_output(formatted_value) + if not self.validate_value_type(value): + result.add_invalid_type(key, value) + result.add_output(self.template) return result - result.add_invalid_type(key, value) - result.add_output(self.template) + fill_data = root_fill_data = {} + parent_fill_data = None + parent_key = None + fill_value = data + value_filled = False + for used_key in used_keys: + if isinstance(fill_value, list): + parent_fill_data[parent_key] = fill_value + value_filled = True + break + fill_value = fill_value[used_key] + parent_fill_data = fill_data + fill_data = parent_fill_data.setdefault(used_key, {}) + parent_key = used_key + if not value_filled: + parent_fill_data[used_keys[-1]] = value + + template = f"{{{field_name}{self._format_spec}{self._conversion}}}" + formatted_value = template.format(**root_fill_data) + used_key = key + if keys_to_value is not None: + used_key = self.keys_to_template_base(keys_to_value) + + if used_value is None: + if isinstance(value, numbers.Number): + used_value = value + else: + used_value = formatted_value + result.add_realy_used_value(self._field_name, used_value) + result.add_used_value(used_key, used_value) + result.add_output(formatted_value) return result From e625901aebe0bab4a97e11ca4725c95ac29a86dc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 22 Nov 2024 16:05:17 +0800 Subject: [PATCH 116/463] big roy's comment - code tweak --- client/ayon_core/tools/sceneinventory/model.py | 6 +++--- .../tools/sceneinventory/models/containers.py | 2 +- client/ayon_core/tools/sceneinventory/view.py | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 75af957cfa..849d8b8d17 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -132,7 +132,7 @@ class InventoryModel(QtGui.QStandardItemModel): # for debugging or testing, injecting items from outside container_items = self._controller.get_container_items() self._clear_items() - repre_id = set() + repre_ids = set() repre_ids_by_project = collections.defaultdict(set) version_items_by_product_id = collections.defaultdict(dict) repre_info_by_id_by_project = collections.defaultdict(dict) @@ -146,7 +146,7 @@ class InventoryModel(QtGui.QStandardItemModel): # continue project_name = container_item.project_name representation_id = container_item.representation_id - repre_id.add(representation_id) + repre_ids.add(representation_id) repre_ids_by_project[project_name].add(representation_id) item_by_repre_id_by_project_id[project_name][representation_id].add(container_item) @@ -168,7 +168,7 @@ class InventoryModel(QtGui.QStandardItemModel): # SiteSync addon information progress_by_id = self._controller.get_representations_site_progress( - repre_id + repre_ids ) sites_info = self._controller.get_sites_information() site_icons = { diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index dc41bdc8fa..aea94b97ef 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -359,9 +359,9 @@ class ContainersModel: containers_by_id = {} container_items_by_id = {} invalid_ids_mapping = {} + current_project_name = self._controller.get_current_project_name() for container in containers: try: - current_project_name = self._controller.get_current_project_name() item = ContainerItem.from_container_data(current_project_name, container) repre_id = item.representation_id try: diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 025bff6e9f..d4381f55cd 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -755,7 +755,7 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = container_item.project_name repre_ids_by_project[project_name].add(repre_id) - versions = set() + version_ids = set() repre_info_by_project = collections.defaultdict(dict) version_items_by_product_id_by_project = collections.defaultdict(dict) for project_name, repre_ids in repre_ids_by_project.items(): @@ -784,8 +784,8 @@ class SceneInventoryView(QtWidgets.QTreeView): version_items = list( version_items_by_product_id_by_project[project_name][active_product_id].values() ) - all_versions = {version_item.version for version_item in version_items} - versions.update(all_versions) + version_ids.update(version_item.version for version_item in version_items) + product_ids_by_version = collections.defaultdict(set) for version_items_by_product_id in version_items_by_product_id_by_project.values(): for version_items_by_id in version_items_by_product_id.values(): @@ -797,9 +797,9 @@ class SceneInventoryView(QtWidgets.QTreeView): product_ids_by_version[_prod_version].add( version_item.product_id ) - if version in versions: + if version in version_ids: continue - versions.add(version) + version_ids.add(version) version_items.append(version_item) def version_sorter(item): From 842033ddc65e30e6f5d877be5dc57624d7e8c1b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:55:39 +0100 Subject: [PATCH 117/463] use 'folderPath' to calculate 'hierarchy' --- .../plugins/publish/collect_anatomy_instance_data.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index a0bd57d7dc..abd64ec03d 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -413,14 +413,16 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # Backwards compatible (Deprecated since 24/06/06) or instance.data.get("newAssetPublishing") ): - hierarchy = instance.data["hierarchy"] - anatomy_data["hierarchy"] = hierarchy + folder_path = instance.data["folderPath"] + parents = folder_path.lstrip("/").split("/") + folder_name = parents.pop(-1) parent_name = project_entity["name"] - if hierarchy: - parent_name = hierarchy.split("/")[-1] + hierarchy = "" + if parents: + parent_name = parents[-1] + hierarchy = "/".join(parents) - folder_name = instance.data["folderPath"].split("/")[-1] anatomy_data.update({ "asset": folder_name, "hierarchy": hierarchy, From 7edc759842024453cf60702b1bf3b5c431c96fae Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:57:49 +0100 Subject: [PATCH 118/463] remove unused variables --- client/ayon_core/tools/sceneinventory/models/containers.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index aea94b97ef..ad78061468 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -194,19 +194,15 @@ class ContainersModel: self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} - self._container_items_by_project = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} - self._product_ids_by_project = {} def reset(self): self._items_cache = None self._containers_by_id = {} self._container_items_by_id = {} - self._container_items_by_project = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} - self._product_ids_by_project = {} def get_containers(self): self._update_cache() From ef26dc2dc2cb50957710e25c2e7cc9aacad8d7da Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:58:02 +0100 Subject: [PATCH 119/463] added empty lines for readability --- client/ayon_core/tools/sceneinventory/models/containers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index ad78061468..b1cbb38587 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -294,10 +294,12 @@ class ContainersModel: def version_sorted(entity): return entity["version"] + version_entities_by_product_id = { product_id: [] for product_id in missing_ids } + version_entities = list(ayon_api.get_versions( project_name, product_ids=missing_ids, @@ -309,6 +311,7 @@ class ContainersModel: version_entities_by_product_id[product_id].append( version_entity ) + for product_id, version_entities in ( version_entities_by_product_id.items() ): @@ -334,6 +337,7 @@ class ContainersModel: self._version_items_by_product_id[product_id] = ( version_items_by_id ) + return { product_id: dict(self._version_items_by_product_id[product_id]) for product_id in product_ids From 7935ed3284fca091eed45eb13acfd3ea456e0145 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:58:23 +0100 Subject: [PATCH 120/463] site sync expects project name --- client/ayon_core/tools/sceneinventory/control.py | 14 ++++++++++---- .../tools/sceneinventory/models/sitesync.py | 10 +++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 8c02881b82..12dfc72e77 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -124,14 +124,20 @@ class SceneInventoryController: def get_site_provider_icons(self): return self._sitesync_model.get_site_provider_icons() - def get_representations_site_progress(self, representation_ids): + def get_representations_site_progress( + self, project_name, representation_ids + ): return self._sitesync_model.get_representations_site_progress( - representation_ids + project_name, representation_ids ) - def resync_representations(self, representation_ids, site_type): + def resync_representations( + self, project_name, representation_ids, site_type + ): return self._sitesync_model.resync_representations( - representation_ids, site_type + project_name, + representation_ids, + site_type ) # Switch dialog methods diff --git a/client/ayon_core/tools/sceneinventory/models/sitesync.py b/client/ayon_core/tools/sceneinventory/models/sitesync.py index 1a1f08bf02..1738ec2c15 100644 --- a/client/ayon_core/tools/sceneinventory/models/sitesync.py +++ b/client/ayon_core/tools/sceneinventory/models/sitesync.py @@ -54,7 +54,9 @@ class SiteSyncModel: "remote_site_provider": self._get_remote_site_provider() } - def get_representations_site_progress(self, representation_ids): + def get_representations_site_progress( + self, project_name, representation_ids + ): """Get progress of representations sync.""" representation_ids = set(representation_ids) @@ -68,7 +70,6 @@ class SiteSyncModel: if not self.is_sitesync_enabled(): return output - project_name = self._controller.get_current_project_name() sitesync_addon = self._get_sitesync_addon() repre_entities = ayon_api.get_representations( project_name, representation_ids @@ -86,10 +87,13 @@ class SiteSyncModel: return output - def resync_representations(self, representation_ids, site_type): + def resync_representations( + self, project_name, representation_ids, site_type + ): """ Args: + project_name (str): Project name. representation_ids (Iterable[str]): Representation ids. site_type (Literal[active_site, remote_site]): Site type. """ From b25715ee2bfc801486da97259890eb6425e1e35d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:59:47 +0100 Subject: [PATCH 121/463] use project name in site sync calls --- .../ayon_core/tools/sceneinventory/model.py | 13 +++++---- .../tools/sceneinventory/models/sitesync.py | 2 +- client/ayon_core/tools/sceneinventory/view.py | 29 +++++++++++++------ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 849d8b8d17..cf9814a8e1 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -132,7 +132,6 @@ class InventoryModel(QtGui.QStandardItemModel): # for debugging or testing, injecting items from outside container_items = self._controller.get_container_items() self._clear_items() - repre_ids = set() repre_ids_by_project = collections.defaultdict(set) version_items_by_product_id = collections.defaultdict(dict) repre_info_by_id_by_project = collections.defaultdict(dict) @@ -146,7 +145,6 @@ class InventoryModel(QtGui.QStandardItemModel): # continue project_name = container_item.project_name representation_id = container_item.representation_id - repre_ids.add(representation_id) repre_ids_by_project[project_name].add(representation_id) item_by_repre_id_by_project_id[project_name][representation_id].add(container_item) @@ -167,9 +165,13 @@ class InventoryModel(QtGui.QStandardItemModel): version_items_by_product_id[project_name] = version_items # SiteSync addon information - progress_by_id = self._controller.get_representations_site_progress( - repre_ids - ) + progress_by_project = {} + for project_name, repre_ids in repre_ids_by_project.items(): + progress_by_id = self._controller.get_representations_site_progress( + project_name, repre_ids + ) + progress_by_project[project_name] = progress_by_id + sites_info = self._controller.get_sites_information() site_icons = { provider: get_qt_icon(icon_def) @@ -207,6 +209,7 @@ class InventoryModel(QtGui.QStandardItemModel): root_item = self.invisibleRootItem() group_items = [] for project_name, items_by_repre_id in item_by_repre_id_by_project_id.items(): + progress_by_id = progress_by_project[project_name] for repre_id, container_items in items_by_repre_id.items(): repre_info = repre_info_by_id_by_project[project_name][repre_id] version_color = None diff --git a/client/ayon_core/tools/sceneinventory/models/sitesync.py b/client/ayon_core/tools/sceneinventory/models/sitesync.py index 1738ec2c15..c8e1ac2cd3 100644 --- a/client/ayon_core/tools/sceneinventory/models/sitesync.py +++ b/client/ayon_core/tools/sceneinventory/models/sitesync.py @@ -103,7 +103,7 @@ class SiteSyncModel: active_site = self._get_active_site() remote_site = self._get_remote_site() progress = self.get_representations_site_progress( - representation_ids + project_name, representation_ids ) for repre_id in representation_ids: repre_progress = progress.get(repre_id) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index d4381f55cd..9bd2d65cd0 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -413,12 +413,13 @@ class SceneInventoryView(QtWidgets.QTreeView): self._handle_sitesync(menu, valid_repre_ids) - def _handle_sitesync(self, menu, repre_ids): + def _handle_sitesync(self, menu, repre_ids_by_project_name): """Adds actions for download/upload when SyncServer is enabled Args: menu (OptionMenu) - repre_ids (list) of object_ids + repre_ids_by_project_name (Dict[str, Set[str]]): Representation + ids by project name. Returns: (OptionMenu) @@ -427,7 +428,7 @@ class SceneInventoryView(QtWidgets.QTreeView): if not self._controller.is_sitesync_enabled(): return - if not repre_ids: + if not repre_ids_by_project_name: return menu.addSeparator() @@ -439,7 +440,10 @@ class SceneInventoryView(QtWidgets.QTreeView): menu ) download_active_action.triggered.connect( - lambda: self._add_sites(repre_ids, "active_site")) + lambda: self._add_sites( + repre_ids_by_project_name, "active_site" + ) + ) upload_icon = qtawesome.icon("fa.upload", color=DEFAULT_COLOR) upload_remote_action = QtWidgets.QAction( @@ -448,23 +452,30 @@ class SceneInventoryView(QtWidgets.QTreeView): menu ) upload_remote_action.triggered.connect( - lambda: self._add_sites(repre_ids, "remote_site")) + lambda: self._add_sites( + repre_ids_by_project_name, "remote_site" + ) + ) menu.addAction(download_active_action) menu.addAction(upload_remote_action) - def _add_sites(self, repre_ids, site_type): + def _add_sites(self, repre_ids_by_project_name, site_type): """(Re)sync all 'repre_ids' to specific site. It checks if opposite site has fully available content to limit accidents. (ReSync active when no remote >> losing active content) Args: - repre_ids (list) + repre_ids_by_project_name (Dict[str, Set[str]]): Representation + ids by project name. site_type (Literal[active_site, remote_site]): Site type. - """ - self._controller.resync_representations(repre_ids, site_type) + """ + for project_name, repre_ids in repre_ids_by_project_name.items(): + self._controller.resync_representations( + project_name, repre_ids, site_type + ) self.data_changed.emit() From a30698eb4b12381ec7c01f7ca1c98ae0e12fab3c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:02:49 +0100 Subject: [PATCH 122/463] refactor view codebase --- client/ayon_core/tools/sceneinventory/view.py | 105 ++++++++++-------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 9bd2d65cd0..b3322ffc60 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -198,34 +198,41 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = container_item.project_name repre_ids_by_project[project_name].add(repre_id) - repre_info_by_project = collections.defaultdict(dict) + repre_info_by_project = {} + repre_ids_by_project_name = {} + version_ids_by_project = {} + product_ids_by_project = {} for project_name, repre_ids in repre_ids_by_project.items(): - repre_info = self._controller.get_representation_info_items( - project_name, repre_ids) - repre_info_by_project[project_name].update(repre_info) + repres_info = self._controller.get_representation_info_items( + project_name, repre_ids + ) + + repre_info_by_project[project_name] = repres_info + repre_ids = set() + version_ids = set() + product_ids = set() + for repre_id, repre_info in repres_info.items(): + if not repre_info.is_valid: + continue + repre_ids.add(repre_id) + version_ids.add(repre_info.version_id) + product_ids.add(repre_info.product_id) + + repre_ids_by_project_name[project_name] = repre_ids + version_ids_by_project[project_name] = version_ids + product_ids_by_project[project_name] = product_ids + # Exclude items that are "NOT FOUND" since setting versions, updating # and removal won't work for those items. filtered_items = [] - version_ids = set() - valid_repre_ids = set() - product_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): project_name = container_item.project_name - repre_info_by_id = repre_info_by_project.get(project_name) repre_id = container_item.representation_id - all_valid_repre_ids = { - repre_id - for repre_id, repre_info in repre_info_by_id.items() - if repre_info.is_valid - } - valid_repre_ids.update(all_valid_repre_ids) - + repre_info_by_id = repre_info_by_project.get(project_name, {}) repre_info = repre_info_by_id.get(repre_id) if repre_info and repre_info.is_valid: filtered_items.append(container_item) - version_ids.add(repre_info.version_id) - product_id = repre_info.product_id - product_ids_by_project[project_name].add(product_id) + # remove remove_icon = qtawesome.icon("fa.remove", color=DEFAULT_COLOR) remove_action = QtWidgets.QAction(remove_icon, "Remove items", menu) @@ -236,18 +243,23 @@ class SceneInventoryView(QtWidgets.QTreeView): # Keep remove action for invalid items menu.addAction(remove_action) return - version_items_by_product_id_by_project = collections.defaultdict(dict) - for project_name, product_ids in product_ids_by_project.items(): - version_items = self._controller.get_version_items( + + version_items_by_project = { + project_name: self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id_by_project[project_name] = version_items + for project_name, product_ids in product_ids_by_project.items() + } + has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False has_outdated_approved = False last_version_by_product_id = {} - for version_items_by_product_id in version_items_by_product_id_by_project.values(): + for project_name, version_items_by_product_id in ( + version_items_by_project.items() + ): + version_ids = version_ids_by_project[project_name] for product_id, version_items_by_id in ( version_items_by_product_id.items() ): @@ -411,7 +423,7 @@ class SceneInventoryView(QtWidgets.QTreeView): menu.addAction(remove_action) - self._handle_sitesync(menu, valid_repre_ids) + self._handle_sitesync(menu, repre_ids_by_project_name) def _handle_sitesync(self, menu, repre_ids_by_project_name): """Adds actions for download/upload when SyncServer is enabled @@ -979,44 +991,47 @@ class SceneInventoryView(QtWidgets.QTreeView): repre_ids_by_project[project_name].add(repre_id) # Get representation info items by ID - repre_info_by_project = collections.defaultdict(dict) + repres_info_by_project = {} + version_items_by_project = {} for project_name, repre_ids in repre_ids_by_project.items(): - repre_info = self._controller.get_representation_info_items( - project_name, repre_ids) - repre_info_by_project[project_name].update(repre_info) + repre_info_by_id = self._controller.get_representation_info_items( + project_name, repre_ids + ) + repres_info_by_project[project_name] = repre_info_by_id - version_items_by_product_id_by_project = collections.defaultdict(dict) - for container_item in containers_items_by_id.values(): - project_name = container_item.project_name - repre_info_by_id = repre_info_by_project.get(project_name) - repre_id = container_item.representation_id - repre_info = repre_info_by_id.get(repre_id) product_ids = { repre_info.product_id for repre_info in repre_info_by_id.values() if repre_info.is_valid } - version_items = self._controller.get_version_items( + version_items_by_product_id = self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id_by_project[project_name] = version_items + version_items_by_project[project_name] = ( + version_items_by_product_id + ) update_containers = [] update_versions = [] - for item_id, container_item in containers_items_by_id.items(): - repre_id = container_item.representation_id + for container_item in containers_items_by_id.values(): project_name = container_item.project_name + repre_id = container_item.representation_id + + repre_info_by_id = repres_info_by_project[project_name] repre_info = repre_info_by_id[repre_id] - product_id = repre_info.product_id - version_items_id = ( - version_items_by_product_id_by_project[project_name][product_id] + + version_items_by_product_id = ( + version_items_by_project[project_name] ) - version_item = version_items_id.get(repre_info.version_id, {}) + product_id = repre_info.product_id + version_items_by_id = version_items_by_product_id[product_id] + version_item = version_items_by_id.get(repre_info.version_id, {}) if not version_item or not version_item.is_hero: continue + version = abs(version_item.version) version_found = False - for version_item in version_items_id.values(): + for version_item in version_items_by_id.values(): if version_item.is_hero: continue if version_item.version == version: @@ -1029,8 +1044,8 @@ class SceneInventoryView(QtWidgets.QTreeView): update_containers.append(container_item.item_id) update_versions.append(version) - # Specify version per item to update to - self._update_containers(update_containers, update_versions) + # Specify version per item to update to + self._update_containers(update_containers, update_versions) def _update_containers(self, item_ids, versions): """Helper to update items to given version (or version per item) From 562f2edacee460709963213c1aeae155416c097f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:22:27 +0100 Subject: [PATCH 123/463] site sync is fully project specific --- .../ayon_core/tools/sceneinventory/control.py | 4 +- .../ayon_core/tools/sceneinventory/model.py | 52 +++++--- .../tools/sceneinventory/models/sitesync.py | 121 +++++++++--------- 3 files changed, 98 insertions(+), 79 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 12dfc72e77..4f23b8e942 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -118,8 +118,8 @@ class SceneInventoryController: def is_sitesync_enabled(self): return self._sitesync_model.is_sitesync_enabled() - def get_sites_information(self): - return self._sitesync_model.get_sites_information() + def get_sites_information(self, project_name): + return self._sitesync_model.get_sites_information(project_name) def get_site_provider_icons(self): return self._sitesync_model.get_site_provider_icons() diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index cf9814a8e1..95272470aa 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -133,10 +133,10 @@ class InventoryModel(QtGui.QStandardItemModel): container_items = self._controller.get_container_items() self._clear_items() repre_ids_by_project = collections.defaultdict(set) - version_items_by_product_id = collections.defaultdict(dict) + version_items_by_project = collections.defaultdict(dict) repre_info_by_id_by_project = collections.defaultdict(dict) - item_by_repre_id_by_project_id = collections.defaultdict( - lambda: collections.defaultdict(set)) + item_by_repre_id_by_project = collections.defaultdict( + lambda: collections.defaultdict(list)) for container_item in container_items: # if ( # selected is not None @@ -146,7 +146,11 @@ class InventoryModel(QtGui.QStandardItemModel): project_name = container_item.project_name representation_id = container_item.representation_id repre_ids_by_project[project_name].add(representation_id) - item_by_repre_id_by_project_id[project_name][representation_id].add(container_item) + ( + item_by_repre_id_by_project + [project_name] + [representation_id] + ).append(container_item) for project_name, representation_ids in repre_ids_by_project.items(): repre_info = self._controller.get_representation_info_items( @@ -162,17 +166,20 @@ class InventoryModel(QtGui.QStandardItemModel): version_items = self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id[project_name] = version_items + version_items_by_project[project_name] = version_items # SiteSync addon information - progress_by_project = {} - for project_name, repre_ids in repre_ids_by_project.items(): - progress_by_id = self._controller.get_representations_site_progress( + progress_by_project = { + project_name: self._controller.get_representations_site_progress( project_name, repre_ids ) - progress_by_project[project_name] = progress_by_id + for project_name, repre_ids in repre_ids_by_project.items() + } - sites_info = self._controller.get_sites_information() + sites_info_by_project_name = { + project_name: self._controller.get_sites_information(project_name) + for project_name in repre_ids_by_project.keys() + } site_icons = { provider: get_qt_icon(icon_def) for provider, icon_def in ( @@ -203,15 +210,26 @@ class InventoryModel(QtGui.QStandardItemModel): group_item_font = QtGui.QFont() group_item_font.setBold(True) - active_site_icon = site_icons.get(sites_info["active_site_provider"]) - remote_site_icon = site_icons.get(sites_info["remote_site_provider"]) - root_item = self.invisibleRootItem() group_items = [] - for project_name, items_by_repre_id in item_by_repre_id_by_project_id.items(): + for project_name, items_by_repre_id in ( + item_by_repre_id_by_project.items() + ): + sites_info = sites_info_by_project_name[project_name] + active_site_icon = site_icons.get( + sites_info["active_site_provider"] + ) + remote_site_icon = site_icons.get( + sites_info["remote_site_provider"] + ) + progress_by_id = progress_by_project[project_name] + repre_info_by_id = repre_info_by_id_by_project[project_name] + version_items_by_product_id = ( + version_items_by_project[project_name] + ) for repre_id, container_items in items_by_repre_id.items(): - repre_info = repre_info_by_id_by_project[project_name][repre_id] + repre_info = repre_info_by_id[repre_id] version_color = None if not repre_info.is_valid: version_label = "N/A" @@ -230,7 +248,7 @@ class InventoryModel(QtGui.QStandardItemModel): item_icon = valid_item_icon version_items = ( - version_items_by_product_id[project_name][repre_info.product_id] + version_items_by_product_id[repre_info.product_id] ) version_item = version_items[repre_info.version_id] version_label = format_version(version_item.version) @@ -266,8 +284,6 @@ class InventoryModel(QtGui.QStandardItemModel): item.setData(True, IS_CONTAINER_ITEM_ROLE) item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) container_model_items.append(item) - if not container_model_items: - continue progress = progress_by_id[repre_id] active_site_progress = "{}%".format( diff --git a/client/ayon_core/tools/sceneinventory/models/sitesync.py b/client/ayon_core/tools/sceneinventory/models/sitesync.py index c8e1ac2cd3..546d2b15c0 100644 --- a/client/ayon_core/tools/sceneinventory/models/sitesync.py +++ b/client/ayon_core/tools/sceneinventory/models/sitesync.py @@ -11,18 +11,18 @@ class SiteSyncModel: self._sitesync_addon = NOT_SET self._sitesync_enabled = None - self._active_site = NOT_SET - self._remote_site = NOT_SET - self._active_site_provider = NOT_SET - self._remote_site_provider = NOT_SET + self._active_site = {} + self._remote_site = {} + self._active_site_provider = {} + self._remote_site_provider = {} def reset(self): self._sitesync_addon = NOT_SET self._sitesync_enabled = None - self._active_site = NOT_SET - self._remote_site = NOT_SET - self._active_site_provider = NOT_SET - self._remote_site_provider = NOT_SET + self._active_site = {} + self._remote_site = {} + self._active_site_provider = {} + self._remote_site_provider = {} def is_sitesync_enabled(self): """Site sync is enabled. @@ -46,12 +46,16 @@ class SiteSyncModel: sitesync_addon = self._get_sitesync_addon() return sitesync_addon.get_site_icons() - def get_sites_information(self): + def get_sites_information(self, project_name): return { - "active_site": self._get_active_site(), - "active_site_provider": self._get_active_site_provider(), - "remote_site": self._get_remote_site(), - "remote_site_provider": self._get_remote_site_provider() + "active_site": self._get_active_site(project_name), + "remote_site": self._get_remote_site(project_name), + "active_site_provider": self._get_active_site_provider( + project_name + ), + "remote_site_provider": self._get_remote_site_provider( + project_name + ) } def get_representations_site_progress( @@ -74,8 +78,8 @@ class SiteSyncModel: repre_entities = ayon_api.get_representations( project_name, representation_ids ) - active_site = self._get_active_site() - remote_site = self._get_remote_site() + active_site = self._get_active_site(project_name) + remote_site = self._get_remote_site(project_name) for repre_entity in repre_entities: repre_output = output[repre_entity["id"]] @@ -97,11 +101,9 @@ class SiteSyncModel: representation_ids (Iterable[str]): Representation ids. site_type (Literal[active_site, remote_site]): Site type. """ - - project_name = self._controller.get_current_project_name() sitesync_addon = self._get_sitesync_addon() - active_site = self._get_active_site() - remote_site = self._get_remote_site() + active_site = self._get_active_site(project_name) + remote_site = self._get_remote_site(project_name) progress = self.get_representations_site_progress( project_name, representation_ids ) @@ -136,48 +138,49 @@ class SiteSyncModel: self._sitesync_addon = sitesync_addon self._sitesync_enabled = sync_enabled - def _get_active_site(self): - if self._active_site is NOT_SET: - self._cache_sites() - return self._active_site + def _get_active_site(self, project_name): + if project_name not in self._active_site: + self._cache_sites(project_name) + return self._active_site[project_name] - def _get_remote_site(self): - if self._remote_site is NOT_SET: - self._cache_sites() - return self._remote_site + def _get_remote_site(self, project_name): + if project_name not in self._remote_site: + self._cache_sites(project_name) + return self._remote_site[project_name] - def _get_active_site_provider(self): - if self._active_site_provider is NOT_SET: - self._cache_sites() - return self._active_site_provider + def _get_active_site_provider(self, project_name): + if project_name not in self._active_site_provider: + self._cache_sites(project_name) + return self._active_site_provider[project_name] - def _get_remote_site_provider(self): - if self._remote_site_provider is NOT_SET: - self._cache_sites() - return self._remote_site_provider + def _get_remote_site_provider(self, project_name): + if project_name not in self._remote_site_provider: + self._cache_sites(project_name) + return self._remote_site_provider[project_name] - def _cache_sites(self): - active_site = None - remote_site = None - active_site_provider = None - remote_site_provider = None - if self.is_sitesync_enabled(): - sitesync_addon = self._get_sitesync_addon() - project_name = self._controller.get_current_project_name() - active_site = sitesync_addon.get_active_site(project_name) - remote_site = sitesync_addon.get_remote_site(project_name) - active_site_provider = "studio" - remote_site_provider = "studio" - if active_site != "studio": - active_site_provider = sitesync_addon.get_provider_for_site( - project_name, active_site - ) - if remote_site != "studio": - remote_site_provider = sitesync_addon.get_provider_for_site( - project_name, remote_site - ) + def _cache_sites(self, project_name): + self._active_site[project_name] = None + self._remote_site[project_name] = None + self._active_site_provider[project_name] = None + self._remote_site_provider[project_name] = None + if not self.is_sitesync_enabled(): + return - self._active_site = active_site - self._remote_site = remote_site - self._active_site_provider = active_site_provider - self._remote_site_provider = remote_site_provider + sitesync_addon = self._get_sitesync_addon() + active_site = sitesync_addon.get_active_site(project_name) + remote_site = sitesync_addon.get_remote_site(project_name) + active_site_provider = "studio" + remote_site_provider = "studio" + if active_site != "studio": + active_site_provider = sitesync_addon.get_provider_for_site( + project_name, active_site + ) + if remote_site != "studio": + remote_site_provider = sitesync_addon.get_provider_for_site( + project_name, remote_site + ) + + self._active_site[project_name] = active_site + self._remote_site[project_name] = remote_site + self._active_site_provider[project_name] = active_site_provider + self._remote_site_provider[project_name] = remote_site_provider From eea31b676d91c7ae746f7f1d2417737273c6cb05 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:25:46 +0100 Subject: [PATCH 124/463] don't slow down project name getter --- client/ayon_core/pipeline/load/utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index a6c5f0ce1f..de8e1676e7 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -465,7 +465,9 @@ def update_container(container, version=-1): from ayon_core.pipeline import get_current_project_name # Compute the different version from 'representation' - project_name = container.get("project_name", get_current_project_name()) + project_name = container.get("project_name") + if project_name is None: + project_name = get_current_project_name() repre_id = container["representation"] if not _is_valid_representation_id(repre_id): raise ValueError( @@ -588,7 +590,9 @@ def switch_container(container, representation, loader_plugin=None): ) # Get the new representation to switch to - project_name = container.get("project_name", get_current_project_name()) + project_name = container.get("project_name") + if project_name is None: + project_name = get_current_project_name() context = get_representation_context( project_name, representation["id"] From e21c7a157e5009e521d804fb0621bf55d750cba2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:46:56 +0100 Subject: [PATCH 125/463] use project name to get correct status icons --- .../ayon_core/tools/sceneinventory/control.py | 5 +- .../ayon_core/tools/sceneinventory/model.py | 48 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/control.py b/client/ayon_core/tools/sceneinventory/control.py index 4f23b8e942..60d9bc77a9 100644 --- a/client/ayon_core/tools/sceneinventory/control.py +++ b/client/ayon_core/tools/sceneinventory/control.py @@ -86,8 +86,9 @@ class SceneInventoryController: self._current_folder_set = True return self._current_folder_id - def get_project_status_items(self): - project_name = self.get_current_project_name() + def get_project_status_items(self, project_name=None): + if project_name is None: + project_name = self.get_current_project_name() return self._projects_model.get_project_status_items( project_name, None ) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 95272470aa..79af0e5cf5 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -121,8 +121,8 @@ class InventoryModel(QtGui.QStandardItemModel): self._default_icon_color = get_default_entity_icon_color() - self._last_project_statuses = {} - self._last_status_icons_by_name = {} + self._last_project_statuses = collections.defaultdict(dict) + self._last_status_icons_by_name = collections.defaultdict(dict) def outdated(self, item): return item.get("isOutdated", True) @@ -131,7 +131,10 @@ class InventoryModel(QtGui.QStandardItemModel): """Refresh the model""" # for debugging or testing, injecting items from outside container_items = self._controller.get_container_items() + self._clear_items() + + project_names = set() repre_ids_by_project = collections.defaultdict(set) version_items_by_project = collections.defaultdict(dict) repre_info_by_id_by_project = collections.defaultdict(dict) @@ -145,6 +148,7 @@ class InventoryModel(QtGui.QStandardItemModel): # continue project_name = container_item.project_name representation_id = container_item.representation_id + project_names.add(project_name) repre_ids_by_project[project_name].add(representation_id) ( item_by_repre_id_by_project @@ -178,7 +182,7 @@ class InventoryModel(QtGui.QStandardItemModel): sites_info_by_project_name = { project_name: self._controller.get_sites_information(project_name) - for project_name in repre_ids_by_project.keys() + for project_name in project_names } site_icons = { provider: get_qt_icon(icon_def) @@ -186,11 +190,17 @@ class InventoryModel(QtGui.QStandardItemModel): self._controller.get_site_provider_icons().items() ) } - self._last_project_statuses = { - status_item.name: status_item - for status_item in self._controller.get_project_status_items() - } - self._last_status_icons_by_name = {} + last_project_statuses = collections.defaultdict(dict) + for project_name in project_names: + status_items_by_name = { + status_item.name: status_item + for status_item in self._controller.get_project_status_items( + project_name + ) + } + last_project_statuses[project_name] = status_items_by_name + self._last_project_statuses = last_project_statuses + self._last_status_icons_by_name = collections.defaultdict(dict) group_item_icon = qtawesome.icon( "fa.folder", color=self._default_icon_color @@ -258,9 +268,9 @@ class InventoryModel(QtGui.QStandardItemModel): version_color = self.OUTDATED_COLOR status_name = version_item.status - status_color, status_short, status_icon = self._get_status_data( - status_name - ) + ( + status_color, status_short, status_icon + ) = self._get_status_data(project_name, status_name) repre_name = ( repre_info.representation_name or "" @@ -392,17 +402,21 @@ class InventoryModel(QtGui.QStandardItemModel): root_item = self.invisibleRootItem() root_item.removeRows(0, root_item.rowCount()) - def _get_status_data(self, status_name): - status_item = self._last_project_statuses.get(status_name) - status_icon = self._get_status_icon(status_name, status_item) + def _get_status_data(self, project_name, status_name): + status_item = self._last_project_statuses[project_name].get( + status_name + ) + status_icon = self._get_status_icon( + project_name, status_name, status_item + ) status_color = status_short = None if status_item is not None: status_color = status_item.color status_short = status_item.short return status_color, status_short, status_icon - def _get_status_icon(self, status_name, status_item): - icon = self._last_status_icons_by_name.get(status_name) + def _get_status_icon(self, project_name, status_name, status_item): + icon = self._last_status_icons_by_name[project_name].get(status_name) if icon is not None: return icon @@ -415,7 +429,7 @@ class InventoryModel(QtGui.QStandardItemModel): }) if icon is None: icon = QtGui.QIcon() - self._last_status_icons_by_name[status_name] = icon + self._last_status_icons_by_name[project_name][status_name] = icon return icon From 87907b550b1410a6b6912f611287123e310e7f33 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 19:05:29 +0100 Subject: [PATCH 126/463] fix switch version --- client/ayon_core/tools/sceneinventory/view.py | 92 +++++++++++-------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index b3322ffc60..9112db2ef3 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -772,60 +772,71 @@ class SceneInventoryView(QtWidgets.QTreeView): container_items_by_id = self._controller.get_container_items_by_id( item_ids ) + project_names = set() repre_ids_by_project = collections.defaultdict(set) for container_item in container_items_by_id.values(): repre_id = container_item.representation_id project_name = container_item.project_name + project_names.add(project_name) repre_ids_by_project[project_name].add(repre_id) - version_ids = set() - repre_info_by_project = collections.defaultdict(dict) - version_items_by_product_id_by_project = collections.defaultdict(dict) + active_project_name = None + active_repre_info = None + repre_info_by_project = {} + version_items_by_project = {} for project_name, repre_ids in repre_ids_by_project.items(): - repre_info = self._controller.get_representation_info_items( + repres_info = self._controller.get_representation_info_items( project_name, repre_ids ) - repre_info_by_project[project_name].update(repre_info) + if active_repre_info is None: + active_project_name = project_name + active_repre_info = repres_info.get(active_repre_id) - for container_item in container_items_by_id.values(): - project_name = container_item.project_name - repre_info_by_id = repre_info_by_project.get(project_name) - repre_id = container_item.representation_id - repre_info = repre_info_by_id.get(repre_id) product_ids = { repre_info.product_id - for repre_info in repre_info_by_id.values() + for repre_info in repres_info.values() if repre_info.is_valid } - version_items = self._controller.get_version_items( + version_items_by_product_id = self._controller.get_version_items( project_name, product_ids ) - version_items_by_product_id_by_project[project_name] = version_items - active_repre_info = repre_info_by_id[active_repre_id] - active_version_id = active_repre_info.version_id - active_product_id = active_repre_info.product_id - version_items = list( - version_items_by_product_id_by_project[project_name][active_product_id].values() + + repre_info_by_project[project_name] = repres_info + version_items_by_project[project_name] = version_items_by_product_id + + active_version_id = active_repre_info.version_id + active_product_id = active_repre_info.product_id + + versions = set() + product_ids = set() + version_items = [] + product_ids_by_version_by_project = {} + for project_name, version_items_by_product_id in ( + version_items_by_project.items() + ): + product_ids_by_version = collections.defaultdict(set) + product_ids_by_version_by_project[project_name] = ( + product_ids_by_version ) - version_ids.update(version_item.version for version_item in version_items) + versions |= { + version_item.version + for version_item in version_items_by_product_id.values() + } + for version_item in version_items_by_product_id.values(): + version = version_item.version + _prod_version = version + if _prod_version < 0: + _prod_version = -1 + product_ids_by_version[_prod_version].add( + version_item.product_id + ) + product_ids.add(version_item.product_id) + if version in versions: + continue + versions.add(version) + version_items.append((project_name, version_item)) - product_ids_by_version = collections.defaultdict(set) - for version_items_by_product_id in version_items_by_product_id_by_project.values(): - for version_items_by_id in version_items_by_product_id.values(): - for version_item in version_items_by_id.values(): - version = version_item.version - _prod_version = version - if _prod_version < 0: - _prod_version = -1 - product_ids_by_version[_prod_version].add( - version_item.product_id - ) - if version in version_ids: - continue - version_ids.add(version) - version_items.append(version_item) - - def version_sorter(item): + def version_sorter(_, item): hero_value = 0 i_version = item.version if i_version < 0: @@ -844,7 +855,8 @@ class SceneInventoryView(QtWidgets.QTreeView): version_options = [] active_version_idx = 0 - for idx, version_item in enumerate(version_items): + for idx, item in enumerate(version_items): + project_name, version_item = item version = version_item.version label = format_version(version) if version_item.version_id == active_version_id: @@ -884,11 +896,13 @@ class SceneInventoryView(QtWidgets.QTreeView): product_version = -1 version = HeroVersionType(version) - product_ids = product_ids_by_version[product_version] - filtered_item_ids = set() for container_item in container_items_by_id.values(): project_name = container_item.project_name + product_ids_by_version = ( + product_ids_by_version_by_project[project_name] + ) + product_ids = product_ids_by_version[product_version] repre_id = container_item.representation_id repre_info = repre_info_by_project[project_name][repre_id] if repre_info.product_id in product_ids: From b28f4b0ff1ea9662c169f508302659f459352075 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 22 Nov 2024 19:10:05 +0100 Subject: [PATCH 127/463] comment out unused variables --- client/ayon_core/tools/sceneinventory/view.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 9112db2ef3..5892e4f983 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -780,7 +780,7 @@ class SceneInventoryView(QtWidgets.QTreeView): project_names.add(project_name) repre_ids_by_project[project_name].add(repre_id) - active_project_name = None + # active_project_name = None active_repre_info = None repre_info_by_project = {} version_items_by_project = {} @@ -789,7 +789,7 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name, repre_ids ) if active_repre_info is None: - active_project_name = project_name + # active_project_name = project_name active_repre_info = repres_info.get(active_repre_id) product_ids = { @@ -805,7 +805,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_items_by_project[project_name] = version_items_by_product_id active_version_id = active_repre_info.version_id - active_product_id = active_repre_info.product_id + # active_product_id = active_repre_info.product_id versions = set() product_ids = set() From 207b1961a441b4d13df7eac7e80faf39b4468ace Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:35:21 +0100 Subject: [PATCH 128/463] support all errors in ruff linter --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f2d09d925d..d09fabf8b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ target-version = "py39" [tool.ruff.lint] # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -select = ["E4", "E7", "E9", "F", "W"] +select = ["E", "F", "W"] ignore = [] # Allow fix for all enabled rules (when `--fix`) is provided. From d30d5b541994a2243d41252a44459d39d0696123 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:37:05 +0100 Subject: [PATCH 129/463] fix line lengths --- client/ayon_core/addon/base.py | 4 +-- client/ayon_core/cli.py | 3 +- client/ayon_core/lib/attribute_definitions.py | 4 ++- client/ayon_core/pipeline/create/context.py | 14 ++++++--- .../ayon_core/pipeline/create/product_name.py | 6 +++- client/ayon_core/pipeline/editorial.py | 13 ++++++-- .../pipeline/farm/pyblish_functions.py | 20 +++++++++--- .../plugins/publish/collect_hierarchy.py | 3 +- .../plugins/publish/extract_otio_review.py | 31 ++++++++++++------- .../publish/validate_unique_subsets.py | 10 +++--- client/ayon_core/scripts/otio_burnin.py | 17 ++++++---- client/ayon_core/settings/lib.py | 3 +- client/ayon_core/tools/creator/widgets.py | 4 ++- .../tools/launcher/models/actions.py | 6 ++-- .../tools/loader/ui/_multicombobox.py | 6 +++- .../tools/loader/ui/products_model.py | 6 ++-- .../publisher/widgets/overview_widget.py | 4 ++- .../publisher/widgets/product_context.py | 6 ++-- .../tools/publisher/widgets/tasks_model.py | 4 +-- client/ayon_core/tools/publisher/window.py | 6 +++- .../tools/sceneinventory/models/containers.py | 4 +-- client/ayon_core/tools/utils/lib.py | 7 +++-- server/settings/publish_plugins.py | 21 +++++++++---- server/settings/tools.py | 8 +++-- .../editorial/test_extract_otio_review.py | 31 +++++++++++-------- 25 files changed, 159 insertions(+), 82 deletions(-) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index 982626ad9d..364a84cb7b 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -535,8 +535,8 @@ class AYONAddon(ABC): Implementation of this method is optional. Note: - The logic can be similar to logic in tray, but tray does not require - to be logged in. + The logic can be similar to logic in tray, but tray does not + require to be logged in. Args: process_context (ProcessContext): Context of child diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index b80b243db2..6b4a1f824f 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -146,7 +146,8 @@ def publish_report_viewer(): @main_cli.command() @click.argument("output_path") @click.option("--project", help="Define project context") -@click.option("--folder", help="Define folder in project (project must be set)") +@click.option( + "--folder", help="Define folder in project (project must be set)") @click.option( "--strict", is_flag=True, diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e1381944f6..e8327a45b6 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -616,7 +616,9 @@ class EnumDef(AbstractAttrDef): return data @staticmethod - def prepare_enum_items(items: "EnumItemsInputType") -> List["EnumItemDict"]: + def prepare_enum_items( + items: "EnumItemsInputType" + ) -> List["EnumItemDict"]: """Convert items to unified structure. Output is a list where each item is dictionary with 'value' diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 6bfd64b822..e29971415d 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -1283,12 +1283,16 @@ class CreateContext: @contextmanager def bulk_pre_create_attr_defs_change(self, sender=None): - with self._bulk_context("pre_create_attrs_change", sender) as bulk_info: + with self._bulk_context( + "pre_create_attrs_change", sender + ) as bulk_info: yield bulk_info @contextmanager def bulk_create_attr_defs_change(self, sender=None): - with self._bulk_context("create_attrs_change", sender) as bulk_info: + with self._bulk_context( + "create_attrs_change", sender + ) as bulk_info: yield bulk_info @contextmanager @@ -1946,9 +1950,9 @@ class CreateContext: creator are just removed from context. Args: - instances (List[CreatedInstance]): Instances that should be removed. - Remove logic is done using creator, which may require to - do other cleanup than just remove instance from context. + instances (List[CreatedInstance]): Instances that should be + removed. Remove logic is done using creator, which may require + to do other cleanup than just remove instance from context. sender (Optional[str]): Sender of the event. """ diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index eaeef6500e..0daec8a7ad 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -1,5 +1,9 @@ import ayon_api -from ayon_core.lib import StringTemplate, filter_profiles, prepare_template_data +from ayon_core.lib import ( + StringTemplate, + filter_profiles, + prepare_template_data, +) from ayon_core.settings import get_project_settings from .constants import DEFAULT_PRODUCT_TEMPLATE diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index a49a981d2a..2928ef5f63 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -222,6 +222,9 @@ def remap_range_on_file_sequence(otio_clip, in_out_range): source_range = otio_clip.source_range available_range_rate = available_range.start_time.rate media_in = available_range.start_time.value + available_range_start_frame = ( + available_range.start_time.to_frames() + ) # Temporary. # Some AYON custom OTIO exporter were implemented with relative @@ -230,7 +233,7 @@ def remap_range_on_file_sequence(otio_clip, in_out_range): # while we are updating those. if ( is_clip_from_media_sequence(otio_clip) - and otio_clip.available_range().start_time.to_frames() == media_ref.start_frame + and available_range_start_frame == media_ref.start_frame and source_range.start_time.to_frames() < media_ref.start_frame ): media_in = 0 @@ -303,8 +306,12 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): rounded_av_rate = round(available_range_rate, 2) rounded_src_rate = round(source_range.start_time.rate, 2) if rounded_av_rate != rounded_src_rate: - conformed_src_in = source_range.start_time.rescaled_to(available_range_rate) - conformed_src_duration = source_range.duration.rescaled_to(available_range_rate) + conformed_src_in = source_range.start_time.rescaled_to( + available_range_rate + ) + conformed_src_duration = source_range.duration.rescaled_to( + available_range_rate + ) conformed_source_range = otio.opentime.TimeRange( start_time=conformed_src_in, duration=conformed_src_duration diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 16364a17ee..559561c827 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -8,7 +8,10 @@ import attr import ayon_api import clique from ayon_core.lib import Logger, collect_frames -from ayon_core.pipeline import get_current_project_name, get_representation_path +from ayon_core.pipeline import ( + get_current_project_name, + get_representation_path, +) from ayon_core.pipeline.create import get_product_name from ayon_core.pipeline.farm.patterning import match_aov_pattern from ayon_core.pipeline.publish import KnownPublishError @@ -771,9 +774,14 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, project_settings = instance.context.data.get("project_settings") - use_legacy_product_name = True try: - use_legacy_product_name = project_settings["core"]["tools"]["creator"]["use_legacy_product_names_for_renders"] # noqa: E501 + use_legacy_product_name = ( + project_settings + ["core"] + ["tools"] + ["creator"] + ["use_legacy_product_names_for_renders"] + ) except KeyError: warnings.warn( ("use_legacy_for_renders not found in project settings. " @@ -789,7 +797,9 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, dynamic_data=dynamic_data) else: - product_name, group_name = get_product_name_and_group_from_template( + ( + product_name, group_name + ) = get_product_name_and_group_from_template( task_entity=instance.data["taskEntity"], project_name=instance.context.data["projectName"], host_name=instance.context.data["hostName"], @@ -932,7 +942,7 @@ def _collect_expected_files_for_aov(files): # but we really expect only one collection. # Nothing else make sense. if len(cols) != 1: - raise ValueError("Only one image sequence type is expected.") # noqa: E501 + raise ValueError("Only one image sequence type is expected.") return list(cols[0]) diff --git a/client/ayon_core/plugins/publish/collect_hierarchy.py b/client/ayon_core/plugins/publish/collect_hierarchy.py index 00f5c06c0b..266c2e1458 100644 --- a/client/ayon_core/plugins/publish/collect_hierarchy.py +++ b/client/ayon_core/plugins/publish/collect_hierarchy.py @@ -43,7 +43,8 @@ class CollectHierarchy(pyblish.api.ContextPlugin): shot_data = { "entity_type": "folder", - # WARNING unless overwritten, default folder type is hardcoded to shot + # WARNING unless overwritten, default folder type is hardcoded + # to shot "folder_type": instance.data.get("folder_type") or "Shot", "tasks": instance.data.get("tasks") or {}, "comments": instance.data.get("comments", []), diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index b222c6efc3..fb9b269258 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -129,26 +129,33 @@ class ExtractOTIOReview( res_data[key] = value break - self.to_width, self.to_height = res_data["width"], res_data["height"] - self.log.debug("> self.to_width x self.to_height: {} x {}".format( - self.to_width, self.to_height - )) + self.to_width, self.to_height = ( + res_data["width"], res_data["height"] + ) + self.log.debug( + "> self.to_width x self.to_height:" + f" {self.to_width} x {self.to_height}" + ) available_range = r_otio_cl.available_range() + available_range_start_frame = ( + available_range.start_time.to_frames() + ) processing_range = None self.actual_fps = available_range.duration.rate start = src_range.start_time.rescaled_to(self.actual_fps) duration = src_range.duration.rescaled_to(self.actual_fps) + src_frame_start = src_range.start_time.to_frames() # Temporary. - # Some AYON custom OTIO exporter were implemented with relative - # source range for image sequence. Following code maintain - # backward-compatibility by adjusting available range + # Some AYON custom OTIO exporter were implemented with + # relative source range for image sequence. Following code + # maintain backward-compatibility by adjusting available range # while we are updating those. if ( is_clip_from_media_sequence(r_otio_cl) - and available_range.start_time.to_frames() == media_ref.start_frame - and src_range.start_time.to_frames() < media_ref.start_frame + and available_range_start_frame == media_ref.start_frame + and src_frame_start < media_ref.start_frame ): available_range = otio.opentime.TimeRange( otio.opentime.RationalTime(0, rate=self.actual_fps), @@ -246,7 +253,8 @@ class ExtractOTIOReview( # Extraction via FFmpeg. else: path = media_ref.target_url - # Set extract range from 0 (FFmpeg ignores embedded timecode). + # Set extract range from 0 (FFmpeg ignores + # embedded timecode). extract_range = otio.opentime.TimeRange( otio.opentime.RationalTime( ( @@ -414,7 +422,8 @@ class ExtractOTIOReview( to defined image sequence format. Args: - sequence (list): input dir path string, collection object, fps in list + sequence (list): input dir path string, collection object, + fps in list. video (list)[optional]: video_path string, otio_range in list gap (int)[optional]: gap duration end_offset (int)[optional]: offset gap frame start in frames diff --git a/client/ayon_core/plugins/publish/validate_unique_subsets.py b/client/ayon_core/plugins/publish/validate_unique_subsets.py index 4badeb8112..4067dd75a5 100644 --- a/client/ayon_core/plugins/publish/validate_unique_subsets.py +++ b/client/ayon_core/plugins/publish/validate_unique_subsets.py @@ -11,8 +11,8 @@ class ValidateProductUniqueness(pyblish.api.ContextPlugin): """Validate all product names are unique. This only validates whether the instances currently set to publish from - the workfile overlap one another for the folder + product they are publishing - to. + the workfile overlap one another for the folder + product they are + publishing to. This does not perform any check against existing publishes in the database since it is allowed to publish into existing products resulting in @@ -72,8 +72,10 @@ class ValidateProductUniqueness(pyblish.api.ContextPlugin): # All is ok return - msg = ("Instance product names {} are not unique. ".format(non_unique) + - "Please remove or rename duplicates.") + msg = ( + f"Instance product names {non_unique} are not unique." + " Please remove or rename duplicates." + ) formatting_data = { "non_unique": ",".join(non_unique) } diff --git a/client/ayon_core/scripts/otio_burnin.py b/client/ayon_core/scripts/otio_burnin.py index 6b132b9a6a..cb72606222 100644 --- a/client/ayon_core/scripts/otio_burnin.py +++ b/client/ayon_core/scripts/otio_burnin.py @@ -79,7 +79,8 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): - Datatypes explanation: string format must be supported by FFmpeg. Examples: "#000000", "0x000000", "black" - must be accesible by ffmpeg = name of registered Font in system or path to font file. + must be accesible by ffmpeg = name of registered Font in system + or path to font file. Examples: "Arial", "C:/Windows/Fonts/arial.ttf" - Possible keys: @@ -87,17 +88,21 @@ class ModifiedBurnins(ffmpeg_burnins.Burnins): "bg_opacity" - Opacity of background (box around text) - "bg_color" - Background color - "bg_padding" - Background padding in pixels - - "x_offset" - offsets burnin vertically by entered pixels from border - - "y_offset" - offsets burnin horizontally by entered pixels from border - + "x_offset" - offsets burnin vertically by entered pixels + from border - + "y_offset" - offsets burnin horizontally by entered pixels + from border - - x_offset & y_offset should be set at least to same value as bg_padding!! "font" - Font Family for text - "font_size" - Font size in pixels - "font_color" - Color of text - "frame_offset" - Default start frame - - - required IF start frame is not set when using frames or timecode burnins + - required IF start frame is not set when using frames + or timecode burnins - On initializing class can be set General options through "options_init" arg. - General can be overridden when adding burnin + On initializing class can be set General options through + "options_init" arg. + General options can be overridden when adding burnin. ''' TOP_CENTERED = ffmpeg_burnins.TOP_CENTERED diff --git a/client/ayon_core/settings/lib.py b/client/ayon_core/settings/lib.py index 3126bafd57..aa56fa8326 100644 --- a/client/ayon_core/settings/lib.py +++ b/client/ayon_core/settings/lib.py @@ -190,6 +190,7 @@ def get_current_project_settings(): project_name = os.environ.get("AYON_PROJECT_NAME") if not project_name: raise ValueError( - "Missing context project in environemt variable `AYON_PROJECT_NAME`." + "Missing context project in environment" + " variable `AYON_PROJECT_NAME`." ) return get_project_settings(project_name) diff --git a/client/ayon_core/tools/creator/widgets.py b/client/ayon_core/tools/creator/widgets.py index 96ce899881..bbc6848e6c 100644 --- a/client/ayon_core/tools/creator/widgets.py +++ b/client/ayon_core/tools/creator/widgets.py @@ -217,7 +217,9 @@ class ProductTypeDescriptionWidget(QtWidgets.QWidget): product_type_label = QtWidgets.QLabel(self) product_type_label.setObjectName("CreatorProductTypeLabel") - product_type_label.setAlignment(QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft) + product_type_label.setAlignment( + QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft + ) help_label = QtWidgets.QLabel(self) help_label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 7158c05431..8bd30daffa 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -21,9 +21,9 @@ except ImportError: Application action based on 'ApplicationManager' system. - Handling of applications in launcher is not ideal and should be completely - redone from scratch. This is just a temporary solution to keep backwards - compatibility with AYON launcher. + Handling of applications in launcher is not ideal and should be + completely redone from scratch. This is just a temporary solution + to keep backwards compatibility with AYON launcher. Todos: Move handling of errors to frontend. diff --git a/client/ayon_core/tools/loader/ui/_multicombobox.py b/client/ayon_core/tools/loader/ui/_multicombobox.py index c026952418..9efe57ef0f 100644 --- a/client/ayon_core/tools/loader/ui/_multicombobox.py +++ b/client/ayon_core/tools/loader/ui/_multicombobox.py @@ -517,7 +517,11 @@ class CustomPaintMultiselectComboBox(QtWidgets.QComboBox): def setItemCheckState(self, index, state): self.setItemData(index, state, QtCore.Qt.CheckStateRole) - def set_value(self, values: Optional[Iterable[Any]], role: Optional[int] = None): + def set_value( + self, + values: Optional[Iterable[Any]], + role: Optional[int] = None, + ): if role is None: role = self._value_role diff --git a/client/ayon_core/tools/loader/ui/products_model.py b/client/ayon_core/tools/loader/ui/products_model.py index bc24d4d7f7..3571788134 100644 --- a/client/ayon_core/tools/loader/ui/products_model.py +++ b/client/ayon_core/tools/loader/ui/products_model.py @@ -499,8 +499,10 @@ class ProductsModel(QtGui.QStandardItemModel): version_item.version_id for version_item in last_version_by_product_id.values() } - repre_count_by_version_id = self._controller.get_versions_representation_count( - project_name, version_ids + repre_count_by_version_id = ( + self._controller.get_versions_representation_count( + project_name, version_ids + ) ) sync_availability_by_version_id = ( self._controller.get_version_sync_availability( diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index a09ee80ed5..c6c3b774f0 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -339,7 +339,9 @@ class OverviewWidget(QtWidgets.QFrame): self._change_visibility_for_state() self._product_content_layout.addWidget(self._create_widget, 7) self._product_content_layout.addWidget(self._product_views_widget, 3) - self._product_content_layout.addWidget(self._product_attributes_wrap, 7) + self._product_content_layout.addWidget( + self._product_attributes_wrap, 7 + ) def _change_visibility_for_state(self): self._create_widget.setVisible( diff --git a/client/ayon_core/tools/publisher/widgets/product_context.py b/client/ayon_core/tools/publisher/widgets/product_context.py index 04c9ca7e56..30b318982b 100644 --- a/client/ayon_core/tools/publisher/widgets/product_context.py +++ b/client/ayon_core/tools/publisher/widgets/product_context.py @@ -214,8 +214,8 @@ class TasksCombobox(QtWidgets.QComboBox): Combobox gives ability to select only from intersection of task names for folder paths in selected instances. - If folder paths in selected instances does not have same tasks then combobox - will be empty. + If folder paths in selected instances does not have same tasks + then combobox will be empty. """ value_changed = QtCore.Signal() @@ -604,7 +604,7 @@ class VariantInputWidget(PlaceholderLineEdit): class GlobalAttrsWidget(QtWidgets.QWidget): - """Global attributes mainly to define context and product name of instances. + """Global attributes to define context and product name of instances. product name is or may be affected on context. Gives abiity to modify context and product name of instance. This change is not autopromoted but diff --git a/client/ayon_core/tools/publisher/widgets/tasks_model.py b/client/ayon_core/tools/publisher/widgets/tasks_model.py index 16a4111f59..8bfa81116a 100644 --- a/client/ayon_core/tools/publisher/widgets/tasks_model.py +++ b/client/ayon_core/tools/publisher/widgets/tasks_model.py @@ -22,8 +22,8 @@ class TasksModel(QtGui.QStandardItemModel): tasks with same names then model is empty too. Args: - controller (AbstractPublisherFrontend): Controller which handles creation and - publishing. + controller (AbstractPublisherFrontend): Controller which handles + creation and publishing. """ def __init__( diff --git a/client/ayon_core/tools/publisher/window.py b/client/ayon_core/tools/publisher/window.py index a912495d4e..ed5b909a55 100644 --- a/client/ayon_core/tools/publisher/window.py +++ b/client/ayon_core/tools/publisher/window.py @@ -998,7 +998,11 @@ class PublisherWindow(QtWidgets.QDialog): new_item["label"] = new_item.pop("creator_label") new_item["identifier"] = new_item.pop("creator_identifier") new_failed_info.append(new_item) - self.add_error_message_dialog(event["title"], new_failed_info, "Creator:") + self.add_error_message_dialog( + event["title"], + new_failed_info, + "Creator:" + ) def _on_convertor_error(self, event): new_failed_info = [] diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 4f3ddf1ded..4280445b60 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -366,8 +366,8 @@ class ContainersModel: try: uuid.UUID(repre_id) except (ValueError, TypeError, AttributeError): - # Fake not existing representation id so container is shown in UI - # but as invalid + # Fake not existing representation id so container + # is shown in UI but as invalid item.representation_id = invalid_ids_mapping.setdefault( repre_id, uuid.uuid4().hex ) diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index 200e281664..4b303c0143 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -556,9 +556,10 @@ class _IconsCache: log.info("Didn't find icon \"{}\"".format(icon_name)) elif used_variant != icon_name: - log.debug("Icon \"{}\" was not found \"{}\" is used instead".format( - icon_name, used_variant - )) + log.debug( + f"Icon \"{icon_name}\" was not found" + f" \"{used_variant}\" is used instead" + ) cls._qtawesome_cache[full_icon_name] = icon return icon diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 16b1f37187..8893b00e23 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -358,7 +358,10 @@ class ExtractOIIOTranscodeOutputModel(BaseSettingsModel): custom_tags: list[str] = SettingsField( default_factory=list, title="Custom Tags", - description="Additional custom tags that will be added to the created representation." + description=( + "Additional custom tags that will be added" + " to the created representation." + ) ) @@ -892,9 +895,11 @@ class PublishPuginsModel(BaseSettingsModel): default_factory=CollectFramesFixDefModel, title="Collect Frames to Fix", ) - CollectUSDLayerContributions: CollectUSDLayerContributionsModel = SettingsField( - default_factory=CollectUSDLayerContributionsModel, - title="Collect USD Layer Contributions", + CollectUSDLayerContributions: CollectUSDLayerContributionsModel = ( + SettingsField( + default_factory=CollectUSDLayerContributionsModel, + title="Collect USD Layer Contributions", + ) ) ValidateEditorialAssetName: ValidateBaseModel = SettingsField( default_factory=ValidateBaseModel, @@ -1214,7 +1219,9 @@ DEFAULT_PUBLISH_VALUES = { "TOP_RIGHT": "{anatomy[version]}", "BOTTOM_LEFT": "{username}", "BOTTOM_CENTERED": "{folder[name]}", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "BOTTOM_RIGHT": ( + "{frame_start}-{current_frame}-{frame_end}" + ), "filter": { "families": [], "tags": [] @@ -1240,7 +1247,9 @@ DEFAULT_PUBLISH_VALUES = { "TOP_RIGHT": "{anatomy[version]}", "BOTTOM_LEFT": "{username}", "BOTTOM_CENTERED": "{folder[name]}", - "BOTTOM_RIGHT": "{frame_start}-{current_frame}-{frame_end}", + "BOTTOM_RIGHT": ( + "{frame_start}-{current_frame}-{frame_end}" + ), "filter": { "families": [], "tags": [] diff --git a/server/settings/tools.py b/server/settings/tools.py index a2785c1edf..96851be1da 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -83,8 +83,8 @@ class CreatorToolModel(BaseSettingsModel): filter_creator_profiles: list[FilterCreatorProfile] = SettingsField( default_factory=list, title="Filter creator profiles", - description="Allowed list of creator labels that will be only shown if " - "profile matches context." + description="Allowed list of creator labels that will be only shown" + " if profile matches context." ) @validator("product_types_smart_select") @@ -426,7 +426,9 @@ DEFAULT_TOOLS_VALUES = { ], "task_types": [], "tasks": [], - "template": "{product[type]}{Task[name]}_{Renderlayer}_{Renderpass}" + "template": ( + "{product[type]}{Task[name]}_{Renderlayer}_{Renderpass}" + ) }, { "product_types": [ diff --git a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py index ea31e1a260..8b1c9da30e 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py +++ b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py @@ -130,19 +130,20 @@ def test_image_sequence_and_handles_out_of_range(): expected = [ # 5 head black frames generated from gap (991-995) - "/path/to/ffmpeg -t 0.2 -r 25.0 -f lavfi -i color=c=black:s=1280x720 -tune " - "stillimage -start_number 991 C:/result/output.%03d.jpg", + "/path/to/ffmpeg -t 0.2 -r 25.0 -f lavfi -i color=c=black:s=1280x720" + " -tune stillimage -start_number 991 C:/result/output.%03d.jpg", # 9 tail back frames generated from gap (1097-1105) - "/path/to/ffmpeg -t 0.36 -r 25.0 -f lavfi -i color=c=black:s=1280x720 -tune " - "stillimage -start_number 1097 C:/result/output.%03d.jpg", + "/path/to/ffmpeg -t 0.36 -r 25.0 -f lavfi -i color=c=black:s=1280x720" + " -tune stillimage -start_number 1097 C:/result/output.%03d.jpg", # Report from source tiff (996-1096) # 996-1000 = additional 5 head frames # 1001-1095 = source range conformed to 25fps # 1096-1096 = additional 1 tail frames "/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i " - f"C:\\tif_seq{os.sep}output.%04d.tif -start_number 996 C:/result/output.%03d.jpg" + f"C:\\tif_seq{os.sep}output.%04d.tif -start_number 996" + f" C:/result/output.%03d.jpg" ] assert calls == expected @@ -179,13 +180,13 @@ def test_short_movie_head_gap_handles(): expected = [ # 10 head black frames generated from gap (991-1000) - "/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi -i color=c=black:s=1280x720 -tune " - "stillimage -start_number 991 C:/result/output.%03d.jpg", + "/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi -i color=c=black:s=1280x720" + " -tune stillimage -start_number 991 C:/result/output.%03d.jpg", # source range + 10 tail frames # duration = 50fr (source) + 10fr (tail handle) = 60 fr = 2.4s - "/path/to/ffmpeg -ss 0.0 -t 2.4 -i C:\\data\\movie.mp4 -start_number 1001 " - "C:/result/output.%03d.jpg" + "/path/to/ffmpeg -ss 0.0 -t 2.4 -i C:\\data\\movie.mp4" + " -start_number 1001 C:/result/output.%03d.jpg" ] assert calls == expected @@ -208,7 +209,8 @@ def test_short_movie_tail_gap_handles(): # 10 head frames + source range # duration = 10fr (head handle) + 66fr (source) = 76fr = 3.16s "/path/to/ffmpeg -ss 1.0416666666666667 -t 3.1666666666666665 -i " - "C:\\data\\qt_no_tc_24fps.mov -start_number 991 C:/result/output.%03d.jpg" + "C:\\data\\qt_no_tc_24fps.mov -start_number 991" + " C:/result/output.%03d.jpg" ] assert calls == expected @@ -234,10 +236,12 @@ def test_multiple_review_clips_no_gap(): expected = [ # 10 head black frames generated from gap (991-1000) - '/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi -i color=c=black:s=1280x720 -tune ' + '/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi' + ' -i color=c=black:s=1280x720 -tune ' 'stillimage -start_number 991 C:/result/output.%03d.jpg', - # Alternance 25fps tiff sequence and 24fps exr sequence for 100 frames each + # Alternance 25fps tiff sequence and 24fps exr sequence + # for 100 frames each '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' '-start_number 1001 C:/result/output.%03d.jpg', @@ -315,7 +319,8 @@ def test_multiple_review_clips_with_gap(): expected = [ # Gap on review track (12 frames) - '/path/to/ffmpeg -t 0.5 -r 24.0 -f lavfi -i color=c=black:s=1280x720 -tune ' + '/path/to/ffmpeg -t 0.5 -r 24.0 -f lavfi' + ' -i color=c=black:s=1280x720 -tune ' 'stillimage -start_number 991 C:/result/output.%03d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' From 3de2755de52567694e4dd1a248cbbfa69499e1b6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:37:18 +0100 Subject: [PATCH 130/463] remove unrelated information from docstring --- client/ayon_core/lib/local_settings.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/lib/local_settings.py b/client/ayon_core/lib/local_settings.py index 690781151c..08030ae87e 100644 --- a/client/ayon_core/lib/local_settings.py +++ b/client/ayon_core/lib/local_settings.py @@ -276,12 +276,7 @@ class ASettingRegistry(ABC): @abstractmethod def _delete_item(self, name): # type: (str) -> None - """Delete item from settings. - - Note: - see :meth:`ayon_core.lib.user_settings.ARegistrySettings.delete_item` - - """ + """Delete item from settings.""" pass def __delitem__(self, name): @@ -433,12 +428,7 @@ class IniSettingRegistry(ASettingRegistry): config.write(cfg) def _delete_item(self, name): - """Delete item from default section. - - Note: - See :meth:`~ayon_core.lib.IniSettingsRegistry.delete_item_from_section` - - """ + """Delete item from default section.""" self.delete_item_from_section("MAIN", name) From ef6d7b5a6ce863c0a0231fb8da5ea939b6d29717 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:37:27 +0100 Subject: [PATCH 131/463] put noqa at correct place --- client/ayon_core/pipeline/entity_uri.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/entity_uri.py b/client/ayon_core/pipeline/entity_uri.py index 1dee9a1423..1362389ee9 100644 --- a/client/ayon_core/pipeline/entity_uri.py +++ b/client/ayon_core/pipeline/entity_uri.py @@ -18,13 +18,13 @@ def parse_ayon_entity_uri(uri: str) -> Optional[dict]: Example: >>> parse_ayon_entity_uri( - >>> "ayon://test/char/villain?product=modelMain&version=2&representation=usd" # noqa: E501 + >>> "ayon://test/char/villain?product=modelMain&version=2&representation=usd" >>> ) {'project': 'test', 'folderPath': '/char/villain', 'product': 'modelMain', 'version': 1, 'representation': 'usd'} >>> parse_ayon_entity_uri( - >>> "ayon+entity://project/folder?product=renderMain&version=3&representation=exr" # noqa: E501 + >>> "ayon+entity://project/folder?product=renderMain&version=3&representation=exr" >>> ) {'project': 'project', 'folderPath': '/folder', 'product': 'renderMain', 'version': 3, @@ -34,7 +34,7 @@ def parse_ayon_entity_uri(uri: str) -> Optional[dict]: dict[str, Union[str, int]]: The individual key with their values as found in the ayon entity URI. - """ + """ # noqa: E501 if not (uri.startswith("ayon+entity://") or uri.startswith("ayon://")): return {} From 9cd354efb299c43e27864119ea1779116407ee23 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:09:45 +0100 Subject: [PATCH 132/463] added constant to define store key for env variables --- client/ayon_core/pipeline/publish/__init__.py | 2 ++ client/ayon_core/pipeline/publish/constants.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/client/ayon_core/pipeline/publish/__init__.py b/client/ayon_core/pipeline/publish/__init__.py index ac71239acf..5363e0b378 100644 --- a/client/ayon_core/pipeline/publish/__init__.py +++ b/client/ayon_core/pipeline/publish/__init__.py @@ -3,6 +3,7 @@ from .constants import ( ValidateContentsOrder, ValidateSceneOrder, ValidateMeshOrder, + FARM_JOB_ENV_DATA_KEY, ) from .publish_plugins import ( @@ -59,6 +60,7 @@ __all__ = ( "ValidateContentsOrder", "ValidateSceneOrder", "ValidateMeshOrder", + "FARM_JOB_ENV_DATA_KEY", "AbstractMetaInstancePlugin", "AbstractMetaContextPlugin", diff --git a/client/ayon_core/pipeline/publish/constants.py b/client/ayon_core/pipeline/publish/constants.py index 38f5ffef3f..f2f4e851a9 100644 --- a/client/ayon_core/pipeline/publish/constants.py +++ b/client/ayon_core/pipeline/publish/constants.py @@ -9,3 +9,5 @@ ValidateMeshOrder = pyblish.api.ValidatorOrder + 0.3 DEFAULT_PUBLISH_TEMPLATE = "default" DEFAULT_HERO_PUBLISH_TEMPLATE = "default" TRANSIENT_DIR_TEMPLATE = "default" + +FARM_JOB_ENV_DATA_KEY: str = "farmJobEnv" From fae4eed3ed97b306b93bd2e98f79bbee335b8604 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:10:59 +0100 Subject: [PATCH 133/463] added new plugin collecting environment variables to context --- .../publish/collect_farm_env_variables.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 client/ayon_core/plugins/publish/collect_farm_env_variables.py diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py new file mode 100644 index 0000000000..935b4d5c9f --- /dev/null +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -0,0 +1,48 @@ +import os + +import pyblish.api + +from ayon_core.pipeline.publish import FARM_JOB_ENV_DATA_KEY + + +class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): + """Collect set of environment variables to submit with deadline jobs""" + order = pyblish.api.CollectorOrder - 0.45 + label = "AYON core Farm Environment Variables" + targets = ["local"] + + ENV_KEYS = [ + # AYON + "AYON_BUNDLE_NAME", + "AYON_DEFAULT_SETTINGS_VARIANT", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_APP_NAME", + "AYON_WORKDIR", + "AYON_APP_NAME", + "AYON_LOG_NO_COLORS", + "AYON_IN_TESTS", + "IS_TEST", # backwards compatibility + ] + + def process(self, context): + env = context.data.setdefault(FARM_JOB_ENV_DATA_KEY, {}) + for key in [ + # AYON + "AYON_BUNDLE_NAME", + "AYON_DEFAULT_SETTINGS_VARIANT", + "AYON_PROJECT_NAME", + "AYON_FOLDER_PATH", + "AYON_TASK_NAME", + "AYON_WORKDIR", + "AYON_LOG_NO_COLORS", + "AYON_IN_TESTS", + # backwards compatibility + "IS_TEST", + ]: + value = os.getenv(key) + if value: + self.log.debug(f"Setting job env: {key}: {value}") + env[key] = value + From 85be6b2e422b327d6c506018192f3e10a8fee998 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:53:38 +0100 Subject: [PATCH 134/463] remove unnecessary attribute --- .../plugins/publish/collect_farm_env_variables.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 935b4d5c9f..0201973643 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -11,21 +11,6 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): label = "AYON core Farm Environment Variables" targets = ["local"] - ENV_KEYS = [ - # AYON - "AYON_BUNDLE_NAME", - "AYON_DEFAULT_SETTINGS_VARIANT", - "AYON_PROJECT_NAME", - "AYON_FOLDER_PATH", - "AYON_TASK_NAME", - "AYON_APP_NAME", - "AYON_WORKDIR", - "AYON_APP_NAME", - "AYON_LOG_NO_COLORS", - "AYON_IN_TESTS", - "IS_TEST", # backwards compatibility - ] - def process(self, context): env = context.data.setdefault(FARM_JOB_ENV_DATA_KEY, {}) for key in [ From 07c246ba74ceb628a05ccd11bfea5e20bf393cfe Mon Sep 17 00:00:00 2001 From: ynbot Date: Mon, 25 Nov 2024 13:57:32 +0000 Subject: [PATCH 135/463] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/assign_pr_to_project.yml diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml new file mode 100644 index 0000000000..86707fc9da --- /dev/null +++ b/.github/workflows/assign_pr_to_project.yml @@ -0,0 +1,15 @@ +name: 🔸Auto assign pr +on: + pull_request: + types: + - opened + +jobs: + auto-assign-pr: + uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@develop + with: + repo: "${{ github.repository }}" + project_id: 16 + pull_request_number: ${{ github.event.pull_request.number }} + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} From 333363f024119022437e5b520b2faa7bcf82e392 Mon Sep 17 00:00:00 2001 From: ynbot Date: Mon, 25 Nov 2024 14:07:54 +0000 Subject: [PATCH 136/463] [Automated] Update validate_pr_labels caller workflow --- .github/workflows/validate_pr_labels.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/validate_pr_labels.yml diff --git a/.github/workflows/validate_pr_labels.yml b/.github/workflows/validate_pr_labels.yml new file mode 100644 index 0000000000..00e5742afe --- /dev/null +++ b/.github/workflows/validate_pr_labels.yml @@ -0,0 +1,18 @@ +name: 🔎 Validate PR Labels +on: + pull_request: + types: + - opened + - edited + - labeled + - unlabeled + +jobs: + validate-type-label: + uses: ynput/ops-repo-automation/.github/workflows/validate_pr_labels.yml@develop + with: + repo: "${{ github.repository }}" + pull_request_number: ${{ github.event.pull_request.number }} + query_prefix: "type: " + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} From 463ad79a062b1fe3e23b3351189ef1200de678fb Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Mon, 25 Nov 2024 10:14:38 -0500 Subject: [PATCH 137/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/stagingdir.py | 46 +++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/stagingdir.py index c7cc95ff55..4395f1a5d5 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/stagingdir.py @@ -1,7 +1,7 @@ from ayon_core.lib import Logger, filter_profiles, StringTemplate from ayon_core.settings import get_project_settings -from ayon_core.pipeline.template_data import get_template_data +from .template_data import get_template_data from .anatomy import Anatomy from .tempdir import get_temp_dir @@ -71,7 +71,7 @@ def get_staging_dir_config( template_name = profile["template_name"] _validate_template_name(project_name, template_name, anatomy) - template = anatomy.templates[STAGING_DIR_TEMPLATES][template_name] + template = anatomy.get_template_item("staging", template_name) if not template: # template should always be found either from anatomy or from profile @@ -93,7 +93,7 @@ def _validate_template_name(project_name, template_name, anatomy): Raises: ValueError - if misconfigured template """ - if template_name not in anatomy.templates[STAGING_DIR_TEMPLATES]: + if template_name not in anatomy.templates["staging"]: raise ValueError( ( 'Anatomy of project "{}" does not have set' @@ -195,23 +195,25 @@ def get_staging_dir_info( log=log, ) - if not staging_dir_config: - if always_return_path: # no config found but force an output - return { - "stagingDir": get_temp_dir( - project_name=project_entity["name"], - anatomy=anatomy, - prefix=prefix, - suffix=suffix, - ), - "stagingDir_persistent": False, - } - else: - return None + if staging_dir_config: + return { + "stagingDir": StringTemplate.format_template( + staging_dir_config["template"]["directory"], + ctx_data + ), + "stagingDir_persistent": staging_dir_config["persistence"], + } - return { - "stagingDir": StringTemplate.format_template( - staging_dir_config["template"]["directory"], ctx_data - ), - "stagingDir_persistent": staging_dir_config["persistence"], - } + # no config found but force an output + if always_return_path: + return { + "stagingDir": get_temp_dir( + project_name=project_entity["name"], + anatomy=anatomy, + prefix=prefix, + suffix=suffix, + ), + "stagingDir_persistent": False, + } + + return None From 0a13574509b517e3d3dd796e7f73ee4d42ce10a4 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 10:20:12 -0500 Subject: [PATCH 138/463] Rename stagingdir to staging_dir. --- client/ayon_core/pipeline/__init__.py | 2 +- client/ayon_core/pipeline/publish/lib.py | 2 +- client/ayon_core/pipeline/{stagingdir.py => staging_dir.py} | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) rename client/ayon_core/pipeline/{stagingdir.py => staging_dir.py} (99%) diff --git a/client/ayon_core/pipeline/__init__.py b/client/ayon_core/pipeline/__init__.py index c58e385d79..41bcd0dbd1 100644 --- a/client/ayon_core/pipeline/__init__.py +++ b/client/ayon_core/pipeline/__init__.py @@ -9,7 +9,7 @@ from .anatomy import Anatomy from .tempdir import get_temp_dir -from .stagingdir import get_staging_dir_info +from .staging_dir import get_staging_dir_info from .create import ( BaseCreator, diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 4c36f473d1..c0dfe8c910 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -642,7 +642,7 @@ def get_custom_staging_dir_info( anatomy=None, log=None, ): - from ayon_core.pipeline.stagingdir import get_staging_dir_config + from ayon_core.pipeline.staging_dir import get_staging_dir_config warnings.warn( ( "Function 'get_custom_staging_dir_info' in" diff --git a/client/ayon_core/pipeline/stagingdir.py b/client/ayon_core/pipeline/staging_dir.py similarity index 99% rename from client/ayon_core/pipeline/stagingdir.py rename to client/ayon_core/pipeline/staging_dir.py index 4395f1a5d5..0e993ecae1 100644 --- a/client/ayon_core/pipeline/stagingdir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -5,8 +5,6 @@ from .template_data import get_template_data from .anatomy import Anatomy from .tempdir import get_temp_dir -STAGING_DIR_TEMPLATES = "staging" - def get_staging_dir_config( project_name, From 2d6911513feab68c4aaf3bba66051bf0babbf196 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 10:23:43 -0500 Subject: [PATCH 139/463] Fix lint. --- client/ayon_core/pipeline/staging_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 0e993ecae1..e46426057d 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -203,7 +203,7 @@ def get_staging_dir_info( } # no config found but force an output - if always_return_path: + if always_return_path: return { "stagingDir": get_temp_dir( project_name=project_entity["name"], From 2066fe61a124e0639735cf533ac33894287271cf Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 10:55:04 -0500 Subject: [PATCH 140/463] Fix anatomy template. --- client/ayon_core/pipeline/staging_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index e46426057d..86aaf3002f 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -196,7 +196,7 @@ def get_staging_dir_info( if staging_dir_config: return { "stagingDir": StringTemplate.format_template( - staging_dir_config["template"]["directory"], + str(staging_dir_config["template"]["directory"]), ctx_data ), "stagingDir_persistent": staging_dir_config["persistence"], From a60796eb73f631ed14f8c0c4bcd193b05c40a5c3 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 14:27:13 -0500 Subject: [PATCH 141/463] Adjust missing taskEntity. --- client/ayon_core/pipeline/staging_dir.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 86aaf3002f..c8e3251e7b 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -147,6 +147,7 @@ def get_staging_dir_info( Optional[Dict[str, Any]]: Staging dir info data """ + task_entity = task_entity or {} log = logger or Logger.get_logger("get_staging_dir_info") if anatomy is None: From 1c7ab66246365903fc8aef14be184bf56cc8731c Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 16:07:17 -0500 Subject: [PATCH 142/463] Fix audio extraction from OTIO timeline. --- .../publish/extract_otio_audio_tracks.py | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index 98723beffa..88eb2da059 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -71,20 +71,17 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): name = inst.data["folderPath"] recycling_file = [f for f in created_files if name in f] - - # frameranges - timeline_in_h = inst.data["clipInH"] - timeline_out_h = inst.data["clipOutH"] - fps = inst.data["fps"] - - # create duration - duration = (timeline_out_h - timeline_in_h) + 1 + audio_clip = inst.data["otioClip"] + audio_range = audio_clip.range_in_parent() + duration = audio_range.duration.to_frames() # ffmpeg generate new file only if doesn't exists already if not recycling_file: - # convert to seconds - start_sec = float(timeline_in_h / fps) - duration_sec = float(duration / fps) + parent_track = audio_clip.parent() + parent_track_start = parent_track.range_in_parent().start_time + relative_start_time = audio_range.start_time - parent_track_start + start_sec = relative_start_time.to_seconds() + duration_sec = audio_range.duration.to_seconds() # temp audio file audio_fpath = self.create_temp_file(name) @@ -163,9 +160,7 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): output = [] # go trough all audio tracks - for otio_track in otio_timeline.tracks: - if "Audio" not in otio_track.kind: - continue + for otio_track in otio_timeline.audio_tracks(): self.log.debug("_" * 50) playhead = 0 for otio_clip in otio_track: @@ -173,19 +168,22 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): if isinstance(otio_clip, otio.schema.Gap): playhead += otio_clip.source_range.duration.value elif isinstance(otio_clip, otio.schema.Clip): - start = otio_clip.source_range.start_time.value - duration = otio_clip.source_range.duration.value - fps = otio_clip.source_range.start_time.rate + media_av_start = otio_clip.available_range().start_time + clip_start = otio_clip.source_range.start_time + fps = clip_start.rate + conformed_av_start = media_av_start.rescaled_to(fps) + start = clip_start - conformed_av_start # ffmpeg ignores embedded tc + duration = otio_clip.source_range.duration media_path = otio_clip.media_reference.target_url input = { "mediaPath": media_path, "delayFrame": playhead, - "startFrame": start, - "durationFrame": duration, + "startFrame": start.to_frames(), + "durationFrame": duration.to_frames(), "delayMilSec": int(float(playhead / fps) * 1000), - "startSec": float(start / fps), - "durationSec": float(duration / fps), - "fps": fps + "startSec": start.to_seconds(), + "durationSec": duration.to_seconds(), + "fps": float(fps) } if input not in output: output.append(input) From 3f8430dceac2132629a510e298a56f165a2998a0 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 16:13:28 -0500 Subject: [PATCH 143/463] Fix lint. --- client/ayon_core/plugins/publish/extract_otio_audio_tracks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index 88eb2da059..d80d745111 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -172,7 +172,8 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): clip_start = otio_clip.source_range.start_time fps = clip_start.rate conformed_av_start = media_av_start.rescaled_to(fps) - start = clip_start - conformed_av_start # ffmpeg ignores embedded tc + # ffmpeg ignores embedded tc + start = clip_start - conformed_av_start duration = otio_clip.source_range.duration media_path = otio_clip.media_reference.target_url input = { From d891a0088fdb90bfbddb5bcc332dd3691596a1e1 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 25 Nov 2024 16:14:53 -0500 Subject: [PATCH 144/463] Fix lint. --- client/ayon_core/plugins/publish/extract_otio_audio_tracks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index d80d745111..3d22894a75 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -79,7 +79,8 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): if not recycling_file: parent_track = audio_clip.parent() parent_track_start = parent_track.range_in_parent().start_time - relative_start_time = audio_range.start_time - parent_track_start + relative_start_time = ( + audio_range.start_time - parent_track_start) start_sec = relative_start_time.to_seconds() duration_sec = audio_range.duration.to_seconds() From 0c80fe0ad6d48e854ba0bed5fdeba61e4bcf116f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 25 Nov 2024 23:26:35 +0100 Subject: [PATCH 145/463] The `_representation_conversion` method converts in-place - it does not return anything --- client/ayon_core/pipeline/delivery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/delivery.py b/client/ayon_core/pipeline/delivery.py index 366c261e08..55c840f3a5 100644 --- a/client/ayon_core/pipeline/delivery.py +++ b/client/ayon_core/pipeline/delivery.py @@ -387,7 +387,7 @@ def get_representations_delivery_template_data( # convert representation entity. Fixed in 'ayon_api' 1.0.10. if isinstance(template_data, str): con = ayon_api.get_server_api_connection() - repre_entity = con._representation_conversion(repre_entity) + con._representation_conversion(repre_entity) template_data = repre_entity["context"] template_data.update(copy.deepcopy(general_template_data)) From cd5f89afe572637dc111337ac343655b785dc25f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:58:05 +0100 Subject: [PATCH 146/463] remove OpenPype env key --- client/ayon_core/plugins/publish/collect_farm_env_variables.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 0201973643..7b4618527b 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -23,8 +23,6 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): "AYON_WORKDIR", "AYON_LOG_NO_COLORS", "AYON_IN_TESTS", - # backwards compatibility - "IS_TEST", ]: value = os.getenv(key) if value: From c7940b4fd0f175892139a424ceb9922c5325e820 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:00:25 +0100 Subject: [PATCH 147/463] added comment to workdir env --- .../ayon_core/plugins/publish/collect_farm_env_variables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 7b4618527b..a7d9bce08d 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -14,15 +14,15 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): def process(self, context): env = context.data.setdefault(FARM_JOB_ENV_DATA_KEY, {}) for key in [ - # AYON "AYON_BUNDLE_NAME", "AYON_DEFAULT_SETTINGS_VARIANT", "AYON_PROJECT_NAME", "AYON_FOLDER_PATH", "AYON_TASK_NAME", - "AYON_WORKDIR", "AYON_LOG_NO_COLORS", "AYON_IN_TESTS", + # NOTE Not sure why workdir is needed? + "AYON_WORKDIR", ]: value = os.getenv(key) if value: From a5842c4fdfc01088ad21bc6a3743145878a64ac8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:22:59 +0100 Subject: [PATCH 148/463] added missing env keys for farm --- .../ayon_core/plugins/publish/collect_farm_env_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index a7d9bce08d..2e28b1b164 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -13,9 +13,14 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): def process(self, context): env = context.data.setdefault(FARM_JOB_ENV_DATA_KEY, {}) + + # Disable colored logs on farm + env["AYON_LOG_NO_COLORS"] = "1" + for key in [ "AYON_BUNDLE_NAME", "AYON_DEFAULT_SETTINGS_VARIANT", + "AYON_USERNAME", "AYON_PROJECT_NAME", "AYON_FOLDER_PATH", "AYON_TASK_NAME", From 73420cd8a0c8c3f60bcad65fba29096c7c3de7df Mon Sep 17 00:00:00 2001 From: ynbot Date: Tue, 26 Nov 2024 11:46:45 +0000 Subject: [PATCH 149/463] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 86707fc9da..4bb3d1742c 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -6,7 +6,7 @@ on: jobs: auto-assign-pr: - uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@develop + uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" project_id: 16 From d663d68890fc9c77f4a8e22414c79cbaf5c1e2b5 Mon Sep 17 00:00:00 2001 From: ynbot Date: Tue, 26 Nov 2024 11:52:16 +0000 Subject: [PATCH 150/463] [Automated] Update validate_pr_labels caller workflow --- .github/workflows/validate_pr_labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate_pr_labels.yml b/.github/workflows/validate_pr_labels.yml index 00e5742afe..f25e263c98 100644 --- a/.github/workflows/validate_pr_labels.yml +++ b/.github/workflows/validate_pr_labels.yml @@ -9,7 +9,7 @@ on: jobs: validate-type-label: - uses: ynput/ops-repo-automation/.github/workflows/validate_pr_labels.yml@develop + uses: ynput/ops-repo-automation/.github/workflows/validate_pr_labels.yml@main with: repo: "${{ github.repository }}" pull_request_number: ${{ github.event.pull_request.number }} From 47fa5e56027d7ac182b78b64391cea64f61fa032 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 26 Nov 2024 20:21:21 +0800 Subject: [PATCH 151/463] check on the active product id before adding version_items --- client/ayon_core/tools/sceneinventory/view.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 5892e4f983..93c889d037 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -805,7 +805,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_items_by_project[project_name] = version_items_by_product_id active_version_id = active_repre_info.version_id - # active_product_id = active_repre_info.product_id + active_product_id = active_repre_info.product_id versions = set() product_ids = set() @@ -820,21 +820,23 @@ class SceneInventoryView(QtWidgets.QTreeView): ) versions |= { version_item.version - for version_item in version_items_by_product_id.values() + for version_item in + version_items_by_product_id[active_product_id].values() } - for version_item in version_items_by_product_id.values(): - version = version_item.version - _prod_version = version - if _prod_version < 0: - _prod_version = -1 - product_ids_by_version[_prod_version].add( - version_item.product_id - ) - product_ids.add(version_item.product_id) - if version in versions: - continue - versions.add(version) - version_items.append((project_name, version_item)) + for version_item_by_id in version_items_by_product_id.values(): + for version_item in version_item_by_id.values(): + version = version_item.version + _prod_version = version + if _prod_version < 0: + _prod_version = -1 + product_ids_by_version[_prod_version].add( + version_item.product_id + ) + product_ids.add(version_item.product_id) + if version in versions: + continue + versions.add(version) + version_items.append((project_name, version_item)) def version_sorter(_, item): hero_value = 0 From fafcbe8992e5d22efacdfb72b53bca39c16b5126 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 26 Nov 2024 20:24:57 +0800 Subject: [PATCH 152/463] check on the active product id before adding version_items --- client/ayon_core/tools/sceneinventory/view.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 93c889d037..741587c064 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -805,7 +805,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_items_by_project[project_name] = version_items_by_product_id active_version_id = active_repre_info.version_id - active_product_id = active_repre_info.product_id + # active_product_id = active_repre_info.product_id versions = set() product_ids = set() @@ -818,11 +818,6 @@ class SceneInventoryView(QtWidgets.QTreeView): product_ids_by_version_by_project[project_name] = ( product_ids_by_version ) - versions |= { - version_item.version - for version_item in - version_items_by_product_id[active_product_id].values() - } for version_item_by_id in version_items_by_product_id.values(): for version_item in version_item_by_id.values(): version = version_item.version @@ -838,8 +833,9 @@ class SceneInventoryView(QtWidgets.QTreeView): versions.add(version) version_items.append((project_name, version_item)) - def version_sorter(_, item): + def version_sorter(items): hero_value = 0 + item = items[-1] i_version = item.version if i_version < 0: hero_value = 1 From 10e66c4c39fb6505823255e1e0b040e1c4dacb69 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 26 Nov 2024 20:30:28 +0800 Subject: [PATCH 153/463] comsetic fix --- client/ayon_core/tools/sceneinventory/model.py | 9 ++++++--- .../ayon_core/tools/sceneinventory/models/containers.py | 3 ++- client/ayon_core/tools/sceneinventory/view.py | 7 +++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 79af0e5cf5..235b125eab 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -273,7 +273,8 @@ class InventoryModel(QtGui.QStandardItemModel): ) = self._get_status_data(project_name, status_name) repre_name = ( - repre_info.representation_name or "" + repre_info.representation_name or + "" ) container_model_items = [] for container_item in container_items: @@ -281,7 +282,8 @@ class InventoryModel(QtGui.QStandardItemModel): unique_name = repre_name + object_name item = QtGui.QStandardItem() item.setColumnCount(root_item.columnCount()) - item.setData(container_item.namespace, QtCore.Qt.DisplayRole) + item.setData(container_item.namespace, + QtCore.Qt.DisplayRole) item.setData(self.GRAYOUT_COLOR, NAME_COLOR_ROLE) item.setData(self.GRAYOUT_COLOR, VERSION_COLOR_ROLE) item.setData(item_icon, QtCore.Qt.DecorationRole) @@ -290,7 +292,8 @@ class InventoryModel(QtGui.QStandardItemModel): item.setData(version_label, VERSION_LABEL_ROLE) item.setData(container_item.loader_name, LOADER_NAME_ROLE) item.setData(container_item.object_name, OBJECT_NAME_ROLE) - item.setData(container_item.project_name, PROJECT_NAME_ROLE) + item.setData(container_item.project_name, + PROJECT_NAME_ROLE) item.setData(True, IS_CONTAINER_ITEM_ROLE) item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) container_model_items.append(item) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index b1cbb38587..ec1ed39e87 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -362,7 +362,8 @@ class ContainersModel: current_project_name = self._controller.get_current_project_name() for container in containers: try: - item = ContainerItem.from_container_data(current_project_name, container) + item = ContainerItem.from_container_data( + current_project_name, container) repre_id = item.representation_id try: uuid.UUID(repre_id) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 741587c064..ba23e115c0 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -802,7 +802,9 @@ class SceneInventoryView(QtWidgets.QTreeView): ) repre_info_by_project[project_name] = repres_info - version_items_by_project[project_name] = version_items_by_product_id + version_items_by_project[project_name] = ( + version_items_by_product_id + ) active_version_id = active_repre_info.version_id # active_product_id = active_repre_info.product_id @@ -994,7 +996,8 @@ class SceneInventoryView(QtWidgets.QTreeView): def _on_switch_to_versioned(self, item_ids): # Get container items by ID - containers_items_by_id = self._controller.get_container_items_by_id(item_ids) + containers_items_by_id = self._controller.get_container_items_by_id( + item_ids) # Extract project names and their corresponding representation IDs repre_ids_by_project = collections.defaultdict(set) for container_item in containers_items_by_id.values(): From 899b50ec93a5480d63e6c219119eeff5be4e1683 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 26 Nov 2024 09:08:59 -0500 Subject: [PATCH 154/463] Adjust for missing reference. --- .../plugins/publish/extract_otio_audio_tracks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py index 3d22894a75..472694d334 100644 --- a/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py +++ b/client/ayon_core/plugins/publish/extract_otio_audio_tracks.py @@ -166,9 +166,8 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): playhead = 0 for otio_clip in otio_track: self.log.debug(otio_clip) - if isinstance(otio_clip, otio.schema.Gap): - playhead += otio_clip.source_range.duration.value - elif isinstance(otio_clip, otio.schema.Clip): + if (isinstance(otio_clip, otio.schema.Clip) and + not otio_clip.media_reference.is_missing_reference): media_av_start = otio_clip.available_range().start_time clip_start = otio_clip.source_range.start_time fps = clip_start.rate @@ -190,7 +189,8 @@ class ExtractOtioAudioTracks(pyblish.api.ContextPlugin): if input not in output: output.append(input) self.log.debug("__ input: {}".format(input)) - playhead += otio_clip.source_range.duration.value + + playhead += otio_clip.source_range.duration.value return output From 7ccf04ed586b69200d3fba744ef6ab470d86577d Mon Sep 17 00:00:00 2001 From: ynbot Date: Tue, 26 Nov 2024 15:17:38 +0000 Subject: [PATCH 155/463] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 4bb3d1742c..92d2ff2916 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -1,5 +1,16 @@ name: 🔸Auto assign pr on: + workflow_dispatch: + inputs: + pr_number: + type: number + description: "Run workflow for this PR number" + required: true + project_id: + type: number + description: "Github Project Number" + required: true + default: 16 pull_request: types: - opened @@ -9,7 +20,7 @@ jobs: uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" - project_id: 16 - pull_request_number: ${{ github.event.pull_request.number }} + project_id: ${{ inputs.project_id || 16 }} + pull_request_number: ${{ github.event.pull_request.number || inputs.pr_number }} secrets: token: ${{ secrets.YNPUT_BOT_TOKEN }} From 5d7aeaf0a707efac9347213c56a8d27cd741c88a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:50:48 +0100 Subject: [PATCH 156/463] better variable name --- client/ayon_core/tools/sceneinventory/view.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index ba23e115c0..43c6c8e2d0 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -820,8 +820,8 @@ class SceneInventoryView(QtWidgets.QTreeView): product_ids_by_version_by_project[project_name] = ( product_ids_by_version ) - for version_item_by_id in version_items_by_product_id.values(): - for version_item in version_item_by_id.values(): + for version_items_by_id in version_items_by_product_id.values(): + for version_item in version_items_by_id.values(): version = version_item.version _prod_version = version if _prod_version < 0: From 1776df18e2375e66a0520fddd9d8ef919d6bcdb3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:50:59 +0100 Subject: [PATCH 157/463] don't store project name to version items --- client/ayon_core/tools/sceneinventory/view.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 43c6c8e2d0..a95a51ae37 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -833,11 +833,10 @@ class SceneInventoryView(QtWidgets.QTreeView): if version in versions: continue versions.add(version) - version_items.append((project_name, version_item)) + version_items.append(version_item) - def version_sorter(items): + def version_sorter(item): hero_value = 0 - item = items[-1] i_version = item.version if i_version < 0: hero_value = 1 @@ -855,8 +854,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_options = [] active_version_idx = 0 - for idx, item in enumerate(version_items): - project_name, version_item = item + for idx, version_item in enumerate(version_items): version = version_item.version label = format_version(version) if version_item.version_id == active_version_id: From 5c115ce166b70391048b83597d06635783da1118 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:04:11 +0100 Subject: [PATCH 158/463] switch dialog can work per project --- .../sceneinventory/switch_dialog/dialog.py | 18 ++++++++------ client/ayon_core/tools/sceneinventory/view.py | 24 ++++++++++++++----- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py index 4977ad13c6..a6d88ed44a 100644 --- a/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py +++ b/client/ayon_core/tools/sceneinventory/switch_dialog/dialog.py @@ -46,8 +46,13 @@ class SwitchAssetDialog(QtWidgets.QDialog): switched = QtCore.Signal() - def __init__(self, controller, parent=None, items=None): - super(SwitchAssetDialog, self).__init__(parent) + def __init__(self, controller, project_name, items, parent=None): + super().__init__(parent) + + current_project_name = controller.get_current_project_name() + folder_id = None + if current_project_name == project_name: + folder_id = controller.get_current_folder_id() self.setWindowTitle("Switch selected items ...") @@ -147,11 +152,10 @@ class SwitchAssetDialog(QtWidgets.QDialog): self._init_repre_name = None self._fill_check = False + self._project_name = project_name + self._folder_id = folder_id - self._project_name = controller.get_current_project_name() - self._folder_id = controller.get_current_folder_id() - - self._current_folder_btn.setEnabled(self._folder_id is not None) + self._current_folder_btn.setEnabled(folder_id is not None) self._controller = controller @@ -159,7 +163,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): self._prepare_content_data() def showEvent(self, event): - super(SwitchAssetDialog, self).showEvent(event) + super().showEvent(event) self._show_timer.start() def refresh(self, init_refresh=False): diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index a95a51ae37..918de6f7a4 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -912,14 +912,26 @@ class SceneInventoryView(QtWidgets.QTreeView): def _show_switch_dialog(self, item_ids): """Display Switch dialog""" - containers_by_id = self._controller.get_containers_by_item_ids( + container_items_by_id = self._controller.get_container_items_by_id( item_ids ) - dialog = SwitchAssetDialog( - self._controller, self, list(containers_by_id.values()) - ) - dialog.switched.connect(self.data_changed.emit) - dialog.show() + container_ids_by_project_name = collections.defaultdict(set) + for container_id, container_item in container_items_by_id.values(): + project_name = container_item.project_name + container_ids_by_project_name[project_name].add(container_id) + + for project_name, container_ids in container_ids_by_project_name.items(): + containers_by_id = self._controller.get_containers_by_item_ids( + container_ids + ) + dialog = SwitchAssetDialog( + self._controller, + project_name, + list(containers_by_id.values()), + self + ) + dialog.switched.connect(self.data_changed.emit) + dialog.show() def _show_remove_warning_dialog(self, item_ids): """Prompt a dialog to inform the user the action will remove items""" From 2eb97b972e0adbaef92d3a381b127d52aefa7b0d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:12:04 +0100 Subject: [PATCH 159/463] show project name on group instead of items --- client/ayon_core/tools/sceneinventory/model.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 235b125eab..885553acaf 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -292,8 +292,6 @@ class InventoryModel(QtGui.QStandardItemModel): item.setData(version_label, VERSION_LABEL_ROLE) item.setData(container_item.loader_name, LOADER_NAME_ROLE) item.setData(container_item.object_name, OBJECT_NAME_ROLE) - item.setData(container_item.project_name, - PROJECT_NAME_ROLE) item.setData(True, IS_CONTAINER_ITEM_ROLE) item.setData(unique_name, ITEM_UNIQUE_NAME_ROLE) container_model_items.append(item) @@ -323,6 +321,7 @@ class InventoryModel(QtGui.QStandardItemModel): group_item.setData(status_short, STATUS_SHORT_ROLE) group_item.setData(status_color, STATUS_COLOR_ROLE) group_item.setData(status_icon, STATUS_ICON_ROLE) + group_item.setData(project_name, PROJECT_NAME_ROLE) group_item.setData( active_site_progress, ACTIVE_SITE_PROGRESS_ROLE From 1ab7a652a3e38fe995105a3658be0ee48cc25a82 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:13:55 +0100 Subject: [PATCH 160/463] fix formatting --- client/ayon_core/tools/sceneinventory/view.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 918de6f7a4..fd67c43ac7 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -920,7 +920,9 @@ class SceneInventoryView(QtWidgets.QTreeView): project_name = container_item.project_name container_ids_by_project_name[project_name].add(container_id) - for project_name, container_ids in container_ids_by_project_name.items(): + for project_name, container_ids in ( + container_ids_by_project_name.items() + ): containers_by_id = self._controller.get_containers_by_item_ids( container_ids ) From 2842c904d161b05d5e6d8b19473a41a67b6d8646 Mon Sep 17 00:00:00 2001 From: ynbot Date: Fri, 29 Nov 2024 08:17:04 +0000 Subject: [PATCH 161/463] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 92d2ff2916..14e1a02075 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -17,10 +17,11 @@ on: jobs: auto-assign-pr: + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" - project_id: ${{ inputs.project_id || 16 }} - pull_request_number: ${{ github.event.pull_request.number || inputs.pr_number }} + project_id: "${{ inputs.project_id }}" + pull_request_number: "${{ github.event.pull_request.number || inputs.pr_number }}" secrets: token: ${{ secrets.YNPUT_BOT_TOKEN }} From 630f7f6c1e7c53cb69a92a4ed1b42820806e2bb4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:59:14 +0100 Subject: [PATCH 162/463] fill values for farm with correct values --- .../publish/collect_farm_env_variables.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 2e28b1b164..cb52e5c32e 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -2,6 +2,7 @@ import os import pyblish.api +from ayon_core.lib import get_ayon_username from ayon_core.pipeline.publish import FARM_JOB_ENV_DATA_KEY @@ -15,16 +16,22 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): env = context.data.setdefault(FARM_JOB_ENV_DATA_KEY, {}) # Disable colored logs on farm - env["AYON_LOG_NO_COLORS"] = "1" + for key, value in ( + ("AYON_LOG_NO_COLORS", "1"), + ("AYON_PROJECT_NAME", context.data["projectName"]), + ("AYON_FOLDER_PATH", context.data.get("folderPath")), + ("AYON_TASK_NAME", context.data.get("task")), + # NOTE we should use 'context.data["user"]' but that has higher + # order. + ("AYON_USERNAME", get_ayon_username()), + ): + if value: + self.log.debug(f"Setting job env: {key}: {value}") + env[key] = value for key in [ "AYON_BUNDLE_NAME", "AYON_DEFAULT_SETTINGS_VARIANT", - "AYON_USERNAME", - "AYON_PROJECT_NAME", - "AYON_FOLDER_PATH", - "AYON_TASK_NAME", - "AYON_LOG_NO_COLORS", "AYON_IN_TESTS", # NOTE Not sure why workdir is needed? "AYON_WORKDIR", From fcbf8ddd91f5ad2e39ba807b24533638d81e985d Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 29 Nov 2024 10:25:25 +0000 Subject: [PATCH 163/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index ab8c9424fa..a7373cd291 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.9+dev" +__version__ = "1.0.10" diff --git a/package.py b/package.py index b90db4cde4..b14c38bdd5 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.9+dev" +version = "1.0.10" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index d09fabf8b2..31f00a0fc2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.9+dev" +version = "1.0.10" description = "" authors = ["Ynput Team "] readme = "README.md" From 457f234266f0a55486b946a626923abe341df5db Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 29 Nov 2024 10:26:08 +0000 Subject: [PATCH 164/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index a7373cd291..b2ece45120 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.10" +__version__ = "1.0.10+dev" diff --git a/package.py b/package.py index b14c38bdd5..58ae5c08d9 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.10" +version = "1.0.10+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 31f00a0fc2..d7cf9fa6ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.10" +version = "1.0.10+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From fdc351f4d05457516191ddf305482c8128296f69 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Nov 2024 14:33:46 +0200 Subject: [PATCH 165/463] fix a typo --- client/ayon_core/tools/attribute_defs/files_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index 46399c5fce..aa500720ed 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -254,7 +254,7 @@ class FilesModel(QtGui.QStandardItemModel): """Make sure that removed items are removed from items mapping. Connected with '_on_insert'. When user drag item and drop it to same - view the item is actually removed and creted again but it happens in + view the item is actually removed and created again but it happens in inner calls of Qt. """ From f40ee8f54793dd8125006935d7ea9d4fc2048fef Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 29 Nov 2024 14:34:24 +0200 Subject: [PATCH 166/463] add missing argument in `context_menu_requested` signal --- client/ayon_core/tools/attribute_defs/files_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index aa500720ed..6199d0c202 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -522,7 +522,7 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel): class ItemWidget(QtWidgets.QWidget): - context_menu_requested = QtCore.Signal(QtCore.QPoint) + context_menu_requested = QtCore.Signal(QtCore.QPoint, bool) def __init__( self, item_id, label, pixmap_icon, is_sequence, multivalue, parent=None @@ -589,7 +589,7 @@ class ItemWidget(QtWidgets.QWidget): def _on_actions_clicked(self): pos = self._split_btn.rect().bottomLeft() point = self._split_btn.mapToGlobal(pos) - self.context_menu_requested.emit(point) + self.context_menu_requested.emit(point, False) class InViewButton(IconButton): From 230ae53e4e2d1fbf2ce1c2765b657a6d64365d69 Mon Sep 17 00:00:00 2001 From: ynbot Date: Sat, 30 Nov 2024 14:31:16 +0000 Subject: [PATCH 167/463] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 14e1a02075..e61d281c2a 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -3,25 +3,46 @@ on: workflow_dispatch: inputs: pr_number: - type: number + type: string description: "Run workflow for this PR number" required: true project_id: - type: number + type: string description: "Github Project Number" required: true - default: 16 + default: "16" pull_request: types: - opened +env: + GH_TOKEN: ${{ github.token }} + jobs: + get-pr-repo: + runs-on: ubuntu-latest + outputs: + pr_repo_name: ${{ steps.get-repo-name.outputs.repo_name || github.event.pull_request.head.repo.full_name }} + + # INFO `github.event.pull_request.head.repo.full_name` is not available on manual triggered (dispatched) runs + steps: + - name: Get PR repo name + if: ${{ github.event_name == 'workflow_dispatch' }} + id: get-repo-name + run: | + repo_name=$(gh pr view ${{ inputs.pr_number }} --json headRepository,headRepositoryOwner --repo ${{ github.repository }} | jq -r '.headRepositoryOwner.login + "/" + .headRepository.name') + echo "repo_name=$repo_name" >> $GITHUB_OUTPUT + auto-assign-pr: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + needs: + - get-pr-repo + if: ${{ needs.get-pr-repo.outputs.pr_repo_name == github.repository }} uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" - project_id: "${{ inputs.project_id }}" - pull_request_number: "${{ github.event.pull_request.number || inputs.pr_number }}" + project_id: ${{ inputs.project_id != '' && fromJSON(inputs.project_id) || 16 }} + pull_request_number: ${{ github.event.pull_request.number || fromJSON(inputs.pr_number) }} secrets: - token: ${{ secrets.YNPUT_BOT_TOKEN }} + # INFO fallback to default `github.token` is required for PRs from forks + # INFO organization secrets won't be available to forks + token: ${{ secrets.YNPUT_BOT_TOKEN || github.token}} From f5a67f099d291f29789230931bc29f9b9184b4e7 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 2 Dec 2024 14:58:42 -0500 Subject: [PATCH 168/463] Append {version} regex to staging dir. --- .../pipeline/create/creator_plugins.py | 17 +++++++++++--- client/ayon_core/pipeline/publish/lib.py | 8 ++++--- client/ayon_core/pipeline/staging_dir.py | 23 +++++++++++-------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 93e1f6f5cb..37f3e5b943 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Optional, Dict, Any from abc import ABC, abstractmethod from ayon_core.settings import get_project_settings -from ayon_core.lib import Logger +from ayon_core.lib import Logger, get_version_from_path from ayon_core.pipeline.plugin_discover import ( discover, register_plugin, @@ -860,6 +860,14 @@ class Creator(BaseCreator): else: template_data = {} + # TODO: confirm feature + anatomy_data_settings = self.project_settings["core"]["publish"]["CollectAnatomyInstanceData"] + follow_workfile_version = anatomy_data_settings["follow_workfile_version"] + if follow_workfile_version: + current_workfile = self.create_context.get_current_workfile_path() + workfile_version = get_version_from_path(current_workfile) + template_data = {"version": int(workfile_version)} + staging_dir_info = get_staging_dir_info( create_ctx.get_current_project_entity(), create_ctx.get_current_folder_entity(), @@ -877,12 +885,15 @@ class Creator(BaseCreator): if not staging_dir_info: return None - staging_dir_path = staging_dir_info["stagingDir"] + staging_dir_path = staging_dir_info.dir # path might be already created by get_staging_dir_info os.makedirs(staging_dir_path, exist_ok=True) - instance.transient_data.update(staging_dir_info) + instance.transient_data.update({ + "stagingDir": staging_dir_path, + "stagingDir_persistent": staging_dir_info.persistent, + }) self.log.info(f"Applied staging dir to instance: {staging_dir_path}") diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index c0dfe8c910..b86e439b72 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -710,12 +710,14 @@ def get_instance_staging_dir(instance): always_return_path=True, ) - staging_dir_path = staging_dir_info["stagingDir"] + staging_dir_path = staging_dir_info.dir # path might be already created by get_staging_dir_info os.makedirs(staging_dir_path, exist_ok=True) - - instance.data.update(staging_dir_info) + instance.data.update({ + "stagingDir": staging_dir_path, + "stagingDir_persistent": staging_dir_info.persistent, + }) return staging_dir_path diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index c8e3251e7b..fa216712b2 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -1,3 +1,5 @@ +from collections import namedtuple + from ayon_core.lib import Logger, filter_profiles, StringTemplate from ayon_core.settings import get_project_settings @@ -6,6 +8,9 @@ from .anatomy import Anatomy from .tempdir import get_temp_dir +StagingDir = namedtuple("StagingDir", ["dir", "persistent"]) + + def get_staging_dir_config( project_name, task_type, @@ -144,7 +149,7 @@ def get_staging_dir_info( suffix (Optional[str]): Optional suffix for staging dir name. Returns: - Optional[Dict[str, Any]]: Staging dir info data + Optional[StagingDir]: Staging dir info data """ task_entity = task_entity or {} @@ -195,24 +200,24 @@ def get_staging_dir_info( ) if staging_dir_config: - return { - "stagingDir": StringTemplate.format_template( + return StagingDir( + StringTemplate.format_template( str(staging_dir_config["template"]["directory"]), ctx_data ), - "stagingDir_persistent": staging_dir_config["persistence"], - } + staging_dir_config["persistence"], + ) # no config found but force an output if always_return_path: - return { - "stagingDir": get_temp_dir( + return StagingDir( + get_temp_dir( project_name=project_entity["name"], anatomy=anatomy, prefix=prefix, suffix=suffix, ), - "stagingDir_persistent": False, - } + False, + ) return None From fa014fa93cdd311ea7086d5cc3216deb14c3c14d Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 2 Dec 2024 15:02:08 -0500 Subject: [PATCH 169/463] Fix lint. --- client/ayon_core/pipeline/create/creator_plugins.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 37f3e5b943..87f67a3e80 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -861,8 +861,9 @@ class Creator(BaseCreator): template_data = {} # TODO: confirm feature - anatomy_data_settings = self.project_settings["core"]["publish"]["CollectAnatomyInstanceData"] - follow_workfile_version = anatomy_data_settings["follow_workfile_version"] + publish_settings = self.project_settings["core"]["publish"] + anatomy_settings = publish_settings["CollectAnatomyInstanceData"] + follow_workfile_version = anatomy_settings["follow_workfile_version"] if follow_workfile_version: current_workfile = self.create_context.get_current_workfile_path() workfile_version = get_version_from_path(current_workfile) From 9a0e490233151c116ea0ec2e88ad93fde1d7adda Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 3 Dec 2024 16:52:19 +0800 Subject: [PATCH 170/463] use items() for key, value --- client/ayon_core/tools/sceneinventory/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index fd67c43ac7..bb95e37d4e 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -916,7 +916,7 @@ class SceneInventoryView(QtWidgets.QTreeView): item_ids ) container_ids_by_project_name = collections.defaultdict(set) - for container_id, container_item in container_items_by_id.values(): + for container_id, container_item in container_items_by_id.items(): project_name = container_item.project_name container_ids_by_project_name[project_name].add(container_id) From 648c2c52fe0ac20e1f8bcdb74c19aa6089db0fff Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Tue, 3 Dec 2024 10:50:09 -0500 Subject: [PATCH 171/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../pipeline/create/creator_plugins.py | 11 +++-- client/ayon_core/pipeline/staging_dir.py | 43 ++++++++++--------- client/ayon_core/pipeline/tempdir.py | 3 +- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 87f67a3e80..780cb71fca 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -862,8 +862,11 @@ class Creator(BaseCreator): # TODO: confirm feature publish_settings = self.project_settings["core"]["publish"] - anatomy_settings = publish_settings["CollectAnatomyInstanceData"] - follow_workfile_version = anatomy_settings["follow_workfile_version"] + follow_workfile_version = ( + publish_settings + ["CollectAnatomyInstanceData"] + ["follow_workfile_version"] + ) if follow_workfile_version: current_workfile = self.create_context.get_current_workfile_path() workfile_version = get_version_from_path(current_workfile) @@ -871,8 +874,8 @@ class Creator(BaseCreator): staging_dir_info = get_staging_dir_info( create_ctx.get_current_project_entity(), - create_ctx.get_current_folder_entity(), - create_ctx.get_current_task_entity(), + create_ctx.get_folder_entity(folder_path), + create_ctx.get_task_entity(folder_path, instance.get("task")), product_type, product_name, create_ctx.host_name, diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index fa216712b2..3c1c7c1ab2 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -8,7 +8,10 @@ from .anatomy import Anatomy from .tempdir import get_temp_dir -StagingDir = namedtuple("StagingDir", ["dir", "persistent"]) +@dataclass +class StagingDir: + directory: str + persistent: bool def get_staging_dir_config( @@ -78,10 +81,9 @@ def get_staging_dir_config( if not template: # template should always be found either from anatomy or from profile - raise ValueError( - "Staging dir profile is misconfigured! " - f"No template was found for profile: {profile}! " - "Check your project settings at: " + raise KeyError( + f"Staging template '{template_name}' was not found." + "Check project anatomy or settings at: " "'ayon+settings://core/tools/publish/custom_staging_dir_profiles'" ) @@ -98,10 +100,8 @@ def _validate_template_name(project_name, template_name, anatomy): """ if template_name not in anatomy.templates["staging"]: raise ValueError( - ( - 'Anatomy of project "{}" does not have set' - ' "{}" template key at Staging Dir section!' - ).format(project_name, template_name) + f'Anatomy of project "{project_name}" does not have set' + f' "{template_name}" template key at Staging Dir category!' ) @@ -131,14 +131,14 @@ def get_staging_dir_info( Arguments: host_name (str): Name of host. project_entity (Dict[str, Any]): Project entity. - folder_entity (Dict[str, Any]): Folder entity. - task_entity (Dict[str, Any]): Task entity. + folder_entity (Optional[Dict[str, Any]]): Folder entity. + task_entity (Optional[Dict[str, Any]]): Task entity. product_type (str): Type of product. product_name (str): Name of product. - anatomy (ayon_core.pipeline.Anatomy): Anatomy object. + anatomy (Optional[Anatomy]): Anatomy object. project_settings (Optional[Dict[str, Any]]): Prepared project settings. - template_data (Optional[Dict[str, Any]]): Data for formatting staging - dir template. + template_data (Optional[Dict[str, Any]]): Additional data for + formatting staging dir template. always_return_path (Optional[bool]): If True, staging dir will be created as tempdir if no staging dir profile is found. Input value False will return None if no staging dir profile is found. @@ -152,7 +152,6 @@ def get_staging_dir_info( Optional[StagingDir]: Staging dir info data """ - task_entity = task_entity or {} log = logger or Logger.get_logger("get_staging_dir_info") if anatomy is None: @@ -185,12 +184,16 @@ def get_staging_dir_info( # add additional template formatting data if template_data: ctx_data.update(template_data) + task_name = task_type = None + if task_entity: + task_name = task_entity["name"] + task_type = task_entity["taskType"] # get staging dir config staging_dir_config = get_staging_dir_config( project_entity["name"], - task_entity.get("taskType"), - task_entity.get("name"), + task_type, + task_name , product_type, product_name, host_name, @@ -200,11 +203,9 @@ def get_staging_dir_info( ) if staging_dir_config: + dir_template = staging_dir_config["template"]["directory"] return StagingDir( - StringTemplate.format_template( - str(staging_dir_config["template"]["directory"]), - ctx_data - ), + dir_template.format_strict(ctx_data), staging_dir_config["persistence"], ) diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index af2ff44a8f..52995d3f6a 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -36,7 +36,8 @@ def get_temp_dir( str: Path to staging dir of instance. """ - prefix = prefix or "ay_tmp_" + if prefix is None: + prefix = "ay_tmp_" suffix = suffix or "" if use_local_temp: From 0672f5c8bb2125ebaafed227f48111e1b1396aeb Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 3 Dec 2024 11:38:29 -0500 Subject: [PATCH 172/463] Address feedback from PR. --- .../pipeline/create/creator_plugins.py | 57 ++++++++++++------- client/ayon_core/pipeline/publish/lib.py | 2 +- client/ayon_core/pipeline/staging_dir.py | 9 +-- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 780cb71fca..6ccafe1bc7 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -833,17 +833,15 @@ class Creator(BaseCreator): """ return self.pre_create_attr_defs - def apply_staging_dir(self, instance): - """Apply staging dir with persistence to instance's transient data. - - Method is called on instance creation and on instance update. + def get_staging_dir(self, instance): + """Return the staging dir and persistence from instance. Args: instance (CreatedInstance): Instance for which should be staging - dir applied. + dir gathered. Returns: - Optional[str]: Staging dir path or None if not applied. + Optional[namedtuple]: Staging dir path and persistence or None """ create_ctx = self.create_context product_name = instance.get("productName") @@ -852,25 +850,32 @@ class Creator(BaseCreator): # this can only work if product name and folder path are available if not product_name or not folder_path: - return + return None - version = instance.get("version") - if version is not None: - template_data = {"version": version} - else: - template_data = {} - - # TODO: confirm feature publish_settings = self.project_settings["core"]["publish"] follow_workfile_version = ( publish_settings ["CollectAnatomyInstanceData"] ["follow_workfile_version"] ) - if follow_workfile_version: + + # Gather version number provided from the instance. + version = instance.get("version") + + # If follow workfile, gather version from workfile path. + if version is None and follow_workfile_version: current_workfile = self.create_context.get_current_workfile_path() workfile_version = get_version_from_path(current_workfile) - template_data = {"version": int(workfile_version)} + version = int(workfile_version) + + # Fill-up version with next version available. + elif version is None: + versions = self.get_next_versions_for_instances( + [instance] + ) + version, = tuple(versions.values()) + + template_data = {"version": version} staging_dir_info = get_staging_dir_info( create_ctx.get_current_project_entity(), @@ -886,12 +891,26 @@ class Creator(BaseCreator): template_data=template_data, ) - if not staging_dir_info: + return staging_dir_info or None + + def apply_staging_dir(self, instance): + """Apply staging dir with persistence to instance's transient data. + + Method is called on instance creation and on instance update. + + Args: + instance (CreatedInstance): Instance for which should be staging + dir applied. + + Returns: + Optional[str]: Staging dir path or None if not applied. + """ + staging_dir_info = self.get_staging_dir(instance) + if staging_dir_info is None: return None - staging_dir_path = staging_dir_info.dir - # path might be already created by get_staging_dir_info + staging_dir_path = staging_dir_info.directory os.makedirs(staging_dir_path, exist_ok=True) instance.transient_data.update({ diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index b86e439b72..2ba40d7687 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -710,7 +710,7 @@ def get_instance_staging_dir(instance): always_return_path=True, ) - staging_dir_path = staging_dir_info.dir + staging_dir_path = staging_dir_info.directory # path might be already created by get_staging_dir_info os.makedirs(staging_dir_path, exist_ok=True) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 3c1c7c1ab2..0317e55720 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -1,6 +1,6 @@ -from collections import namedtuple +from dataclasses import dataclass -from ayon_core.lib import Logger, filter_profiles, StringTemplate +from ayon_core.lib import Logger, filter_profiles from ayon_core.settings import get_project_settings from .template_data import get_template_data @@ -42,7 +42,7 @@ def get_staging_dir_config( Dict or None: Data with directory template and is_persistent or None Raises: - ValueError - if misconfigured template should be used + KeyError - if misconfigured template should be used """ settings = project_settings or get_project_settings(project_name) @@ -129,12 +129,12 @@ def get_staging_dir_info( If `prefix` or `suffix` is not set, default values will be used. Arguments: - host_name (str): Name of host. project_entity (Dict[str, Any]): Project entity. folder_entity (Optional[Dict[str, Any]]): Folder entity. task_entity (Optional[Dict[str, Any]]): Task entity. product_type (str): Type of product. product_name (str): Name of product. + host_name (str): Name of host. anatomy (Optional[Anatomy]): Anatomy object. project_settings (Optional[Dict[str, Any]]): Prepared project settings. template_data (Optional[Dict[str, Any]]): Additional data for @@ -184,6 +184,7 @@ def get_staging_dir_info( # add additional template formatting data if template_data: ctx_data.update(template_data) + task_name = task_type = None if task_entity: task_name = task_entity["name"] From ed7752a4df7fdfc2c1a27de98ccf890c9a5395ea Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 3 Dec 2024 16:56:53 -0500 Subject: [PATCH 173/463] Fix extract_otio_review --- client/ayon_core/plugins/publish/extract_otio_review.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index fb9b269258..c8d2086865 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -78,6 +78,7 @@ class ExtractOTIOReview( if otio_review_clips is None: self.log.info(f"Instance `{instance}` has no otioReviewClips") + return # add plugin wide attributes self.representation_files = [] From 9bcc9b40191d0ecdbd435e5a58e714f6dfbd86fe Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:08:04 +0100 Subject: [PATCH 174/463] fix 'realy' typo to 'really' --- client/ayon_core/lib/path_templates.py | 35 +++++++++++++++++++------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index 9b545f2851..bc4ed648b7 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -292,7 +292,7 @@ class TemplatePartResult: # Used values stored by key with all modifirs # - value is already formatted string # Example: {"version:0>3": "001"} - self._realy_used_values = {} + self._really_used_values: Dict[str, Any] = {} # Concatenated string output after formatting self._output = "" # Is this result from optional part @@ -314,7 +314,7 @@ class TemplatePartResult: if other.optional and not other.solved: return self._used_values.update(other.used_values) - self._realy_used_values.update(other.realy_used_values) + self._really_used_values.update(other.really_used_values) else: raise TypeError("Cannot add data from \"{}\" to \"{}\"".format( @@ -359,8 +359,17 @@ class TemplatePartResult: return self._invalid_optional_types @property - def realy_used_values(self): - return self._realy_used_values + def really_used_values(self) -> Dict[str, Any]: + return self._really_used_values + + @property + def realy_used_values(self) -> Dict[str, Any]: + warnings.warn( + "Property 'realy_used_values' is deprecated." + " Use 'really_used_values' instead.", + DeprecationWarning + ) + return self._really_used_values @property def used_values(self): @@ -391,8 +400,16 @@ class TemplatePartResult: return self.split_keys_to_subdicts(new_used_values) - def add_realy_used_value(self, key, value): - self._realy_used_values[key] = value + def add_really_used_value(self, key: str, value: Any): + self._really_used_values[key] = value + + def add_realy_used_value(self, key: str, value: Any): + warnings.warn( + "Method 'add_realy_used_value' is deprecated." + " Use 'add_really_used_value' instead.", + DeprecationWarning + ) + self.add_really_used_value(key, value) def add_used_value(self, key, value): self._used_values[key] = value @@ -519,8 +536,8 @@ class FormattingPart: result(TemplatePartResult): Object where result is stored. """ key = self._template_base - if key in result.realy_used_values: - result.add_output(result.realy_used_values[key]) + if key in result.really_used_values: + result.add_output(result.really_used_values[key]) return result # ensure key is properly formed [({})] properly closed. @@ -625,7 +642,7 @@ class FormattingPart: used_value = value else: used_value = formatted_value - result.add_realy_used_value(self._field_name, used_value) + result.add_really_used_value(self._field_name, used_value) result.add_used_value(used_key, used_value) result.add_output(formatted_value) return result From a80bbfbd5764b33bf51e44b0e92152e6311b58e0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:13:33 +0100 Subject: [PATCH 175/463] added basic typehints --- client/ayon_core/lib/path_templates.py | 174 +++++++++++++++---------- 1 file changed, 103 insertions(+), 71 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index bc4ed648b7..3e66344ff4 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -2,8 +2,13 @@ import os import re import copy import numbers -from typing import List +import warnings from string import Formatter +import typing +from typing import List, Dict, Any, Set, Optional + +if typing.TYPE_CHECKING: + from typing import Union SUB_DICT_PATTERN = re.compile(r"([^\[\]]+)") OPTIONAL_PATTERN = re.compile(r"(<.*?[^{0]*>)[^0-9]*?") @@ -19,9 +24,7 @@ class TemplateUnsolved(Exception): def __init__(self, template, missing_keys, invalid_types): invalid_type_items = [] for _key, _type in invalid_types.items(): - invalid_type_items.append( - "\"{0}\" {1}".format(_key, str(_type)) - ) + invalid_type_items.append(f"\"{_key}\" {str(_type)}") invalid_types_msg = "" if invalid_type_items: @@ -34,20 +37,21 @@ class TemplateUnsolved(Exception): missing_keys_msg = self.missing_keys_msg.format( ", ".join(missing_keys) ) - super(TemplateUnsolved, self).__init__( + super().__init__( self.msg.format(template, missing_keys_msg, invalid_types_msg) ) class StringTemplate: """String that can be formatted.""" - def __init__(self, template): + def __init__(self, template: str): if not isinstance(template, str): - raise TypeError("<{}> argument must be a string, not {}.".format( - self.__class__.__name__, str(type(template)) - )) + raise TypeError( + f"<{self.__class__.__name__}> argument must be a string," + f" not {str(type(template))}." + ) - self._template = template + self._template: str = template parts = [] formatter = Formatter() @@ -78,15 +82,17 @@ class StringTemplate: if substr: new_parts.append(substr) - self._parts = self.find_optional_parts(new_parts) + self._parts: List["Union[str, OptionalPart, FormattingPart]"] = ( + self.find_optional_parts(new_parts) + ) - def __str__(self): + def __str__(self) -> str: return self.template - def __repr__(self): - return "<{}> {}".format(self.__class__.__name__, self.template) + def __repr__(self) -> str: + return f"<{self.__class__.__name__}> {self.template}" - def __contains__(self, other): + def __contains__(self, other: str) -> bool: return other in self.template def replace(self, *args, **kwargs): @@ -94,10 +100,10 @@ class StringTemplate: return self @property - def template(self): + def template(self) -> str: return self._template - def format(self, data): + def format(self, data: Dict[str, Any]) -> "TemplateResult": """ Figure out with whole formatting. Separate advanced keys (*Like '{project[name]}') from string which must @@ -109,6 +115,7 @@ class StringTemplate: Returns: TemplateResult: Filled or partially filled template containing all data needed or missing for filling template. + """ result = TemplatePartResult() for part in self._parts: @@ -136,23 +143,29 @@ class StringTemplate: invalid_types ) - def format_strict(self, *args, **kwargs): - result = self.format(*args, **kwargs) + def format_strict(self, data: Dict[str, Any]) -> "TemplateResult": + result = self.format(data) result.validate() return result @classmethod - def format_template(cls, template, data): + def format_template( + cls, template: str, data: Dict[str, Any] + ) -> "TemplateResult": objected_template = cls(template) return objected_template.format(data) @classmethod - def format_strict_template(cls, template, data): + def format_strict_template( + cls, template: str, data: Dict[str, Any] + ) -> "TemplateResult": objected_template = cls(template) return objected_template.format_strict(data) @staticmethod - def find_optional_parts(parts): + def find_optional_parts( + parts: List["Union[str, FormattingPart]"] + ) -> List["Union[str, OptionalPart, FormattingPart]"]: new_parts = [] tmp_parts = {} counted_symb = -1 @@ -217,11 +230,11 @@ class TemplateResult(str): of number. """ - used_values = None - solved = None - template = None - missing_keys = None - invalid_types = None + used_values: Dict[str, Any] = None + solved: bool = None + template: str = None + missing_keys: List[str] = None + invalid_types: Dict[str, Any] = None def __new__( cls, filled_template, template, solved, @@ -249,7 +262,7 @@ class TemplateResult(str): self.invalid_types ) - def copy(self): + def copy(self) -> "TemplateResult": cls = self.__class__ return cls( str(self), @@ -260,7 +273,7 @@ class TemplateResult(str): self.invalid_types ) - def normalized(self): + def normalized(self) -> "TemplateResult": """Convert to normalized path.""" cls = self.__class__ @@ -276,27 +289,28 @@ class TemplateResult(str): class TemplatePartResult: """Result to store result of template parts.""" - def __init__(self, optional=False): + def __init__(self, optional: bool = False): # Missing keys or invalid value types of required keys - self._missing_keys = set() - self._invalid_types = {} + self._missing_keys: Set[str] = set() + self._invalid_types: Dict[str, Any] = {} # Missing keys or invalid value types of optional keys - self._missing_optional_keys = set() - self._invalid_optional_types = {} + self._missing_optional_keys: Set[str] = set() + self._invalid_optional_types: Dict[str, Any] = {} # Used values stored by key with origin type # - key without any padding or key modifiers # - value from filling data # Example: {"version": 1} - self._used_values = {} + self._used_values: Dict[str, Any] = {} # Used values stored by key with all modifirs # - value is already formatted string # Example: {"version:0>3": "001"} self._really_used_values: Dict[str, Any] = {} # Concatenated string output after formatting - self._output = "" + self._output: str = "" # Is this result from optional part - self._optional = True + # TODO find out why we don't use 'optional' from args + self._optional: bool = True def add_output(self, other): if isinstance(other, str): @@ -322,7 +336,7 @@ class TemplatePartResult: ) @property - def solved(self): + def solved(self) -> bool: if self.optional: if ( len(self.missing_optional_keys) > 0 @@ -335,27 +349,27 @@ class TemplatePartResult: ) @property - def optional(self): + def optional(self) -> bool: return self._optional @property - def output(self): + def output(self) -> str: return self._output @property - def missing_keys(self): + def missing_keys(self) -> Set[str]: return self._missing_keys @property - def missing_optional_keys(self): + def missing_optional_keys(self) -> Set[str]: return self._missing_optional_keys @property - def invalid_types(self): + def invalid_types(self) -> Dict[str, Any]: return self._invalid_types @property - def invalid_optional_types(self): + def invalid_optional_types(self) -> Dict[str, Any]: return self._invalid_optional_types @property @@ -372,11 +386,11 @@ class TemplatePartResult: return self._really_used_values @property - def used_values(self): + def used_values(self) -> Dict[str, Any]: return self._used_values @staticmethod - def split_keys_to_subdicts(values): + def split_keys_to_subdicts(values: Dict[str, Any]) -> Dict[str, Any]: output = {} formatter = Formatter() for key, value in values.items(): @@ -391,7 +405,7 @@ class TemplatePartResult: data[last_key] = value return output - def get_clean_used_values(self): + def get_clean_used_values(self) -> Dict[str, Any]: new_used_values = {} for key, value in self.used_values.items(): if isinstance(value, FormatObject): @@ -411,16 +425,16 @@ class TemplatePartResult: ) self.add_really_used_value(key, value) - def add_used_value(self, key, value): + def add_used_value(self, key: str, value: Any): self._used_values[key] = value - def add_missing_key(self, key): + def add_missing_key(self, key: str): if self._optional: self._missing_optional_keys.add(key) else: self._missing_keys.add(key) - def add_invalid_type(self, key, value): + def add_invalid_type(self, key: str, value: Any): if self._optional: self._invalid_optional_types[key] = type(value) else: @@ -438,10 +452,10 @@ class FormatObject: def __format__(self, *args, **kwargs): return self.value.__format__(*args, **kwargs) - def __str__(self): + def __str__(self) -> str: return str(self.value) - def __repr__(self): + def __repr__(self) -> str: return self.__str__() @@ -451,9 +465,17 @@ class FormattingPart: Containt only single key to format e.g. "{project[name]}". Args: - template(str): String containing the formatting key. + field_name (str): Name of key. + format_spec (str): Format specification. + conversion (Union[str, None]): Conversion type. + """ - def __init__(self, field_name, format_spec, conversion): + def __init__( + self, + field_name: str, + format_spec: str, + conversion: "Union[str, None]", + ): format_spec_v = "" if format_spec: format_spec_v = f":{format_spec}" @@ -461,26 +483,26 @@ class FormattingPart: if conversion: conversion_v = f"!{conversion}" - self._field_name = field_name - self._format_spec = format_spec_v - self._conversion = conversion_v + self._field_name: str = field_name + self._format_spec: str = format_spec_v + self._conversion: str = conversion_v template_base = f"{field_name}{format_spec_v}{conversion_v}" - self._template_base = template_base - self._template = f"{{{template_base}}}" + self._template_base: str = template_base + self._template: str = f"{{{template_base}}}" @property - def template(self): + def template(self) -> str: return self._template - def __repr__(self): + def __repr__(self) -> str: return "".format(self._template) - def __str__(self): + def __str__(self) -> str: return self._template @staticmethod - def validate_value_type(value): + def validate_value_type(value: Any) -> bool: """Check if value can be used for formatting of single key.""" if isinstance(value, (numbers.Number, FormatObject)): return True @@ -491,7 +513,7 @@ class FormattingPart: return False @staticmethod - def validate_key_is_matched(key): + def validate_key_is_matched(key: str) -> bool: """Validate that opening has closing at correct place. Future-proof, only square brackets are currently used in keys. @@ -528,12 +550,15 @@ class FormattingPart: joined_keys = "".join([f"[{key}]" for key in keys]) return f"{template_base}{joined_keys}" - def format(self, data, result): + def format( + self, data: Dict[str, Any], result: TemplatePartResult + ) -> TemplatePartResult: """Format the formattings string. Args: data(dict): Data that should be used for formatting. result(TemplatePartResult): Object where result is stored. + """ key = self._template_base if key in result.really_used_values: @@ -658,20 +683,27 @@ class OptionalPart: 'FormattingPart'. """ - def __init__(self, parts): - self._parts = parts + def __init__( + self, + parts: List["Union[str, OptionalPart, FormattingPart]"] + ): + self._parts: List["Union[str, OptionalPart, FormattingPart]"] = parts @property - def parts(self): + def parts(self) -> List["Union[str, OptionalPart, FormattingPart]"]: return self._parts - def __str__(self): + def __str__(self) -> str: return "<{}>".format("".join([str(p) for p in self._parts])) - def __repr__(self): + def __repr__(self) -> str: return "".format("".join([str(p) for p in self._parts])) - def format(self, data, result): + def format( + self, + data: Dict[str, Any], + result: TemplatePartResult, + ) -> TemplatePartResult: new_result = TemplatePartResult(True) for part in self._parts: if isinstance(part, str): From 04daa9306c3a0f5d70d24fa75fe936a415c6532f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:14:59 +0100 Subject: [PATCH 176/463] remove unused import --- client/ayon_core/lib/path_templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index 3e66344ff4..e3cae78a87 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -5,7 +5,7 @@ import numbers import warnings from string import Formatter import typing -from typing import List, Dict, Any, Set, Optional +from typing import List, Dict, Any, Set if typing.TYPE_CHECKING: from typing import Union From cc23f407afb8d78816dd8c124b0236cf9b8dd2ad Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 4 Dec 2024 11:23:29 -0500 Subject: [PATCH 177/463] Address feedback from PR. --- client/ayon_core/pipeline/staging_dir.py | 11 ++++------- client/ayon_core/pipeline/tempdir.py | 6 ++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 0317e55720..ea22d99389 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -173,13 +173,10 @@ def get_staging_dir_info( ) # add additional data - ctx_data.update({ - "product": { - "type": product_type, - "name": product_name - }, - "root": anatomy.roots - }) + ctx_data["product"] = { + "type": product_type, + "name": product_name + } # add additional template formatting data if template_data: diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index 52995d3f6a..fe057b7fc7 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -65,11 +65,9 @@ def _create_local_staging_dir(prefix, suffix, dirpath=None): str: path to tempdir """ # use pathlib for creating tempdir - staging_dir = Path(tempfile.mkdtemp( + return tempfile.mkdtemp( prefix=prefix, suffix=suffix, dir=dirpath - )) - - return staging_dir.as_posix() + ) def _create_custom_tempdir(project_name, anatomy): From 9f590cd2cec1656c19c407bc77a19dfe728f3fc1 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 4 Dec 2024 16:37:25 -0500 Subject: [PATCH 178/463] Implement review representations in OTIO subset resources. --- .../publish/collect_otio_subset_resources.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index c142036b83..f7b1c9d9b2 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -149,6 +149,7 @@ class CollectOtioSubsetResources( self.log.info( "frame_start-frame_end: {}-{}".format(frame_start, frame_end)) + review_repre = None if is_sequence: # file sequence way @@ -177,6 +178,11 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, collection=collection) + if "review" in instance.data["families"]: + review_repre = self._create_representation( + frame_start, frame_end, collection=collection, + delete=True, review=True) + else: _trim = False dirname, filename = os.path.split(media_ref.target_url) @@ -191,17 +197,26 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) + if "review" in instance.data["families"]: + review_repre = self._create_representation( + frame_start, frame_end, + file=filename, delete=True, review=True) + instance.data["originalDirname"] = self.staging_dir + # add representation to instance data if repre: colorspace = instance.data.get("colorspace") # add colorspace data to representation self.set_representation_colorspace( repre, instance.context, colorspace) - # add representation to instance data instance.data["representations"].append(repre) + # add review representation to instance data + if review_repre: + instance.data["representations"].append(review_repre) + self.log.debug(instance.data) def _create_representation(self, start, end, **kwargs): @@ -221,7 +236,8 @@ class CollectOtioSubsetResources( representation_data = { "frameStart": start, "frameEnd": end, - "stagingDir": self.staging_dir + "stagingDir": self.staging_dir, + "tags": [], } if kwargs.get("collection"): @@ -247,8 +263,10 @@ class CollectOtioSubsetResources( "frameEnd": end, }) - if kwargs.get("trim") is True: - representation_data["tags"] = ["trim"] + for tag_name in ("trim", "delete", "review"): + if kwargs.get(tag_name) is True: + representation_data["tags"].append(tag_name) + return representation_data def get_template_name(self, instance): From 156d3e6a1cd1e9807486ba8c5278f382b3c15058 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 4 Dec 2024 16:45:20 -0500 Subject: [PATCH 179/463] Fix lint. --- .../ayon_core/plugins/publish/collect_otio_subset_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index f7b1c9d9b2..cc1ef3edef 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -215,7 +215,7 @@ class CollectOtioSubsetResources( # add review representation to instance data if review_repre: - instance.data["representations"].append(review_repre) + instance.data["representations"].append(review_repre) self.log.debug(instance.data) From 023e0722f8935b84238292f283a90916e920bedc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:43:18 +0100 Subject: [PATCH 180/463] capture all possible errors that can happen during UUID conversion --- client/ayon_core/tools/loader/control.py | 2 +- client/ayon_core/tools/sceneinventory/models/containers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 2da77337fb..412e6677f0 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -382,7 +382,7 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): try: uuid.UUID(repre_id) repre_ids.add(repre_id) - except ValueError: + except (ValueError, TypeError, AttributeError): pass product_ids = self._products_model.get_product_ids_by_repre_ids( diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 9059485dff..572a96976b 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -230,7 +230,7 @@ class ContainersModel: for repre_id in representation_ids: try: uuid.UUID(repre_id) - except ValueError: + except (ValueError, TypeError, AttributeError): output[repre_id] = RepresentationInfo.new_invalid() continue repre_info = self._repre_info_by_id.get(repre_id) From 2292ecbac11da62427c2007665f587123503cc66 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:45:19 +0100 Subject: [PATCH 181/463] log about invalid representation id --- client/ayon_core/tools/sceneinventory/models/containers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 572a96976b..08b86f6456 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -4,6 +4,7 @@ import collections import ayon_api from ayon_api.graphql import GraphQlQuery +from ayon_core.lib import Logger from ayon_core.host import ILoadHost from ayon_core.tools.common_models.projects import StatusStates @@ -196,6 +197,7 @@ class ContainersModel: self._container_items_by_id = {} self._version_items_by_product_id = {} self._repre_info_by_id = {} + self._log = Logger.get_logger("ContainersModel") def reset(self): self._items_cache = None @@ -368,6 +370,10 @@ class ContainersModel: try: uuid.UUID(repre_id) except (ValueError, TypeError, AttributeError): + self._log.warning( + "Container contains invalid representation id." + f"\n{container}" + ) # Fake not existing representation id so container # is shown in UI but as invalid item.representation_id = invalid_ids_mapping.setdefault( From 373df562543b1fed3c8d00d0b425cd6cbddf61aa Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:45:35 +0100 Subject: [PATCH 182/463] fix calling of missing method --- client/ayon_core/tools/sceneinventory/models/containers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 08b86f6456..f25ef2b94c 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -380,10 +380,10 @@ class ContainersModel: repre_id, uuid.uuid4().hex ) - except Exception as e: + except Exception: # skip item if required data are missing - self._controller.log_error( - f"Failed to create item: {e}" + self._log.warning( + f"Failed to create container item", exc_info=True ) continue From b6d3ddc1c8f288e98d68f528334c8c61394f3ecd Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:54:47 +0100 Subject: [PATCH 183/463] more safeguard for invalid containers --- client/ayon_core/tools/loader/control.py | 14 +++++++------- .../tools/sceneinventory/models/containers.py | 17 +++++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 412e6677f0..4ce220f282 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -372,14 +372,14 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): repre_ids = set() for container in containers: - repre_id = container.get("representation") - # Ignore invalid representation ids. - # - invalid representation ids may be available if e.g. is - # opened scene from OpenPype whe 'ObjectId' was used instead - # of 'uuid'. - # NOTE: Server call would crash if there is any invalid id. - # That would cause crash we won't get any information. try: + repre_id = container.get("representation") + # Ignore invalid representation ids. + # - invalid representation ids may be available if e.g. is + # opened scene from OpenPype whe 'ObjectId' was used instead + # of 'uuid'. + # NOTE: Server call would crash if there is any invalid id. + # That would cause crash we won't get any information. uuid.UUID(repre_id) repre_ids.add(repre_id) except (ValueError, TypeError, AttributeError): diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index f25ef2b94c..c761121d4d 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -350,12 +350,14 @@ class ContainersModel: return host = self._controller.get_host() - if isinstance(host, ILoadHost): - containers = list(host.get_containers()) - elif hasattr(host, "ls"): - containers = list(host.ls()) - else: - containers = [] + containers = [] + try: + if isinstance(host, ILoadHost): + containers = list(host.get_containers()) + elif hasattr(host, "ls"): + containers = list(host.ls()) + except Exception: + self._log.error("Failed to get containers", exc_info=True) container_items = [] containers_by_id = {} @@ -363,6 +365,9 @@ class ContainersModel: invalid_ids_mapping = {} current_project_name = self._controller.get_current_project_name() for container in containers: + if not container: + continue + try: item = ContainerItem.from_container_data( current_project_name, container) From 3c697b92f57fa597364da39b7e73a03ad963e563 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:57:22 +0100 Subject: [PATCH 184/463] remove unnecessary f-string --- client/ayon_core/tools/sceneinventory/models/containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index c761121d4d..f841f87c8e 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -388,7 +388,7 @@ class ContainersModel: except Exception: # skip item if required data are missing self._log.warning( - f"Failed to create container item", exc_info=True + "Failed to create container item", exc_info=True ) continue From a26e9207d2957402c3de2a23fc0633d85e094675 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:10:33 +0100 Subject: [PATCH 185/463] fix long line --- client/ayon_core/tools/loader/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 4ce220f282..16cf7c31c7 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -376,8 +376,8 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): repre_id = container.get("representation") # Ignore invalid representation ids. # - invalid representation ids may be available if e.g. is - # opened scene from OpenPype whe 'ObjectId' was used instead - # of 'uuid'. + # opened scene from OpenPype whe 'ObjectId' was used + # instead of 'uuid'. # NOTE: Server call would crash if there is any invalid id. # That would cause crash we won't get any information. uuid.UUID(repre_id) From 72862554b4df26b304efb609200b901748c01c4f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:13:46 +0100 Subject: [PATCH 186/463] Fix missing slate frame frames_to_render should contain also additional slate frame --- client/ayon_core/pipeline/farm/pyblish_functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 559561c827..c399855044 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -384,6 +384,7 @@ def prepare_representations( frame_end = frames_to_render[-1] if skeleton_data.get("slate"): frame_start -= 1 + frames_to_render.insert(0, frame_start) files = _get_real_files_to_rendered(collection, frames_to_render) From 49c5b875a8a275adb6264474df31a2fc4be3584a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:19:58 +0100 Subject: [PATCH 187/463] Refactor removed filtering by frame Remainder has now real way to find frame pattern from single file. Doesn't make sense to filter on single file. --- client/ayon_core/pipeline/farm/pyblish_functions.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 559561c827..147763391b 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -435,13 +435,10 @@ def prepare_representations( " This may cause issues on farm." ).format(staging)) - files = _get_real_files_to_rendered( - [os.path.basename(remainder)], frames_to_render) - rep = { "name": ext, "ext": ext, - "files": files[0], + "files": os.path.basename(remainder), "stagingDir": staging, } From 2a20a9d169b2ed49eada6679166bd61a5db8889e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 16:20:57 +0100 Subject: [PATCH 188/463] Refactor filtering based on frame_to_render Previous implementation was naive and could be dangerous. Updated docstrings. Renamed. --- .../pipeline/farm/pyblish_functions.py | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 147763391b..722dc4b5c6 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -385,8 +385,7 @@ def prepare_representations( if skeleton_data.get("slate"): frame_start -= 1 - files = _get_real_files_to_rendered(collection, frames_to_render) - + files = _get_real_files_to_render(collection, frames_to_render) # explicitly disable review by user preview = preview and not do_not_add_review rep = { @@ -492,31 +491,47 @@ def _get_real_frames_to_render(frames): return frames_to_render -def _get_real_files_to_rendered(collection, frames_to_render): - """Use expected files based on real frames_to_render. +def _get_real_files_to_render(collection, frames_to_render): + """Filter files with frames that should be really rendered. + + 'expected_files' are collected from DCC based on timeline setting. This is + being calculated differently in each DCC. Filtering here is on single place + + But artists might explicitly set frames they want to render in Publisher UI + This range would override and filter previously prepared expected files + from DCC. - Artists might explicitly set frames they want to render via Publisher UI. - This uses this value to filter out files Args: - frames_to_render (list): of str '1001' + collection (clique.Collection): absolute paths + frames_to_render (list[int]): of int 1001 + Returns: + (list[str]) + + Example: + -------- + + expectedFiles = [ + "foo_v01.0001.exr", + "foo_v01.0002.exr", + ] + frames_to_render = '0001' + >> + ["foo_v01.0001.exr"] - only explicitly requested frame returned """ - files = [os.path.basename(f) for f in list(collection)] - file_name, extracted_frame = list(collect_frames(files).items())[0] - - if not extracted_frame: - return files - - found_frame_pattern_length = len(extracted_frame) + found_frame_pattern_length = collection.padding normalized_frames_to_render = { str(frame_to_render).zfill(found_frame_pattern_length) for frame_to_render in frames_to_render } + head_name = os.path.basename(collection.head) + + file_names = [os.path.basename(f) for f in collection] return [ file_name - for file_name in files + for file_name in file_names if any( - frame in file_name + f"{head_name}{frame}{collection.tail}" == file_name for frame in normalized_frames_to_render ) ] From b832c850c33cba13bb63cf6b7b58feaba5297510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 5 Dec 2024 16:57:47 +0100 Subject: [PATCH 189/463] Update client/ayon_core/plugins/publish/collect_otio_subset_resources.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/collect_otio_subset_resources.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index cc1ef3edef..2d8e91fe09 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -199,8 +199,8 @@ class CollectOtioSubsetResources( if "review" in instance.data["families"]: review_repre = self._create_representation( - frame_start, frame_end, - file=filename, delete=True, review=True) + frame_start, frame_end, + file=filename, delete=True, review=True) instance.data["originalDirname"] = self.staging_dir From 81c71a757fae3ea0120b397017192336becb4a5a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 17:15:40 +0100 Subject: [PATCH 190/463] Prepare normalized expected file names Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 722dc4b5c6..a7780ba97c 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -525,7 +525,10 @@ def _get_real_files_to_render(collection, frames_to_render): } head_name = os.path.basename(collection.head) - + normalized_filenames = { + f"{head_name}{frame}{collection.tail}" + for frame in normalized_frames_to_render + } file_names = [os.path.basename(f) for f in collection] return [ file_name From c886be22bdf2494e952c39d4f38959e35c732150 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 5 Dec 2024 17:19:19 +0100 Subject: [PATCH 191/463] Refactor logic to use set --- client/ayon_core/pipeline/farm/pyblish_functions.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index a7780ba97c..1f6e17972a 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -533,10 +533,7 @@ def _get_real_files_to_render(collection, frames_to_render): return [ file_name for file_name in file_names - if any( - f"{head_name}{frame}{collection.tail}" == file_name - for frame in normalized_frames_to_render - ) + if file_name in normalized_filenames ] From 06f6b519f0d45aa5e9dfadf3f184106d98166eb7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 9 Dec 2024 12:42:07 +0100 Subject: [PATCH 192/463] Refactor logic to be even simpler --- .../pipeline/farm/pyblish_functions.py | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 1f6e17972a..902aa41af4 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -518,23 +518,14 @@ def _get_real_files_to_render(collection, frames_to_render): >> ["foo_v01.0001.exr"] - only explicitly requested frame returned """ - found_frame_pattern_length = collection.padding - normalized_frames_to_render = { - str(frame_to_render).zfill(found_frame_pattern_length) - for frame_to_render in frames_to_render - } - - head_name = os.path.basename(collection.head) - normalized_filenames = { - f"{head_name}{frame}{collection.tail}" - for frame in normalized_frames_to_render - } - file_names = [os.path.basename(f) for f in collection] - return [ - file_name - for file_name in file_names - if file_name in normalized_filenames - ] + included_frames = set(collection.indexes).intersection(frames_to_render) + real_collection = clique.Collection( + collection.head, + collection.tail, + collection.padding, + indexes=included_frames + ) + return list(real_collection) def create_instances_for_aov(instance, skeleton, aov_filter, From 6836a7f79b546e7dc500d2c8ba911469e3035012 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 9 Dec 2024 13:51:09 +0100 Subject: [PATCH 193/463] Fix docstring --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 902aa41af4..425b616adc 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -514,7 +514,7 @@ def _get_real_files_to_render(collection, frames_to_render): "foo_v01.0001.exr", "foo_v01.0002.exr", ] - frames_to_render = '0001' + frames_to_render = 1 >> ["foo_v01.0001.exr"] - only explicitly requested frame returned """ From d65865563f7aa34ede47b17e2dfc671a03f84255 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 9 Dec 2024 14:53:17 +0100 Subject: [PATCH 194/463] Fix expected files must be only file names --- client/ayon_core/pipeline/farm/pyblish_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 425b616adc..71068cb093 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -525,7 +525,8 @@ def _get_real_files_to_render(collection, frames_to_render): collection.padding, indexes=included_frames ) - return list(real_collection) + real_full_paths = list(real_collection) + return [os.path.basename(file_url) for file_url in real_full_paths] def create_instances_for_aov(instance, skeleton, aov_filter, From 4bf03f0a51ed5b73034b537f55d6c853fb572b3a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 9 Dec 2024 15:39:08 +0100 Subject: [PATCH 195/463] Remove unused import 'collect_frames' from pyblish_functions --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 616e25596c..e48d99602e 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -7,7 +7,7 @@ from copy import deepcopy import attr import ayon_api import clique -from ayon_core.lib import Logger, collect_frames +from ayon_core.lib import Logger from ayon_core.pipeline import ( get_current_project_name, get_representation_path, From 55eb8e497fc30f3de22229fea904b223136cf33f Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 9 Dec 2024 15:13:06 +0000 Subject: [PATCH 196/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index b2ece45120..0d3b533f57 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.10+dev" +__version__ = "1.0.11" diff --git a/package.py b/package.py index 58ae5c08d9..464fbb007b 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.10+dev" +version = "1.0.11" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index d7cf9fa6ed..ab452816ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.10+dev" +version = "1.0.11" description = "" authors = ["Ynput Team "] readme = "README.md" From a84a3cad33be7e6fc29d6b5539f1e33f556374e4 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 9 Dec 2024 15:13:44 +0000 Subject: [PATCH 197/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 0d3b533f57..a4ae75914c 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.11" +__version__ = "1.0.11+dev" diff --git a/package.py b/package.py index 464fbb007b..b8d88fc2ad 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.11" +version = "1.0.11+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index ab452816ad..bdfaf797e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.11" +version = "1.0.11+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 49d15156799dc5f8338ad51cea7689a2174d1a3d Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 9 Dec 2024 18:49:56 -0500 Subject: [PATCH 198/463] AY-7222 Fix otio_review no handles and tempdir for Resolve --- client/ayon_core/pipeline/tempdir.py | 13 +++++++++++++ .../plugins/publish/extract_otio_review.py | 7 ++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index fe057b7fc7..7fb539bf0b 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -5,6 +5,7 @@ Temporary folder operations import os import tempfile from pathlib import Path +import warnings from ayon_core.lib import StringTemplate from ayon_core.pipeline import Anatomy @@ -70,6 +71,18 @@ def _create_local_staging_dir(prefix, suffix, dirpath=None): ) +def create_custom_tempdir(project_name, anatomy): + """ Deprecated 09/12/2024, here for backward-compatibility with Resolve. + """ + warnings.warn( + "Used deprecated 'create_custom_tempdir' " + "use 'ayon_core.pipeline.tempdir.get_temp_dir' instead.", + DeprecationWarning, + ) + + return _create_custom_tempdir(project_name, anatomy) + + def _create_custom_tempdir(project_name, anatomy): """ Create custom tempdir diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index c8d2086865..712ae7a886 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -71,15 +71,16 @@ class ExtractOTIOReview( # TODO: convert resulting image sequence to mp4 # get otio clip and other time info from instance clip - # TODO: what if handles are different in `versionData`? - handle_start = instance.data["handleStart"] - handle_end = instance.data["handleEnd"] otio_review_clips = instance.data.get("otioReviewClips") if otio_review_clips is None: self.log.info(f"Instance `{instance}` has no otioReviewClips") return + # TODO: what if handles are different in `versionData`? + handle_start = instance.data["handleStart"] + handle_end = instance.data["handleEnd"] + # add plugin wide attributes self.representation_files = [] self.used_frames = [] From 55dacd5cec1eb58681bce5bf6d784c6b35ddc401 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:08:37 +0100 Subject: [PATCH 199/463] use 'taskType' instead of 'type' --- .../pipeline/workfile/path_resolving.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index 47d6f4ddfa..dee27ae4db 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -34,15 +34,23 @@ def get_workfile_template_key_from_context( host_name (str): Host name. project_settings (Dict[str, Any]): Project settings for passed 'project_name'. Not required at all but makes function faster. - """ + Returns: + str: Workfile template name. + + """ folder_entity = ayon_api.get_folder_by_path( - project_name, folder_path, fields={"id"} + project_name, + folder_path, + fields={"id"}, ) task_entity = ayon_api.get_task_by_name( - project_name, folder_entity["id"], task_name + project_name, + folder_entity["id"], + task_name, + fields={"taskType"}, ) - task_type = task_entity.get("type") + task_type = task_entity.get("taskType") return get_workfile_template_key( project_name, task_type, host_name, project_settings From 55a4c42c8377ab67777062cf046191b2e83c91ff Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:08:45 +0100 Subject: [PATCH 200/463] added typehints --- .../ayon_core/pipeline/workfile/path_resolving.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/pipeline/workfile/path_resolving.py b/client/ayon_core/pipeline/workfile/path_resolving.py index dee27ae4db..61c6e5b876 100644 --- a/client/ayon_core/pipeline/workfile/path_resolving.py +++ b/client/ayon_core/pipeline/workfile/path_resolving.py @@ -2,6 +2,7 @@ import os import re import copy import platform +from typing import Optional, Dict, Any import ayon_api @@ -16,12 +17,12 @@ from ayon_core.pipeline.template_data import get_template_data def get_workfile_template_key_from_context( - project_name, - folder_path, - task_name, - host_name, - project_settings=None -): + project_name: str, + folder_path: str, + task_name: str, + host_name: str, + project_settings: Optional[Dict[str, Any]] = None, +) -> str: """Helper function to get template key for workfile template. Do the same as `get_workfile_template_key` but returns value for "session From c1904dff39ca2923855dc19999efddb15e41ff6c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 10 Dec 2024 14:31:45 +0100 Subject: [PATCH 201/463] Make sure to operate on copy of data and leave workfile instance data unaffected --- client/ayon_core/pipeline/publish/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 2ba40d7687..ecdcc0f0c1 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -764,7 +764,7 @@ def replace_with_published_scene_path(instance, replace_in_path=True): return # determine published path from Anatomy. - template_data = workfile_instance.data.get("anatomyData") + template_data = copy.deepcopy(workfile_instance.data["anatomyData"]) rep = workfile_instance.data["representations"][0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") From 6d7415360e1b3177affd5b734a7e0b332015efc7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:18:08 +0100 Subject: [PATCH 202/463] fix item menu request --- client/ayon_core/tools/attribute_defs/files_widget.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index 6199d0c202..42e805d72e 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -522,7 +522,7 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel): class ItemWidget(QtWidgets.QWidget): - context_menu_requested = QtCore.Signal(QtCore.QPoint, bool) + context_menu_requested = QtCore.Signal(QtCore.QPoint) def __init__( self, item_id, label, pixmap_icon, is_sequence, multivalue, parent=None @@ -841,7 +841,7 @@ class FilesWidget(QtWidgets.QFrame): self._multivalue ) widget.context_menu_requested.connect( - self._on_context_menu_requested + self._on_item_context_menu_request ) self._files_view.setIndexWidget(index, widget) self._files_proxy_model.setData( @@ -923,6 +923,9 @@ class FilesWidget(QtWidgets.QFrame): if menu.actions(): menu.popup(pos) + def _on_item_context_menu_request(self, pos): + self._on_context_menu_requested(pos, True) + def dragEnterEvent(self, event): if self._multivalue: return From 38b6aeadbac8978f9416c41a3a8d1be9f9d02b42 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:18:36 +0100 Subject: [PATCH 203/463] don't pass boolean to signal --- client/ayon_core/tools/attribute_defs/files_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index 42e805d72e..652a33e29a 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -589,7 +589,7 @@ class ItemWidget(QtWidgets.QWidget): def _on_actions_clicked(self): pos = self._split_btn.rect().bottomLeft() point = self._split_btn.mapToGlobal(pos) - self.context_menu_requested.emit(point, False) + self.context_menu_requested.emit(point) class InViewButton(IconButton): From 9ea0c79f42da8c2b80ef87b13ee2d1e579ca7a7d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:18:48 +0100 Subject: [PATCH 204/463] use unused variable --- client/ayon_core/tools/attribute_defs/files_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/files_widget.py b/client/ayon_core/tools/attribute_defs/files_widget.py index 652a33e29a..8a40b3ff38 100644 --- a/client/ayon_core/tools/attribute_defs/files_widget.py +++ b/client/ayon_core/tools/attribute_defs/files_widget.py @@ -859,7 +859,7 @@ class FilesWidget(QtWidgets.QFrame): for row in range(self._files_proxy_model.rowCount()): index = self._files_proxy_model.index(row, 0) item_id = index.data(ITEM_ID_ROLE) - available_item_ids.add(index.data(ITEM_ID_ROLE)) + available_item_ids.add(item_id) widget_ids = set(self._widgets_by_id.keys()) for item_id in available_item_ids: From c40062878759558d61e1c6f1d4b5345136f254f3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:41:01 +0100 Subject: [PATCH 205/463] added launcher and browser actions to tray --- client/ayon_core/tools/tray/ui/tray.py | 45 ++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/tray/ui/tray.py b/client/ayon_core/tools/tray/ui/tray.py index f6a8add861..e61f903c80 100644 --- a/client/ayon_core/tools/tray/ui/tray.py +++ b/client/ayon_core/tools/tray/ui/tray.py @@ -23,6 +23,7 @@ from ayon_core.addon import ( ITrayAction, ITrayService, ) +from ayon_core.pipeline import install_ayon_plugins from ayon_core.tools.utils import ( WrappedCallbackItem, get_ayon_qt_app, @@ -32,6 +33,8 @@ from ayon_core.tools.tray.lib import ( remove_tray_server_url, TrayIsRunningError, ) +from ayon_core.tools.launcher.ui import LauncherWindow +from ayon_core.tools.loader.ui import LoaderWindow from .addons_manager import TrayAddonsManager from .host_console_listener import HostListener @@ -82,6 +85,9 @@ class TrayManager: self._outdated_dialog = None + self._launcher_window = None + self._browser_window = None + self._update_check_timer = update_check_timer self._update_check_interval = update_check_interval self._main_thread_timer = main_thread_timer @@ -109,12 +115,15 @@ class TrayManager: @property def doubleclick_callback(self): """Double-click callback for Tray icon.""" - return self._addons_manager.get_doubleclick_callback() + callback = self._addons_manager.get_doubleclick_callback() + if callback is None: + callback = self._show_launcher_window + return callback def execute_doubleclick(self): """Execute double click callback in main thread.""" callback = self.doubleclick_callback - if callback: + if callback is not None: self.execute_in_main_thread(callback) def show_tray_message(self, title, message, icon=None, msecs=None): @@ -144,8 +153,22 @@ class TrayManager: return tray_menu = self.tray_widget.menu + self._addons_manager.initialize(tray_menu) + # Add default actions under addon actions + launcher_action = QtWidgets.QAction( + "Launcher", tray_menu + ) + launcher_action.triggered.connect(self._show_launcher_window) + tray_menu.addAction(launcher_action) + + browser_action = QtWidgets.QAction( + "Browser", tray_menu + ) + browser_action.triggered.connect(self._show_browser_window) + tray_menu.addAction(browser_action) + self._addons_manager.add_route( "GET", "/tray", self._web_get_tray_info ) @@ -522,6 +545,24 @@ class TrayManager: self._info_widget.raise_() self._info_widget.activateWindow() + def _show_launcher_window(self): + if self._launcher_window is None: + self._launcher_window = LauncherWindow() + + self._launcher_window.show() + self._launcher_window.raise_() + self._launcher_window.activateWindow() + + def _show_browser_window(self): + if self._browser_window is None: + self._browser_window = LoaderWindow() + self._browser_window.setWindowTitle("AYON Browser") + install_ayon_plugins() + + self._browser_window.show() + self._browser_window.raise_() + self._browser_window.activateWindow() + class SystemTrayIcon(QtWidgets.QSystemTrayIcon): """Tray widget. From 167cea29b5d8bba2c5af44cc22346cdabfbf7eba Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:41:11 +0100 Subject: [PATCH 206/463] remove action addons --- client/ayon_core/modules/launcher_action.py | 60 ------------------ client/ayon_core/modules/loader_action.py | 68 --------------------- 2 files changed, 128 deletions(-) delete mode 100644 client/ayon_core/modules/launcher_action.py delete mode 100644 client/ayon_core/modules/loader_action.py diff --git a/client/ayon_core/modules/launcher_action.py b/client/ayon_core/modules/launcher_action.py deleted file mode 100644 index 344b0bc389..0000000000 --- a/client/ayon_core/modules/launcher_action.py +++ /dev/null @@ -1,60 +0,0 @@ -import os - -from ayon_core import AYON_CORE_ROOT -from ayon_core.addon import AYONAddon, ITrayAction - - -class LauncherAction(AYONAddon, ITrayAction): - label = "Launcher" - name = "launcher_tool" - version = "1.0.0" - - def initialize(self, settings): - - # Tray attributes - self._window = None - - def tray_init(self): - self._create_window() - - self.add_doubleclick_callback(self._show_launcher) - - def tray_start(self): - return - - def connect_with_addons(self, enabled_modules): - # Register actions - if not self.tray_initialized: - return - - from ayon_core.pipeline.actions import register_launcher_action_path - - actions_dir = os.path.join(AYON_CORE_ROOT, "plugins", "actions") - if os.path.exists(actions_dir): - register_launcher_action_path(actions_dir) - - actions_paths = self.manager.collect_plugin_paths()["actions"] - for path in actions_paths: - if path and os.path.exists(path): - register_launcher_action_path(path) - - def on_action_trigger(self): - """Implementation for ITrayAction interface. - - Show launcher tool on action trigger. - """ - - self._show_launcher() - - def _create_window(self): - if self._window: - return - from ayon_core.tools.launcher.ui import LauncherWindow - self._window = LauncherWindow() - - def _show_launcher(self): - if self._window is None: - return - self._window.show() - self._window.raise_() - self._window.activateWindow() diff --git a/client/ayon_core/modules/loader_action.py b/client/ayon_core/modules/loader_action.py deleted file mode 100644 index a58d7fd456..0000000000 --- a/client/ayon_core/modules/loader_action.py +++ /dev/null @@ -1,68 +0,0 @@ -from ayon_core.addon import AYONAddon, ITrayAddon - - -class LoaderAddon(AYONAddon, ITrayAddon): - name = "loader_tool" - version = "1.0.0" - - def initialize(self, settings): - # Tray attributes - self._loader_imported = None - self._loader_window = None - - def tray_init(self): - # Add library tool - self._loader_imported = False - try: - from ayon_core.tools.loader.ui import LoaderWindow # noqa F401 - - self._loader_imported = True - except Exception: - self.log.warning( - "Couldn't load Loader tool for tray.", - exc_info=True - ) - - # Definition of Tray menu - def tray_menu(self, tray_menu): - if not self._loader_imported: - return - - from qtpy import QtWidgets - # Actions - action_loader = QtWidgets.QAction( - "Loader", tray_menu - ) - - action_loader.triggered.connect(self.show_loader) - - tray_menu.addAction(action_loader) - - def tray_start(self, *_a, **_kw): - return - - def tray_exit(self, *_a, **_kw): - return - - def show_loader(self): - if self._loader_window is None: - from ayon_core.pipeline import install_ayon_plugins - - self._init_loader() - - install_ayon_plugins() - - self._loader_window.show() - - # Raise and activate the window - # for MacOS - self._loader_window.raise_() - # for Windows - self._loader_window.activateWindow() - - def _init_loader(self): - from ayon_core.tools.loader.ui import LoaderWindow - - libraryloader = LoaderWindow() - - self._loader_window = libraryloader From 77efd56157470058561fdf7b3fffdd7d29595b51 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:10:35 +0100 Subject: [PATCH 207/463] created tool with basic separation of some logic to controller --- .../tools/console_interpreter/__init__.py | 8 + .../tools/console_interpreter/abstract.py | 33 ++ .../tools/console_interpreter/control.py | 63 ++++ .../tools/console_interpreter/ui/__init__.py | 8 + .../tools/console_interpreter/ui/utils.py | 42 +++ .../tools/console_interpreter/ui/widgets.py | 251 ++++++++++++++ .../tools/console_interpreter/ui/window.py | 324 ++++++++++++++++++ 7 files changed, 729 insertions(+) create mode 100644 client/ayon_core/tools/console_interpreter/__init__.py create mode 100644 client/ayon_core/tools/console_interpreter/abstract.py create mode 100644 client/ayon_core/tools/console_interpreter/control.py create mode 100644 client/ayon_core/tools/console_interpreter/ui/__init__.py create mode 100644 client/ayon_core/tools/console_interpreter/ui/utils.py create mode 100644 client/ayon_core/tools/console_interpreter/ui/widgets.py create mode 100644 client/ayon_core/tools/console_interpreter/ui/window.py diff --git a/client/ayon_core/tools/console_interpreter/__init__.py b/client/ayon_core/tools/console_interpreter/__init__.py new file mode 100644 index 0000000000..0333fe80a0 --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/__init__.py @@ -0,0 +1,8 @@ +from .abstract import AbstractInterpreterController +from .control import InterpreterController + + +__all__ = ( + "AbstractInterpreterController", + "InterpreterController", +) diff --git a/client/ayon_core/tools/console_interpreter/abstract.py b/client/ayon_core/tools/console_interpreter/abstract.py new file mode 100644 index 0000000000..a945e6e498 --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/abstract.py @@ -0,0 +1,33 @@ +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from typing import List, Dict, Optional + + +@dataclass +class TabItem: + name: str + code: str + + +@dataclass +class InterpreterConfig: + width: Optional[int] + height: Optional[int] + splitter_sizes: List[int] = field(default_factory=list) + tabs: List[TabItem] = field(default_factory=list) + + +class AbstractInterpreterController(ABC): + @abstractmethod + def get_config(self) -> InterpreterConfig: + pass + + @abstractmethod + def save_config( + self, + width: int, + height: int, + splitter_sizes: List[int], + tabs: List[Dict[str, str]], + ): + pass diff --git a/client/ayon_core/tools/console_interpreter/control.py b/client/ayon_core/tools/console_interpreter/control.py new file mode 100644 index 0000000000..b931b6252c --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/control.py @@ -0,0 +1,63 @@ +from typing import List, Dict + +from ayon_core.lib import JSONSettingRegistry +from ayon_core.lib.local_settings import get_launcher_local_dir + +from .abstract import ( + AbstractInterpreterController, + TabItem, + InterpreterConfig, +) + + +class InterpreterController(AbstractInterpreterController): + def __init__(self): + self._registry = JSONSettingRegistry( + "python_interpreter_tool", + get_launcher_local_dir(), + ) + + def get_config(self): + width = None + height = None + splitter_sizes = [] + tabs = [] + try: + width = self._registry.get_item("width") + height = self._registry.get_item("height") + + except (ValueError, KeyError): + pass + + try: + splitter_sizes = self._registry.get_item("splitter_sizes") + except (ValueError, KeyError): + pass + + try: + tab_defs = self._registry.get_item("tabs") or [] + for tab_def in tab_defs: + tab_name = tab_def.get("name") + if not tab_name: + continue + code = tab_def.get("code") or "" + tabs.append(TabItem(tab_name, code)) + + except (ValueError, KeyError): + pass + + return InterpreterConfig( + width, height, splitter_sizes, tabs + ) + + def save_config( + self, + width: int, + height: int, + splitter_sizes: List[int], + tabs: List[Dict[str, str]], + ): + self._registry.set_item("width", width) + self._registry.set_item("height", height) + self._registry.set_item("splitter_sizes", splitter_sizes) + self._registry.set_item("tabs", tabs) diff --git a/client/ayon_core/tools/console_interpreter/ui/__init__.py b/client/ayon_core/tools/console_interpreter/ui/__init__.py new file mode 100644 index 0000000000..05b166892c --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/ui/__init__.py @@ -0,0 +1,8 @@ +from .window import ( + ConsoleInterpreterWindow +) + + +__all__ = ( + "ConsoleInterpreterWindow", +) diff --git a/client/ayon_core/tools/console_interpreter/ui/utils.py b/client/ayon_core/tools/console_interpreter/ui/utils.py new file mode 100644 index 0000000000..427483215d --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/ui/utils.py @@ -0,0 +1,42 @@ +import os +import sys +import collections + + +class StdOEWrap: + def __init__(self): + self._origin_stdout_write = None + self._origin_stderr_write = None + self._listening = False + self.lines = collections.deque() + + if not sys.stdout: + sys.stdout = open(os.devnull, "w") + + if not sys.stderr: + sys.stderr = open(os.devnull, "w") + + if self._origin_stdout_write is None: + self._origin_stdout_write = sys.stdout.write + + if self._origin_stderr_write is None: + self._origin_stderr_write = sys.stderr.write + + self._listening = True + sys.stdout.write = self._stdout_listener + sys.stderr.write = self._stderr_listener + + def stop_listen(self): + self._listening = False + + def _stdout_listener(self, text): + if self._listening: + self.lines.append(text) + if self._origin_stdout_write is not None: + self._origin_stdout_write(text) + + def _stderr_listener(self, text): + if self._listening: + self.lines.append(text) + if self._origin_stderr_write is not None: + self._origin_stderr_write(text) diff --git a/client/ayon_core/tools/console_interpreter/ui/widgets.py b/client/ayon_core/tools/console_interpreter/ui/widgets.py new file mode 100644 index 0000000000..2b9361666e --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/ui/widgets.py @@ -0,0 +1,251 @@ +from code import InteractiveInterpreter + +from qtpy import QtCore, QtWidgets, QtGui + + +class PythonCodeEditor(QtWidgets.QPlainTextEdit): + execute_requested = QtCore.Signal() + + def __init__(self, parent): + super().__init__(parent) + + self.setObjectName("PythonCodeEditor") + + self._indent = 4 + + def _tab_shift_right(self): + cursor = self.textCursor() + selected_text = cursor.selectedText() + if not selected_text: + cursor.insertText(" " * self._indent) + return + + sel_start = cursor.selectionStart() + sel_end = cursor.selectionEnd() + cursor.setPosition(sel_end) + end_line = cursor.blockNumber() + cursor.setPosition(sel_start) + while True: + cursor.movePosition(QtGui.QTextCursor.StartOfLine) + text = cursor.block().text() + spaces = len(text) - len(text.lstrip(" ")) + new_spaces = spaces % self._indent + if not new_spaces: + new_spaces = self._indent + + cursor.insertText(" " * new_spaces) + if cursor.blockNumber() == end_line: + break + + cursor.movePosition(QtGui.QTextCursor.NextBlock) + + def _tab_shift_left(self): + tmp_cursor = self.textCursor() + sel_start = tmp_cursor.selectionStart() + sel_end = tmp_cursor.selectionEnd() + + cursor = QtGui.QTextCursor(self.document()) + cursor.setPosition(sel_end) + end_line = cursor.blockNumber() + cursor.setPosition(sel_start) + while True: + cursor.movePosition(QtGui.QTextCursor.StartOfLine) + text = cursor.block().text() + spaces = len(text) - len(text.lstrip(" ")) + if spaces: + spaces_to_remove = (spaces % self._indent) or self._indent + if spaces_to_remove > spaces: + spaces_to_remove = spaces + + cursor.setPosition( + cursor.position() + spaces_to_remove, + QtGui.QTextCursor.KeepAnchor + ) + cursor.removeSelectedText() + + if cursor.blockNumber() == end_line: + break + + cursor.movePosition(QtGui.QTextCursor.NextBlock) + + def keyPressEvent(self, event): + if event.key() == QtCore.Qt.Key_Backtab: + self._tab_shift_left() + event.accept() + return + + if event.key() == QtCore.Qt.Key_Tab: + if event.modifiers() == QtCore.Qt.NoModifier: + self._tab_shift_right() + event.accept() + return + + if ( + event.key() == QtCore.Qt.Key_Return + and event.modifiers() == QtCore.Qt.ControlModifier + ): + self.execute_requested.emit() + event.accept() + return + + super().keyPressEvent(event) + + +class PythonTabWidget(QtWidgets.QWidget): + add_tab_requested = QtCore.Signal() + before_execute = QtCore.Signal(str) + + def __init__(self, parent): + super().__init__(parent) + + code_input = PythonCodeEditor(self) + + self.setFocusProxy(code_input) + + add_tab_btn = QtWidgets.QPushButton("Add tab...", self) + add_tab_btn.setDefault(False) + add_tab_btn.setToolTip("Add new tab") + + execute_btn = QtWidgets.QPushButton("Execute", self) + execute_btn.setDefault(False) + execute_btn.setToolTip("Execute command (Ctrl + Enter)") + + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.setContentsMargins(0, 0, 0, 0) + btns_layout.addWidget(add_tab_btn) + btns_layout.addStretch(1) + btns_layout.addWidget(execute_btn) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(code_input, 1) + layout.addLayout(btns_layout, 0) + + add_tab_btn.clicked.connect(self._on_add_tab_clicked) + execute_btn.clicked.connect(self._on_execute_clicked) + code_input.execute_requested.connect(self.execute) + + self._code_input = code_input + self._interpreter = InteractiveInterpreter() + + def _on_add_tab_clicked(self): + self.add_tab_requested.emit() + + def _on_execute_clicked(self): + self.execute() + + def get_code(self): + return self._code_input.toPlainText() + + def set_code(self, code_text): + self._code_input.setPlainText(code_text) + + def execute(self): + code_text = self._code_input.toPlainText() + self.before_execute.emit(code_text) + self._interpreter.runcode(code_text) + + +class TabNameDialog(QtWidgets.QDialog): + default_width = 330 + default_height = 85 + + def __init__(self, parent): + super().__init__(parent) + + self.setWindowTitle("Enter tab name") + + name_label = QtWidgets.QLabel("Tab name:", self) + name_input = QtWidgets.QLineEdit(self) + + inputs_layout = QtWidgets.QHBoxLayout() + inputs_layout.addWidget(name_label) + inputs_layout.addWidget(name_input) + + ok_btn = QtWidgets.QPushButton("Ok", self) + cancel_btn = QtWidgets.QPushButton("Cancel", self) + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.addStretch(1) + btns_layout.addWidget(ok_btn) + btns_layout.addWidget(cancel_btn) + + layout = QtWidgets.QVBoxLayout(self) + layout.addLayout(inputs_layout) + layout.addStretch(1) + layout.addLayout(btns_layout) + + ok_btn.clicked.connect(self._on_ok_clicked) + cancel_btn.clicked.connect(self._on_cancel_clicked) + + self._name_input = name_input + self._ok_btn = ok_btn + self._cancel_btn = cancel_btn + + self._result = None + + self.resize(self.default_width, self.default_height) + + def set_tab_name(self, name): + self._name_input.setText(name) + + def result(self): + return self._result + + def showEvent(self, event): + super().showEvent(event) + btns_width = max( + self._ok_btn.width(), + self._cancel_btn.width() + ) + + self._ok_btn.setMinimumWidth(btns_width) + self._cancel_btn.setMinimumWidth(btns_width) + + def _on_ok_clicked(self): + self._result = self._name_input.text() + self.accept() + + def _on_cancel_clicked(self): + self._result = None + self.reject() + + +class OutputTextWidget(QtWidgets.QTextEdit): + v_max_offset = 4 + + def vertical_scroll_at_max(self): + v_scroll = self.verticalScrollBar() + return v_scroll.value() > v_scroll.maximum() - self.v_max_offset + + def scroll_to_bottom(self): + v_scroll = self.verticalScrollBar() + return v_scroll.setValue(v_scroll.maximum()) + + +class EnhancedTabBar(QtWidgets.QTabBar): + double_clicked = QtCore.Signal(QtCore.QPoint) + right_clicked = QtCore.Signal(QtCore.QPoint) + mid_clicked = QtCore.Signal(QtCore.QPoint) + + def __init__(self, parent): + super().__init__(parent) + + self.setDrawBase(False) + + def mouseDoubleClickEvent(self, event): + self.double_clicked.emit(event.globalPos()) + event.accept() + + def mouseReleaseEvent(self, event): + if event.button() == QtCore.Qt.RightButton: + self.right_clicked.emit(event.globalPos()) + event.accept() + return + + elif event.button() == QtCore.Qt.MidButton: + self.mid_clicked.emit(event.globalPos()) + event.accept() + + else: + super().mouseReleaseEvent(event) + diff --git a/client/ayon_core/tools/console_interpreter/ui/window.py b/client/ayon_core/tools/console_interpreter/ui/window.py new file mode 100644 index 0000000000..a5065f96f9 --- /dev/null +++ b/client/ayon_core/tools/console_interpreter/ui/window.py @@ -0,0 +1,324 @@ +import re +from typing import Optional + +from qtpy import QtWidgets, QtGui, QtCore + +from ayon_core import resources +from ayon_core.style import load_stylesheet +from ayon_core.tools.console_interpreter import ( + AbstractInterpreterController, + InterpreterController, +) + +from .utils import StdOEWrap +from .widgets import ( + PythonTabWidget, + OutputTextWidget, + EnhancedTabBar, + TabNameDialog, +) + +ANSI_ESCAPE = re.compile( + r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]" +) +AYON_ART = r""" + + ▄██▄ + ▄███▄ ▀██▄ ▀██▀ ▄██▀ ▄██▀▀▀██▄ ▀███▄ █▄ + ▄▄ ▀██▄ ▀██▄ ▄██▀ ██▀ ▀██▄ ▄ ▀██▄ ███ + ▄██▀ ██▄ ▀ ▄▄ ▀ ██ ▄██ ███ ▀██▄ ███ + ▄██▀ ▀██▄ ██ ▀██▄ ▄██▀ ███ ▀██ ▀█▀ + ▄██▀ ▀██▄ ▀█ ▀██▄▄▄▄██▀ █▀ ▀██▄ + + · · - =[ by YNPUT ]:[ http://ayon.ynput.io ]= - · · + +""" + + +class ConsoleInterpreterWindow(QtWidgets.QWidget): + default_width = 1000 + default_height = 600 + + def __init__( + self, + controller: Optional[AbstractInterpreterController] = None, + parent: Optional[QtWidgets.QWidget] = None, + ): + super().__init__(parent) + + self.setWindowTitle("AYON Console") + self.setWindowIcon(QtGui.QIcon(resources.get_ayon_icon_filepath())) + + if controller is None: + controller = InterpreterController() + + output_widget = OutputTextWidget(self) + output_widget.setObjectName("PythonInterpreterOutput") + output_widget.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) + output_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + + tab_widget = QtWidgets.QTabWidget(self) + tab_bar = EnhancedTabBar(tab_widget) + tab_widget.setTabBar(tab_bar) + tab_widget.setTabsClosable(False) + tab_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + + widgets_splitter = QtWidgets.QSplitter(self) + widgets_splitter.setOrientation(QtCore.Qt.Vertical) + widgets_splitter.addWidget(output_widget) + widgets_splitter.addWidget(tab_widget) + widgets_splitter.setStretchFactor(0, 1) + widgets_splitter.setStretchFactor(1, 1) + height = int(self.default_height / 2) + widgets_splitter.setSizes([height, self.default_height - height]) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(widgets_splitter) + + line_check_timer = QtCore.QTimer() + line_check_timer.setInterval(200) + + line_check_timer.timeout.connect(self._on_timer_timeout) + tab_bar.right_clicked.connect(self._on_tab_right_click) + tab_bar.double_clicked.connect(self._on_tab_double_click) + tab_bar.mid_clicked.connect(self._on_tab_mid_click) + tab_widget.tabCloseRequested.connect(self._on_tab_close_req) + + self._tabs = [] + + self._stdout_err_wrapper = StdOEWrap() + + self._widgets_splitter = widgets_splitter + self._output_widget = output_widget + self._tab_widget = tab_widget + self._line_check_timer = line_check_timer + + self._append_lines([AYON_ART]) + + self._first_show = True + self._controller = controller + + def showEvent(self, event): + self._line_check_timer.start() + super().showEvent(event) + # First show setup + if self._first_show: + self._first_show = False + self._on_first_show() + + if self._tab_widget.count() < 1: + self.add_tab("Python") + + self._output_widget.scroll_to_bottom() + + def closeEvent(self, event): + self._save_registry() + super().closeEvent(event) + self._line_check_timer.stop() + + def add_tab(self, tab_name, index=None): + widget = PythonTabWidget(self) + widget.before_execute.connect(self._on_before_execute) + widget.add_tab_requested.connect(self._on_add_requested) + if index is None: + if self._tab_widget.count() > 0: + index = self._tab_widget.currentIndex() + 1 + else: + index = 0 + + self._tabs.append(widget) + self._tab_widget.insertTab(index, widget, tab_name) + self._tab_widget.setCurrentIndex(index) + + if self._tab_widget.count() > 1: + self._tab_widget.setTabsClosable(True) + widget.setFocus() + return widget + + def _on_first_show(self): + config = self._controller.get_config() + width = config.width + height = config.height + if width is None or width < 200: + width = self.default_width + if height is None or height < 200: + height = self.default_height + + for tab_item in config.tabs: + widget = self.add_tab(tab_item.name) + widget.set_code(tab_item.code) + + self.resize(width, height) + # Change stylesheet + self.setStyleSheet(load_stylesheet()) + # Check if splitter sizes are set + splitters_count = len(self._widgets_splitter.sizes()) + if len(config.splitter_sizes) == splitters_count: + self._widgets_splitter.setSizes(config.splitter_sizes) + + def _save_registry(self): + tabs = [] + for tab_idx in range(self._tab_widget.count()): + widget = self._tab_widget.widget(tab_idx) + tabs.append({ + "name": self._tab_widget.tabText(tab_idx), + "code": widget.get_code() + }) + + self._controller.save_config( + self.width(), + self.height(), + self._widgets_splitter.sizes(), + tabs + ) + + def _on_tab_right_click(self, global_point): + point = self._tab_widget.mapFromGlobal(global_point) + tab_bar = self._tab_widget.tabBar() + tab_idx = tab_bar.tabAt(point) + last_index = tab_bar.count() - 1 + if tab_idx < 0 or tab_idx > last_index: + return + + menu = QtWidgets.QMenu(self._tab_widget) + + add_tab_action = QtWidgets.QAction("Add tab...", menu) + add_tab_action.setToolTip("Add new tab") + + rename_tab_action = QtWidgets.QAction("Rename...", menu) + rename_tab_action.setToolTip("Rename tab") + + duplicate_tab_action = QtWidgets.QAction("Duplicate...", menu) + duplicate_tab_action.setToolTip("Duplicate code to new tab") + + close_tab_action = QtWidgets.QAction("Close", menu) + close_tab_action.setToolTip("Close tab and lose content") + close_tab_action.setEnabled(self._tab_widget.tabsClosable()) + + menu.addAction(add_tab_action) + menu.addAction(rename_tab_action) + menu.addAction(duplicate_tab_action) + menu.addAction(close_tab_action) + + result = menu.exec_(global_point) + if result is None: + return + + if result is rename_tab_action: + self._rename_tab_req(tab_idx) + + elif result is add_tab_action: + self._on_add_requested() + + elif result is duplicate_tab_action: + self._duplicate_requested(tab_idx) + + elif result is close_tab_action: + self._on_tab_close_req(tab_idx) + + def _rename_tab_req(self, tab_idx): + dialog = TabNameDialog(self) + dialog.set_tab_name(self._tab_widget.tabText(tab_idx)) + dialog.exec_() + tab_name = dialog.result() + if tab_name: + self._tab_widget.setTabText(tab_idx, tab_name) + + def _duplicate_requested(self, tab_idx=None): + if tab_idx is None: + tab_idx = self._tab_widget.currentIndex() + + src_widget = self._tab_widget.widget(tab_idx) + dst_widget = self._add_tab() + if dst_widget is None: + return + dst_widget.set_code(src_widget.get_code()) + + def _on_tab_mid_click(self, global_point): + point = self._tab_widget.mapFromGlobal(global_point) + tab_bar = self._tab_widget.tabBar() + tab_idx = tab_bar.tabAt(point) + last_index = tab_bar.count() - 1 + if tab_idx < 0 or tab_idx > last_index: + return + + self._on_tab_close_req(tab_idx) + + def _on_tab_double_click(self, global_point): + point = self._tab_widget.mapFromGlobal(global_point) + tab_bar = self._tab_widget.tabBar() + tab_idx = tab_bar.tabAt(point) + last_index = tab_bar.count() - 1 + if tab_idx < 0 or tab_idx > last_index: + return + + self._rename_tab_req(tab_idx) + + def _on_tab_close_req(self, tab_index): + if self._tab_widget.count() == 1: + return + + widget = self._tab_widget.widget(tab_index) + if widget in self._tabs: + self._tabs.remove(widget) + self._tab_widget.removeTab(tab_index) + + if self._tab_widget.count() == 1: + self._tab_widget.setTabsClosable(False) + + def _append_lines(self, lines): + at_max = self._output_widget.vertical_scroll_at_max() + tmp_cursor = QtGui.QTextCursor(self._output_widget.document()) + tmp_cursor.movePosition(QtGui.QTextCursor.End) + for line in lines: + tmp_cursor.insertText(line) + + if at_max: + self._output_widget.scroll_to_bottom() + + def _on_timer_timeout(self): + if self._stdout_err_wrapper.lines: + lines = [] + while self._stdout_err_wrapper.lines: + line = self._stdout_err_wrapper.lines.popleft() + lines.append(ANSI_ESCAPE.sub("", line)) + self._append_lines(lines) + + def _on_add_requested(self): + self._add_tab() + + def _add_tab(self): + dialog = TabNameDialog(self) + dialog.exec_() + tab_name = dialog.result() + if tab_name: + return self.add_tab(tab_name) + + return None + + def _on_before_execute(self, code_text): + at_max = self._output_widget.vertical_scroll_at_max() + document = self._output_widget.document() + tmp_cursor = QtGui.QTextCursor(document) + tmp_cursor.movePosition(QtGui.QTextCursor.End) + tmp_cursor.insertText("{}\nExecuting command:\n".format(20 * "-")) + + code_block_format = QtGui.QTextFrameFormat() + code_block_format.setBackground(QtGui.QColor(27, 27, 27)) + code_block_format.setPadding(4) + + tmp_cursor.insertFrame(code_block_format) + char_format = tmp_cursor.charFormat() + char_format.setForeground( + QtGui.QBrush(QtGui.QColor(114, 224, 198)) + ) + tmp_cursor.setCharFormat(char_format) + tmp_cursor.insertText(code_text) + + # Create new cursor + tmp_cursor = QtGui.QTextCursor(document) + tmp_cursor.movePosition(QtGui.QTextCursor.End) + tmp_cursor.insertText("{}\n".format(20 * "-")) + + if at_max: + self._output_widget.scroll_to_bottom() From bf631d565d2bfff14409d41023d7a4f0ed3e73ae Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:10:52 +0100 Subject: [PATCH 208/463] add Console to default tray actions --- client/ayon_core/tools/tray/ui/tray.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ayon_core/tools/tray/ui/tray.py b/client/ayon_core/tools/tray/ui/tray.py index e61f903c80..638a316634 100644 --- a/client/ayon_core/tools/tray/ui/tray.py +++ b/client/ayon_core/tools/tray/ui/tray.py @@ -35,6 +35,7 @@ from ayon_core.tools.tray.lib import ( ) from ayon_core.tools.launcher.ui import LauncherWindow from ayon_core.tools.loader.ui import LoaderWindow +from ayon_core.tools.console_interpreter.ui import ConsoleInterpreterWindow from .addons_manager import TrayAddonsManager from .host_console_listener import HostListener @@ -87,6 +88,7 @@ class TrayManager: self._launcher_window = None self._browser_window = None + self._console_window = ConsoleInterpreterWindow() self._update_check_timer = update_check_timer self._update_check_interval = update_check_interval @@ -154,6 +156,11 @@ class TrayManager: tray_menu = self.tray_widget.menu + console_action = ITrayAction.add_action_to_admin_submenu( + "Console", tray_menu + ) + console_action.triggered.connect(self._show_console_window) + self._addons_manager.initialize(tray_menu) # Add default actions under addon actions @@ -563,6 +570,11 @@ class TrayManager: self._browser_window.raise_() self._browser_window.activateWindow() + def _show_console_window(self): + self._console_window.show() + self._console_window.raise_() + self._console_window.activateWindow() + class SystemTrayIcon(QtWidgets.QSystemTrayIcon): """Tray widget. From 21e60135f434b2b8f6553cc2d0aeb12e4e68049e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:11:20 +0100 Subject: [PATCH 209/463] remove 'ayon_core.modules' --- client/ayon_core/addon/base.py | 58 +- client/ayon_core/modules/__init__.py | 0 .../python_console_interpreter/__init__.py | 8 - .../python_console_interpreter/addon.py | 42 -- .../window/__init__.py | 8 - .../window/widgets.py | 660 ------------------ 6 files changed, 1 insertion(+), 775 deletions(-) delete mode 100644 client/ayon_core/modules/__init__.py delete mode 100644 client/ayon_core/modules/python_console_interpreter/__init__.py delete mode 100644 client/ayon_core/modules/python_console_interpreter/addon.py delete mode 100644 client/ayon_core/modules/python_console_interpreter/window/__init__.py delete mode 100644 client/ayon_core/modules/python_console_interpreter/window/widgets.py diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index 364a84cb7b..ed6b82ef52 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -370,67 +370,11 @@ def _load_ayon_addons(log): return all_addon_modules -def _load_addons_in_core(log): - # Add current directory at first place - # - has small differences in import logic - addon_modules = [] - modules_dir = os.path.join(AYON_CORE_ROOT, "modules") - if not os.path.exists(modules_dir): - log.warning( - f"Could not find path when loading AYON addons \"{modules_dir}\"" - ) - return addon_modules - - ignored_filenames = IGNORED_FILENAMES | IGNORED_DEFAULT_FILENAMES - for filename in os.listdir(modules_dir): - # Ignore filenames - if filename in ignored_filenames: - continue - - fullpath = os.path.join(modules_dir, filename) - basename, ext = os.path.splitext(filename) - - # Validations - if os.path.isdir(fullpath): - # Check existence of init file - init_path = os.path.join(fullpath, "__init__.py") - if not os.path.exists(init_path): - log.debug(( - "Addon directory does not contain __init__.py" - f" file {fullpath}" - )) - continue - - elif ext != ".py": - continue - - # TODO add more logic how to define if folder is addon or not - # - check manifest and content of manifest - try: - # Don't import dynamically current directory modules - import_str = f"ayon_core.modules.{basename}" - default_module = __import__(import_str, fromlist=("", )) - addon_modules.append(default_module) - - except Exception: - log.error( - f"Failed to import in-core addon '{basename}'.", - exc_info=True - ) - return addon_modules - - def _load_addons(): log = Logger.get_logger("AddonsLoader") - addon_modules = _load_ayon_addons(log) - # All addon in 'modules' folder are tray actions and should be moved - # to tray tool. - # TODO remove - addon_modules.extend(_load_addons_in_core(log)) - # Store modules to local cache - _LoadCache.addon_modules = addon_modules + _LoadCache.addon_modules = _load_ayon_addons(log) class AYONAddon(ABC): diff --git a/client/ayon_core/modules/__init__.py b/client/ayon_core/modules/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/ayon_core/modules/python_console_interpreter/__init__.py b/client/ayon_core/modules/python_console_interpreter/__init__.py deleted file mode 100644 index 8d5c23bdba..0000000000 --- a/client/ayon_core/modules/python_console_interpreter/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .addon import ( - PythonInterpreterAction -) - - -__all__ = ( - "PythonInterpreterAction", -) diff --git a/client/ayon_core/modules/python_console_interpreter/addon.py b/client/ayon_core/modules/python_console_interpreter/addon.py deleted file mode 100644 index b0dce2585e..0000000000 --- a/client/ayon_core/modules/python_console_interpreter/addon.py +++ /dev/null @@ -1,42 +0,0 @@ -from ayon_core.addon import AYONAddon, ITrayAction - - -class PythonInterpreterAction(AYONAddon, ITrayAction): - label = "Console" - name = "python_interpreter" - version = "1.0.0" - admin_action = True - - def initialize(self, settings): - self._interpreter_window = None - - def tray_init(self): - self.create_interpreter_window() - - def tray_exit(self): - if self._interpreter_window is not None: - self._interpreter_window.save_registry() - - def create_interpreter_window(self): - """Initializa Settings Qt window.""" - if self._interpreter_window: - return - - from ayon_core.modules.python_console_interpreter.window import ( - PythonInterpreterWidget - ) - - self._interpreter_window = PythonInterpreterWidget() - - def on_action_trigger(self): - self.show_interpreter_window() - - def show_interpreter_window(self): - self.create_interpreter_window() - - if self._interpreter_window.isVisible(): - self._interpreter_window.activateWindow() - self._interpreter_window.raise_() - return - - self._interpreter_window.show() diff --git a/client/ayon_core/modules/python_console_interpreter/window/__init__.py b/client/ayon_core/modules/python_console_interpreter/window/__init__.py deleted file mode 100644 index 92fd6f1df2..0000000000 --- a/client/ayon_core/modules/python_console_interpreter/window/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .widgets import ( - PythonInterpreterWidget -) - - -__all__ = ( - "PythonInterpreterWidget", -) diff --git a/client/ayon_core/modules/python_console_interpreter/window/widgets.py b/client/ayon_core/modules/python_console_interpreter/window/widgets.py deleted file mode 100644 index 628a2e72ff..0000000000 --- a/client/ayon_core/modules/python_console_interpreter/window/widgets.py +++ /dev/null @@ -1,660 +0,0 @@ -import os -import re -import sys -import collections -from code import InteractiveInterpreter - -import appdirs -from qtpy import QtCore, QtWidgets, QtGui - -from ayon_core import resources -from ayon_core.style import load_stylesheet -from ayon_core.lib import JSONSettingRegistry - - -ayon_art = r""" - - ▄██▄ - ▄███▄ ▀██▄ ▀██▀ ▄██▀ ▄██▀▀▀██▄ ▀███▄ █▄ - ▄▄ ▀██▄ ▀██▄ ▄██▀ ██▀ ▀██▄ ▄ ▀██▄ ███ - ▄██▀ ██▄ ▀ ▄▄ ▀ ██ ▄██ ███ ▀██▄ ███ - ▄██▀ ▀██▄ ██ ▀██▄ ▄██▀ ███ ▀██ ▀█▀ - ▄██▀ ▀██▄ ▀█ ▀██▄▄▄▄██▀ █▀ ▀██▄ - - · · - =[ by YNPUT ]:[ http://ayon.ynput.io ]= - · · - -""" - - -class PythonInterpreterRegistry(JSONSettingRegistry): - """Class handling OpenPype general settings registry. - - Attributes: - vendor (str): Name used for path construction. - product (str): Additional name used for path construction. - - """ - - def __init__(self): - self.vendor = "Ynput" - self.product = "AYON" - name = "python_interpreter_tool" - path = appdirs.user_data_dir(self.product, self.vendor) - super(PythonInterpreterRegistry, self).__init__(name, path) - - -class StdOEWrap: - def __init__(self): - self._origin_stdout_write = None - self._origin_stderr_write = None - self._listening = False - self.lines = collections.deque() - - if not sys.stdout: - sys.stdout = open(os.devnull, "w") - - if not sys.stderr: - sys.stderr = open(os.devnull, "w") - - if self._origin_stdout_write is None: - self._origin_stdout_write = sys.stdout.write - - if self._origin_stderr_write is None: - self._origin_stderr_write = sys.stderr.write - - self._listening = True - sys.stdout.write = self._stdout_listener - sys.stderr.write = self._stderr_listener - - def stop_listen(self): - self._listening = False - - def _stdout_listener(self, text): - if self._listening: - self.lines.append(text) - if self._origin_stdout_write is not None: - self._origin_stdout_write(text) - - def _stderr_listener(self, text): - if self._listening: - self.lines.append(text) - if self._origin_stderr_write is not None: - self._origin_stderr_write(text) - - -class PythonCodeEditor(QtWidgets.QPlainTextEdit): - execute_requested = QtCore.Signal() - - def __init__(self, parent): - super(PythonCodeEditor, self).__init__(parent) - - self.setObjectName("PythonCodeEditor") - - self._indent = 4 - - def _tab_shift_right(self): - cursor = self.textCursor() - selected_text = cursor.selectedText() - if not selected_text: - cursor.insertText(" " * self._indent) - return - - sel_start = cursor.selectionStart() - sel_end = cursor.selectionEnd() - cursor.setPosition(sel_end) - end_line = cursor.blockNumber() - cursor.setPosition(sel_start) - while True: - cursor.movePosition(QtGui.QTextCursor.StartOfLine) - text = cursor.block().text() - spaces = len(text) - len(text.lstrip(" ")) - new_spaces = spaces % self._indent - if not new_spaces: - new_spaces = self._indent - - cursor.insertText(" " * new_spaces) - if cursor.blockNumber() == end_line: - break - - cursor.movePosition(QtGui.QTextCursor.NextBlock) - - def _tab_shift_left(self): - tmp_cursor = self.textCursor() - sel_start = tmp_cursor.selectionStart() - sel_end = tmp_cursor.selectionEnd() - - cursor = QtGui.QTextCursor(self.document()) - cursor.setPosition(sel_end) - end_line = cursor.blockNumber() - cursor.setPosition(sel_start) - while True: - cursor.movePosition(QtGui.QTextCursor.StartOfLine) - text = cursor.block().text() - spaces = len(text) - len(text.lstrip(" ")) - if spaces: - spaces_to_remove = (spaces % self._indent) or self._indent - if spaces_to_remove > spaces: - spaces_to_remove = spaces - - cursor.setPosition( - cursor.position() + spaces_to_remove, - QtGui.QTextCursor.KeepAnchor - ) - cursor.removeSelectedText() - - if cursor.blockNumber() == end_line: - break - - cursor.movePosition(QtGui.QTextCursor.NextBlock) - - def keyPressEvent(self, event): - if event.key() == QtCore.Qt.Key_Backtab: - self._tab_shift_left() - event.accept() - return - - if event.key() == QtCore.Qt.Key_Tab: - if event.modifiers() == QtCore.Qt.NoModifier: - self._tab_shift_right() - event.accept() - return - - if ( - event.key() == QtCore.Qt.Key_Return - and event.modifiers() == QtCore.Qt.ControlModifier - ): - self.execute_requested.emit() - event.accept() - return - - super(PythonCodeEditor, self).keyPressEvent(event) - - -class PythonTabWidget(QtWidgets.QWidget): - add_tab_requested = QtCore.Signal() - before_execute = QtCore.Signal(str) - - def __init__(self, parent): - super(PythonTabWidget, self).__init__(parent) - - code_input = PythonCodeEditor(self) - - self.setFocusProxy(code_input) - - add_tab_btn = QtWidgets.QPushButton("Add tab...", self) - add_tab_btn.setToolTip("Add new tab") - - execute_btn = QtWidgets.QPushButton("Execute", self) - execute_btn.setToolTip("Execute command (Ctrl + Enter)") - - btns_layout = QtWidgets.QHBoxLayout() - btns_layout.setContentsMargins(0, 0, 0, 0) - btns_layout.addWidget(add_tab_btn) - btns_layout.addStretch(1) - btns_layout.addWidget(execute_btn) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(code_input, 1) - layout.addLayout(btns_layout, 0) - - add_tab_btn.clicked.connect(self._on_add_tab_clicked) - execute_btn.clicked.connect(self._on_execute_clicked) - code_input.execute_requested.connect(self.execute) - - self._code_input = code_input - self._interpreter = InteractiveInterpreter() - - def _on_add_tab_clicked(self): - self.add_tab_requested.emit() - - def _on_execute_clicked(self): - self.execute() - - def get_code(self): - return self._code_input.toPlainText() - - def set_code(self, code_text): - self._code_input.setPlainText(code_text) - - def execute(self): - code_text = self._code_input.toPlainText() - self.before_execute.emit(code_text) - self._interpreter.runcode(code_text) - - -class TabNameDialog(QtWidgets.QDialog): - default_width = 330 - default_height = 85 - - def __init__(self, parent): - super(TabNameDialog, self).__init__(parent) - - self.setWindowTitle("Enter tab name") - - name_label = QtWidgets.QLabel("Tab name:", self) - name_input = QtWidgets.QLineEdit(self) - - inputs_layout = QtWidgets.QHBoxLayout() - inputs_layout.addWidget(name_label) - inputs_layout.addWidget(name_input) - - ok_btn = QtWidgets.QPushButton("Ok", self) - cancel_btn = QtWidgets.QPushButton("Cancel", self) - btns_layout = QtWidgets.QHBoxLayout() - btns_layout.addStretch(1) - btns_layout.addWidget(ok_btn) - btns_layout.addWidget(cancel_btn) - - layout = QtWidgets.QVBoxLayout(self) - layout.addLayout(inputs_layout) - layout.addStretch(1) - layout.addLayout(btns_layout) - - ok_btn.clicked.connect(self._on_ok_clicked) - cancel_btn.clicked.connect(self._on_cancel_clicked) - - self._name_input = name_input - self._ok_btn = ok_btn - self._cancel_btn = cancel_btn - - self._result = None - - self.resize(self.default_width, self.default_height) - - def set_tab_name(self, name): - self._name_input.setText(name) - - def result(self): - return self._result - - def showEvent(self, event): - super(TabNameDialog, self).showEvent(event) - btns_width = max( - self._ok_btn.width(), - self._cancel_btn.width() - ) - - self._ok_btn.setMinimumWidth(btns_width) - self._cancel_btn.setMinimumWidth(btns_width) - - def _on_ok_clicked(self): - self._result = self._name_input.text() - self.accept() - - def _on_cancel_clicked(self): - self._result = None - self.reject() - - -class OutputTextWidget(QtWidgets.QTextEdit): - v_max_offset = 4 - - def vertical_scroll_at_max(self): - v_scroll = self.verticalScrollBar() - return v_scroll.value() > v_scroll.maximum() - self.v_max_offset - - def scroll_to_bottom(self): - v_scroll = self.verticalScrollBar() - return v_scroll.setValue(v_scroll.maximum()) - - -class EnhancedTabBar(QtWidgets.QTabBar): - double_clicked = QtCore.Signal(QtCore.QPoint) - right_clicked = QtCore.Signal(QtCore.QPoint) - mid_clicked = QtCore.Signal(QtCore.QPoint) - - def __init__(self, parent): - super(EnhancedTabBar, self).__init__(parent) - - self.setDrawBase(False) - - def mouseDoubleClickEvent(self, event): - self.double_clicked.emit(event.globalPos()) - event.accept() - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.RightButton: - self.right_clicked.emit(event.globalPos()) - event.accept() - return - - elif event.button() == QtCore.Qt.MidButton: - self.mid_clicked.emit(event.globalPos()) - event.accept() - - else: - super(EnhancedTabBar, self).mouseReleaseEvent(event) - - -class PythonInterpreterWidget(QtWidgets.QWidget): - default_width = 1000 - default_height = 600 - - def __init__(self, allow_save_registry=True, parent=None): - super(PythonInterpreterWidget, self).__init__(parent) - - self.setWindowTitle("AYON Console") - self.setWindowIcon(QtGui.QIcon(resources.get_ayon_icon_filepath())) - - self.ansi_escape = re.compile( - r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]" - ) - - self._tabs = [] - - self._stdout_err_wrapper = StdOEWrap() - - output_widget = OutputTextWidget(self) - output_widget.setObjectName("PythonInterpreterOutput") - output_widget.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) - output_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - - tab_widget = QtWidgets.QTabWidget(self) - tab_bar = EnhancedTabBar(tab_widget) - tab_widget.setTabBar(tab_bar) - tab_widget.setTabsClosable(False) - tab_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - - widgets_splitter = QtWidgets.QSplitter(self) - widgets_splitter.setOrientation(QtCore.Qt.Vertical) - widgets_splitter.addWidget(output_widget) - widgets_splitter.addWidget(tab_widget) - widgets_splitter.setStretchFactor(0, 1) - widgets_splitter.setStretchFactor(1, 1) - height = int(self.default_height / 2) - widgets_splitter.setSizes([height, self.default_height - height]) - - layout = QtWidgets.QVBoxLayout(self) - layout.addWidget(widgets_splitter) - - line_check_timer = QtCore.QTimer() - line_check_timer.setInterval(200) - - line_check_timer.timeout.connect(self._on_timer_timeout) - tab_bar.right_clicked.connect(self._on_tab_right_click) - tab_bar.double_clicked.connect(self._on_tab_double_click) - tab_bar.mid_clicked.connect(self._on_tab_mid_click) - tab_widget.tabCloseRequested.connect(self._on_tab_close_req) - - self._widgets_splitter = widgets_splitter - self._output_widget = output_widget - self._tab_widget = tab_widget - self._line_check_timer = line_check_timer - - self._append_lines([ayon_art]) - - self._first_show = True - self._splitter_size_ratio = None - self._allow_save_registry = allow_save_registry - self._registry_saved = True - - self._init_from_registry() - - if self._tab_widget.count() < 1: - self.add_tab("Python") - - def _init_from_registry(self): - setting_registry = PythonInterpreterRegistry() - width = None - height = None - try: - width = setting_registry.get_item("width") - height = setting_registry.get_item("height") - - except ValueError: - pass - - if width is None or width < 200: - width = self.default_width - - if height is None or height < 200: - height = self.default_height - - self.resize(width, height) - - try: - self._splitter_size_ratio = ( - setting_registry.get_item("splitter_sizes") - ) - - except ValueError: - pass - - try: - tab_defs = setting_registry.get_item("tabs") or [] - for tab_def in tab_defs: - widget = self.add_tab(tab_def["name"]) - widget.set_code(tab_def["code"]) - - except ValueError: - pass - - def save_registry(self): - # Window was not showed - if not self._allow_save_registry or self._registry_saved: - return - - self._registry_saved = True - setting_registry = PythonInterpreterRegistry() - - setting_registry.set_item("width", self.width()) - setting_registry.set_item("height", self.height()) - - setting_registry.set_item( - "splitter_sizes", self._widgets_splitter.sizes() - ) - - tabs = [] - for tab_idx in range(self._tab_widget.count()): - widget = self._tab_widget.widget(tab_idx) - tab_code = widget.get_code() - tab_name = self._tab_widget.tabText(tab_idx) - tabs.append({ - "name": tab_name, - "code": tab_code - }) - - setting_registry.set_item("tabs", tabs) - - def _on_tab_right_click(self, global_point): - point = self._tab_widget.mapFromGlobal(global_point) - tab_bar = self._tab_widget.tabBar() - tab_idx = tab_bar.tabAt(point) - last_index = tab_bar.count() - 1 - if tab_idx < 0 or tab_idx > last_index: - return - - menu = QtWidgets.QMenu(self._tab_widget) - - add_tab_action = QtWidgets.QAction("Add tab...", menu) - add_tab_action.setToolTip("Add new tab") - - rename_tab_action = QtWidgets.QAction("Rename...", menu) - rename_tab_action.setToolTip("Rename tab") - - duplicate_tab_action = QtWidgets.QAction("Duplicate...", menu) - duplicate_tab_action.setToolTip("Duplicate code to new tab") - - close_tab_action = QtWidgets.QAction("Close", menu) - close_tab_action.setToolTip("Close tab and lose content") - close_tab_action.setEnabled(self._tab_widget.tabsClosable()) - - menu.addAction(add_tab_action) - menu.addAction(rename_tab_action) - menu.addAction(duplicate_tab_action) - menu.addAction(close_tab_action) - - result = menu.exec_(global_point) - if result is None: - return - - if result is rename_tab_action: - self._rename_tab_req(tab_idx) - - elif result is add_tab_action: - self._on_add_requested() - - elif result is duplicate_tab_action: - self._duplicate_requested(tab_idx) - - elif result is close_tab_action: - self._on_tab_close_req(tab_idx) - - def _rename_tab_req(self, tab_idx): - dialog = TabNameDialog(self) - dialog.set_tab_name(self._tab_widget.tabText(tab_idx)) - dialog.exec_() - tab_name = dialog.result() - if tab_name: - self._tab_widget.setTabText(tab_idx, tab_name) - - def _duplicate_requested(self, tab_idx=None): - if tab_idx is None: - tab_idx = self._tab_widget.currentIndex() - - src_widget = self._tab_widget.widget(tab_idx) - dst_widget = self._add_tab() - if dst_widget is None: - return - dst_widget.set_code(src_widget.get_code()) - - def _on_tab_mid_click(self, global_point): - point = self._tab_widget.mapFromGlobal(global_point) - tab_bar = self._tab_widget.tabBar() - tab_idx = tab_bar.tabAt(point) - last_index = tab_bar.count() - 1 - if tab_idx < 0 or tab_idx > last_index: - return - - self._on_tab_close_req(tab_idx) - - def _on_tab_double_click(self, global_point): - point = self._tab_widget.mapFromGlobal(global_point) - tab_bar = self._tab_widget.tabBar() - tab_idx = tab_bar.tabAt(point) - last_index = tab_bar.count() - 1 - if tab_idx < 0 or tab_idx > last_index: - return - - self._rename_tab_req(tab_idx) - - def _on_tab_close_req(self, tab_index): - if self._tab_widget.count() == 1: - return - - widget = self._tab_widget.widget(tab_index) - if widget in self._tabs: - self._tabs.remove(widget) - self._tab_widget.removeTab(tab_index) - - if self._tab_widget.count() == 1: - self._tab_widget.setTabsClosable(False) - - def _append_lines(self, lines): - at_max = self._output_widget.vertical_scroll_at_max() - tmp_cursor = QtGui.QTextCursor(self._output_widget.document()) - tmp_cursor.movePosition(QtGui.QTextCursor.End) - for line in lines: - tmp_cursor.insertText(line) - - if at_max: - self._output_widget.scroll_to_bottom() - - def _on_timer_timeout(self): - if self._stdout_err_wrapper.lines: - lines = [] - while self._stdout_err_wrapper.lines: - line = self._stdout_err_wrapper.lines.popleft() - lines.append(self.ansi_escape.sub("", line)) - self._append_lines(lines) - - def _on_add_requested(self): - self._add_tab() - - def _add_tab(self): - dialog = TabNameDialog(self) - dialog.exec_() - tab_name = dialog.result() - if tab_name: - return self.add_tab(tab_name) - - return None - - def _on_before_execute(self, code_text): - at_max = self._output_widget.vertical_scroll_at_max() - document = self._output_widget.document() - tmp_cursor = QtGui.QTextCursor(document) - tmp_cursor.movePosition(QtGui.QTextCursor.End) - tmp_cursor.insertText("{}\nExecuting command:\n".format(20 * "-")) - - code_block_format = QtGui.QTextFrameFormat() - code_block_format.setBackground(QtGui.QColor(27, 27, 27)) - code_block_format.setPadding(4) - - tmp_cursor.insertFrame(code_block_format) - char_format = tmp_cursor.charFormat() - char_format.setForeground( - QtGui.QBrush(QtGui.QColor(114, 224, 198)) - ) - tmp_cursor.setCharFormat(char_format) - tmp_cursor.insertText(code_text) - - # Create new cursor - tmp_cursor = QtGui.QTextCursor(document) - tmp_cursor.movePosition(QtGui.QTextCursor.End) - tmp_cursor.insertText("{}\n".format(20 * "-")) - - if at_max: - self._output_widget.scroll_to_bottom() - - def add_tab(self, tab_name, index=None): - widget = PythonTabWidget(self) - widget.before_execute.connect(self._on_before_execute) - widget.add_tab_requested.connect(self._on_add_requested) - if index is None: - if self._tab_widget.count() > 0: - index = self._tab_widget.currentIndex() + 1 - else: - index = 0 - - self._tabs.append(widget) - self._tab_widget.insertTab(index, widget, tab_name) - self._tab_widget.setCurrentIndex(index) - - if self._tab_widget.count() > 1: - self._tab_widget.setTabsClosable(True) - widget.setFocus() - return widget - - def showEvent(self, event): - self._line_check_timer.start() - self._registry_saved = False - super(PythonInterpreterWidget, self).showEvent(event) - # First show setup - if self._first_show: - self._first_show = False - self._on_first_show() - - self._output_widget.scroll_to_bottom() - - def _on_first_show(self): - # Change stylesheet - self.setStyleSheet(load_stylesheet()) - # Check if splitter size ratio is set - # - first store value to local variable and then unset it - splitter_size_ratio = self._splitter_size_ratio - self._splitter_size_ratio = None - # Skip if is not set - if not splitter_size_ratio: - return - - # Skip if number of size items does not match to splitter - splitters_count = len(self._widgets_splitter.sizes()) - if len(splitter_size_ratio) == splitters_count: - self._widgets_splitter.setSizes(splitter_size_ratio) - - def closeEvent(self, event): - self.save_registry() - super(PythonInterpreterWidget, self).closeEvent(event) - self._line_check_timer.stop() From a8441e3036816e6fe3cb44239e4bc3cdc8c8b4a4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:14:51 +0100 Subject: [PATCH 210/463] enhanced admin menu options --- client/ayon_core/addon/interfaces.py | 40 +++++++++++++++----------- client/ayon_core/tools/tray/ui/tray.py | 6 ++-- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/addon/interfaces.py b/client/ayon_core/addon/interfaces.py index b273e7839b..2616913dc0 100644 --- a/client/ayon_core/addon/interfaces.py +++ b/client/ayon_core/addon/interfaces.py @@ -125,6 +125,7 @@ class ITrayAddon(AYONInterface): tray_initialized = False _tray_manager = None + _admin_submenu = None @abstractmethod def tray_init(self): @@ -198,6 +199,27 @@ class ITrayAddon(AYONInterface): if hasattr(self.manager, "add_doubleclick_callback"): self.manager.add_doubleclick_callback(self, callback) + @staticmethod + def admin_submenu(tray_menu): + if ITrayAddon._admin_submenu is None: + from qtpy import QtWidgets + + admin_submenu = QtWidgets.QMenu("Admin", tray_menu) + admin_submenu.menuAction().setVisible(False) + ITrayAddon._admin_submenu = admin_submenu + return ITrayAddon._admin_submenu + + @staticmethod + def add_action_to_admin_submenu(label, tray_menu): + from qtpy import QtWidgets + + menu = ITrayAddon.admin_submenu(tray_menu) + action = QtWidgets.QAction(label, menu) + menu.addAction(action) + if not menu.menuAction().isVisible(): + menu.menuAction().setVisible(True) + return action + class ITrayAction(ITrayAddon): """Implementation of Tray action. @@ -211,7 +233,6 @@ class ITrayAction(ITrayAddon): """ admin_action = False - _admin_submenu = None _action_item = None @property @@ -229,12 +250,7 @@ class ITrayAction(ITrayAddon): from qtpy import QtWidgets if self.admin_action: - menu = self.admin_submenu(tray_menu) - action = QtWidgets.QAction(self.label, menu) - menu.addAction(action) - if not menu.menuAction().isVisible(): - menu.menuAction().setVisible(True) - + action = self.add_action_to_admin_submenu(self.label, tray_menu) else: action = QtWidgets.QAction(self.label, tray_menu) tray_menu.addAction(action) @@ -248,16 +264,6 @@ class ITrayAction(ITrayAddon): def tray_exit(self): return - @staticmethod - def admin_submenu(tray_menu): - if ITrayAction._admin_submenu is None: - from qtpy import QtWidgets - - admin_submenu = QtWidgets.QMenu("Admin", tray_menu) - admin_submenu.menuAction().setVisible(False) - ITrayAction._admin_submenu = admin_submenu - return ITrayAction._admin_submenu - class ITrayService(ITrayAddon): # Module's property diff --git a/client/ayon_core/tools/tray/ui/tray.py b/client/ayon_core/tools/tray/ui/tray.py index 638a316634..dbaf13dfe9 100644 --- a/client/ayon_core/tools/tray/ui/tray.py +++ b/client/ayon_core/tools/tray/ui/tray.py @@ -20,7 +20,7 @@ from ayon_core.lib import ( ) from ayon_core.settings import get_studio_settings from ayon_core.addon import ( - ITrayAction, + ITrayAddon, ITrayService, ) from ayon_core.pipeline import install_ayon_plugins @@ -156,7 +156,7 @@ class TrayManager: tray_menu = self.tray_widget.menu - console_action = ITrayAction.add_action_to_admin_submenu( + console_action = ITrayAddon.add_action_to_admin_submenu( "Console", tray_menu ) console_action.triggered.connect(self._show_console_window) @@ -183,7 +183,7 @@ class TrayManager: "POST", "/tray/message", self._web_show_tray_message ) - admin_submenu = ITrayAction.admin_submenu(tray_menu) + admin_submenu = ITrayAddon.admin_submenu(tray_menu) tray_menu.addMenu(admin_submenu) # Add services if they are From 14d4c75a123b203f2d27a73316d122fea88426b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:24:20 +0100 Subject: [PATCH 211/463] add publish report viewer to admin actions --- .../publisher/publish_report_viewer/window.py | 70 +++++++++++-------- client/ayon_core/tools/tray/ui/tray.py | 17 +++++ 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/client/ayon_core/tools/publisher/publish_report_viewer/window.py b/client/ayon_core/tools/publisher/publish_report_viewer/window.py index 6921c5d162..77db65588a 100644 --- a/client/ayon_core/tools/publisher/publish_report_viewer/window.py +++ b/client/ayon_core/tools/publisher/publish_report_viewer/window.py @@ -484,6 +484,34 @@ class LoadedFilesView(QtWidgets.QTreeView): self._time_delegate = time_delegate self._remove_btn = remove_btn + def showEvent(self, event): + super().showEvent(event) + self._model.refresh() + header = self.header() + header.resizeSections(QtWidgets.QHeaderView.ResizeToContents) + self._update_remove_btn() + + def resizeEvent(self, event): + super().resizeEvent(event) + self._update_remove_btn() + + def add_filepaths(self, filepaths): + self._model.add_filepaths(filepaths) + self._fill_selection() + + def remove_item_by_id(self, item_id): + self._model.remove_item_by_id(item_id) + self._fill_selection() + + def get_current_report(self): + index = self.currentIndex() + item_id = index.data(ITEM_ID_ROLE) + return self._model.get_report_by_id(item_id) + + def refresh(self): + self._model.refresh() + self._fill_selection() + def _update_remove_btn(self): viewport = self.viewport() height = viewport.height() + self.header().height() @@ -496,28 +524,9 @@ class LoadedFilesView(QtWidgets.QTreeView): header.resizeSections(QtWidgets.QHeaderView.ResizeToContents) self._update_remove_btn() - def resizeEvent(self, event): - super().resizeEvent(event) - self._update_remove_btn() - - def showEvent(self, event): - super().showEvent(event) - self._model.refresh() - header = self.header() - header.resizeSections(QtWidgets.QHeaderView.ResizeToContents) - self._update_remove_btn() - def _on_selection_change(self): self.selection_changed.emit() - def add_filepaths(self, filepaths): - self._model.add_filepaths(filepaths) - self._fill_selection() - - def remove_item_by_id(self, item_id): - self._model.remove_item_by_id(item_id) - self._fill_selection() - def _on_remove_clicked(self): index = self.currentIndex() item_id = index.data(ITEM_ID_ROLE) @@ -533,11 +542,6 @@ class LoadedFilesView(QtWidgets.QTreeView): if index.isValid(): self.setCurrentIndex(index) - def get_current_report(self): - index = self.currentIndex() - item_id = index.data(ITEM_ID_ROLE) - return self._model.get_report_by_id(item_id) - class LoadedFilesWidget(QtWidgets.QWidget): report_changed = QtCore.Signal() @@ -577,15 +581,18 @@ class LoadedFilesWidget(QtWidgets.QWidget): self._add_filepaths(filepaths) event.accept() + def refresh(self): + self._view.refresh() + + def get_current_report(self): + return self._view.get_current_report() + def _on_report_change(self): self.report_changed.emit() def _add_filepaths(self, filepaths): self._view.add_filepaths(filepaths) - def get_current_report(self): - return self._view.get_current_report() - class PublishReportViewerWindow(QtWidgets.QWidget): default_width = 1200 @@ -624,9 +631,12 @@ class PublishReportViewerWindow(QtWidgets.QWidget): self.resize(self.default_width, self.default_height) self.setStyleSheet(style.load_stylesheet()) - def _on_report_change(self): - report = self._loaded_files_widget.get_current_report() - self.set_report(report) + def refresh(self): + self._loaded_files_widget.refresh() def set_report(self, report_data): self._main_widget.set_report(report_data) + + def _on_report_change(self): + report = self._loaded_files_widget.get_current_report() + self.set_report(report) diff --git a/client/ayon_core/tools/tray/ui/tray.py b/client/ayon_core/tools/tray/ui/tray.py index dbaf13dfe9..98e3c783c4 100644 --- a/client/ayon_core/tools/tray/ui/tray.py +++ b/client/ayon_core/tools/tray/ui/tray.py @@ -36,6 +36,9 @@ from ayon_core.tools.tray.lib import ( from ayon_core.tools.launcher.ui import LauncherWindow from ayon_core.tools.loader.ui import LoaderWindow from ayon_core.tools.console_interpreter.ui import ConsoleInterpreterWindow +from ayon_core.tools.publisher.publish_report_viewer import ( + PublishReportViewerWindow, +) from .addons_manager import TrayAddonsManager from .host_console_listener import HostListener @@ -89,6 +92,7 @@ class TrayManager: self._launcher_window = None self._browser_window = None self._console_window = ConsoleInterpreterWindow() + self._publish_report_viewer_window = PublishReportViewerWindow() self._update_check_timer = update_check_timer self._update_check_interval = update_check_interval @@ -161,6 +165,13 @@ class TrayManager: ) console_action.triggered.connect(self._show_console_window) + publish_report_viewer_action = ITrayAddon.add_action_to_admin_submenu( + "Publish report viewer", tray_menu + ) + publish_report_viewer_action.triggered.connect( + self._show_publish_report_viewer + ) + self._addons_manager.initialize(tray_menu) # Add default actions under addon actions @@ -575,6 +586,12 @@ class TrayManager: self._console_window.raise_() self._console_window.activateWindow() + def _show_publish_report_viewer(self): + self._publish_report_viewer_window.refresh() + self._publish_report_viewer_window.show() + self._publish_report_viewer_window.raise_() + self._publish_report_viewer_window.activateWindow() + class SystemTrayIcon(QtWidgets.QSystemTrayIcon): """Tray widget. From b995c51f1cf4845ca3b9ac7f4bce68cad9fbbd17 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:38:39 +0100 Subject: [PATCH 212/463] small ux improvements in push to library project action --- .../tools/push_to_project/ui/window.py | 103 +++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/ui/window.py b/client/ayon_core/tools/push_to_project/ui/window.py index 4d64509afd..0f2537db06 100644 --- a/client/ayon_core/tools/push_to_project/ui/window.py +++ b/client/ayon_core/tools/push_to_project/ui/window.py @@ -14,6 +14,62 @@ from ayon_core.tools.push_to_project.control import ( ) +class ErrorDetailDialog(QtWidgets.QDialog): + def __init__(self, parent): + super().__init__(parent) + + self.setWindowTitle("Error detail") + self.setWindowIcon(QtGui.QIcon(get_app_icon_path())) + + title_label = QtWidgets.QLabel(self) + + sep_1 = SeparatorWidget(parent=self) + + detail_widget = QtWidgets.QTextBrowser(self) + detail_widget.setReadOnly(True) + detail_widget.setTextInteractionFlags( + QtCore.Qt.TextBrowserInteraction + ) + + sep_2 = SeparatorWidget(parent=self) + + btns_widget = QtWidgets.QWidget(self) + + copy_btn = QtWidgets.QPushButton("Copy", btns_widget) + close_btn = QtWidgets.QPushButton("Close", btns_widget) + + btns_layout = QtWidgets.QHBoxLayout(btns_widget) + btns_layout.setContentsMargins(0, 0, 0, 0) + btns_layout.addStretch(1) + btns_layout.addWidget(copy_btn, 0) + btns_layout.addWidget(close_btn, 0) + + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(6, 6, 6, 6) + main_layout.addWidget(title_label, 0) + main_layout.addWidget(sep_1, 0) + main_layout.addWidget(detail_widget, 1) + main_layout.addWidget(sep_2, 0) + main_layout.addWidget(btns_widget, 0) + + copy_btn.clicked.connect(self._on_copy_click) + close_btn.clicked.connect(self._on_close_click) + + self._title_label = title_label + self._detail_widget = detail_widget + + def set_detail(self, title, detail): + self._title_label.setText(title) + self._detail_widget.setText(detail) + + def _on_copy_click(self): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(self._detail_widget.toPlainText()) + + def _on_close_click(self): + self.close() + + class PushToContextSelectWindow(QtWidgets.QWidget): def __init__(self, controller=None): super(PushToContextSelectWindow, self).__init__() @@ -113,6 +169,10 @@ class PushToContextSelectWindow(QtWidgets.QWidget): overlay_label = QtWidgets.QLabel(overlay_widget) overlay_label.setAlignment(QtCore.Qt.AlignCenter) + overlay_label.setWordWrap(True) + overlay_label.setTextInteractionFlags( + QtCore.Qt.TextBrowserInteraction + ) overlay_btns_widget = QtWidgets.QWidget(overlay_widget) overlay_btns_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) @@ -121,13 +181,28 @@ class PushToContextSelectWindow(QtWidgets.QWidget): overlay_try_btn = QtWidgets.QPushButton( "Try again", overlay_btns_widget ) + overlay_try_btn.setToolTip( + "Hide overlay and modify submit information." + ) + + show_detail_btn = QtWidgets.QPushButton( + "Show error detail", overlay_btns_widget + ) + show_detail_btn.setToolTip( + "Show error detail dialog to copy full error." + ) + overlay_close_btn = QtWidgets.QPushButton( "Close", overlay_btns_widget ) + overlay_close_btn.setToolTip("Discard changes and close window.") overlay_btns_layout = QtWidgets.QHBoxLayout(overlay_btns_widget) + overlay_btns_layout.setContentsMargins(0, 0, 0, 0) + overlay_btns_layout.setSpacing(10) overlay_btns_layout.addStretch(1) overlay_btns_layout.addWidget(overlay_try_btn, 0) + overlay_btns_layout.addWidget(show_detail_btn, 0) overlay_btns_layout.addWidget(overlay_close_btn, 0) overlay_btns_layout.addStretch(1) @@ -162,6 +237,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): publish_btn.clicked.connect(self._on_select_click) cancel_btn.clicked.connect(self._on_close_click) + show_detail_btn.clicked.connect(self._on_show_detail_click) overlay_close_btn.clicked.connect(self._on_close_click) overlay_try_btn.clicked.connect(self._on_try_again_click) @@ -209,10 +285,13 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._publish_btn = publish_btn self._overlay_widget = overlay_widget + self._show_detail_btn = show_detail_btn self._overlay_close_btn = overlay_close_btn self._overlay_try_btn = overlay_try_btn self._overlay_label = overlay_label + self._error_detail_dialog = ErrorDetailDialog(self) + self._user_input_changed_timer = user_input_changed_timer # Store current value on input text change # The value is unset when is passed to controller @@ -235,6 +314,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._folder_is_valid = None publish_btn.setEnabled(False) + show_detail_btn.setVisible(False) overlay_close_btn.setVisible(False) overlay_try_btn.setVisible(False) @@ -374,6 +454,9 @@ class PushToContextSelectWindow(QtWidgets.QWidget): def _on_submission_change(self, event): self._publish_btn.setEnabled(event["enabled"]) + def _on_show_detail_click(self): + self._error_detail_dialog.show() + def _on_close_click(self): self.close() @@ -384,8 +467,11 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._process_item_id = None self._last_submit_message = None + self._error_detail_dialog.close() + self._overlay_close_btn.setVisible(False) self._overlay_try_btn.setVisible(False) + self._show_detail_btn.setVisible(False) self._main_layout.setCurrentWidget(self._main_context_widget) def _on_main_thread_timer(self): @@ -401,13 +487,24 @@ class PushToContextSelectWindow(QtWidgets.QWidget): if self._main_thread_timer_can_stop: self._main_thread_timer.stop() self._overlay_close_btn.setVisible(True) - if push_failed and not fail_traceback: + if push_failed: self._overlay_try_btn.setVisible(True) + if fail_traceback: + self._show_detail_btn.setVisible(True) if push_failed: - message = "Push Failed:\n{}".format(process_status["fail_reason"]) + reason = process_status["fail_reason"] if fail_traceback: - message += "\n{}".format(fail_traceback) + message = ( + "Unhandled error happened." + " Check error detail for more information." + ) + self._error_detail_dialog.set_detail( + reason, fail_traceback + ) + else: + message = f"Push Failed:\n{reason}" + self._overlay_label.setText(message) set_style_property(self._overlay_close_btn, "state", "error") From 5d91c9ba98915ac30f859aadd202ca1f09f0e728 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:39:05 +0100 Subject: [PATCH 213/463] capture 'TaskNotSetError' --- .../tools/push_to_project/models/integrate.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index ba603699bc..32aa562a7b 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -26,7 +26,7 @@ from ayon_core.pipeline import Anatomy from ayon_core.pipeline.version_start import get_versioning_start from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.publish import get_publish_template_name -from ayon_core.pipeline.create import get_product_name +from ayon_core.pipeline.create import get_product_name, TaskNotSetError UNKNOWN = object() @@ -823,15 +823,23 @@ class ProjectPushItemProcess: task_name = task_info["name"] task_type = task_info["taskType"] - product_name = get_product_name( - self._item.dst_project_name, - task_name, - task_type, - self.host_name, - product_type, - self._item.variant, - project_settings=self._project_settings - ) + try: + product_name = get_product_name( + self._item.dst_project_name, + task_name, + task_type, + self.host_name, + product_type, + self._item.variant, + project_settings=self._project_settings + ) + except TaskNotSetError: + self._status.set_failed( + "Product name template requires task name." + " Please select target task to continue." + ) + raise PushToProjectError(self._status.fail_reason) + self._log_info( f"Push will be integrating to product with name '{product_name}'" ) From 4010183250c512c00e6fa816bd5f2ef44d76f339 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:46:27 +0100 Subject: [PATCH 214/463] bigger margins for dialog --- client/ayon_core/tools/push_to_project/ui/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/push_to_project/ui/window.py b/client/ayon_core/tools/push_to_project/ui/window.py index 0f2537db06..94dda58916 100644 --- a/client/ayon_core/tools/push_to_project/ui/window.py +++ b/client/ayon_core/tools/push_to_project/ui/window.py @@ -45,7 +45,7 @@ class ErrorDetailDialog(QtWidgets.QDialog): btns_layout.addWidget(close_btn, 0) main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(6, 6, 6, 6) + main_layout.setContentsMargins(10, 10, 10, 10) main_layout.addWidget(title_label, 0) main_layout.addWidget(sep_1, 0) main_layout.addWidget(detail_widget, 1) From 69cbbeb6a7d3371bd8421de6c6cbec6738f92aaf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 11 Dec 2024 10:37:38 +0100 Subject: [PATCH 215/463] better message --- client/ayon_core/tools/push_to_project/models/integrate.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 32aa562a7b..4fe4ead9df 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -835,8 +835,10 @@ class ProjectPushItemProcess: ) except TaskNotSetError: self._status.set_failed( - "Product name template requires task name." - " Please select target task to continue." + "Target product name template requires task name. To continue" + " you have to select target task or change settings" + " `ayon+settings://core/tools/publish/template_name_profiles" + f"?project={self._item.dst_project_name}`." ) raise PushToProjectError(self._status.fail_reason) From 6f8af3f65ee73ec4d81a7f954ce18b5862399cd8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 11 Dec 2024 11:36:41 +0100 Subject: [PATCH 216/463] fix settings path --- client/ayon_core/tools/push_to_project/models/integrate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/models/integrate.py b/client/ayon_core/tools/push_to_project/models/integrate.py index 4fe4ead9df..6bd4279219 100644 --- a/client/ayon_core/tools/push_to_project/models/integrate.py +++ b/client/ayon_core/tools/push_to_project/models/integrate.py @@ -837,8 +837,8 @@ class ProjectPushItemProcess: self._status.set_failed( "Target product name template requires task name. To continue" " you have to select target task or change settings" - " `ayon+settings://core/tools/publish/template_name_profiles" - f"?project={self._item.dst_project_name}`." + " ayon+settings://core/tools/creator/product_name_profiles" + f"?project={self._item.dst_project_name}." ) raise PushToProjectError(self._status.fail_reason) From fa9e53e159a434433d027bae497f592113fb076c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:02:26 +0100 Subject: [PATCH 217/463] added checkbox to create new folder --- .../tools/push_to_project/control.py | 2 +- .../push_to_project/models/user_values.py | 7 ++-- .../tools/push_to_project/ui/window.py | 32 ++++++++++++++----- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/tools/push_to_project/control.py b/client/ayon_core/tools/push_to_project/control.py index 58447a8389..fb080d158b 100644 --- a/client/ayon_core/tools/push_to_project/control.py +++ b/client/ayon_core/tools/push_to_project/control.py @@ -321,7 +321,7 @@ class PushToContextController: return False if ( - not self._user_values.new_folder_name + self._user_values.new_folder_name is None and not self._selection_model.get_selected_folder_id() ): return False diff --git a/client/ayon_core/tools/push_to_project/models/user_values.py b/client/ayon_core/tools/push_to_project/models/user_values.py index edef2fe4fb..e52cb2917c 100644 --- a/client/ayon_core/tools/push_to_project/models/user_values.py +++ b/client/ayon_core/tools/push_to_project/models/user_values.py @@ -84,8 +84,11 @@ class UserPublishValuesModel: return self._new_folder_name = folder_name - is_valid = True - if folder_name: + if folder_name is None: + is_valid = True + elif not folder_name: + is_valid = False + else: is_valid = ( self.folder_name_regex.match(folder_name) is not None ) diff --git a/client/ayon_core/tools/push_to_project/ui/window.py b/client/ayon_core/tools/push_to_project/ui/window.py index 94dda58916..a69c512fcd 100644 --- a/client/ayon_core/tools/push_to_project/ui/window.py +++ b/client/ayon_core/tools/push_to_project/ui/window.py @@ -8,6 +8,7 @@ from ayon_core.tools.utils import ( ProjectsCombobox, FoldersWidget, TasksWidget, + NiceCheckbox, ) from ayon_core.tools.push_to_project.control import ( PushToContextController, @@ -122,9 +123,12 @@ class PushToContextSelectWindow(QtWidgets.QWidget): # --- Inputs widget --- inputs_widget = QtWidgets.QWidget(main_splitter) + new_folder_checkbox = NiceCheckbox(True, parent=inputs_widget) + folder_name_input = PlaceholderLineEdit(inputs_widget) folder_name_input.setPlaceholderText("< Name of new folder >") folder_name_input.setObjectName("ValidatedLineEdit") + folder_name_input.setEnabled(new_folder_checkbox.isChecked()) variant_input = PlaceholderLineEdit(inputs_widget) variant_input.setPlaceholderText("< Variant >") @@ -135,6 +139,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): inputs_layout = QtWidgets.QFormLayout(inputs_widget) inputs_layout.setContentsMargins(0, 0, 0, 0) + inputs_layout.addRow("Create new folder", new_folder_checkbox) inputs_layout.addRow("New folder name", folder_name_input) inputs_layout.addRow("Variant", variant_input) inputs_layout.addRow("Comment", comment_input) @@ -231,6 +236,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): main_thread_timer.timeout.connect(self._on_main_thread_timer) show_timer.timeout.connect(self._on_show_timer) user_input_changed_timer.timeout.connect(self._on_user_input_timer) + new_folder_checkbox.stateChanged.connect(self._on_new_folder_check) folder_name_input.textChanged.connect(self._on_new_folder_change) variant_input.textChanged.connect(self._on_variant_change) comment_input.textChanged.connect(self._on_comment_change) @@ -279,6 +285,7 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._tasks_widget = tasks_widget self._variant_input = variant_input + self._new_folder_checkbox = new_folder_checkbox self._folder_name_input = folder_name_input self._comment_input = comment_input @@ -297,8 +304,9 @@ class PushToContextSelectWindow(QtWidgets.QWidget): # The value is unset when is passed to controller # The goal is to have controll over changes happened during user change # in UI and controller auto-changes - self._variant_input_text = None + self._new_folder_name_enabled = None self._new_folder_name_input_text = None + self._variant_input_text = None self._comment_input_text = None self._first_show = True @@ -369,6 +377,11 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self.refresh() + def _on_new_folder_check(self): + self._new_folder_name_enabled = self._new_folder_checkbox.isChecked() + self._folder_name_input.setEnabled(self._new_folder_name_enabled) + self._user_input_changed_timer.start() + def _on_new_folder_change(self, text): self._new_folder_name_input_text = text self._user_input_changed_timer.start() @@ -382,9 +395,15 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._user_input_changed_timer.start() def _on_user_input_timer(self): + folder_name_enabled = self._new_folder_name_enabled folder_name = self._new_folder_name_input_text - if folder_name is not None: + if folder_name is not None or folder_name_enabled is not None: self._new_folder_name_input_text = None + self._new_folder_name_enabled = None + if not self._new_folder_checkbox.isChecked(): + folder_name = None + elif folder_name is None: + folder_name = self._folder_name_input.text() self._controller.set_user_value_folder_name(folder_name) variant = self._variant_input_text @@ -430,16 +449,13 @@ class PushToContextSelectWindow(QtWidgets.QWidget): self._header_label.setText(self._controller.get_source_label()) def _invalidate_new_folder_name(self, folder_name, is_valid): - self._tasks_widget.setVisible(not folder_name) + self._tasks_widget.setVisible(folder_name is None) if self._folder_is_valid is is_valid: return self._folder_is_valid = is_valid state = "" - if folder_name: - if is_valid is True: - state = "valid" - elif is_valid is False: - state = "invalid" + if folder_name is not None: + state = "valid" if is_valid else "invalid" set_style_property( self._folder_name_input, "state", state ) From f29f8748af94b21112802338eafe3c7fba9ec62d Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Wed, 11 Dec 2024 10:50:37 -0500 Subject: [PATCH 218/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/tempdir.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index 7fb539bf0b..cd7db852a1 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -71,8 +71,8 @@ def _create_local_staging_dir(prefix, suffix, dirpath=None): ) -def create_custom_tempdir(project_name, anatomy): - """ Deprecated 09/12/2024, here for backward-compatibility with Resolve. +def create_custom_tempdir(project_name, anatomy=None): + """Backward compatibility deprecated since 2024/12/09. """ warnings.warn( "Used deprecated 'create_custom_tempdir' " From 46fcc29af138d980857adf3198408f473b6fa1e6 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 11 Dec 2024 10:59:57 -0500 Subject: [PATCH 219/463] Address feedback from PR. --- client/ayon_core/pipeline/tempdir.py | 3 +++ .../ayon_core/plugins/publish/collect_otio_subset_resources.py | 1 + 2 files changed, 4 insertions(+) diff --git a/client/ayon_core/pipeline/tempdir.py b/client/ayon_core/pipeline/tempdir.py index cd7db852a1..38b03f5c85 100644 --- a/client/ayon_core/pipeline/tempdir.py +++ b/client/ayon_core/pipeline/tempdir.py @@ -80,6 +80,9 @@ def create_custom_tempdir(project_name, anatomy=None): DeprecationWarning, ) + if anatomy is None: + anatomy = Anatomy(project_name) + return _create_custom_tempdir(project_name, anatomy) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index 2d8e91fe09..10a7d53971 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -157,6 +157,7 @@ class CollectOtioSubsetResources( self.staging_dir = media_ref.target_url_base head = media_ref.name_prefix tail = media_ref.name_suffix + import rpdb ; rpdb.Rpdb().set_trace() collection = clique.Collection( head=head, tail=tail, From 80057ebf8a37bd551c5280846566ebb9bf48292e Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 11 Dec 2024 11:04:06 -0500 Subject: [PATCH 220/463] Fix lint. --- .../ayon_core/plugins/publish/collect_otio_subset_resources.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index 10a7d53971..2d8e91fe09 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -157,7 +157,6 @@ class CollectOtioSubsetResources( self.staging_dir = media_ref.target_url_base head = media_ref.name_prefix tail = media_ref.name_suffix - import rpdb ; rpdb.Rpdb().set_trace() collection = clique.Collection( head=head, tail=tail, From cb39512b868a5960e7b18eea5015c004be8d531c Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 12 Dec 2024 13:44:26 +0200 Subject: [PATCH 221/463] add houdini to thumbnail extraction --- client/ayon_core/plugins/publish/extract_thumbnail.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 37bbac8898..8ae18f4abf 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -37,7 +37,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "substancepainter", "nuke", "aftereffects", - "unreal" + "unreal", + "houdini" ] enabled = False From 40e5a4a3ade8f2062d7c7944b3c78e77f740d943 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:44:09 +0100 Subject: [PATCH 222/463] move launcher to the top --- client/ayon_core/tools/tray/ui/tray.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/tray/ui/tray.py b/client/ayon_core/tools/tray/ui/tray.py index 98e3c783c4..aad89b6081 100644 --- a/client/ayon_core/tools/tray/ui/tray.py +++ b/client/ayon_core/tools/tray/ui/tray.py @@ -159,6 +159,12 @@ class TrayManager: return tray_menu = self.tray_widget.menu + # Add launcher at first place + launcher_action = QtWidgets.QAction( + "Launcher", tray_menu + ) + launcher_action.triggered.connect(self._show_launcher_window) + tray_menu.addAction(launcher_action) console_action = ITrayAddon.add_action_to_admin_submenu( "Console", tray_menu @@ -174,13 +180,7 @@ class TrayManager: self._addons_manager.initialize(tray_menu) - # Add default actions under addon actions - launcher_action = QtWidgets.QAction( - "Launcher", tray_menu - ) - launcher_action.triggered.connect(self._show_launcher_window) - tray_menu.addAction(launcher_action) - + # Add browser action after addon actions browser_action = QtWidgets.QAction( "Browser", tray_menu ) From bf0f7df4cdf253968f5858687ffac315e22cf0e4 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 13 Dec 2024 12:56:24 +0000 Subject: [PATCH 223/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index a4ae75914c..bc99b11e06 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.11+dev" +__version__ = "1.0.12" diff --git a/package.py b/package.py index b8d88fc2ad..df9bafba1e 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.11+dev" +version = "1.0.12" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index bdfaf797e4..b35359abdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.11+dev" +version = "1.0.12" description = "" authors = ["Ynput Team "] readme = "README.md" From 704b011474c99a60ef2584de6fd5b59d230422fd Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 13 Dec 2024 12:57:09 +0000 Subject: [PATCH 224/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index bc99b11e06..2417897a47 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.12" +__version__ = "1.0.12+dev" diff --git a/package.py b/package.py index df9bafba1e..8ade5ceeed 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.12" +version = "1.0.12+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index b35359abdb..b8d6a5a537 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.12" +version = "1.0.12+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 63ed2f21d00a188301a844a932227d22d6fa5104 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 13 Dec 2024 18:40:40 +0100 Subject: [PATCH 225/463] Refactor OTIO frame range collection - Removed unused function import. - Added detailed logging for data updates. - Streamlined frame range calculations and handling. - Introduced a new class for collecting source frame ranges. - Improved readability by cleaning up code structure. --- .../publish/collect_otio_frame_ranges.py | 87 +++++++++++++++---- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 62b4cefec6..0d1f76f338 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -24,11 +24,65 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): # Not all hosts can import these modules. import opentimelineio as otio from ayon_core.pipeline.editorial import ( - get_media_range_with_retimes, otio_range_to_frame_range, otio_range_with_handles ) + if not instance.data.get("otioClip"): + self.log.debug("Skipping collect OTIO frame range.") + return + + # get basic variables + otio_clip = instance.data["otioClip"] + workfile_start = instance.data["workfileFrameStart"] + + # get ranges + otio_tl_range = otio_clip.range_in_parent() + otio_tl_range_handles = otio_range_with_handles( + otio_tl_range, instance) + + # convert to frames + range_convert = otio_range_to_frame_range + tl_start, tl_end = range_convert(otio_tl_range) + tl_start_h, tl_end_h = range_convert(otio_tl_range_handles) + frame_start = workfile_start + frame_end = frame_start + otio.opentime.to_frames( + otio_tl_range.duration, otio_tl_range.duration.rate) - 1 + + data = { + "frameStart": frame_start, + "frameEnd": frame_end, + "clipIn": tl_start, + "clipOut": tl_end - 1, + "clipInH": tl_start_h, + "clipOutH": tl_end_h - 1, + } + instance.data.update(data) + self.log.debug( + "_ data: {}".format(pformat(data))) + self.log.debug( + "_ instance.data: {}".format(pformat(instance.data))) + + +class CollectOtioSourceFrameRanges(pyblish.api.InstancePlugin): + """Getting otio ranges from otio_clip + + Adding timeline and source ranges to instance data""" + + label = "Collect OTIO Frame Ranges (with media range)" + order = pyblish.api.CollectorOrder - 0.07 + families = ["shot", "clip"] + hosts = ["hiero", "flame"] + + def process(self, instance): + # Not all hosts can import these modules. + import opentimelineio as otio + from ayon_core.pipeline.editorial import ( + get_media_range_with_retimes, + otio_range_to_frame_range, + otio_range_with_handles, + ) + if not instance.data.get("otioClip"): self.log.debug("Skipping collect OTIO frame range.") return @@ -42,15 +96,13 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): otio_tl_range = otio_clip.range_in_parent() otio_src_range = otio_clip.source_range otio_avalable_range = otio_clip.available_range() - otio_tl_range_handles = otio_range_with_handles( - otio_tl_range, instance) - otio_src_range_handles = otio_range_with_handles( - otio_src_range, instance) + otio_tl_range_handles = otio_range_with_handles(otio_tl_range, instance) + otio_src_range_handles = otio_range_with_handles(otio_src_range, instance) # get source avalable start frame src_starting_from = otio.opentime.to_frames( - otio_avalable_range.start_time, - otio_avalable_range.start_time.rate) + otio_avalable_range.start_time, otio_avalable_range.start_time.rate + ) # convert to frames range_convert = otio_range_to_frame_range @@ -59,16 +111,19 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): src_start, src_end = range_convert(otio_src_range) src_start_h, src_end_h = range_convert(otio_src_range_handles) frame_start = workfile_start - frame_end = frame_start + otio.opentime.to_frames( - otio_tl_range.duration, otio_tl_range.duration.rate) - 1 + frame_end = ( + frame_start + + otio.opentime.to_frames( + otio_tl_range.duration, otio_tl_range.duration.rate + ) + - 1 + ) # in case of retimed clip and frame range should not be retimed if workfile_source_duration: # get available range trimmed with processed retimes - retimed_attributes = get_media_range_with_retimes( - otio_clip, 0, 0) - self.log.debug( - ">> retimed_attributes: {}".format(retimed_attributes)) + retimed_attributes = get_media_range_with_retimes(otio_clip, 0, 0) + self.log.debug(">> retimed_attributes: {}".format(retimed_attributes)) media_in = int(retimed_attributes["mediaIn"]) media_out = int(retimed_attributes["mediaOut"]) frame_end = frame_start + (media_out - media_in) + 1 @@ -87,7 +142,5 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): "sourceEndH": src_starting_from + src_end_h - 1, } instance.data.update(data) - self.log.debug( - "_ data: {}".format(pformat(data))) - self.log.debug( - "_ instance.data: {}".format(pformat(instance.data))) + self.log.debug("_ data: {}".format(pformat(data))) + self.log.debug("_ instance.data: {}".format(pformat(instance.data))) From b8269f7b3106eefecf7ec30967d7f8bb4260816e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 15 Dec 2024 11:44:57 +0100 Subject: [PATCH 226/463] Always increment workfile when requested - instead of only when no unsaved changes --- client/ayon_core/pipeline/context_tools.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/pipeline/context_tools.py b/client/ayon_core/pipeline/context_tools.py index 44c9e5d673..b9ae906ab4 100644 --- a/client/ayon_core/pipeline/context_tools.py +++ b/client/ayon_core/pipeline/context_tools.py @@ -585,9 +585,6 @@ def version_up_current_workfile(): """Function to increment and save workfile """ host = registered_host() - if not host.has_unsaved_changes(): - print("No unsaved changes, skipping file save..") - return project_name = get_current_project_name() folder_path = get_current_folder_path() From 145688d56f28aee11ab9eb4e97e40a94a3926841 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 16 Dec 2024 10:27:01 +0100 Subject: [PATCH 227/463] Editorial: Fix clip_media source for review track. --- .../plugins/publish/collect_otio_subset_resources.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index 2d8e91fe09..199e952769 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -178,7 +178,8 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, collection=collection) - if "review" in instance.data["families"]: + if ("review" in instance.data["families"] and + not instance.data.get("otioReviewClips")): review_repre = self._create_representation( frame_start, frame_end, collection=collection, delete=True, review=True) @@ -197,7 +198,8 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - if "review" in instance.data["families"]: + if ("review" in instance.data["families"] and + not instance.data.get("otioReviewClips")): review_repre = self._create_representation( frame_start, frame_end, file=filename, delete=True, review=True) From e7d95c1d5d82a391e311952fc4a3143ad9bd6d77 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:29:25 +0100 Subject: [PATCH 228/463] add methods to get launcher action paths --- client/ayon_core/addon/base.py | 15 +++++++++++++++ client/ayon_core/addon/interfaces.py | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/client/ayon_core/addon/base.py b/client/ayon_core/addon/base.py index ed6b82ef52..72270fa585 100644 --- a/client/ayon_core/addon/base.py +++ b/client/ayon_core/addon/base.py @@ -894,6 +894,21 @@ class AddonsManager: output.extend(paths) return output + def collect_launcher_action_paths(self): + """Helper to collect launcher action paths from addons. + + Returns: + list: List of paths to launcher actions. + + """ + output = self._collect_plugin_paths( + "get_launcher_action_paths" + ) + # Add default core actions + actions_dir = os.path.join(AYON_CORE_ROOT, "plugins", "actions") + output.insert(0, actions_dir) + return output + def collect_create_plugin_paths(self, host_name): """Helper to collect creator plugin paths from addons. diff --git a/client/ayon_core/addon/interfaces.py b/client/ayon_core/addon/interfaces.py index 2616913dc0..72191e3453 100644 --- a/client/ayon_core/addon/interfaces.py +++ b/client/ayon_core/addon/interfaces.py @@ -54,6 +54,13 @@ class IPluginPaths(AYONInterface): paths = [paths] return paths + def get_launcher_action_paths(self): + """Receive launcher actions paths. + + Give addons ability to add launcher actions paths. + """ + return self._get_plugin_paths_by_type("actions") + def get_create_plugin_paths(self, host_name): """Receive create plugin paths. From 397a85de5ab1b1032c558d5fe4c157bbeb90925f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:42:02 +0100 Subject: [PATCH 229/463] fix discovery of actions --- client/ayon_core/tools/launcher/models/actions.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/ayon_core/tools/launcher/models/actions.py b/client/ayon_core/tools/launcher/models/actions.py index 8bd30daffa..e1612e2b9f 100644 --- a/client/ayon_core/tools/launcher/models/actions.py +++ b/client/ayon_core/tools/launcher/models/actions.py @@ -7,6 +7,7 @@ from ayon_core.pipeline.actions import ( discover_launcher_actions, LauncherAction, LauncherActionSelection, + register_launcher_action_path, ) from ayon_core.pipeline.workfile import should_use_last_workfile_on_launch @@ -459,6 +460,14 @@ class ActionsModel: def _get_discovered_action_classes(self): if self._discovered_actions is None: + # NOTE We don't need to register the paths, but that would + # require to change discovery logic and deprecate all functions + # related to registering and discovering launcher actions. + addons_manager = self._get_addons_manager() + actions_paths = addons_manager.collect_launcher_action_paths() + for path in actions_paths: + if path and os.path.exists(path): + register_launcher_action_path(path) self._discovered_actions = ( discover_launcher_actions() + self._get_applications_action_classes() From 8b663ef4400fe99736da40b59a707ecf492f5437 Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Mon, 16 Dec 2024 11:07:21 +0100 Subject: [PATCH 230/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/collect_otio_subset_resources.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index 199e952769..0fb30326c6 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -178,8 +178,10 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, collection=collection) - if ("review" in instance.data["families"] and - not instance.data.get("otioReviewClips")): + if ( + not instance.data.get("otioReviewClips") + and "review" in instance.data["families"] + ): review_repre = self._create_representation( frame_start, frame_end, collection=collection, delete=True, review=True) @@ -198,8 +200,10 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - if ("review" in instance.data["families"] and - not instance.data.get("otioReviewClips")): + if ( + not instance.data.get("otioReviewClips") + and "review" in instance.data["families"] + ): review_repre = self._create_representation( frame_start, frame_end, file=filename, delete=True, review=True) From 699da55d53cf0d48046f854062057f3797b2ca78 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:57:25 +0100 Subject: [PATCH 231/463] refresh actions when on projects page --- client/ayon_core/tools/launcher/ui/window.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/launcher/ui/window.py b/client/ayon_core/tools/launcher/ui/window.py index 34aeab35bb..2d52a73c38 100644 --- a/client/ayon_core/tools/launcher/ui/window.py +++ b/client/ayon_core/tools/launcher/ui/window.py @@ -202,8 +202,9 @@ class LauncherWindow(QtWidgets.QWidget): self._go_to_hierarchy_page(project_name) def _on_projects_refresh(self): - # There is nothing to do, we're on projects page + # Refresh only actions on projects page if self._is_on_projects_page: + self._actions_widget.refresh() return # No projects were found -> go back to projects page From ea292add98bae40e45388949207290bcc992788a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 16 Dec 2024 22:56:27 +0100 Subject: [PATCH 232/463] Use underscore separator like in Maya settings `maya_dirmap`. Only other integration I can see that has dirmapping is Nuke, which uses just `dirmap` without host prefix - which I suppose would then be broken regardless. It may make more sense to remove the `host` specific prefix from the label because it's already looking in host specific settings anyway. --- client/ayon_core/host/dirmap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/host/dirmap.py b/client/ayon_core/host/dirmap.py index 19841845e7..c932c13c10 100644 --- a/client/ayon_core/host/dirmap.py +++ b/client/ayon_core/host/dirmap.py @@ -118,7 +118,7 @@ class HostDirmap(ABC): site, in that case configuration in Local Settings takes precedence """ - dirmap_label = "{}-dirmap".format(self.host_name) + dirmap_label = "{}_dirmap".format(self.host_name) mapping_sett = self.project_settings[self.host_name].get(dirmap_label, {}) local_mapping = self._get_local_sync_dirmap() From 5e503d0b51f3f587f369f877c52a304e907e7bec Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 17 Dec 2024 10:46:47 +0100 Subject: [PATCH 233/463] Remove host name prefix from dirmap settings mapping --- client/ayon_core/host/dirmap.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/host/dirmap.py b/client/ayon_core/host/dirmap.py index c932c13c10..3f02be6614 100644 --- a/client/ayon_core/host/dirmap.py +++ b/client/ayon_core/host/dirmap.py @@ -117,10 +117,7 @@ class HostDirmap(ABC): It checks if Site Sync is enabled and user chose to use local site, in that case configuration in Local Settings takes precedence """ - - dirmap_label = "{}_dirmap".format(self.host_name) - mapping_sett = self.project_settings[self.host_name].get(dirmap_label, - {}) + mapping_sett = self.project_settings[self.host_name].get("dirmap", {}) local_mapping = self._get_local_sync_dirmap() mapping_enabled = mapping_sett.get("enabled") or bool(local_mapping) if not mapping_enabled: From 4c33041de1fa83cf320640147b14848258190d87 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 17 Dec 2024 11:13:14 +0100 Subject: [PATCH 234/463] Fix broken editorial tests. --- .../editorial/test_extract_otio_review.py | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py index 8b1c9da30e..e1fbf514d4 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py +++ b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py @@ -103,17 +103,17 @@ def test_image_sequence_with_embedded_tc_and_handles_out_of_range(): # 10 head black handles generated from gap (991-1000) "/path/to/ffmpeg -t 0.4166666666666667 -r 24.0 -f lavfi -i " "color=c=black:s=1280x720 -tune stillimage -start_number 991 " - "C:/result/output.%03d.jpg", + "C:/result/output.%04d.jpg", # 10 tail black handles generated from gap (1102-1111) "/path/to/ffmpeg -t 0.4166666666666667 -r 24.0 -f lavfi -i " "color=c=black:s=1280x720 -tune stillimage -start_number 1102 " - "C:/result/output.%03d.jpg", + "C:/result/output.%04d.jpg", # Report from source exr (1001-1101) with enforce framerate "/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i " f"C:\\exr_embedded_tc{os.sep}output.%04d.exr -start_number 1001 " - "C:/result/output.%03d.jpg" + "C:/result/output.%04d.jpg" ] assert calls == expected @@ -131,11 +131,11 @@ def test_image_sequence_and_handles_out_of_range(): expected = [ # 5 head black frames generated from gap (991-995) "/path/to/ffmpeg -t 0.2 -r 25.0 -f lavfi -i color=c=black:s=1280x720" - " -tune stillimage -start_number 991 C:/result/output.%03d.jpg", + " -tune stillimage -start_number 991 C:/result/output.%04d.jpg", # 9 tail back frames generated from gap (1097-1105) "/path/to/ffmpeg -t 0.36 -r 25.0 -f lavfi -i color=c=black:s=1280x720" - " -tune stillimage -start_number 1097 C:/result/output.%03d.jpg", + " -tune stillimage -start_number 1097 C:/result/output.%04d.jpg", # Report from source tiff (996-1096) # 996-1000 = additional 5 head frames @@ -143,7 +143,7 @@ def test_image_sequence_and_handles_out_of_range(): # 1096-1096 = additional 1 tail frames "/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i " f"C:\\tif_seq{os.sep}output.%04d.tif -start_number 996" - f" C:/result/output.%03d.jpg" + f" C:/result/output.%04d.jpg" ] assert calls == expected @@ -164,7 +164,7 @@ def test_movie_with_embedded_tc_no_gap_handles(): # - duration = 68fr (source) + 20fr (handles) = 88frames = 3.666s "/path/to/ffmpeg -ss 0.16666666666666666 -t 3.6666666666666665 " "-i C:\\data\\qt_embedded_tc.mov -start_number 991 " - "C:/result/output.%03d.jpg" + "C:/result/output.%04d.jpg" ] assert calls == expected @@ -181,12 +181,12 @@ def test_short_movie_head_gap_handles(): expected = [ # 10 head black frames generated from gap (991-1000) "/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi -i color=c=black:s=1280x720" - " -tune stillimage -start_number 991 C:/result/output.%03d.jpg", + " -tune stillimage -start_number 991 C:/result/output.%04d.jpg", # source range + 10 tail frames # duration = 50fr (source) + 10fr (tail handle) = 60 fr = 2.4s "/path/to/ffmpeg -ss 0.0 -t 2.4 -i C:\\data\\movie.mp4" - " -start_number 1001 C:/result/output.%03d.jpg" + " -start_number 1001 C:/result/output.%04d.jpg" ] assert calls == expected @@ -204,13 +204,13 @@ def test_short_movie_tail_gap_handles(): # 10 tail black frames generated from gap (1067-1076) "/path/to/ffmpeg -t 0.4166666666666667 -r 24.0 -f lavfi -i " "color=c=black:s=1280x720 -tune stillimage -start_number 1067 " - "C:/result/output.%03d.jpg", + "C:/result/output.%04d.jpg", # 10 head frames + source range # duration = 10fr (head handle) + 66fr (source) = 76fr = 3.16s "/path/to/ffmpeg -ss 1.0416666666666667 -t 3.1666666666666665 -i " "C:\\data\\qt_no_tc_24fps.mov -start_number 991" - " C:/result/output.%03d.jpg" + " C:/result/output.%04d.jpg" ] assert calls == expected @@ -238,62 +238,62 @@ def test_multiple_review_clips_no_gap(): # 10 head black frames generated from gap (991-1000) '/path/to/ffmpeg -t 0.4 -r 25.0 -f lavfi' ' -i color=c=black:s=1280x720 -tune ' - 'stillimage -start_number 991 C:/result/output.%03d.jpg', + 'stillimage -start_number 991 C:/result/output.%04d.jpg', # Alternance 25fps tiff sequence and 24fps exr sequence # for 100 frames each '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1001 C:/result/output.%03d.jpg', + '-start_number 1001 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' f'C:\\with_tc{os.sep}output.%04d.exr ' - '-start_number 1102 C:/result/output.%03d.jpg', + '-start_number 1102 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1199 C:/result/output.%03d.jpg', + '-start_number 1199 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' f'C:\\with_tc{os.sep}output.%04d.exr ' - '-start_number 1300 C:/result/output.%03d.jpg', + '-start_number 1300 C:/result/output.%04d.jpg', # Repeated 25fps tiff sequence multiple times till the end '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1397 C:/result/output.%03d.jpg', + '-start_number 1397 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1498 C:/result/output.%03d.jpg', + '-start_number 1498 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1599 C:/result/output.%03d.jpg', + '-start_number 1599 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1700 C:/result/output.%03d.jpg', + '-start_number 1700 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1801 C:/result/output.%03d.jpg', + '-start_number 1801 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1902 C:/result/output.%03d.jpg', + '-start_number 1902 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2003 C:/result/output.%03d.jpg', + '-start_number 2003 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2104 C:/result/output.%03d.jpg', + '-start_number 2104 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2205 C:/result/output.%03d.jpg' + '-start_number 2205 C:/result/output.%04d.jpg' ] assert calls == expected @@ -321,15 +321,15 @@ def test_multiple_review_clips_with_gap(): # Gap on review track (12 frames) '/path/to/ffmpeg -t 0.5 -r 24.0 -f lavfi' ' -i color=c=black:s=1280x720 -tune ' - 'stillimage -start_number 991 C:/result/output.%03d.jpg', + 'stillimage -start_number 991 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' f'C:\\with_tc{os.sep}output.%04d.exr ' - '-start_number 1003 C:/result/output.%03d.jpg', + '-start_number 1003 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' f'C:\\with_tc{os.sep}output.%04d.exr ' - '-start_number 1091 C:/result/output.%03d.jpg' + '-start_number 1091 C:/result/output.%04d.jpg' ] assert calls == expected From 5780a1797115554ebff370bd4634420ddba4fc0f Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 18 Dec 2024 18:07:16 +0100 Subject: [PATCH 235/463] Consolidate 23.976 trim computation. --- client/ayon_core/pipeline/editorial.py | 81 +++++--- .../plugins/publish/extract_otio_review.py | 6 +- .../resources/qt_23.976_embedded_long_tc.json | 174 ++++++++++++++++++ .../editorial/test_extract_otio_review.py | 22 +-- .../test_media_range_with_retimes.py | 22 +++ 5 files changed, 262 insertions(+), 43 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/qt_23.976_embedded_long_tc.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 2928ef5f63..d71cf6c344 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -196,11 +196,11 @@ def is_clip_from_media_sequence(otio_clip): return is_input_sequence or is_input_sequence_legacy -def remap_range_on_file_sequence(otio_clip, in_out_range): +def remap_range_on_file_sequence(otio_clip, otio_range): """ Args: otio_clip (otio.schema.Clip): The OTIO clip to check. - in_out_range (tuple[float, float]): The in-out range to remap. + otio_range (otio.schema.TimeRange): The trim range to apply. Returns: tuple(int, int): The remapped range as discrete frame number. @@ -211,17 +211,25 @@ def remap_range_on_file_sequence(otio_clip, in_out_range): if not is_clip_from_media_sequence(otio_clip): raise ValueError(f"Cannot map on non-file sequence clip {otio_clip}.") - try: - media_in_trimmed, media_out_trimmed = in_out_range - - except ValueError as error: - raise ValueError("Invalid in_out_range provided.") from error - media_ref = otio_clip.media_reference available_range = otio_clip.available_range() - source_range = otio_clip.source_range available_range_rate = available_range.start_time.rate - media_in = available_range.start_time.value + + # Backward-compatibility for Hiero OTIO exporter. + # NTSC compatibility might introduce floating rates, when these are + # not exactly the same (23.976 vs 23.976024627685547) + # this will cause precision issue in computation. + # Currently round to 2 decimals for comparison, + # but this should always rescale after that. + rounded_av_rate = round(available_range_rate, 2) + rounded_range_rate = round(otio_range.start_time.rate, 2) + + if rounded_av_rate != rounded_range_rate: + raise ValueError("Inconsistent range between clip and provided clip") + + source_range = otio_clip.source_range + source_range_rate = source_range.start_time.rate + media_in = available_range.start_time available_range_start_frame = ( available_range.start_time.to_frames() ) @@ -236,14 +244,20 @@ def remap_range_on_file_sequence(otio_clip, in_out_range): and available_range_start_frame == media_ref.start_frame and source_range.start_time.to_frames() < media_ref.start_frame ): - media_in = 0 + media_in = otio.opentime.RationalTime( + 0, rate=available_range_rate + ) + src_offset_in = otio_range.start_time - media_in frame_in = otio.opentime.RationalTime.from_frames( - media_in_trimmed - media_in + media_ref.start_frame, + media_ref.start_frame + src_offset_in.to_frames(), rate=available_range_rate, ).to_frames() + + range_duration = otio_range.duration + frame_out = otio.opentime.RationalTime.from_frames( - media_out_trimmed - media_in + media_ref.start_frame, + frame_in + otio_range.duration.to_frames() - 1, rate=available_range_rate, ).to_frames() @@ -374,31 +388,44 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): offset_in, offset_out = offset_out, offset_in handle_start, handle_end = handle_end, handle_start - # compute retimed range - media_in_trimmed = conformed_source_range.start_time.value + offset_in - media_out_trimmed = media_in_trimmed + ( - ( - conformed_source_range.duration.value - * abs(time_scalar) - + offset_out - ) - 1 - ) - - media_in = available_range.start_time.value - media_out = available_range.end_time_inclusive().value - # If media source is an image sequence, returned # mediaIn/mediaOut have to correspond # to frame numbers from source sequence. if is_input_sequence: + + src_in = conformed_source_range.start_time + src_duration = conformed_source_range.duration + + offset_in = otio.opentime.RationalTime(offset_in, rate=src_in.rate) + offset_duration = otio.opentime.RationalTime(offset_out, rate=src_duration.rate) + + trim_range = otio.opentime.TimeRange( + start_time=src_in + offset_in, + duration=src_duration + offset_duration + ) + # preserve discrete frame numbers media_in_trimmed, media_out_trimmed = remap_range_on_file_sequence( otio_clip, - (media_in_trimmed, media_out_trimmed) + trim_range, ) media_in = media_ref.start_frame media_out = media_in + available_range.duration.to_frames() - 1 + else: + # compute retimed range + media_in_trimmed = conformed_source_range.start_time.value + offset_in + media_out_trimmed = media_in_trimmed + ( + ( + conformed_source_range.duration.value + * abs(time_scalar) + + offset_out + ) - 1 + ) + + media_in = available_range.start_time.value + media_out = available_range.end_time_inclusive().value + # adjust available handles if needed if (media_in_trimmed - media_in) < handle_start: handle_start = max(0, media_in_trimmed - media_in) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index 712ae7a886..d5f5f43cc9 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -209,13 +209,9 @@ class ExtractOTIOReview( # File sequence way if is_sequence: # Remap processing range to input file sequence. - processing_range_as_frames = ( - processing_range.start_time.to_frames(), - processing_range.end_time_inclusive().to_frames() - ) first, last = remap_range_on_file_sequence( r_otio_cl, - processing_range_as_frames, + processing_range, ) input_fps = processing_range.start_time.rate diff --git a/tests/client/ayon_core/pipeline/editorial/resources/qt_23.976_embedded_long_tc.json b/tests/client/ayon_core/pipeline/editorial/resources/qt_23.976_embedded_long_tc.json new file mode 100644 index 0000000000..01d81508d1 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/qt_23.976_embedded_long_tc.json @@ -0,0 +1,174 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "Main088sh110", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 82.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 1937905.9905694576 + } + }, + "effects": [], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/088/Main088sh110\", \"task\": null, \"clip_index\": \"70C9FA86-76A5-A045-A004-3158FB3F27C5\", \"hierarchy\": \"shots/088\", \"folder\": \"shots\", \"episode\": \"404\", \"sequence\": \"088\", \"track\": \"Main\", \"shot\": \"sh110\", \"reviewableSource\": \"Reference\", \"sourceResolution\": false, \"workfileFrameStart\": 1009, \"handleStart\": 8, \"handleEnd\": 8, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"088\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"404\", \"sequence\": \"088\", \"track\": \"Main\"}, \"heroTrack\": true, \"uuid\": \"8b0d1db8-7094-48ba-b2cd-df0d43cfffda\", \"reviewTrack\": \"Reference\", \"review\": true, \"folderName\": \"Main088sh110\", \"label\": \"/shots/088/Main088sh110 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"f6b7f12c-f3a8-44fd-b4e4-acc63ed80bb1\", \"creator_attributes\": {\"workfileFrameStart\": 1009, \"handleStart\": 8, \"handleEnd\": 8, \"frameStart\": 1009, \"frameEnd\": 1091, \"clipIn\": 80, \"clipOut\": 161, \"clipDuration\": 82, \"sourceIn\": 8.0, \"sourceOut\": 89.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Main\", \"folderPath\": \"/shots/088/Main088sh110\", \"task\": null, \"clip_index\": \"70C9FA86-76A5-A045-A004-3158FB3F27C5\", \"hierarchy\": \"shots/088\", \"folder\": \"shots\", \"episode\": \"404\", \"sequence\": \"088\", \"track\": \"Main\", \"shot\": \"sh110\", \"reviewableSource\": \"Reference\", \"sourceResolution\": false, \"workfileFrameStart\": 1009, \"handleStart\": 8, \"handleEnd\": 8, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"088\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"404\", \"sequence\": \"088\", \"track\": \"Main\"}, \"heroTrack\": true, \"uuid\": \"8b0d1db8-7094-48ba-b2cd-df0d43cfffda\", \"reviewTrack\": \"Reference\", \"review\": true, \"folderName\": \"Main088sh110\", \"parent_instance_id\": \"f6b7f12c-f3a8-44fd-b4e4-acc63ed80bb1\", \"label\": \"/shots/088/Main088sh110 plateMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"64b54c11-7ab1-45ef-b156-9ed5d5552b9b\", \"creator_attributes\": {\"parentInstance\": \"/shots/088/Main088sh110 shotMain\", \"review\": true, \"reviewableSource\": \"Reference\"}, \"publish_attributes\": {}}}, \"clip_index\": \"70C9FA86-76A5-A045-A004-3158FB3F27C5\"}", + "label": "AYONdata_6b797112", + "note": "AYON data container" + }, + "name": "AYONdata_6b797112", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + }, + "comment": "" + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "Input - Sony - Linear - Venice S-Gamut3.Cine", + "ayon.source.height": 2160, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 4096, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "Input - Sony - Linear - Venice S-Gamut3.Cine", + "foundry.source.duration": "98", + "foundry.source.filename": "409_083_0015.%04d.exr 1001-1098", + "foundry.source.filesize": "", + "foundry.source.fragments": "98", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "2160", + "foundry.source.layers": "colour", + "foundry.source.path": "X:/prj/AYON_CIRCUIT_TEST/data/OBX_20240729_P159_DOG_409/EXR/409_083_0015/409_083_0015.%04d.exr 1001-1098", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 368", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "409_083_0015.%04d.exr 1001-1098", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "1001", + "foundry.source.timecode": "1937896", + "foundry.source.umid": "4b3e13b3-e465-4df4-cb1f-257091b63815", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "4096", + "foundry.timeline.colorSpace": "Input - Sony - Linear - Venice S-Gamut3.Cine", + "foundry.timeline.duration": "98", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABqAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.camera_camera_type": "AXS-R7", + "media.exr.camera_fps": "23.976", + "media.exr.camera_id": "MPC-3610 0010762 Version6.30", + "media.exr.camera_iso": "2500", + "media.exr.camera_lens_type": "Unknown", + "media.exr.camera_monitor_space": "OBX4_LUT_1_Night.cube", + "media.exr.camera_nd_filter": "1", + "media.exr.camera_roll_angle": "0.3", + "media.exr.camera_shutter_angle": "180.0", + "media.exr.camera_shutter_speed": "0.0208333", + "media.exr.camera_shutter_type": "Speed and Angle", + "media.exr.camera_sl_num": "00011434", + "media.exr.camera_tilt_angle": "-7.4", + "media.exr.camera_type": "Sony", + "media.exr.camera_white_kelvin": "3200", + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.clip_details_codec": "F55_X-OCN_ST_4096_2160", + "media.exr.clip_details_pixel_aspect_ratio": "1", + "media.exr.clip_details_shot_frame_rate": "23.98p", + "media.exr.compression": "0", + "media.exr.compressionName": "none", + "media.exr.dataWindow": "0,0,4095,2159", + "media.exr.displayWindow": "0,0,4095,2159", + "media.exr.lineOrder": "0", + "media.exr.owner": "C272C010_240530HO", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.tech_details_aspect_ratio": "1.8963", + "media.exr.tech_details_cdl_sat": "1", + "media.exr.tech_details_cdl_sop": "(1 1 1)(0 0 0)(1 1 1)", + "media.exr.tech_details_gamma_space": "R709 Video", + "media.exr.tech_details_par": "1", + "media.exr.type": "scanlineimage", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2024-07-30 18:51:38", + "media.input.filename": "X:/prj/AYON_CIRCUIT_TEST/data/OBX_20240729_P159_DOG_409/EXR/409_083_0015/409_083_0015.1001.exr", + "media.input.filereader": "exr", + "media.input.filesize": "53120020", + "media.input.frame": "1", + "media.input.frame_rate": "23.976", + "media.input.height": "2160", + "media.input.mtime": "2024-07-30 18:51:38", + "media.input.timecode": "22:25:45:16", + "media.input.width": "4096", + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 98.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 1937896.0 + } + }, + "available_image_bounds": null, + "target_url_base": "X:/prj/AYON_CIRCUIT_TEST/data/OBX_20240729_P159_DOG_409/EXR/409_083_0015\\", + "name_prefix": "409_083_0015.", + "name_suffix": ".exr", + "start_frame": 1001, + "frame_step": 1, + "rate": 23.976, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py index e1fbf514d4..8ad2e44b06 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py +++ b/tests/client/ayon_core/pipeline/editorial/test_extract_otio_review.py @@ -252,48 +252,48 @@ def test_multiple_review_clips_no_gap(): '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1199 C:/result/output.%04d.jpg', + '-start_number 1198 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' f'C:\\with_tc{os.sep}output.%04d.exr ' - '-start_number 1300 C:/result/output.%04d.jpg', + '-start_number 1299 C:/result/output.%04d.jpg', # Repeated 25fps tiff sequence multiple times till the end '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1397 C:/result/output.%04d.jpg', + '-start_number 1395 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1498 C:/result/output.%04d.jpg', + '-start_number 1496 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1599 C:/result/output.%04d.jpg', + '-start_number 1597 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1700 C:/result/output.%04d.jpg', + '-start_number 1698 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1801 C:/result/output.%04d.jpg', + '-start_number 1799 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1902 C:/result/output.%04d.jpg', + '-start_number 1900 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2003 C:/result/output.%04d.jpg', + '-start_number 2001 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2104 C:/result/output.%04d.jpg', + '-start_number 2102 C:/result/output.%04d.jpg', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2205 C:/result/output.%04d.jpg' + '-start_number 2203 C:/result/output.%04d.jpg' ] assert calls == expected diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 7f9256c6d8..5a375e4499 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -64,6 +64,28 @@ def test_movie_embedded_tc_handle(): ) +def test_movie_23fps_qt_embedded_tc(): + """ + Movie clip (embedded timecode 1h) + available_range = 1937896-1937994 23.976fps + source_range = 1937905-1937987 23.97602462768554fps + """ + expected_data = { + 'mediaIn': 1009, + 'mediaOut': 1090, + 'handleStart': 8, + 'handleEnd': 8, + 'speed': 1.0 + } + + _check_expected_retimed_values( + "qt_23.976_embedded_long_tc.json", + expected_data, + handle_start=8, + handle_end=8, + ) + + def test_movie_retime_effect(): """ Movie clip (embedded timecode 1h) From 037db5dbd31ab615f80600d5926bb2ec901f0bf5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 20 Dec 2024 12:23:43 +0100 Subject: [PATCH 236/463] Store in instance data whether the staging dir set is a custom one --- client/ayon_core/pipeline/publish/lib.py | 1 + client/ayon_core/pipeline/staging_dir.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index ecdcc0f0c1..586b90a3fd 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -717,6 +717,7 @@ def get_instance_staging_dir(instance): instance.data.update({ "stagingDir": staging_dir_path, "stagingDir_persistent": staging_dir_info.persistent, + "stagingDir_custom": staging_dir_info.custom }) return staging_dir_path diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index ea22d99389..83878f17a2 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -12,6 +12,7 @@ from .tempdir import get_temp_dir class StagingDir: directory: str persistent: bool + custom: bool # Whether the staging dir is a custom staging dir def get_staging_dir_config( @@ -204,7 +205,8 @@ def get_staging_dir_info( dir_template = staging_dir_config["template"]["directory"] return StagingDir( dir_template.format_strict(ctx_data), - staging_dir_config["persistence"], + persistent=staging_dir_config["persistence"], + custom=True ) # no config found but force an output @@ -216,7 +218,8 @@ def get_staging_dir_info( prefix=prefix, suffix=suffix, ), - False, + persistent=False, + custom=False ) return None From 58d3852f2893e87cc16f47e81112232c2282f4e4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 7 Jan 2025 06:27:26 +0100 Subject: [PATCH 237/463] Fix red dot for FORCE_NOT_OPEN_WORKFILE_ROLE to be drawn on wrong location if app is not on first row --- client/ayon_core/tools/launcher/ui/actions_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/launcher/ui/actions_widget.py b/client/ayon_core/tools/launcher/ui/actions_widget.py index 2ffce13292..c64d718172 100644 --- a/client/ayon_core/tools/launcher/ui/actions_widget.py +++ b/client/ayon_core/tools/launcher/ui/actions_widget.py @@ -265,7 +265,7 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate): if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE): rect = QtCore.QRectF( - option.rect.x(), option.rect.height(), 5, 5) + option.rect.x(), option.rect.y() + option.rect.height(), 5, 5) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(QtGui.QColor(200, 0, 0)) painter.drawEllipse(rect) From c6ea24edcfc3d110c26a5c0084136035d3f1745a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:37:03 +0100 Subject: [PATCH 238/463] return StagingDir object instead of string --- client/ayon_core/pipeline/staging_dir.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 83878f17a2..b7ca1a2cd6 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -161,11 +161,15 @@ def get_staging_dir_info( ) if force_tmp_dir: - return get_temp_dir( - project_name=project_entity["name"], - anatomy=anatomy, - prefix=prefix, - suffix=suffix, + return StagingDir( + get_temp_dir( + project_name=project_entity["name"], + anatomy=anatomy, + prefix=prefix, + suffix=suffix, + ), + is_persistent=False, + is_custom=False ) # making few queries to database From 228a3c6f054388bad49dfeb020615e4b3eb2fdb8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:37:33 +0100 Subject: [PATCH 239/463] remove duplicated validation of template name --- client/ayon_core/pipeline/staging_dir.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index b7ca1a2cd6..7f9ec85466 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -76,7 +76,6 @@ def get_staging_dir_config( # get template from template name template_name = profile["template_name"] - _validate_template_name(project_name, template_name, anatomy) template = anatomy.get_template_item("staging", template_name) @@ -93,19 +92,6 @@ def get_staging_dir_config( return {"template": template, "persistence": data_persistence} -def _validate_template_name(project_name, template_name, anatomy): - """Check that staging dir section with appropriate template exist. - - Raises: - ValueError - if misconfigured template - """ - if template_name not in anatomy.templates["staging"]: - raise ValueError( - f'Anatomy of project "{project_name}" does not have set' - f' "{template_name}" template key at Staging Dir category!' - ) - - def get_staging_dir_info( project_entity, folder_entity, From 2b1a04b5c0742320f1eb5adc501aa610e3d5e11c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:38:44 +0100 Subject: [PATCH 240/463] added typehints --- .../pipeline/create/creator_plugins.py | 4 +- client/ayon_core/pipeline/staging_dir.py | 53 ++++++++++--------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 6ccafe1bc7..28e9de20ee 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -15,7 +15,7 @@ from ayon_core.pipeline.plugin_discover import ( deregister_plugin, deregister_plugin_path ) -from ayon_core.pipeline import get_staging_dir_info +from ayon_core.pipeline.staging_dir import get_staging_dir_info, StagingDir from .constants import DEFAULT_VARIANT_VALUE from .product_name import get_product_name @@ -833,7 +833,7 @@ class Creator(BaseCreator): """ return self.pre_create_attr_defs - def get_staging_dir(self, instance): + def get_staging_dir(self, instance) -> Optional[StagingDir]: """Return the staging dir and persistence from instance. Args: diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 7f9ec85466..7e0874fbef 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -1,3 +1,6 @@ +import logging +import warnings +from typing import Optional, Dict, Any from dataclasses import dataclass from ayon_core.lib import Logger, filter_profiles @@ -16,16 +19,16 @@ class StagingDir: def get_staging_dir_config( - project_name, - task_type, - task_name, - product_type, - product_name, - host_name, - project_settings=None, - anatomy=None, - log=None, -): + project_name: str, + task_type: Optional[str, None], + task_name: Optional[str, None], + product_type: str, + product_name: str, + host_name: str, + project_settings: Optional[Dict[str, Any]] = None, + anatomy: Optional[Anatomy] = None, + log: Optional[logging.Logger] = None, +) -> Optional[Dict[str, Any]]: """Get matching staging dir profile. Args: @@ -93,21 +96,21 @@ def get_staging_dir_config( def get_staging_dir_info( - project_entity, - folder_entity, - task_entity, - product_type, - product_name, - host_name, - anatomy=None, - project_settings=None, - template_data=None, - always_return_path=True, - force_tmp_dir=False, - logger=None, - prefix=None, - suffix=None, -): + project_entity: Dict[str, Any], + folder_entity: Optional[Dict[str, Any]], + task_entity: Optional[Dict[str, Any]], + product_type: str, + product_name: str, + host_name: str, + anatomy: Optional[Anatomy] = None, + project_settings: Optional[Dict[str, Any]] = None, + template_data: Optional[Dict[str, Any]] = None, + always_return_path: bool = True, + force_tmp_dir: bool = False, + logger: Optional[logging.Logger] = None, + prefix: Optional[str] = None, + suffix: Optional[str] = None, +) -> Optional[StagingDir]: """Get staging dir info data. If `force_temp` is set, staging dir will be created as tempdir. From 25f6ec241bbbbc3fb95e7770561a96e875341b1e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:39:57 +0100 Subject: [PATCH 241/463] added is_ prefix to StagingDir bools --- .../pipeline/create/creator_plugins.py | 2 +- client/ayon_core/pipeline/publish/lib.py | 4 +-- client/ayon_core/pipeline/staging_dir.py | 32 +++++++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 28e9de20ee..42e8e0b60f 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -915,7 +915,7 @@ class Creator(BaseCreator): instance.transient_data.update({ "stagingDir": staging_dir_path, - "stagingDir_persistent": staging_dir_info.persistent, + "stagingDir_persistent": staging_dir_info.is_persistent, }) self.log.info(f"Applied staging dir to instance: {staging_dir_path}") diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 586b90a3fd..ba0f846fe4 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -716,8 +716,8 @@ def get_instance_staging_dir(instance): os.makedirs(staging_dir_path, exist_ok=True) instance.data.update({ "stagingDir": staging_dir_path, - "stagingDir_persistent": staging_dir_info.persistent, - "stagingDir_custom": staging_dir_info.custom + "stagingDir_persistent": staging_dir_info.is_persistent, + "stagingDir_custom": staging_dir_info.is_custom }) return staging_dir_path diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 7e0874fbef..2d94616faf 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -14,8 +14,28 @@ from .tempdir import get_temp_dir @dataclass class StagingDir: directory: str - persistent: bool - custom: bool # Whether the staging dir is a custom staging dir + is_persistent: bool + # Whether the staging dir is a custom staging dir + is_custom: bool + + def __setattr__(self, key, value): + if key == "persistent": + warnings.warn( + "'StagingDir.persistent' is deprecated." + " Use 'StagingDir.is_persistent' instead.", + DeprecationWarning + ) + key = "is_persistent" + super().__setattr__(key, value) + + @property + def persistent(self): + warnings.warn( + "'StagingDir.persistent' is deprecated." + " Use 'StagingDir.is_persistent' instead.", + DeprecationWarning + ) + return self.is_persistent def get_staging_dir_config( @@ -198,8 +218,8 @@ def get_staging_dir_info( dir_template = staging_dir_config["template"]["directory"] return StagingDir( dir_template.format_strict(ctx_data), - persistent=staging_dir_config["persistence"], - custom=True + is_persistent=staging_dir_config["persistence"], + is_custom=True ) # no config found but force an output @@ -211,8 +231,8 @@ def get_staging_dir_info( prefix=prefix, suffix=suffix, ), - persistent=False, - custom=False + is_persistent=False, + is_custom=False ) return None From 47fee3f54bd7d6b18849cdb59f8b5937988efa57 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:41:58 +0100 Subject: [PATCH 242/463] change custom key to is_custom --- client/ayon_core/pipeline/publish/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index ba0f846fe4..40a9b47aba 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -717,7 +717,7 @@ def get_instance_staging_dir(instance): instance.data.update({ "stagingDir": staging_dir_path, "stagingDir_persistent": staging_dir_info.is_persistent, - "stagingDir_custom": staging_dir_info.is_custom + "stagingDir_is_custom": staging_dir_info.is_custom }) return staging_dir_path From 4641760bd17a232a0d6c7a24482060a46a475ba3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 8 Jan 2025 09:16:21 +0100 Subject: [PATCH 243/463] Initial changes for BorisFX Silhouette --- client/ayon_core/hooks/pre_add_last_workfile_arg.py | 3 ++- client/ayon_core/hooks/pre_ocio_hook.py | 3 ++- client/ayon_core/plugins/publish/validate_file_saved.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hooks/pre_add_last_workfile_arg.py b/client/ayon_core/hooks/pre_add_last_workfile_arg.py index d5914c2352..a931fb0cbe 100644 --- a/client/ayon_core/hooks/pre_add_last_workfile_arg.py +++ b/client/ayon_core/hooks/pre_add_last_workfile_arg.py @@ -29,7 +29,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): "aftereffects", "wrap", "openrv", - "cinema4d" + "cinema4d", + "silhouette" } launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 7406aa42cf..dd81cf053e 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -20,7 +20,8 @@ class OCIOEnvHook(PreLaunchHook): "hiero", "resolve", "openrv", - "cinema4d" + "cinema4d", + "silhouette" } launch_types = set() diff --git a/client/ayon_core/plugins/publish/validate_file_saved.py b/client/ayon_core/plugins/publish/validate_file_saved.py index f52998cef3..4f9e84aee0 100644 --- a/client/ayon_core/plugins/publish/validate_file_saved.py +++ b/client/ayon_core/plugins/publish/validate_file_saved.py @@ -37,7 +37,7 @@ class ValidateCurrentSaveFile(pyblish.api.ContextPlugin): label = "Validate File Saved" order = pyblish.api.ValidatorOrder - 0.1 hosts = ["fusion", "houdini", "max", "maya", "nuke", "substancepainter", - "cinema4d"] + "cinema4d", "silhouette"] actions = [SaveByVersionUpAction, ShowWorkfilesAction] def process(self, context): From be6aac6a72a76bb82300fe4ca612e5408bbea679 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 8 Jan 2025 09:17:21 +0100 Subject: [PATCH 244/463] Remove `TreeViewSpinner` with `QtSvg` dependency - The `TreeViewSpinner` widget was not used anywhere - The `QtSvg` dependency does not exist in BorisFX Silhouette so removing it was easiest to make Silhouette not error on this import --- client/ayon_core/tools/utils/views.py | 45 +-------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/client/ayon_core/tools/utils/views.py b/client/ayon_core/tools/utils/views.py index b501f1ff11..d8ae94bf0c 100644 --- a/client/ayon_core/tools/utils/views.py +++ b/client/ayon_core/tools/utils/views.py @@ -1,7 +1,6 @@ -from ayon_core.resources import get_image_path from ayon_core.tools.flickcharm import FlickCharm -from qtpy import QtWidgets, QtCore, QtGui, QtSvg +from qtpy import QtWidgets, QtCore, QtGui class DeselectableTreeView(QtWidgets.QTreeView): @@ -19,48 +18,6 @@ class DeselectableTreeView(QtWidgets.QTreeView): QtWidgets.QTreeView.mousePressEvent(self, event) -class TreeViewSpinner(QtWidgets.QTreeView): - size = 160 - - def __init__(self, parent=None): - super(TreeViewSpinner, self).__init__(parent=parent) - - loading_image_path = get_image_path("spinner-200.svg") - - self.spinner = QtSvg.QSvgRenderer(loading_image_path) - - self.is_loading = False - self.is_empty = True - - def paint_loading(self, event): - rect = event.rect() - rect = QtCore.QRectF(rect.topLeft(), rect.bottomRight()) - rect.moveTo( - rect.x() + rect.width() / 2 - self.size / 2, - rect.y() + rect.height() / 2 - self.size / 2 - ) - rect.setSize(QtCore.QSizeF(self.size, self.size)) - painter = QtGui.QPainter(self.viewport()) - self.spinner.render(painter, rect) - - def paint_empty(self, event): - painter = QtGui.QPainter(self.viewport()) - rect = event.rect() - rect = QtCore.QRectF(rect.topLeft(), rect.bottomRight()) - qtext_opt = QtGui.QTextOption( - QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter - ) - painter.drawText(rect, "No Data", qtext_opt) - - def paintEvent(self, event): - if self.is_loading: - self.paint_loading(event) - elif self.is_empty: - self.paint_empty(event) - else: - super(TreeViewSpinner, self).paintEvent(event) - - class TreeView(QtWidgets.QTreeView): """Ultimate TreeView with flick charm and double click signals. From 2a22bbb0773700f818ab1f1e7dc4c981f5037373 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 8 Jan 2025 11:52:45 +0100 Subject: [PATCH 245/463] Fix Anatomy.format_all with unpadded int values. --- client/ayon_core/lib/path_templates.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index e3cae78a87..057889403c 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -561,9 +561,6 @@ class FormattingPart: """ key = self._template_base - if key in result.really_used_values: - result.add_output(result.really_used_values[key]) - return result # ensure key is properly formed [({})] properly closed. if not self.validate_key_is_matched(key): From cb2758ef54c22da82d172fa54522b9b4bb1fd28e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 8 Jan 2025 13:03:03 +0100 Subject: [PATCH 246/463] Allow folders to be considered as workfiles in workfiles tool, because Silhouette projects are actually folders (with .sfx extension) --- client/ayon_core/tools/workfiles/models/workfiles.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index a268a9bd0e..2c5ec230a6 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -185,8 +185,6 @@ class WorkareaModel: for filename in os.listdir(workdir): filepath = os.path.join(workdir, filename) - if not os.path.isfile(filepath): - continue ext = os.path.splitext(filename)[1].lower() if ext not in self._extensions: From 05ca2d42cd1b3306f81436c2da9d64f4285d2373 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:23:05 +0100 Subject: [PATCH 247/463] fix typehint --- client/ayon_core/pipeline/staging_dir.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 2d94616faf..37d6b955e2 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -40,8 +40,8 @@ class StagingDir: def get_staging_dir_config( project_name: str, - task_type: Optional[str, None], - task_name: Optional[str, None], + task_type: Optional[str], + task_name: Optional[str], product_type: str, product_name: str, host_name: str, From 2c91d60d6df074ebcc74c1eced35fe191e6edeed Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 8 Jan 2025 16:41:06 +0100 Subject: [PATCH 248/463] Fix lint. --- client/ayon_core/pipeline/editorial.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index d71cf6c344..8ac4d906b1 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -228,7 +228,6 @@ def remap_range_on_file_sequence(otio_clip, otio_range): raise ValueError("Inconsistent range between clip and provided clip") source_range = otio_clip.source_range - source_range_rate = source_range.start_time.rate media_in = available_range.start_time available_range_start_frame = ( available_range.start_time.to_frames() @@ -254,8 +253,6 @@ def remap_range_on_file_sequence(otio_clip, otio_range): rate=available_range_rate, ).to_frames() - range_duration = otio_range.duration - frame_out = otio.opentime.RationalTime.from_frames( frame_in + otio_range.duration.to_frames() - 1, rate=available_range_rate, @@ -397,7 +394,10 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): src_duration = conformed_source_range.duration offset_in = otio.opentime.RationalTime(offset_in, rate=src_in.rate) - offset_duration = otio.opentime.RationalTime(offset_out, rate=src_duration.rate) + offset_duration = otio.opentime.RationalTime( + offset_out, + rate=src_duration.rate + ) trim_range = otio.opentime.TimeRange( start_time=src_in + offset_in, From 8db81098af5d15e20607c661ab510c6b6a6ef767 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 8 Jan 2025 17:55:13 +0100 Subject: [PATCH 249/463] Add comment --- client/ayon_core/tools/workfiles/models/workfiles.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/tools/workfiles/models/workfiles.py b/client/ayon_core/tools/workfiles/models/workfiles.py index 2c5ec230a6..c621a44937 100644 --- a/client/ayon_core/tools/workfiles/models/workfiles.py +++ b/client/ayon_core/tools/workfiles/models/workfiles.py @@ -184,6 +184,9 @@ class WorkareaModel: return items for filename in os.listdir(workdir): + # We want to support both files and folders. e.g. Silhoutte uses + # folders as its project files. So we do not check whether it is + # a file or not. filepath = os.path.join(workdir, filename) ext = os.path.splitext(filename)[1].lower() From ff2893dff04368f26c127080ef824f3bad95b2d1 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 8 Jan 2025 19:04:54 +0100 Subject: [PATCH 250/463] Fix remap with wrongly detected legacy image sequence. --- client/ayon_core/pipeline/editorial.py | 35 +++++++------ .../img_seq_24_to_23.976_no_legacy.json | 51 +++++++++++++++++++ .../test_media_range_with_retimes.py | 26 ++++++++++ 3 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_24_to_23.976_no_legacy.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 2928ef5f63..1d1859cbb8 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -231,10 +231,13 @@ def remap_range_on_file_sequence(otio_clip, in_out_range): # source range for image sequence. Following code maintain # backward-compatibility by adjusting media_in # while we are updating those. + conformed_src_in = source_range.start_time.rescaled_to( + available_range_rate + ) if ( is_clip_from_media_sequence(otio_clip) and available_range_start_frame == media_ref.start_frame - and source_range.start_time.to_frames() < media_ref.start_frame + and conformed_src_in.to_frames() < media_ref.start_frame ): media_in = 0 @@ -261,21 +264,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): media_ref = otio_clip.media_reference is_input_sequence = is_clip_from_media_sequence(otio_clip) - # Temporary. - # Some AYON custom OTIO exporter were implemented with relative - # source range for image sequence. Following code maintain - # backward-compatibility by adjusting available range - # while we are updating those. - if ( - is_input_sequence - and available_range.start_time.to_frames() == media_ref.start_frame - and source_range.start_time.to_frames() < media_ref.start_frame - ): - available_range = _ot.TimeRange( - _ot.RationalTime(0, rate=available_range_rate), - available_range.duration, - ) - # Conform source range bounds to available range rate # .e.g. embedded TC of (3600 sec/ 1h), duration 100 frames # @@ -320,6 +308,21 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): else: conformed_source_range = source_range + # Temporary. + # Some AYON custom OTIO exporter were implemented with relative + # source range for image sequence. Following code maintain + # backward-compatibility by adjusting available range + # while we are updating those. + if ( + is_input_sequence + and available_range.start_time.to_frames() == media_ref.start_frame + and conformed_source_range.start_time.to_frames() < media_ref.start_frame + ): + available_range = _ot.TimeRange( + _ot.RationalTime(0, rate=available_range_rate), + available_range.duration, + ) + # modifiers time_scalar = 1. offset_in = 0 diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_24_to_23.976_no_legacy.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_24_to_23.976_no_legacy.json new file mode 100644 index 0000000000..108af0f2c1 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_24_to_23.976_no_legacy.json @@ -0,0 +1,51 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 108.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 883159.0 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 755.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 883750.0 + } + }, + "available_image_bounds": null, + "target_url_base": "/mnt/jobs/yahoo_theDog_1132/IN/FOOTAGE/SCANS_LINEAR/Panasonic Rec 709 to ACESCG/Panasonic P2 /A001_S001_S001_T004/", + "name_prefix": "A001_S001_S001_T004.", + "name_suffix": ".exr", + "start_frame": 883750, + "frame_step": 1, + "rate": 1.0, + "frame_zero_padding": 0, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 7f9256c6d8..331732b6a4 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -187,3 +187,29 @@ def test_img_sequence_conform_to_23_976fps(): handle_start=0, handle_end=8, ) + + +def test_img_sequence_conform_from_24_to_23_976fps(): + """ + Img sequence clip + available files = 883750-884504 24fps + source_range = 883159-883267 23.976fps + + This test ensures such entries do not trigger + the legacy Hiero export compatibility. + """ + expected_data = { + 'mediaIn': 884043, + 'mediaOut': 884150, + 'handleStart': 0, + 'handleEnd': 0, + 'speed': 1.0 + } + + _check_expected_retimed_values( + "img_seq_24_to_23.976_no_legacy.json", + expected_data, + handle_start=0, + handle_end=0, + ) + From 9fbef06cb0c30b1f11e685b9075bf0f53a2064ac Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 8 Jan 2025 19:16:10 +0100 Subject: [PATCH 251/463] Fix lint. --- client/ayon_core/pipeline/editorial.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 1d1859cbb8..a33f439651 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -316,7 +316,8 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): if ( is_input_sequence and available_range.start_time.to_frames() == media_ref.start_frame - and conformed_source_range.start_time.to_frames() < media_ref.start_frame + and conformed_source_range.start_time.to_frames() < + media_ref.start_frame ): available_range = _ot.TimeRange( _ot.RationalTime(0, rate=available_range_rate), From 8ef1d38f79319c615464935d2e6f77cf85604e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 9 Jan 2025 14:10:48 +0100 Subject: [PATCH 252/463] Refactor OTIO frame range collection plugins - Added validation function for OTIO clips. - Improved documentation for each plugin. - Enhanced logging to provide clearer debug messages. - Separated logic for collecting timeline, source, and retimed ranges into distinct classes. - Updated frame calculations to handle retimed clips more effectively. --- .../publish/collect_otio_frame_ranges.py | 227 +++++++++++------- 1 file changed, 143 insertions(+), 84 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 0d1f76f338..3cf9a5b40c 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -1,50 +1,88 @@ +"""Plugins for collecting OTIO frame ranges and related timing information. + +This module contains three plugins: +- CollectOtioFrameRanges: Collects basic timeline frame ranges +- CollectOtioSourceRanges: Collects source media frame ranges +- CollectOtioRetimedRanges: Handles retimed clip frame ranges """ -Requires: - otioTimeline -> context data attribute - review -> instance data attribute - masterLayer -> instance data attribute - otioClipRange -> instance data attribute -""" + from pprint import pformat +from typing import Any import pyblish.api +try: + import opentimelineio as otio +except ImportError: + raise RuntimeError("OpenTimelineIO is not installed.") + +from ayon_core.pipeline.editorial import ( + get_media_range_with_retimes, + otio_range_to_frame_range, + otio_range_with_handles, +) + + +def validate_otio_clip(instance: Any, logger: Any) -> bool: + """Validate if instance has required OTIO clip data. + + Args: + instance: The instance to validate + logger: Logger object to use for debug messages + + Returns: + bool: True if valid, False otherwise + """ + if not instance.data.get("otioClip"): + logger.debug("Skipping collect OTIO range - no clip found.") + return False + return True class CollectOtioFrameRanges(pyblish.api.InstancePlugin): - """Getting otio ranges from otio_clip + """Collect basic timeline frame ranges from OTIO clip. - Adding timeline and source ranges to instance data""" + This plugin extracts and stores basic timeline frame ranges including + handles from the OTIO clip. + + Requires: + otioClip (otio.schema.Clip): OTIO clip object + workfileFrameStart (int): Starting frame of work file + + Provides: + frameStart (int): Start frame in timeline + frameEnd (int): End frame in timeline + clipIn (int): Clip in point + clipOut (int): Clip out point + clipInH (int): Clip in point with handles + clipOutH (int): Clip out point with handles + """ label = "Collect OTIO Frame Ranges" order = pyblish.api.CollectorOrder - 0.08 families = ["shot", "clip"] hosts = ["resolve", "hiero", "flame", "traypublisher"] - def process(self, instance): - # Not all hosts can import these modules. - import opentimelineio as otio - from ayon_core.pipeline.editorial import ( - otio_range_to_frame_range, - otio_range_with_handles - ) + def process(self, instance: Any) -> None: + """Process the instance to collect frame ranges. - if not instance.data.get("otioClip"): - self.log.debug("Skipping collect OTIO frame range.") + Args: + instance: The instance to process + """ + + if not validate_otio_clip(instance, self.log): return - # get basic variables otio_clip = instance.data["otioClip"] workfile_start = instance.data["workfileFrameStart"] - # get ranges + # Get timeline ranges otio_tl_range = otio_clip.range_in_parent() - otio_tl_range_handles = otio_range_with_handles( - otio_tl_range, instance) + otio_tl_range_handles = otio_range_with_handles(otio_tl_range, instance) + + # Convert to frames + tl_start, tl_end = otio_range_to_frame_range(otio_tl_range) + tl_start_h, tl_end_h = otio_range_to_frame_range(otio_tl_range_handles) - # convert to frames - range_convert = otio_range_to_frame_range - tl_start, tl_end = range_convert(otio_tl_range) - tl_start_h, tl_end_h = range_convert(otio_tl_range_handles) frame_start = workfile_start frame_end = frame_start + otio.opentime.to_frames( otio_tl_range.duration, otio_tl_range.duration.rate) - 1 @@ -58,89 +96,110 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): "clipOutH": tl_end_h - 1, } instance.data.update(data) - self.log.debug( - "_ data: {}".format(pformat(data))) - self.log.debug( - "_ instance.data: {}".format(pformat(instance.data))) + self.log.debug(f"Added frame ranges: {pformat(data)}") -class CollectOtioSourceFrameRanges(pyblish.api.InstancePlugin): - """Getting otio ranges from otio_clip +class CollectOtioSourceRanges(pyblish.api.InstancePlugin): + """Collect source media frame ranges from OTIO clip. - Adding timeline and source ranges to instance data""" + This plugin extracts and stores source media frame ranges including + handles from the OTIO clip. - label = "Collect OTIO Frame Ranges (with media range)" + Requires: + otioClip (otio.schema.Clip): OTIO clip object + + Provides: + sourceStart (int): Source media start frame + sourceEnd (int): Source media end frame + sourceStartH (int): Source media start frame with handles + sourceEndH (int): Source media end frame with handles + """ + + label = "Collect Source OTIO Frame Ranges" order = pyblish.api.CollectorOrder - 0.07 families = ["shot", "clip"] hosts = ["hiero", "flame"] - def process(self, instance): - # Not all hosts can import these modules. - import opentimelineio as otio - from ayon_core.pipeline.editorial import ( - get_media_range_with_retimes, - otio_range_to_frame_range, - otio_range_with_handles, - ) + def process(self, instance: Any) -> None: + """Process the instance to collect source frame ranges. - if not instance.data.get("otioClip"): - self.log.debug("Skipping collect OTIO frame range.") + Args: + instance: The instance to process + """ + + if not validate_otio_clip(instance, self.log): return - # get basic variables otio_clip = instance.data["otioClip"] - workfile_start = instance.data["workfileFrameStart"] - workfile_source_duration = instance.data.get("shotDurationFromSource") - # get ranges - otio_tl_range = otio_clip.range_in_parent() + # Get source ranges otio_src_range = otio_clip.source_range - otio_avalable_range = otio_clip.available_range() - otio_tl_range_handles = otio_range_with_handles(otio_tl_range, instance) + otio_available_range = otio_clip.available_range() otio_src_range_handles = otio_range_with_handles(otio_src_range, instance) - # get source avalable start frame + # Get source available start frame src_starting_from = otio.opentime.to_frames( - otio_avalable_range.start_time, otio_avalable_range.start_time.rate + otio_available_range.start_time, + otio_available_range.start_time.rate ) - # convert to frames - range_convert = otio_range_to_frame_range - tl_start, tl_end = range_convert(otio_tl_range) - tl_start_h, tl_end_h = range_convert(otio_tl_range_handles) - src_start, src_end = range_convert(otio_src_range) - src_start_h, src_end_h = range_convert(otio_src_range_handles) - frame_start = workfile_start - frame_end = ( - frame_start - + otio.opentime.to_frames( - otio_tl_range.duration, otio_tl_range.duration.rate - ) - - 1 - ) - - # in case of retimed clip and frame range should not be retimed - if workfile_source_duration: - # get available range trimmed with processed retimes - retimed_attributes = get_media_range_with_retimes(otio_clip, 0, 0) - self.log.debug(">> retimed_attributes: {}".format(retimed_attributes)) - media_in = int(retimed_attributes["mediaIn"]) - media_out = int(retimed_attributes["mediaOut"]) - frame_end = frame_start + (media_out - media_in) + 1 - self.log.debug(frame_end) + # Convert to frames + src_start, src_end = otio_range_to_frame_range(otio_src_range) + src_start_h, src_end_h = otio_range_to_frame_range(otio_src_range_handles) data = { - "frameStart": frame_start, - "frameEnd": frame_end, - "clipIn": tl_start, - "clipOut": tl_end - 1, - "clipInH": tl_start_h, - "clipOutH": tl_end_h - 1, "sourceStart": src_starting_from + src_start, "sourceEnd": src_starting_from + src_end - 1, "sourceStartH": src_starting_from + src_start_h, "sourceEndH": src_starting_from + src_end_h - 1, } instance.data.update(data) - self.log.debug("_ data: {}".format(pformat(data))) - self.log.debug("_ instance.data: {}".format(pformat(instance.data))) + self.log.debug(f"Added source ranges: {pformat(data)}") + + +class CollectOtioRetimedRanges(pyblish.api.InstancePlugin): + """Update frame ranges for retimed clips. + + This plugin updates the frame end value for retimed clips. + + Requires: + otioClip (otio.schema.Clip): OTIO clip object + workfileFrameStart (int): Starting frame of work file + shotDurationFromSource (Optional[int]): Duration from source if retimed + + Provides: + frameEnd (int): Updated end frame for retimed clips + """ + + label = "Collect Retimed OTIO Frame Ranges" + order = pyblish.api.CollectorOrder - 0.06 + families = ["shot", "clip"] + hosts = ["hiero", "flame"] + + def process(self, instance: Any) -> None: + """Process the instance to handle retimed clips. + + Args: + instance: The instance to process + """ + if not validate_otio_clip(instance, self.log): + return + + workfile_source_duration = instance.data.get("shotDurationFromSource") + if not workfile_source_duration: + self.log.debug("No source duration found, skipping retime handling.") + return + + otio_clip = instance.data["otioClip"] + frame_start = instance.data["frameStart"] + + # Handle retimed clip frame range + retimed_attributes = get_media_range_with_retimes(otio_clip, 0, 0) + self.log.debug(f"Retimed attributes: {retimed_attributes}") + + media_in = int(retimed_attributes["mediaIn"]) + media_out = int(retimed_attributes["mediaOut"]) + frame_end = frame_start + (media_out - media_in) + 1 + + instance.data["frameEnd"] = frame_end + self.log.debug(f"Updated frameEnd for retimed clip: {frame_end}") From 5d73b318ec5807c170653bee914959de27e13e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 9 Jan 2025 14:13:24 +0100 Subject: [PATCH 253/463] Refactor type hints in validation functions - Removed type hints for `Any` in function definitions. - Simplified the `validate_otio_clip` and `process` methods across multiple classes. - Cleaned up code for better readability without changing functionality. --- .../plugins/publish/collect_otio_frame_ranges.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 3cf9a5b40c..7c31b6445a 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -7,7 +7,6 @@ This module contains three plugins: """ from pprint import pformat -from typing import Any import pyblish.api @@ -23,7 +22,7 @@ from ayon_core.pipeline.editorial import ( ) -def validate_otio_clip(instance: Any, logger: Any) -> bool: +def validate_otio_clip(instance, logger): """Validate if instance has required OTIO clip data. Args: @@ -62,7 +61,7 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): families = ["shot", "clip"] hosts = ["resolve", "hiero", "flame", "traypublisher"] - def process(self, instance: Any) -> None: + def process(self, instance): """Process the instance to collect frame ranges. Args: @@ -120,7 +119,7 @@ class CollectOtioSourceRanges(pyblish.api.InstancePlugin): families = ["shot", "clip"] hosts = ["hiero", "flame"] - def process(self, instance: Any) -> None: + def process(self, instance): """Process the instance to collect source frame ranges. Args: @@ -176,7 +175,7 @@ class CollectOtioRetimedRanges(pyblish.api.InstancePlugin): families = ["shot", "clip"] hosts = ["hiero", "flame"] - def process(self, instance: Any) -> None: + def process(self, instance): """Process the instance to handle retimed clips. Args: From cd9929dfa192d080e5dcfea0f543104dd12cb4af Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 Jan 2025 17:01:50 +0100 Subject: [PATCH 254/463] set host name environment variable --- client/ayon_core/plugins/publish/collect_farm_env_variables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index cb52e5c32e..ee88985905 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -24,6 +24,7 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): # NOTE we should use 'context.data["user"]' but that has higher # order. ("AYON_USERNAME", get_ayon_username()), + ("AYON_HOST_NAME", context.data["hostName"]), ): if value: self.log.debug(f"Setting job env: {key}: {value}") From 3fbb0f4dfb0ecfe46ca83929bbe00a9a92794d34 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:18:24 +0100 Subject: [PATCH 255/463] metadata file does not require 'job' key to be filled --- client/ayon_core/plugins/publish/collect_rendered_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index 42ba096d14..62b649cdd0 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -105,7 +105,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): instance.data.update(instance_data) # stash render job id for later validation - instance.data["render_job_id"] = data.get("job").get("_id") + instance.data["render_job_id"] = data.get("job", {}).get("_id") staging_dir_persistent = instance.data.get( "stagingDir_persistent", False ) From 8a7e11a1a4645bd886154c70eb9ec39b4ee2a831 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 Jan 2025 18:18:40 +0100 Subject: [PATCH 256/463] store metadata file content to 'publishJobMetadata' on instance --- client/ayon_core/plugins/publish/collect_rendered_files.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index 62b649cdd0..deecf7ba24 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -93,8 +93,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): # now we can just add instances from json file and we are done any_staging_dir_persistent = False - for instance_data in data.get("instances"): - + for instance_data in data["instances"]: self.log.debug(" - processing instance for {}".format( instance_data.get("productName"))) instance = self._context.create_instance( @@ -105,6 +104,10 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): instance.data.update(instance_data) # stash render job id for later validation + instance.data["publishJobMetadata"] = data + # TODO remove 'render_job_id' here and rather use + # 'publishJobMetadata' where is needed. + # - this is deadline specific instance.data["render_job_id"] = data.get("job", {}).get("_id") staging_dir_persistent = instance.data.get( "stagingDir_persistent", False From eec3f61641b1ca21b9c6e2fdb225beffcb3c7827 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Jan 2025 15:56:18 +0100 Subject: [PATCH 257/463] Correctly emit signal --- client/ayon_core/tools/publisher/widgets/card_view_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 095a4eae7c..6bd4725371 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -197,7 +197,7 @@ class ConvertorItemsGroupWidget(BaseGroupWidget): else: widget = ConvertorItemCardWidget(item, self) widget.selected.connect(self._on_widget_selection) - widget.double_clicked(self.double_clicked) + widget.double_clicked.emit(self.double_clicked) self._widgets_by_id[item.id] = widget self._content_layout.insertWidget(widget_idx, widget) widget_idx += 1 From f4ea9aeacff690518822bd76459370fec8596d57 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 13 Jan 2025 16:01:11 +0100 Subject: [PATCH 258/463] Should be ``.connect` --- client/ayon_core/tools/publisher/widgets/card_view_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 6bd4725371..2f633b3149 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -197,7 +197,7 @@ class ConvertorItemsGroupWidget(BaseGroupWidget): else: widget = ConvertorItemCardWidget(item, self) widget.selected.connect(self._on_widget_selection) - widget.double_clicked.emit(self.double_clicked) + widget.double_clicked.connect(self.double_clicked) self._widgets_by_id[item.id] = widget self._content_layout.insertWidget(widget_idx, widget) widget_idx += 1 From 1d23ca6633d5ff8d85ebeda066de7a621a60e869 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 13 Jan 2025 23:35:43 +0200 Subject: [PATCH 259/463] update default order to match values in `CollectUSDLayerContributions` --- server/settings/publish_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 8893b00e23..1bf2e853cf 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1008,8 +1008,8 @@ DEFAULT_PUBLISH_VALUES = { {"name": "model", "order": 100}, {"name": "assembly", "order": 150}, {"name": "groom", "order": 175}, - {"name": "look", "order": 300}, - {"name": "rig", "order": 100}, + {"name": "look", "order": 200}, + {"name": "rig", "order": 300}, # Shot layers {"name": "layout", "order": 200}, {"name": "animation", "order": 300}, From 11b00e61050b3d4027efe7fbedc14111bce87451 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 14 Jan 2025 08:27:36 +0000 Subject: [PATCH 260/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 2417897a47..0a985e2829 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.12+dev" +__version__ = "1.0.13" diff --git a/package.py b/package.py index 8ade5ceeed..dae6a9ca0a 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.12+dev" +version = "1.0.13" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index b8d6a5a537..78edf5c2e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.12+dev" +version = "1.0.13" description = "" authors = ["Ynput Team "] readme = "README.md" From e3b9bfa29d91efc380bde4389783abc0aa9c868d Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 14 Jan 2025 08:28:17 +0000 Subject: [PATCH 261/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 0a985e2829..b0ada09e7c 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.13" +__version__ = "1.0.13+dev" diff --git a/package.py b/package.py index dae6a9ca0a..03b69d4c5c 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.13" +version = "1.0.13+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 78edf5c2e3..5e42aa7093 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.13" +version = "1.0.13+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From e90ca81eded332464f184025521e8815f6202725 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 14 Jan 2025 10:27:58 +0100 Subject: [PATCH 262/463] Append more editorial tests for reverse speed. --- ...img_seq_reverse_speed_24_to_23.976fps.json | 59 +++ .../img_seq_reverse_speed_no_tc.json | 369 ++++++++++++++++++ .../test_media_range_with_retimes.py | 56 +++ 3 files changed, 484 insertions(+) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_24_to_23.976fps.json create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_no_tc.json diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_24_to_23.976fps.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_24_to_23.976fps.json new file mode 100644 index 0000000000..b907f53f3d --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_24_to_23.976fps.json @@ -0,0 +1,59 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 32.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 947726.0 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "", + "effect_name": "LinearTimeWarp", + "time_scalar": -1.0 + } + ], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": {}, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 7607.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 941478.0 + } + }, + "available_image_bounds": null, + "target_url_base": "/mnt/jobs/yahoo_theDog_1132/IN/FOOTAGE/SCANS_LINEAR/Panasonic Rec 709 to ACESCG/Panasonic P2 /A001_S001_S001_T012/", + "name_prefix": "A001_S001_S001_T012.", + "name_suffix": ".exr", + "start_frame": 941478, + "frame_step": 1, + "rate": 24.0, + "frame_zero_padding": 0, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_no_tc.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_no_tc.json new file mode 100644 index 0000000000..ccf33413ec --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_no_tc.json @@ -0,0 +1,369 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": { + "Resolve_OTIO": {} + }, + "name": "output.[1000-1099].tif", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 41.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 20.000000000000004 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "", + "effect_name": "", + "time_scalar": -1.0 + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Transform", + "Enabled": true, + "Name": "Transform", + "Parameters": [], + "Type": 2 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Cropping", + "Enabled": true, + "Name": "Cropping", + "Parameters": [], + "Type": 3 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Dynamic Zoom", + "Enabled": false, + "Name": "Dynamic Zoom", + "Parameters": [ + { + "Default Parameter Value": [ + 0.0, + 0.0 + ], + "Key Frames": { + "-39": { + "Value": [ + 0.0, + 0.0 + ], + "Variant Type": "POINTF" + }, + "961": { + "Value": [ + 0.0, + 0.0 + ], + "Variant Type": "POINTF" + } + }, + "Parameter ID": "dynamicZoomCenter", + "Parameter Value": [ + 0.0, + 0.0 + ], + "Variant Type": "POINTF" + }, + { + "Default Parameter Value": 1.0, + "Key Frames": { + "-39": { + "Value": 0.8, + "Variant Type": "Double" + }, + "961": { + "Value": 1.0, + "Variant Type": "Double" + } + }, + "Parameter ID": "dynamicZoomScale", + "Parameter Value": 1.0, + "Variant Type": "Double", + "maxValue": 100.0, + "minValue": 0.01 + } + ], + "Type": 59 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Composite", + "Enabled": true, + "Name": "Composite", + "Parameters": [], + "Type": 1 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Lens Correction", + "Enabled": true, + "Name": "Lens Correction", + "Parameters": [], + "Type": 43 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Retime and Scaling", + "Enabled": true, + "Name": "Retime and Scaling", + "Parameters": [], + "Type": 22 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Video Faders", + "Enabled": true, + "Name": "Video Faders", + "Parameters": [], + "Type": 36 + } + }, + "name": "", + "effect_name": "Resolve Effect" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "Resolve_OTIO": { + "Keywords": [], + "Note": "{\"resolve_sub_products\": {\"io.ayon.creators.resolve.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.resolve.shot\", \"variant\": \"Main\", \"folderPath\": \"/shots/reverse_speed/sh010\", \"task\": null, \"clip_variant\": \"\", \"clip_index\": \"2cb93726-2f27-41b0-b7f7-f48998327ce8\", \"clip_source_resolution\": {\"width\": \"1920\", \"height\": \"1080\", \"pixelAspect\": 1.0}, \"folder\": \"/shots/reverse_speed/sh010\", \"episode\": \"ep01\", \"sequence\": \"reverse_speed\", \"track\": \"{_track_}\", \"shot\": \"sh###\", \"hierarchy\": \"shots/reverse_speed\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"folder_type\": \"sequence\", \"entity_name\": \"reverse_speed\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"reverse_speed\", \"track\": \"Video_1\", \"shot\": \"sh010\"}, \"heroTrack\": true, \"uuid\": \"d08c8422-29a9-46e9-9d6d-37e2dd9f9f8b\", \"reviewTrack\": null, \"label\": \"/shots/reverse_speed/sh010 shot\", \"has_promised_context\": true, \"newHierarchyIntegration\": true, \"newAssetPublishing\": true, \"instance_id\": \"087e8c66-3ce7-41bf-a27e-3e5f7abc12fb\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1042, \"clipIn\": 86400, \"clipOut\": 86441, \"clipDuration\": 41, \"sourceIn\": 39, \"sourceOut\": 80, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.resolve.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.resolve.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/reverse_speed/sh010\", \"task\": null, \"clip_variant\": \"\", \"clip_index\": \"2cb93726-2f27-41b0-b7f7-f48998327ce8\", \"clip_source_resolution\": {\"width\": \"1920\", \"height\": \"1080\", \"pixelAspect\": 1.0}, \"folder\": \"/shots/reverse_speed/sh010\", \"episode\": \"ep01\", \"sequence\": \"reverse_speed\", \"track\": \"{_track_}\", \"shot\": \"sh###\", \"hierarchy\": \"shots/reverse_speed\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"folder_type\": \"sequence\", \"entity_name\": \"reverse_speed\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"reverse_speed\", \"track\": \"Video_1\", \"shot\": \"sh010\"}, \"heroTrack\": true, \"uuid\": \"d08c8422-29a9-46e9-9d6d-37e2dd9f9f8b\", \"reviewTrack\": null, \"parent_instance_id\": \"087e8c66-3ce7-41bf-a27e-3e5f7abc12fb\", \"label\": \"/shots/reverse_speed/sh010 plate\", \"has_promised_context\": true, \"newHierarchyIntegration\": true, \"newAssetPublishing\": true, \"instance_id\": \"63b97e7f-834f-490d-bba5-ae0e584f4a17\", \"creator_attributes\": {\"parentInstance\": \"/shots/reverse_speed/sh010 shot\", \"vSyncOn\": false, \"vSyncTrack\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"2cb93726-2f27-41b0-b7f7-f48998327ce8\", \"publish\": true}" + }, + "clip_index": "2cb93726-2f27-41b0-b7f7-f48998327ce8", + "publish": true, + "resolve_sub_products": { + "io.ayon.creators.resolve.plate": { + "active": true, + "clip_index": "2cb93726-2f27-41b0-b7f7-f48998327ce8", + "clip_source_resolution": { + "height": "1080", + "pixelAspect": 1.0, + "width": "1920" + }, + "clip_variant": "", + "creator_attributes": { + "parentInstance": "/shots/reverse_speed/sh010 shot", + "vSyncOn": false, + "vSyncTrack": "Video 1" + }, + "creator_identifier": "io.ayon.creators.resolve.plate", + "episode": "ep01", + "folder": "/shots/reverse_speed/sh010", + "folderPath": "/shots/reverse_speed/sh010", + "handleEnd": 10, + "handleStart": 10, + "has_promised_context": true, + "heroTrack": true, + "hierarchy": "shots/reverse_speed", + "hierarchyData": { + "episode": "ep01", + "folder": "shots", + "sequence": "reverse_speed", + "shot": "sh010", + "track": "Video_1" + }, + "id": "pyblish.avalon.instance", + "instance_id": "63b97e7f-834f-490d-bba5-ae0e584f4a17", + "label": "/shots/reverse_speed/sh010 plate", + "newAssetPublishing": true, + "newHierarchyIntegration": true, + "parent_instance_id": "087e8c66-3ce7-41bf-a27e-3e5f7abc12fb", + "parents": [ + { + "entity_name": "shots", + "folder_type": "folder" + }, + { + "entity_name": "reverse_speed", + "folder_type": "sequence" + } + ], + "productName": "plateVideo_1", + "productType": "plate", + "publish_attributes": { + "CollectSlackFamilies": { + "additional_message": "" + } + }, + "reviewTrack": null, + "sequence": "reverse_speed", + "shot": "sh###", + "sourceResolution": false, + "task": null, + "track": "{_track_}", + "uuid": "d08c8422-29a9-46e9-9d6d-37e2dd9f9f8b", + "variant": "Video_1", + "workfileFrameStart": 1001 + }, + "io.ayon.creators.resolve.shot": { + "active": true, + "clip_index": "2cb93726-2f27-41b0-b7f7-f48998327ce8", + "clip_source_resolution": { + "height": "1080", + "pixelAspect": 1.0, + "width": "1920" + }, + "clip_variant": "", + "creator_attributes": { + "clipDuration": 41, + "clipIn": 86400, + "clipOut": 86441, + "fps": "from_selection", + "frameEnd": 1042, + "frameStart": 1001, + "handleEnd": 10, + "handleStart": 10, + "sourceIn": 39, + "sourceOut": 80, + "workfileFrameStart": 1001 + }, + "creator_identifier": "io.ayon.creators.resolve.shot", + "episode": "ep01", + "folder": "/shots/reverse_speed/sh010", + "folderPath": "/shots/reverse_speed/sh010", + "handleEnd": 10, + "handleStart": 10, + "has_promised_context": true, + "heroTrack": true, + "hierarchy": "shots/reverse_speed", + "hierarchyData": { + "episode": "ep01", + "folder": "shots", + "sequence": "reverse_speed", + "shot": "sh010", + "track": "Video_1" + }, + "id": "pyblish.avalon.instance", + "instance_id": "087e8c66-3ce7-41bf-a27e-3e5f7abc12fb", + "label": "/shots/reverse_speed/sh010 shot", + "newAssetPublishing": true, + "newHierarchyIntegration": true, + "parents": [ + { + "entity_name": "shots", + "folder_type": "folder" + }, + { + "entity_name": "reverse_speed", + "folder_type": "sequence" + } + ], + "productName": "shotMain", + "productType": "shot", + "publish_attributes": { + "CollectSlackFamilies": { + "additional_message": "" + } + }, + "reviewTrack": null, + "sequence": "reverse_speed", + "shot": "sh###", + "sourceResolution": false, + "task": null, + "track": "{_track_}", + "uuid": "d08c8422-29a9-46e9-9d6d-37e2dd9f9f8b", + "variant": "Main", + "workfileFrameStart": 1001 + } + } + }, + "name": "AYONData", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 1.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 59.0 + } + }, + "comment": "" + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": {}, + "name": "output.[1000-1099].tif", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 100.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:\\Users\\robin\\OneDrive\\Bureau\\dev_ayon\\data\\img_sequence\\tif", + "name_prefix": "output.", + "name_suffix": ".tif", + "start_frame": 1000, + "frame_step": 1, + "rate": 24.0, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 5147c2394a..2e7193444c 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -235,3 +235,59 @@ def test_img_sequence_conform_from_24_to_23_976fps(): handle_end=0, ) + +def test_img_sequence_reverse_speed_no_tc(): + """ + Img sequence clip + available files = 0-100 24fps + source_range = 20-41 24fps + """ + expected_data = { + 'mediaIn': 1020, + 'mediaOut': 1060, + 'handleStart': 0, + 'handleEnd': 0, + 'speed': -1.0, + 'versionData': { + 'retime': True, + 'speed': -1.0, + 'timewarps': [], + 'handleStart': 0, + 'handleEnd': 0 + } + } + + _check_expected_retimed_values( + "img_seq_reverse_speed_no_tc.json", + expected_data, + handle_start=0, + handle_end=0, + ) + +def test_img_sequence_reverse_speed_from_24_to_23_976fps(): + """ + Img sequence clip + available files = 941478-949084 24fps + source_range = 947726-947757 23.976fps + """ + expected_data = { + 'mediaIn': 948674, + 'mediaOut': 948705, + 'handleStart': 10, + 'handleEnd': 10, + 'speed': -1.0, + 'versionData': { + 'retime': True, + 'speed': -1.0, + 'timewarps': [], + 'handleStart': 10, + 'handleEnd': 10 + } + } + + _check_expected_retimed_values( + "img_seq_reverse_speed_24_to_23.976fps.json", + expected_data, + handle_start=10, + handle_end=10, + ) From 951fd51ab695d2194650237237b73a9170cdc9dc Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 14 Jan 2025 13:17:39 +0100 Subject: [PATCH 263/463] Fix rouding issue with extract_otio_review --- client/ayon_core/plugins/publish/extract_otio_review.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index d5f5f43cc9..275c3e7c58 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -147,7 +147,6 @@ class ExtractOTIOReview( self.actual_fps = available_range.duration.rate start = src_range.start_time.rescaled_to(self.actual_fps) duration = src_range.duration.rescaled_to(self.actual_fps) - src_frame_start = src_range.start_time.to_frames() # Temporary. # Some AYON custom OTIO exporter were implemented with @@ -157,7 +156,7 @@ class ExtractOTIOReview( if ( is_clip_from_media_sequence(r_otio_cl) and available_range_start_frame == media_ref.start_frame - and src_frame_start < media_ref.start_frame + and start.to_frames() < media_ref.start_frame ): available_range = otio.opentime.TimeRange( otio.opentime.RationalTime(0, rate=self.actual_fps), From 3d9459f3c9002cba98652bf05968959deee5cfe8 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 14 Jan 2025 14:17:51 +0100 Subject: [PATCH 264/463] Report stagingDir_is_custom to apply_staging_dir. --- client/ayon_core/pipeline/create/creator_plugins.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 42e8e0b60f..445b41cb4b 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -916,6 +916,7 @@ class Creator(BaseCreator): instance.transient_data.update({ "stagingDir": staging_dir_path, "stagingDir_persistent": staging_dir_info.is_persistent, + "stagingDir_is_custom": staging_dir_info.is_custom, }) self.log.info(f"Applied staging dir to instance: {staging_dir_path}") From 2be4d7c2e8b7ff7d9d133c60d3eb0141640ba3f3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 14 Jan 2025 16:03:30 +0100 Subject: [PATCH 265/463] Disallow work area save as and browse if not in active task --- client/ayon_core/tools/workfiles/widgets/files_widget.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/tools/workfiles/widgets/files_widget.py b/client/ayon_core/tools/workfiles/widgets/files_widget.py index 16f0b6fce3..c5b5047c83 100644 --- a/client/ayon_core/tools/workfiles/widgets/files_widget.py +++ b/client/ayon_core/tools/workfiles/widgets/files_widget.py @@ -136,6 +136,8 @@ class FilesWidget(QtWidgets.QWidget): # Initial setup workarea_btn_open.setEnabled(False) + workarea_btn_browse.setEnabled(False) + workarea_btn_save.setEnabled(False) published_btn_copy_n_open.setEnabled(False) published_btn_change_context.setEnabled(False) published_btn_cancel.setVisible(False) @@ -264,6 +266,9 @@ class FilesWidget(QtWidgets.QWidget): def _on_workarea_path_changed(self, event): valid_path = event["path"] is not None self._workarea_btn_open.setEnabled(valid_path) + valid_task = self._selected_task_id is not None + self._workarea_btn_save.setEnabled(valid_task) + self._workarea_btn_browse.setEnabled(valid_task) # ------------------------------------------------------------- # Published workfiles From 44da46411c765d92611692e870dd6ef9322bfd1a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 14 Jan 2025 16:18:42 +0100 Subject: [PATCH 266/463] Move logic to correct location --- client/ayon_core/tools/workfiles/widgets/files_widget.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/workfiles/widgets/files_widget.py b/client/ayon_core/tools/workfiles/widgets/files_widget.py index c5b5047c83..dbe5966c31 100644 --- a/client/ayon_core/tools/workfiles/widgets/files_widget.py +++ b/client/ayon_core/tools/workfiles/widgets/files_widget.py @@ -266,9 +266,6 @@ class FilesWidget(QtWidgets.QWidget): def _on_workarea_path_changed(self, event): valid_path = event["path"] is not None self._workarea_btn_open.setEnabled(valid_path) - valid_task = self._selected_task_id is not None - self._workarea_btn_save.setEnabled(valid_task) - self._workarea_btn_browse.setEnabled(valid_task) # ------------------------------------------------------------- # Published workfiles @@ -283,8 +280,9 @@ class FilesWidget(QtWidgets.QWidget): self._published_btn_change_context.setEnabled(enabled) def _update_workarea_btns_state(self): - enabled = self._is_save_enabled + enabled = self._is_save_enabled and self._valid_selected_context self._workarea_btn_save.setEnabled(enabled) + self._workarea_btn_browse.setEnabled(self._valid_selected_context) def _on_published_repre_changed(self, event): self._valid_representation_id = event["representation_id"] is not None @@ -299,6 +297,7 @@ class FilesWidget(QtWidgets.QWidget): and self._selected_task_id is not None ) self._update_published_btns_state() + self._update_workarea_btns_state() def _on_published_save_clicked(self): result = self._exec_save_as_dialog() From 6ed19f26114e6e3f738ec9b86b3a653e91586875 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 14 Jan 2025 17:38:29 +0100 Subject: [PATCH 267/463] Append fix for retiming image sequence. --- client/ayon_core/pipeline/editorial.py | 6 +- .../editorial/resources/img_seq_2x_speed.json | 160 ++++++++ .../resources/img_seq_2x_speed_resolve.json | 369 ++++++++++++++++++ .../test_media_range_with_retimes.py | 60 +++ 4 files changed, 594 insertions(+), 1 deletion(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed.json create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed_resolve.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 0e9ee57bf9..c027d90d47 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -403,9 +403,13 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): rate=src_duration.rate ) + retimed_duration = otio.opentime.RationalTime( + src_duration.value * abs(time_scalar), + src_duration.rate + ) trim_range = otio.opentime.TimeRange( start_time=src_in + offset_in, - duration=src_duration + offset_duration + duration=retimed_duration + offset_duration ) # preserve discrete frame numbers diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed.json new file mode 100644 index 0000000000..80dfa34d4c --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed.json @@ -0,0 +1,160 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 909986.0387191772 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "Speed", + "effect_name": "LinearTimeWarp", + "time_scalar": 2.0 + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_retime_2x/sh010\", \"task\": null, \"clip_index\": \"37BA620A-6580-A543-ADF3-5A7133F41BB6\", \"hierarchy\": \"shots/hiero_retime_2x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_retime_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"c60086c3-9ec3-448a-9bc5-6aa9f6af0fd5\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_retime_2x/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"8cdde735-d5a7-4f95-9cff-ded20ff21135\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 176.0, \"sourceOut\": 196.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_retime_2x/sh010\", \"task\": null, \"clip_index\": \"37BA620A-6580-A543-ADF3-5A7133F41BB6\", \"hierarchy\": \"shots/hiero_retime_2x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_retime_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"c60086c3-9ec3-448a-9bc5-6aa9f6af0fd5\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"8cdde735-d5a7-4f95-9cff-ded20ff21135\", \"label\": \"/shots/hiero_retime_2x/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"064a92fc-5704-4316-8cc9-780e430ae2e5\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_retime_2x/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"37BA620A-6580-A543-ADF3-5A7133F41BB6\"}", + "label": "AYONdata_3c3f54af", + "note": "AYON data container" + }, + "name": "AYONdata_3c3f54af", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "301", + "foundry.source.filename": "output.%07d.exr 948674-948974", + "foundry.source.filesize": "", + "foundry.source.fragments": "301", + "foundry.source.framerate": "25", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%07d.exr 948674-948974", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "948674", + "foundry.source.timecode": "948674", + "foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "301", + "foundry.timeline.framerate": "25", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "1,1,1278,718", + "media.exr.displayWindow": "0,0,1279,719", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2025-01-13 14:26:25", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr", + "media.input.filereader": "exr", + "media.input.filesize": "214941", + "media.input.frame": "1", + "media.input.height": "720", + "media.input.mtime": "2025-01-13 14:26:25", + "media.input.width": "1280", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "b13e3153b31d8f14", + "media.nuke.version": "15.0v5", + "padding": 7 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 301.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 948674.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 948674, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 7, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed_resolve.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed_resolve.json new file mode 100644 index 0000000000..07daaf1548 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_speed_resolve.json @@ -0,0 +1,369 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": { + "Resolve_OTIO": {} + }, + "name": "output.[1001-1099].tif", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 39.0 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "", + "effect_name": "", + "time_scalar": 2.0 + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Transform", + "Enabled": true, + "Name": "Transform", + "Parameters": [], + "Type": 2 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Cropping", + "Enabled": true, + "Name": "Cropping", + "Parameters": [], + "Type": 3 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Dynamic Zoom", + "Enabled": false, + "Name": "Dynamic Zoom", + "Parameters": [ + { + "Default Parameter Value": [ + 0.0, + 0.0 + ], + "Key Frames": { + "-19": { + "Value": [ + 0.0, + 0.0 + ], + "Variant Type": "POINTF" + }, + "981": { + "Value": [ + 0.0, + 0.0 + ], + "Variant Type": "POINTF" + } + }, + "Parameter ID": "dynamicZoomCenter", + "Parameter Value": [ + 0.0, + 0.0 + ], + "Variant Type": "POINTF" + }, + { + "Default Parameter Value": 1.0, + "Key Frames": { + "-19": { + "Value": 0.8, + "Variant Type": "Double" + }, + "981": { + "Value": 1.0, + "Variant Type": "Double" + } + }, + "Parameter ID": "dynamicZoomScale", + "Parameter Value": 1.0, + "Variant Type": "Double", + "maxValue": 100.0, + "minValue": 0.01 + } + ], + "Type": 59 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Composite", + "Enabled": true, + "Name": "Composite", + "Parameters": [], + "Type": 1 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Lens Correction", + "Enabled": true, + "Name": "Lens Correction", + "Parameters": [], + "Type": 43 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Retime and Scaling", + "Enabled": true, + "Name": "Retime and Scaling", + "Parameters": [], + "Type": 22 + } + }, + "name": "", + "effect_name": "Resolve Effect" + }, + { + "OTIO_SCHEMA": "Effect.1", + "metadata": { + "Resolve_OTIO": { + "Effect Name": "Video Faders", + "Enabled": true, + "Name": "Video Faders", + "Parameters": [], + "Type": 36 + } + }, + "name": "", + "effect_name": "Resolve Effect" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "Resolve_OTIO": { + "Keywords": [], + "Note": "{\"resolve_sub_products\": {\"io.ayon.creators.resolve.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.resolve.shot\", \"variant\": \"Main\", \"folderPath\": \"/shots/resolve_2x/sh010\", \"task\": null, \"clip_variant\": \"\", \"clip_index\": \"51983d2a-8a54-45fc-b17d-b837bdcb2545\", \"clip_source_resolution\": {\"width\": \"1920\", \"height\": \"1080\", \"pixelAspect\": 1.0}, \"folder\": \"/shots/resolve_2x/sh010\", \"episode\": \"ep01\", \"sequence\": \"resolve_2x\", \"track\": \"{_track_}\", \"shot\": \"sh###\", \"hierarchy\": \"shots/resolve_2x\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"folder_type\": \"sequence\", \"entity_name\": \"resolve_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"resolve_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\"}, \"heroTrack\": true, \"uuid\": \"04cd97b0-7e6e-4f58-b8b1-5f1956d53bfb\", \"reviewTrack\": \"Video 1\", \"label\": \"/shots/resolve_2x/sh010 shot\", \"has_promised_context\": true, \"newHierarchyIntegration\": true, \"newAssetPublishing\": true, \"instance_id\": \"cc8b970c-69c1-4eab-b94f-ae41358a80ba\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 86400, \"clipOut\": 86411, \"clipDuration\": 11, \"sourceIn\": 19, \"sourceOut\": 30, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.resolve.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.resolve.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/resolve_2x/sh010\", \"task\": null, \"clip_variant\": \"\", \"clip_index\": \"51983d2a-8a54-45fc-b17d-b837bdcb2545\", \"clip_source_resolution\": {\"width\": \"1920\", \"height\": \"1080\", \"pixelAspect\": 1.0}, \"folder\": \"/shots/resolve_2x/sh010\", \"episode\": \"ep01\", \"sequence\": \"resolve_2x\", \"track\": \"{_track_}\", \"shot\": \"sh###\", \"hierarchy\": \"shots/resolve_2x\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"folder_type\": \"sequence\", \"entity_name\": \"resolve_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"resolve_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\"}, \"heroTrack\": true, \"uuid\": \"04cd97b0-7e6e-4f58-b8b1-5f1956d53bfb\", \"reviewTrack\": \"Video 1\", \"parent_instance_id\": \"cc8b970c-69c1-4eab-b94f-ae41358a80ba\", \"label\": \"/shots/resolve_2x/sh010 plate\", \"has_promised_context\": true, \"newHierarchyIntegration\": true, \"newAssetPublishing\": true, \"instance_id\": \"564ef731-c518-4c8f-918d-b27d0c35856c\", \"creator_attributes\": {\"parentInstance\": \"/shots/resolve_2x/sh010 shot\", \"vSyncOn\": false, \"vSyncTrack\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"51983d2a-8a54-45fc-b17d-b837bdcb2545\", \"publish\": true}" + }, + "clip_index": "51983d2a-8a54-45fc-b17d-b837bdcb2545", + "publish": true, + "resolve_sub_products": { + "io.ayon.creators.resolve.plate": { + "active": true, + "clip_index": "51983d2a-8a54-45fc-b17d-b837bdcb2545", + "clip_source_resolution": { + "height": "1080", + "pixelAspect": 1.0, + "width": "1920" + }, + "clip_variant": "", + "creator_attributes": { + "parentInstance": "/shots/resolve_2x/sh010 shot", + "vSyncOn": false, + "vSyncTrack": "Video 1" + }, + "creator_identifier": "io.ayon.creators.resolve.plate", + "episode": "ep01", + "folder": "/shots/resolve_2x/sh010", + "folderPath": "/shots/resolve_2x/sh010", + "handleEnd": 10, + "handleStart": 10, + "has_promised_context": true, + "heroTrack": true, + "hierarchy": "shots/resolve_2x", + "hierarchyData": { + "episode": "ep01", + "folder": "shots", + "sequence": "resolve_2x", + "shot": "sh010", + "track": "Video_1" + }, + "id": "pyblish.avalon.instance", + "instance_id": "564ef731-c518-4c8f-918d-b27d0c35856c", + "label": "/shots/resolve_2x/sh010 plate", + "newAssetPublishing": true, + "newHierarchyIntegration": true, + "parent_instance_id": "cc8b970c-69c1-4eab-b94f-ae41358a80ba", + "parents": [ + { + "entity_name": "shots", + "folder_type": "folder" + }, + { + "entity_name": "resolve_2x", + "folder_type": "sequence" + } + ], + "productName": "plateVideo_1", + "productType": "plate", + "publish_attributes": { + "CollectSlackFamilies": { + "additional_message": "" + } + }, + "reviewTrack": "Video 1", + "sequence": "resolve_2x", + "shot": "sh###", + "sourceResolution": false, + "task": null, + "track": "{_track_}", + "uuid": "04cd97b0-7e6e-4f58-b8b1-5f1956d53bfb", + "variant": "Video_1", + "workfileFrameStart": 1001 + }, + "io.ayon.creators.resolve.shot": { + "active": true, + "clip_index": "51983d2a-8a54-45fc-b17d-b837bdcb2545", + "clip_source_resolution": { + "height": "1080", + "pixelAspect": 1.0, + "width": "1920" + }, + "clip_variant": "", + "creator_attributes": { + "clipDuration": 11, + "clipIn": 86400, + "clipOut": 86411, + "fps": "from_selection", + "frameEnd": 1012, + "frameStart": 1001, + "handleEnd": 10, + "handleStart": 10, + "sourceIn": 19, + "sourceOut": 30, + "workfileFrameStart": 1001 + }, + "creator_identifier": "io.ayon.creators.resolve.shot", + "episode": "ep01", + "folder": "/shots/resolve_2x/sh010", + "folderPath": "/shots/resolve_2x/sh010", + "handleEnd": 10, + "handleStart": 10, + "has_promised_context": true, + "heroTrack": true, + "hierarchy": "shots/resolve_2x", + "hierarchyData": { + "episode": "ep01", + "folder": "shots", + "sequence": "resolve_2x", + "shot": "sh010", + "track": "Video_1" + }, + "id": "pyblish.avalon.instance", + "instance_id": "cc8b970c-69c1-4eab-b94f-ae41358a80ba", + "label": "/shots/resolve_2x/sh010 shot", + "newAssetPublishing": true, + "newHierarchyIntegration": true, + "parents": [ + { + "entity_name": "shots", + "folder_type": "folder" + }, + { + "entity_name": "resolve_2x", + "folder_type": "sequence" + } + ], + "productName": "shotMain", + "productType": "shot", + "publish_attributes": { + "CollectSlackFamilies": { + "additional_message": "" + } + }, + "reviewTrack": "Video 1", + "sequence": "resolve_2x", + "shot": "sh###", + "sourceResolution": false, + "task": null, + "track": "{_track_}", + "uuid": "04cd97b0-7e6e-4f58-b8b1-5f1956d53bfb", + "variant": "Main", + "workfileFrameStart": 1001 + } + } + }, + "name": "AYONData", + "color": "GREEN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 1.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 24.0 + } + }, + "comment": "" + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": {}, + "name": "output.[1001-1099].tif", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 99.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:\\Users\\robin\\OneDrive\\Bureau\\dev_ayon\\data\\img_sequence\\tif", + "name_prefix": "output.", + "name_suffix": ".tif", + "start_frame": 1001, + "frame_step": 1, + "rate": 24.0, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 2e7193444c..da257eca58 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -291,3 +291,63 @@ def test_img_sequence_reverse_speed_from_24_to_23_976fps(): handle_start=10, handle_end=10, ) + + +def test_img_sequence_2x_speed(): + """ + Img sequence clip + available files = 948674-948974 25fps + source_range = 948850-948870 23.976fps + speed = 2.0 + """ + expected_data = { + 'mediaIn': 948850, + 'mediaOut': 948871, + 'handleStart': 20, + 'handleEnd': 20, + 'speed': 2.0, + 'versionData': { + 'retime': True, + 'speed': 2.0, + 'timewarps': [], + 'handleStart': 20, + 'handleEnd': 20 + } + } + + _check_expected_retimed_values( + "img_seq_2x_speed.json", + expected_data, + handle_start=10, + handle_end=10, + ) + + +def test_img_sequence_2x_speed_resolve(): + """ + Img sequence clip + available files = 0-99 24fps + source_range = 38-49 24fps + speed = 2.0 + """ + expected_data = { + 'mediaIn': 1040, + 'mediaOut': 1061, + 'handleStart': 20, + 'handleEnd': 20, + 'speed': 2.0, + 'versionData': { + 'retime': True, + 'speed': 2.0, + 'timewarps': [], + 'handleStart': 20, + 'handleEnd': 20 + } + } + + _check_expected_retimed_values( + "img_seq_2x_speed_resolve.json", + expected_data, + handle_start=10, + handle_end=10, + ) From 76c7d90c8c1c8c1eeb81c893adead84f6be5f7c7 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 14 Jan 2025 18:15:01 +0100 Subject: [PATCH 268/463] Add fix + test for image sequence frozen frame. --- client/ayon_core/pipeline/editorial.py | 8 +- .../resources/img_seq_freeze_frame.json | 160 ++++++++++++++++++ .../test_media_range_with_retimes.py | 31 ++++ 3 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_freeze_frame.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index c027d90d47..9a241087f6 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -256,8 +256,14 @@ def remap_range_on_file_sequence(otio_clip, otio_range): rate=available_range_rate, ).to_frames() + # e.g.: + # duration = 10 frames at 24fps + # if frame_in = 1001 then + # frame_out = 1010 + offset_duration = max(0, otio_range.duration.to_frames() - 1) + frame_out = otio.opentime.RationalTime.from_frames( - frame_in + otio_range.duration.to_frames() - 1, + frame_in + offset_duration, rate=available_range_rate, ).to_frames() diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_freeze_frame.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_freeze_frame.json new file mode 100644 index 0000000000..05b48370b2 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_freeze_frame.json @@ -0,0 +1,160 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 5.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 909990.8339241028 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "FreezeFrame.1", + "metadata": {}, + "name": "FreezeFrame", + "effect_name": "FreezeFrame", + "time_scalar": 0.0 + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_freeze_frame/sh010\", \"task\": null, \"clip_index\": \"85ABEEEA-6A90-CE47-9DE2-73BAB11EE31D\", \"hierarchy\": \"shots/hiero_freeze_frame\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_freeze_frame\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_freeze_frame\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_freeze_frame\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"08ba1c0a-fc51-4275-b6c8-1cb81381b043\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_freeze_frame/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"892de813-fc78-4d92-b25f-4ea5c4791bb8\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1006, \"clipIn\": 0, \"clipOut\": 4, \"clipDuration\": 5, \"sourceIn\": 181.0, \"sourceOut\": 181.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_freeze_frame/sh010\", \"task\": null, \"clip_index\": \"85ABEEEA-6A90-CE47-9DE2-73BAB11EE31D\", \"hierarchy\": \"shots/hiero_freeze_frame\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_freeze_frame\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_freeze_frame\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_freeze_frame\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"08ba1c0a-fc51-4275-b6c8-1cb81381b043\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"892de813-fc78-4d92-b25f-4ea5c4791bb8\", \"label\": \"/shots/hiero_freeze_frame/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"24eb8386-4c42-4439-ac41-17ec4efb0073\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_freeze_frame/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"85ABEEEA-6A90-CE47-9DE2-73BAB11EE31D\"}", + "label": "AYONdata_a8304fcf", + "note": "AYON data container" + }, + "name": "AYONdata_a8304fcf", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "301", + "foundry.source.filename": "output.%07d.exr 948674-948974", + "foundry.source.filesize": "", + "foundry.source.fragments": "301", + "foundry.source.framerate": "25", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%07d.exr 948674-948974", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "948674", + "foundry.source.timecode": "948674", + "foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "301", + "foundry.timeline.framerate": "25", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "1,1,1278,718", + "media.exr.displayWindow": "0,0,1279,719", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2025-01-13 14:26:25", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr", + "media.input.filereader": "exr", + "media.input.filesize": "214941", + "media.input.frame": "1", + "media.input.height": "720", + "media.input.mtime": "2025-01-13 14:26:25", + "media.input.width": "1280", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "b13e3153b31d8f14", + "media.nuke.version": "15.0v5", + "padding": 7 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 301.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 948674.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 948674, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 7, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index da257eca58..1041dfe4dd 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -351,3 +351,34 @@ def test_img_sequence_2x_speed_resolve(): handle_start=10, handle_end=10, ) + + +def test_img_sequence_frozen_frame(): + """ + Img sequence clip + available files = 948674-948974 25fps + source_range = 909990.8339241028 + - 909995.8339241028 23.976fps + speed = 0.0 + """ + expected_data = { + 'mediaIn': 948855, + 'mediaOut': 948855, + 'handleStart': 0, + 'handleEnd': 0, + 'speed': 0.0, + 'versionData': { + 'retime': True, + 'speed': 0.0, + 'timewarps': [], + 'handleStart': 0, + 'handleEnd': 0, + } + } + + _check_expected_retimed_values( + "img_seq_freeze_frame.json", + expected_data, + handle_start=10, + handle_end=10, + ) From 1123d31750260fef569df2217990a7daa8157956 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 15 Jan 2025 11:18:23 +0100 Subject: [PATCH 269/463] Add fixes and tests for qt retime and frozen frame. --- client/ayon_core/pipeline/editorial.py | 13 +- .../publish/collect_otio_subset_resources.py | 14 +- .../editorial/resources/qt_freeze_frame.json | 159 +++++++++++++++++ .../resources/qt_reverse_speed_2x.json | 160 ++++++++++++++++++ .../test_media_range_with_retimes.py | 64 +++++++ 5 files changed, 400 insertions(+), 10 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/qt_freeze_frame.json create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_2x.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 9a241087f6..f5a876b5ba 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -429,13 +429,12 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): else: # compute retimed range media_in_trimmed = conformed_source_range.start_time.value + offset_in - media_out_trimmed = media_in_trimmed + ( - ( - conformed_source_range.duration.value - * abs(time_scalar) - + offset_out - ) - 1 - ) + + offset_duration = conformed_source_range.duration.value * abs(time_scalar) + offset_duration += offset_out + offset_duration -= 1 # duration 1 frame -> freeze frame -> end = start + 0 + offset_duration = max(0, offset_duration) # negative duration = frozen frame + media_out_trimmed = media_in_trimmed + offset_duration media_in = available_range.start_time.value media_out = available_range.end_time_inclusive().value diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index 0fb30326c6..ed7e8ac4f1 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -174,9 +174,17 @@ class CollectOtioSubsetResources( path, trimmed_media_range_h, metadata) self.staging_dir, collection = collection_data - self.log.debug(collection) - repre = self._create_representation( - frame_start, frame_end, collection=collection) + if len(collection.indexes) > 1: + self.log.debug(collection) + repre = self._create_representation( + frame_start, frame_end, collection=collection) + else: + filename, = tuple(collection) + self.log.debug(filename) + + # TODO: discuss this, it erases frame number. + repre = self._create_representation( + frame_start, frame_end, file=filename) if ( not instance.data.get("otioReviewClips") diff --git a/tests/client/ayon_core/pipeline/editorial/resources/qt_freeze_frame.json b/tests/client/ayon_core/pipeline/editorial/resources/qt_freeze_frame.json new file mode 100644 index 0000000000..de665eaea7 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/qt_freeze_frame.json @@ -0,0 +1,159 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 29.970030784606934 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "FreezeFrame.1", + "metadata": {}, + "name": "FreezeFrame", + "effect_name": "FreezeFrame", + "time_scalar": 0.0 + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_qt_freeze_frame/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_freeze_frame\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_freeze_frame\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_qt_freeze_frame\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_freeze_frame\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"e896c630-8c44-408f-a1a0-bffbe330dbe9\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_qt_freeze_frame/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"75b9112f-6357-4235-8a74-252467d6553d\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 30.0, \"sourceOut\": 30.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_qt_freeze_frame/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_freeze_frame\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_freeze_frame\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_qt_freeze_frame\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_freeze_frame\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"e896c630-8c44-408f-a1a0-bffbe330dbe9\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"75b9112f-6357-4235-8a74-252467d6553d\", \"label\": \"/shots/hiero_qt_freeze_frame/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"95912bf0-aa1c-47ae-a821-fef410c32687\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_qt_freeze_frame/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\"}", + "label": "AYONdata_e681ec48", + "note": "AYON data container" + }, + "name": "AYONdata_e681ec48", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Gamma2.2", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "error", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "default (Rec 709)", + "com.apple.quicktime.codec": "H.264", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "Gamma2.2", + "foundry.source.duration": "101", + "foundry.source.filename": "qt_no_tc_24fps.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "24", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float32) Open Color IO space: 114", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shoottime": "4294967295", + "foundry.source.shortfilename": "qt_no_tc_24fps.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "0", + "foundry.source.type": "QuickTime H.264", + "foundry.source.umid": "16634e88-6450-4727-6c6e-501f4b31b637", + "foundry.source.width": "1920", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Gamma2.2", + "foundry.timeline.duration": "101", + "foundry.timeline.framerate": "24", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.samplerate": "Invalid", + "media.input.bitsperchannel": "8-bit fixed", + "media.input.ctime": "2024-09-25 17:16:12", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "media.input.filereader": "mov64", + "media.input.filesize": "14631252", + "media.input.frame": "1", + "media.input.frame_rate": "24", + "media.input.height": "1080", + "media.input.mtime": "2024-09-25 17:16:16", + "media.input.pixel_aspect": "1", + "media.input.timecode": "00:00:00:00", + "media.input.width": "1920", + "media.quicktime.codec_id": "avc1", + "media.quicktime.codec_name": "h264", + "media.quicktime.encoder": "H.264", + "media.quicktime.nclc_matrix": "BT709", + "media.quicktime.nclc_primaries": "ITU-R BT.709", + "media.quicktime.nclc_transfer_function": "ITU-R BT.709", + "media.quicktime.thefoundry.Application": "Nuke", + "media.quicktime.thefoundry.ApplicationVersion": "15.0v5", + "media.quicktime.thefoundry.Colorspace": "Gamma2.2", + "media.quicktime.thefoundry.Writer": "mov64", + "media.quicktime.thefoundry.YCbCrMatrix": "Rec 709", + "media.stream.pixel_format": "yuv420p", + "uk.co.thefoundry.Application": "Nuke", + "uk.co.thefoundry.ApplicationVersion": "15.0v5", + "uk.co.thefoundry.Colorspace": "Gamma2.2", + "uk.co.thefoundry.Writer": "mov64", + "uk.co.thefoundry.YCbCrMatrix": "Rec 709" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 101.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_2x.json b/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_2x.json new file mode 100644 index 0000000000..467bb8d7a1 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_2x.json @@ -0,0 +1,160 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 29.970030784606934 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "Speed", + "effect_name": "LinearTimeWarp", + "time_scalar": -2.0 + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_qt_neg_2x/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_neg_2x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_qt_neg_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg_2x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"a59e3db6-0b60-41f5-827c-9d280547bf31\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_qt_neg_2x/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"f98ac652-ed03-4985-bad9-5b028ceeddba\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 50.0, \"sourceOut\": 30.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_qt_neg_2x/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_neg_2x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_qt_neg_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg_2x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"a59e3db6-0b60-41f5-827c-9d280547bf31\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"f98ac652-ed03-4985-bad9-5b028ceeddba\", \"label\": \"/shots/hiero_qt_neg_2x/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"5c9c047c-43fa-42a1-a00f-e9c9d6e5a3c4\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_qt_neg_2x/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\"}", + "label": "AYONdata_fd6d196e", + "note": "AYON data container" + }, + "name": "AYONdata_fd6d196e", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Gamma2.2", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "error", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "H.264", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "Gamma2.2", + "foundry.source.duration": "101", + "foundry.source.filename": "qt_no_tc_24fps.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "24", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float32) Open Color IO space: 114", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shoottime": "4294967295", + "foundry.source.shortfilename": "qt_no_tc_24fps.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "0", + "foundry.source.type": "QuickTime H.264", + "foundry.source.umid": "16634e88-6450-4727-6c6e-501f4b31b637", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1920", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Gamma2.2", + "foundry.timeline.duration": "101", + "foundry.timeline.framerate": "24", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.samplerate": "Invalid", + "media.input.bitsperchannel": "8-bit fixed", + "media.input.ctime": "2024-09-25 17:16:12", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "media.input.filereader": "mov64", + "media.input.filesize": "14631252", + "media.input.frame": "1", + "media.input.frame_rate": "24", + "media.input.height": "1080", + "media.input.mtime": "2024-09-25 17:16:16", + "media.input.pixel_aspect": "1", + "media.input.timecode": "00:00:00:00", + "media.input.width": "1920", + "media.quicktime.codec_id": "avc1", + "media.quicktime.codec_name": "h264", + "media.quicktime.encoder": "H.264", + "media.quicktime.nclc_matrix": "BT709", + "media.quicktime.nclc_primaries": "ITU-R BT.709", + "media.quicktime.nclc_transfer_function": "ITU-R BT.709", + "media.quicktime.thefoundry.Application": "Nuke", + "media.quicktime.thefoundry.ApplicationVersion": "15.0v5", + "media.quicktime.thefoundry.Colorspace": "Gamma2.2", + "media.quicktime.thefoundry.Writer": "mov64", + "media.quicktime.thefoundry.YCbCrMatrix": "Rec 709", + "media.stream.pixel_format": "yuv420p", + "uk.co.thefoundry.Application": "Nuke", + "uk.co.thefoundry.ApplicationVersion": "15.0v5", + "uk.co.thefoundry.Colorspace": "Gamma2.2", + "uk.co.thefoundry.Writer": "mov64", + "uk.co.thefoundry.YCbCrMatrix": "Rec 709" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 101.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 1041dfe4dd..d4badd8345 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -113,6 +113,70 @@ def test_movie_retime_effect(): ) +def test_movie_reverse_speed_2x(): + """ + Movie clip (no timecode) + available files = 0-100 24fps + source_range = 29.97-40.97 23.976fps + speed = -2.0 + """ + expected_data = { + # not exactly 30 because of 23.976 rouding + 'mediaIn': 30.000000000000004, + # not exactly 50 because of 23.976 rouding + 'mediaOut': 51.02199940144827, + 'handleStart': 20, + 'handleEnd': 20, + 'speed': -2.0, + 'versionData': { + 'retime': True, + 'speed': -2.0, + 'timewarps': [], + 'handleStart': 20, + 'handleEnd': 20, + } + } + + _check_expected_retimed_values( + "qt_reverse_speed_2x.json", + expected_data, + handle_start=10, + handle_end=10, + ) + + + +def test_movie_frozen_frame(): + """ + Movie clip (no timecode) + available files = 0-100 24fps + source_range = 29.97-40.97 23.976fps + speed = 0.0 + """ + expected_data = { + # not exactly 30 because of OTIO rounding + 'mediaIn': 30.000000000000004, + 'mediaOut': 30.000000000000004, + 'handleStart': 0, + 'handleEnd': 0, + 'speed': 0.0, + 'versionData': { + 'retime': True, + 'speed': 0.0, + 'timewarps': [], + 'handleStart': 0, + 'handleEnd': 0, + } + } + + _check_expected_retimed_values( + "qt_freeze_frame.json", + expected_data, + handle_start=10, + handle_end=10, + ) + + def test_img_sequence_no_handles(): """ Img sequence clip (no embedded timecode) From 704ef232b6c20ac7f0767f0a997e11092986eb94 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 15 Jan 2025 13:20:22 +0100 Subject: [PATCH 270/463] Edit link to OTIO issue. --- .../pipeline/editorial/test_media_range_with_retimes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index d4badd8345..c045379806 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -122,8 +122,9 @@ def test_movie_reverse_speed_2x(): """ expected_data = { # not exactly 30 because of 23.976 rouding + # https://github.com/AcademySoftwareFoundation/ + # OpenTimelineIO/issues/1822 'mediaIn': 30.000000000000004, - # not exactly 50 because of 23.976 rouding 'mediaOut': 51.02199940144827, 'handleStart': 20, 'handleEnd': 20, @@ -155,6 +156,8 @@ def test_movie_frozen_frame(): """ expected_data = { # not exactly 30 because of OTIO rounding + # https://github.com/AcademySoftwareFoundation/ + # OpenTimelineIO/issues/1822 'mediaIn': 30.000000000000004, 'mediaOut': 30.000000000000004, 'handleStart': 0, From b185972a954f7111591576b90ee3436a7b775bf2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 15 Jan 2025 14:02:20 +0100 Subject: [PATCH 271/463] Avoid database queries when collecting managed staging dir --- client/ayon_core/pipeline/publish/lib.py | 1 + client/ayon_core/pipeline/staging_dir.py | 5 ++++- client/ayon_core/pipeline/template_data.py | 13 ++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 40a9b47aba..25495ed38b 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -708,6 +708,7 @@ def get_instance_staging_dir(instance): project_settings=context.data["project_settings"], template_data=template_data, always_return_path=True, + username=context.data["user"], ) staging_dir_path = staging_dir_info.directory diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 37d6b955e2..2004096bd0 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -130,6 +130,7 @@ def get_staging_dir_info( logger: Optional[logging.Logger] = None, prefix: Optional[str] = None, suffix: Optional[str] = None, + username: Optional[str] = None, ) -> Optional[StagingDir]: """Get staging dir info data. @@ -183,7 +184,9 @@ def get_staging_dir_info( # making few queries to database ctx_data = get_template_data( - project_entity, folder_entity, task_entity, host_name + project_entity, folder_entity, task_entity, host_name, + settings=project_settings, + username=username ) # add additional data diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index c7aa46fd62..0a95a98be8 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -4,7 +4,7 @@ from ayon_core.settings import get_studio_settings from ayon_core.lib.local_settings import get_ayon_username -def get_general_template_data(settings=None): +def get_general_template_data(settings=None, username=None): """General template data based on system settings or machine. Output contains formatting keys: @@ -14,17 +14,22 @@ def get_general_template_data(settings=None): Args: settings (Dict[str, Any]): Studio or project settings. + username (Optional[str]): AYON Username. """ if not settings: settings = get_studio_settings() + + if username is None: + username = get_ayon_username() + core_settings = settings["core"] return { "studio": { "name": core_settings["studio_name"], "code": core_settings["studio_code"] }, - "user": get_ayon_username() + "user": username } @@ -145,6 +150,7 @@ def get_template_data( task_entity=None, host_name=None, settings=None, + username=None ): """Prepare data for templates filling from entered documents and info. @@ -167,12 +173,13 @@ def get_template_data( host_name (Optional[str]): Used to fill '{app}' key. settings (Union[Dict, None]): Prepared studio or project settings. They're queried if not passed (may be slower). + username (Optional[str]): AYON Username. Returns: Dict[str, Any]: Data prepared for filling workdir template. """ - template_data = get_general_template_data(settings) + template_data = get_general_template_data(settings, username=username) template_data.update(get_project_template_data(project_entity)) if folder_entity: template_data.update(get_folder_template_data( From 8351395a8279f09d08e1ec0097bb39402882ab8f Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 15 Jan 2025 18:43:02 +0100 Subject: [PATCH 272/463] Consolidate timewarp computation and add tests. --- client/ayon_core/pipeline/editorial.py | 48 +++- .../resources/img_seq_2x_time_warp.json | 181 ++++++++++++++ .../resources/img_seq_multiple_tws.json | 216 ++++++++++++++++ .../resources/img_seq_tw_beyond_range.json | 174 +++++++++++++ .../editorial/resources/qt_timewarp.json | 174 +++++++++++++ .../test_media_range_with_retimes.py | 235 ++++++++++++++++++ 6 files changed, 1015 insertions(+), 13 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_time_warp.json create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_multiple_tws.json create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_tw_beyond_range.json create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/qt_timewarp.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index f5a876b5ba..ed1cdf9974 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -375,10 +375,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): tw_node.update(metadata) tw_node["lookup"] = list(lookup) - # get first and last frame offsets - offset_in += lookup[0] - offset_out += lookup[-1] - # add to timewarp nodes time_warp_nodes.append(tw_node) @@ -403,19 +399,14 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): src_in = conformed_source_range.start_time src_duration = conformed_source_range.duration - offset_in = otio.opentime.RationalTime(offset_in, rate=src_in.rate) - offset_duration = otio.opentime.RationalTime( - offset_out, - rate=src_duration.rate - ) - retimed_duration = otio.opentime.RationalTime( src_duration.value * abs(time_scalar), src_duration.rate ) trim_range = otio.opentime.TimeRange( - start_time=src_in + offset_in, - duration=retimed_duration + offset_duration + start_time=src_in, + duration=retimed_duration, + ) # preserve discrete frame numbers @@ -431,7 +422,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): media_in_trimmed = conformed_source_range.start_time.value + offset_in offset_duration = conformed_source_range.duration.value * abs(time_scalar) - offset_duration += offset_out offset_duration -= 1 # duration 1 frame -> freeze frame -> end = start + 0 offset_duration = max(0, offset_duration) # negative duration = frozen frame media_out_trimmed = media_in_trimmed + offset_duration @@ -439,6 +429,38 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): media_in = available_range.start_time.value media_out = available_range.end_time_inclusive().value + if time_warp_nodes: + # Naive approach: Resolve consecutive timewarp(s) on range, + # then check if plate range has to be extended beyond source range. + frame_range = [media_in_trimmed] + in_frame = media_in_trimmed + time_scalar + while in_frame <= media_out_trimmed: + frame_range.append(in_frame) + in_frame += time_scalar + + for tw_idx, tw in enumerate(time_warp_nodes): + for idx, frame_number in enumerate(frame_range): + # First timewarp, apply on media range + if tw_idx == 0: + frame_range[idx] = round(frame_number + tw["lookup"][idx] * time_scalar) + # Consecutive timewarp, apply on the previous result + else: + new_idx = round(idx + tw["lookup"][idx]) + + if not 0 <= new_idx < len(frame_range): + # TODO: implementing this would need to actually have + # retiming engine resolve process within AYON, resolving wraps + # as curves, then projecting those into the previous media_range. + raise NotImplementedError( + "Unsupported consecutive timewarps (out of computed range)" + ) + + frame_range[idx] = frame_range[new_idx] + + # adjust range if needed + media_in_trimmed = min(media_in_trimmed, min(frame_range)) + media_out_trimmed = max(media_out_trimmed, max(frame_range)) + # adjust available handles if needed if (media_in_trimmed - media_in) < handle_start: handle_start = max(0, media_in_trimmed - media_in) diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_time_warp.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_time_warp.json new file mode 100644 index 0000000000..0876dcd179 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_2x_time_warp.json @@ -0,0 +1,181 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 909986.0387191772 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "Speed", + "effect_name": "LinearTimeWarp", + "time_scalar": 2.0 + }, + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 4.0, + "lookup": [ + 2.0, + 1.7039999923706057, + 1.431999991416931, + 1.2079999942779533, + 1.055999998092652, + 1.0, + 1.056000007629395, + 1.208000022888184, + 1.432000034332276, + 1.7040000305175766, + 2.0 + ] + }, + "name": "TimeWarp6", + "effect_name": "TimeWarp" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_img_seq_tw_speed/sh010\", \"task\": null, \"clip_index\": \"699C12C3-07B7-E74E-A8BC-07554560B91E\", \"hierarchy\": \"shots/hiero_img_seq_tw_speed\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_img_seq_tw_speed\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 0, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_img_seq_tw_speed\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_img_seq_tw_speed\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"731977d8-6f06-415d-9086-b04b58a16ce3\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_img_seq_tw_speed/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"d157ce1c-3157-4a34-a8b5-14c881387239\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 0, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 176.0, \"sourceOut\": 196.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_img_seq_tw_speed/sh010\", \"task\": null, \"clip_index\": \"699C12C3-07B7-E74E-A8BC-07554560B91E\", \"hierarchy\": \"shots/hiero_img_seq_tw_speed\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_img_seq_tw_speed\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 0, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_img_seq_tw_speed\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_img_seq_tw_speed\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"731977d8-6f06-415d-9086-b04b58a16ce3\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"parent_instance_id\": \"d157ce1c-3157-4a34-a8b5-14c881387239\", \"label\": \"/shots/hiero_img_seq_tw_speed/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"daf5d8e4-5698-4a41-90eb-05eea2992dff\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_img_seq_tw_speed/sh010 shotMain\", \"review\": false, \"reviewableSource\": \"clip_media\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"699C12C3-07B7-E74E-A8BC-07554560B91E\"}", + "label": "AYONdata_9f37cdbf", + "note": "AYON data container" + }, + "name": "AYONdata_9f37cdbf", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "301", + "foundry.source.filename": "output.%07d.exr 948674-948974", + "foundry.source.filesize": "", + "foundry.source.fragments": "301", + "foundry.source.framerate": "25", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%07d.exr 948674-948974", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "948674", + "foundry.source.timecode": "948674", + "foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "301", + "foundry.timeline.framerate": "25", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "1,1,1278,718", + "media.exr.displayWindow": "0,0,1279,719", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2025-01-13 14:26:25", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr", + "media.input.filereader": "exr", + "media.input.filesize": "214941", + "media.input.frame": "1", + "media.input.height": "720", + "media.input.mtime": "2025-01-13 14:26:25", + "media.input.width": "1280", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "b13e3153b31d8f14", + "media.nuke.version": "15.0v5", + "padding": 7 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 301.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 948674.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 948674, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 7, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_multiple_tws.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_multiple_tws.json new file mode 100644 index 0000000000..88f2dbc86c --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_multiple_tws.json @@ -0,0 +1,216 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 909986.0387191772 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 1.0, + "lookup": [ + -5.0, + -3.9440000305175777, + -2.852000034332275, + -1.6880000228881844, + -0.4160000076293944, + 1.0, + 2.5839999923706056, + 4.311999977111817, + 6.147999965667726, + 8.055999969482421, + 10.0 + ] + }, + "name": "TimeWarp3", + "effect_name": "TimeWarp" + }, + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 1.0, + "lookup": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "name": "TimeWarp4", + "effect_name": "TimeWarp" + }, + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 1.0, + "lookup": [ + 0.0, + -1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0 + ] + }, + "name": "TimeWarp5", + "effect_name": "TimeWarp" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_seq_max_tw/sh010\", \"task\": null, \"clip_index\": \"4C055A68-8354-474A-A6F8-B0CBF9A537CD\", \"hierarchy\": \"shots/hiero_seq_max_tw\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_seq_max_tw\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"5e82a346-17c4-4ccb-a795-35e1a809b243\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_seq_max_tw/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"9cb2a119-8aa6-487e-a46b-9b9ff25323be\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 176.0, \"sourceOut\": 186.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_seq_max_tw/sh010\", \"task\": null, \"clip_index\": \"4C055A68-8354-474A-A6F8-B0CBF9A537CD\", \"hierarchy\": \"shots/hiero_seq_max_tw\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_seq_max_tw\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"5e82a346-17c4-4ccb-a795-35e1a809b243\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"parent_instance_id\": \"9cb2a119-8aa6-487e-a46b-9b9ff25323be\", \"label\": \"/shots/hiero_seq_max_tw/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"771e41ed-74b0-4fcc-882c-6a248d45a464\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_seq_max_tw/sh010 shotMain\", \"review\": false, \"reviewableSource\": \"clip_media\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"4C055A68-8354-474A-A6F8-B0CBF9A537CD\"}", + "label": "AYONdata_b6896763", + "note": "AYON data container" + }, + "name": "AYONdata_b6896763", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "301", + "foundry.source.filename": "output.%07d.exr 948674-948974", + "foundry.source.filesize": "", + "foundry.source.fragments": "301", + "foundry.source.framerate": "25", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%07d.exr 948674-948974", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "948674", + "foundry.source.timecode": "948674", + "foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "301", + "foundry.timeline.framerate": "25", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "1,1,1278,718", + "media.exr.displayWindow": "0,0,1279,719", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2025-01-13 14:26:25", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr", + "media.input.filereader": "exr", + "media.input.filesize": "214941", + "media.input.frame": "1", + "media.input.height": "720", + "media.input.mtime": "2025-01-13 14:26:25", + "media.input.width": "1280", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "b13e3153b31d8f14", + "media.nuke.version": "15.0v5", + "padding": 7 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 301.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 948674.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 948674, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 7, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_tw_beyond_range.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_tw_beyond_range.json new file mode 100644 index 0000000000..1a753098d7 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_tw_beyond_range.json @@ -0,0 +1,174 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 909986.0387191772 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 1.0, + "lookup": [ + -5.0, + -3.9440000305175777, + -2.852000034332275, + -1.6880000228881844, + -0.4160000076293944, + 1.0, + 2.5839999923706056, + 4.311999977111817, + 6.147999965667726, + 8.055999969482421, + 10.0 + ] + }, + "name": "TimeWarp3", + "effect_name": "TimeWarp" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_seq_max_tw/sh010\", \"task\": null, \"clip_index\": \"4C055A68-8354-474A-A6F8-B0CBF9A537CD\", \"hierarchy\": \"shots/hiero_seq_max_tw\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_seq_max_tw\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"5e82a346-17c4-4ccb-a795-35e1a809b243\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_seq_max_tw/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"9cb2a119-8aa6-487e-a46b-9b9ff25323be\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 176.0, \"sourceOut\": 186.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_seq_max_tw/sh010\", \"task\": null, \"clip_index\": \"4C055A68-8354-474A-A6F8-B0CBF9A537CD\", \"hierarchy\": \"shots/hiero_seq_max_tw\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_seq_max_tw\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_max_tw\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"5e82a346-17c4-4ccb-a795-35e1a809b243\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"parent_instance_id\": \"9cb2a119-8aa6-487e-a46b-9b9ff25323be\", \"label\": \"/shots/hiero_seq_max_tw/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"771e41ed-74b0-4fcc-882c-6a248d45a464\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_seq_max_tw/sh010 shotMain\", \"review\": false, \"reviewableSource\": \"clip_media\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"4C055A68-8354-474A-A6F8-B0CBF9A537CD\"}", + "label": "AYONdata_b6896763", + "note": "AYON data container" + }, + "name": "AYONdata_b6896763", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "301", + "foundry.source.filename": "output.%07d.exr 948674-948974", + "foundry.source.filesize": "", + "foundry.source.fragments": "301", + "foundry.source.framerate": "25", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%07d.exr 948674-948974", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "948674", + "foundry.source.timecode": "948674", + "foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "301", + "foundry.timeline.framerate": "25", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "1,1,1278,718", + "media.exr.displayWindow": "0,0,1279,719", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2025-01-13 14:26:25", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr", + "media.input.filereader": "exr", + "media.input.filesize": "214941", + "media.input.frame": "1", + "media.input.height": "720", + "media.input.mtime": "2025-01-13 14:26:25", + "media.input.width": "1280", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "b13e3153b31d8f14", + "media.nuke.version": "15.0v5", + "padding": 7 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 301.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 948674.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 948674, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 7, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/resources/qt_timewarp.json b/tests/client/ayon_core/pipeline/editorial/resources/qt_timewarp.json new file mode 100644 index 0000000000..88ee7130f4 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/qt_timewarp.json @@ -0,0 +1,174 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 909986.0387191772 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 4.0, + "lookup": [ + 2.0, + 1.8959999809265136, + 1.767999971389771, + 1.59199997138977, + 1.3439999809265135, + 1.0, + 0.5440000181198119, + -0.007999974250793684, + -0.6319999756813051, + -1.3039999847412114, + -2.0 + ] + }, + "name": "TimeWarp2", + "effect_name": "TimeWarp" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_seq_tw/sh010\", \"task\": null, \"clip_index\": \"27126150-EDFA-9F45-908C-59F5CD1A94E2\", \"hierarchy\": \"shots/hiero_seq_tw\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_tw\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_seq_tw\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_tw\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"5c0e0d32-fa09-4331-afbb-5b194cfa258c\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_seq_tw/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"b88fe40d-f92d-42b0-b7f6-7cb7a206e878\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 176.0, \"sourceOut\": 186.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_seq_tw/sh010\", \"task\": null, \"clip_index\": \"27126150-EDFA-9F45-908C-59F5CD1A94E2\", \"hierarchy\": \"shots/hiero_seq_tw\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_tw\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_seq_tw\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_seq_tw\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"5c0e0d32-fa09-4331-afbb-5b194cfa258c\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"b88fe40d-f92d-42b0-b7f6-7cb7a206e878\", \"label\": \"/shots/hiero_seq_tw/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"e3ea1467-dfaf-48db-bf3c-6cbbbd2cd972\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_seq_tw/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"27126150-EDFA-9F45-908C-59F5CD1A94E2\"}", + "label": "AYONdata_ef8f52f1", + "note": "AYON data container" + }, + "name": "AYONdata_ef8f52f1", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "301", + "foundry.source.filename": "output.%07d.exr 948674-948974", + "foundry.source.filesize": "", + "foundry.source.fragments": "301", + "foundry.source.framerate": "25", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%07d.exr 948674-948974", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "948674", + "foundry.source.timecode": "948674", + "foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "301", + "foundry.timeline.framerate": "25", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "1,1,1278,718", + "media.exr.displayWindow": "0,0,1279,719", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2025-01-13 14:26:25", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr", + "media.input.filereader": "exr", + "media.input.filesize": "214941", + "media.input.frame": "1", + "media.input.height": "720", + "media.input.mtime": "2025-01-13 14:26:25", + "media.input.width": "1280", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "b13e3153b31d8f14", + "media.nuke.version": "15.0v5", + "padding": 7 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 301.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 948674.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 948674, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 7, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index c045379806..45cba64558 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -180,6 +180,55 @@ def test_movie_frozen_frame(): ) +def test_movie_timewarp(): + """ + Movie clip (no timecode) + available files = 0-100 24fps + source_range = 29.97-40.97 23.976fps + speed = timewarp + """ + expected_data = { + 'handleEnd': 10, + 'handleStart': 0, + 'mediaIn': 948850, + 'mediaOut': 948860, + 'speed': 1.0, + 'versionData': {'handleEnd': 10, + 'handleStart': 0, + 'retime': True, + 'speed': 1.0, + 'timewarps': [ + { + 'Class': 'TimeWarp', + 'length': 4.0, + 'lookup': [ + 2.0, + 1.8959999809265136, + 1.767999971389771, + 1.59199997138977, + 1.3439999809265135, + 1.0, + 0.5440000181198119, + -0.007999974250793684, + -0.6319999756813051, + -1.3039999847412114, + -2.0 + ], + 'name': 'TimeWarp2' + } + ] + } + } + + _check_expected_retimed_values( + "qt_timewarp.json", + expected_data, + handle_start=0, + handle_end=10, + ) + + + def test_img_sequence_no_handles(): """ Img sequence clip (no embedded timecode) @@ -449,3 +498,189 @@ def test_img_sequence_frozen_frame(): handle_start=10, handle_end=10, ) + + +def test_img_sequence_timewarp_beyond_range(): + """ + Img sequence clip + available files = 948674-948974 25fps + source_range = 909990.8339241028 + - 909995.8339241028 23.976fps + timewarp to get from 948845 to 948870 + """ + expected_data = { + 'mediaIn': 948845, + 'mediaOut': 948870, + 'handleStart': 0, + 'handleEnd': 10, + 'speed': 1.0, + 'versionData': {'handleEnd': 10, + 'handleStart': 0, + 'retime': True, + 'speed': 1.0, + 'timewarps': [ + { + 'Class': 'TimeWarp', + 'length': 1.0, + 'lookup': [ + -5.0, + -3.9440000305175777, + -2.852000034332275, + -1.6880000228881844, + -0.4160000076293944, + 1.0, + 2.5839999923706056, + 4.311999977111817, + 6.147999965667726, + 8.055999969482421, + 10.0 + ], + 'name': 'TimeWarp3' + } + ] + } + } + + _check_expected_retimed_values( + "img_seq_tw_beyond_range.json", + expected_data, + handle_start=0, + handle_end=10, + ) + + +def test_img_sequence_2X_speed_timewarp(): + """ + Img sequence clip + available files = 948674-948974 25fps + source_range = 909990.8339241028 + - 909995.8339241028 23.976fps + speed: 200% + timewarp to get from 948854 to 948874 + """ + expected_data = { + 'mediaIn': 948850, + 'mediaOut': 948874, + 'handleStart': 0, + 'handleEnd': 20, + 'speed': 2.0, + 'versionData': { + 'handleEnd': 20, + 'handleStart': 0, + 'retime': True, + 'speed': 2.0, + 'timewarps': [ + { + 'Class': 'TimeWarp', + 'length': 4.0, + 'lookup': [ + 2.0, + 1.7039999923706055, + 1.431999991416931, + 1.2079999942779531, + 1.055999998092652, + 1.0, + 1.056000007629395, + 1.208000022888184, + 1.432000034332276, + 1.7040000305175766, + 2.0 + ], + 'name': 'TimeWarp6' + } + ] + } + } + + _check_expected_retimed_values( + "img_seq_2x_time_warp.json", + expected_data, + handle_start=0, + handle_end=10, + ) + + +def test_img_sequence_multiple_timewarps(): + """ + Img sequence clip + available files = 948674-948974 25fps + source_range = 909990.8339241028 + - 909995.8339241028 23.976fps + multiple timewarps to get from 948842 to 948864 + """ + expected_data = { + 'mediaIn': 948845, + 'mediaOut': 948867, + 'handleStart': 0, + 'handleEnd': 10, + 'speed': 1.0, + 'versionData': { + 'handleEnd': 10, + 'handleStart': 0, + 'retime': True, + 'speed': 1.0, + 'timewarps': [ + { + 'Class': 'TimeWarp', + 'length': 1.0, + 'lookup': [ + -5.0, + -3.9440000305175777, + -2.852000034332275, + -1.6880000228881844, + -0.4160000076293944, + 1.0, + 2.5839999923706056, + 4.311999977111817, + 6.147999965667726, + 8.055999969482421, + 10.0 + ], + 'name': 'TimeWarp3' + }, + { + 'Class': 'TimeWarp', + 'length': 1.0, + 'lookup': [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + 'name': 'TimeWarp4' + }, + { + 'Class': 'TimeWarp', + 'length': 1.0, + 'lookup': [ + 0.0, + -1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0 + ], + 'name': 'TimeWarp5' + } + ] + } + } + + _check_expected_retimed_values( + "img_seq_multiple_tws.json", + expected_data, + handle_start=0, + handle_end=10, + ) From 199ed55357fedcb81e84a32c4bc67ecf64478778 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 16 Jan 2025 15:48:02 +0800 Subject: [PATCH 273/463] add substance designer into OCIO and last workfile pre-launch hook --- client/ayon_core/hooks/pre_add_last_workfile_arg.py | 1 + client/ayon_core/hooks/pre_ocio_hook.py | 1 + client/ayon_core/pipeline/farm/pyblish_functions.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/client/ayon_core/hooks/pre_add_last_workfile_arg.py b/client/ayon_core/hooks/pre_add_last_workfile_arg.py index d5914c2352..daea8c5502 100644 --- a/client/ayon_core/hooks/pre_add_last_workfile_arg.py +++ b/client/ayon_core/hooks/pre_add_last_workfile_arg.py @@ -26,6 +26,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): "photoshop", "tvpaint", "substancepainter", + "substancedesigner", "aftereffects", "wrap", "openrv", diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index 7406aa42cf..78fc8c78de 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -10,6 +10,7 @@ class OCIOEnvHook(PreLaunchHook): order = 0 hosts = { "substancepainter", + "substancedesigner", "fusion", "blender", "aftereffects", diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e48d99602e..16174a47a9 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -935,7 +935,9 @@ def _collect_expected_files_for_aov(files): ValueError: If there are multiple collections. """ + print(f"files: {files}") cols, rem = clique.assemble(files) + print(cols) # we shouldn't have any reminders. And if we do, it should # be just one item for single frame renders. if not cols and rem: From 03790a5f0c158f531315a806a2bf5a4dedbad0c1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 16 Jan 2025 23:39:26 +0100 Subject: [PATCH 274/463] Fix too big chunky icons in BorisFX Silhouette - This change was also tested in Fusion (where Qt runs completely separately and this addition didn't result in a visual difference there) --- client/ayon_core/style/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index bd96a3aeed..fa26605354 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -23,6 +23,9 @@ Enabled vs Disabled logic in most of stylesheets font-family: "Noto Sans"; font-weight: 450; outline: none; + + /* Fix icons in BorisFX Silhouette */ + icon-size: 16px; } QWidget { From 43f8764b7fb7d2481927f4bc55e91122765f28e9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 17 Jan 2025 00:01:54 +0100 Subject: [PATCH 275/463] Reduce margins on Workfiles tool due to nested layouts --- client/ayon_core/tools/workfiles/widgets/window.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/tools/workfiles/widgets/window.py b/client/ayon_core/tools/workfiles/widgets/window.py index 8bcff66f50..1649a059cb 100644 --- a/client/ayon_core/tools/workfiles/widgets/window.py +++ b/client/ayon_core/tools/workfiles/widgets/window.py @@ -113,6 +113,7 @@ class WorkfilesToolWindow(QtWidgets.QWidget): main_layout = QtWidgets.QHBoxLayout(self) main_layout.addWidget(pages_widget, 1) + main_layout.setContentsMargins(0, 0, 0, 0) overlay_messages_widget = MessageOverlayObject(self) overlay_invalid_host = InvalidHostOverlay(self) From 17e20a2d0f007843f04a91873cbb7b1f11c323b2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 17 Jan 2025 00:07:14 +0100 Subject: [PATCH 276/463] Set the icon size in the stylesheet to avoid too big clunky icons in BorisFX Silhouette. The sizes appeared the same in Fusion and Maya with this added to the stylesheet (no changes there) --- client/ayon_core/style/style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index bd96a3aeed..fa26605354 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -23,6 +23,9 @@ Enabled vs Disabled logic in most of stylesheets font-family: "Noto Sans"; font-weight: 450; outline: none; + + /* Fix icons in BorisFX Silhouette */ + icon-size: 16px; } QWidget { From e0133f54b66473aa8133e2759d6d95219e552eeb Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 17 Jan 2025 16:15:21 +0800 Subject: [PATCH 277/463] remove unrelated code --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 16174a47a9..e48d99602e 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -935,9 +935,7 @@ def _collect_expected_files_for_aov(files): ValueError: If there are multiple collections. """ - print(f"files: {files}") cols, rem = clique.assemble(files) - print(cols) # we shouldn't have any reminders. And if we do, it should # be just one item for single frame renders. if not cols and rem: From c718d0596f3612780de061ea3c7ce05596067a1d Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 20 Jan 2025 11:25:44 +0100 Subject: [PATCH 278/463] Enforce floating retime speed for Qt. --- client/ayon_core/pipeline/editorial.py | 9 +- .../publish/collect_otio_subset_resources.py | 15 +- .../resources/qt_reverse_speed_0_7.json | 160 ++++++++++++++++++ .../test_media_range_with_retimes.py | 29 ++++ 4 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index ed1cdf9974..7076b31ed9 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -406,7 +406,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): trim_range = otio.opentime.TimeRange( start_time=src_in, duration=retimed_duration, - ) # preserve discrete frame numbers @@ -479,16 +478,16 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): "retime": True, "speed": time_scalar, "timewarps": time_warp_nodes, - "handleStart": int(handle_start), - "handleEnd": int(handle_end) + "handleStart": round(handle_start), + "handleEnd": round(handle_end) } } returning_dict = { "mediaIn": media_in_trimmed, "mediaOut": media_out_trimmed, - "handleStart": int(handle_start), - "handleEnd": int(handle_end), + "handleStart": round(handle_start), + "handleEnd": round(handle_end), "speed": time_scalar } diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index ed7e8ac4f1..bfcf5a71bb 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -6,6 +6,7 @@ Provides: instance -> otioReviewClips """ import os +import math import clique import pyblish.api @@ -69,9 +70,17 @@ class CollectOtioSubsetResources( self.log.debug( ">> retimed_attributes: {}".format(retimed_attributes)) - # break down into variables - media_in = int(retimed_attributes["mediaIn"]) - media_out = int(retimed_attributes["mediaOut"]) + # break down into variables as rounded frame numbers + # + # 0 1 2 3 4 + # |-------------|---------------|--------------|-------------| + # |_______________media range_______________| + # 0.6 3.2 + # + # As rounded frames, media_in = 0 and media_out = 4 + media_in = math.floor(retimed_attributes["mediaIn"]) + media_out = math.ceil(retimed_attributes["mediaOut"]) + handle_start = int(retimed_attributes["handleStart"]) handle_end = int(retimed_attributes["handleEnd"]) diff --git a/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json b/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json new file mode 100644 index 0000000000..3ed27bcf8b --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json @@ -0,0 +1,160 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 11.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 29.970030784606934 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "Speed", + "effect_name": "LinearTimeWarp", + "time_scalar": -0.699999988079071 + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_qt_neg07x/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_neg07x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"clip_media\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_qt_neg07x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"d7c96d32-6884-452f-9f8c-2383e20ca2db\", \"reviewTrack\": \"clip_media\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_qt_neg07x/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"dae8823d-d664-4afd-9d9d-be20647ad756\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceOut\": 30.0, \"fps\": \"from_selection\", \"sourceIn\": 0}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_qt_neg07x/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_neg07x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"clip_media\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_qt_neg07x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"d7c96d32-6884-452f-9f8c-2383e20ca2db\", \"reviewTrack\": \"clip_media\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"dae8823d-d664-4afd-9d9d-be20647ad756\", \"label\": \"/shots/hiero_qt_neg07x/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"a1aa49c0-49a1-4499-a3ec-1ac35982d92b\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_qt_neg07x/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"clip_media\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\"}", + "label": "AYONdata_26480dbf", + "note": "AYON data container" + }, + "name": "AYONdata_26480dbf", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Gamma2.2", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "error", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "H.264", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "Gamma2.2", + "foundry.source.duration": "101", + "foundry.source.filename": "qt_no_tc_24fps.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "24", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float32) Open Color IO space: 114", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shoottime": "4294967295", + "foundry.source.shortfilename": "qt_no_tc_24fps.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "0", + "foundry.source.type": "QuickTime H.264", + "foundry.source.umid": "16634e88-6450-4727-6c6e-501f4b31b637", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1920", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Gamma2.2", + "foundry.timeline.duration": "101", + "foundry.timeline.framerate": "24", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.samplerate": "Invalid", + "media.input.bitsperchannel": "8-bit fixed", + "media.input.ctime": "2024-09-25 17:16:12", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "media.input.filereader": "mov64", + "media.input.filesize": "14631252", + "media.input.frame": "1", + "media.input.frame_rate": "24", + "media.input.height": "1080", + "media.input.mtime": "2024-09-25 17:16:16", + "media.input.pixel_aspect": "1", + "media.input.timecode": "00:00:00:00", + "media.input.width": "1920", + "media.quicktime.codec_id": "avc1", + "media.quicktime.codec_name": "h264", + "media.quicktime.encoder": "H.264", + "media.quicktime.nclc_matrix": "BT709", + "media.quicktime.nclc_primaries": "ITU-R BT.709", + "media.quicktime.nclc_transfer_function": "ITU-R BT.709", + "media.quicktime.thefoundry.Application": "Nuke", + "media.quicktime.thefoundry.ApplicationVersion": "15.0v5", + "media.quicktime.thefoundry.Colorspace": "Gamma2.2", + "media.quicktime.thefoundry.Writer": "mov64", + "media.quicktime.thefoundry.YCbCrMatrix": "Rec 709", + "media.stream.pixel_format": "yuv420p", + "uk.co.thefoundry.Application": "Nuke", + "uk.co.thefoundry.ApplicationVersion": "15.0v5", + "uk.co.thefoundry.Colorspace": "Gamma2.2", + "uk.co.thefoundry.Writer": "mov64", + "uk.co.thefoundry.YCbCrMatrix": "Rec 709" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 101.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 45cba64558..9ff2a7fdb2 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -146,6 +146,35 @@ def test_movie_reverse_speed_2x(): ) +def test_movie_reverse_speed_0_7x(): + """ + Movie clip (no timecode) + available files = 0-100 24fps + source_range = 29.97-40.97 23.976fps + speed = -0.7 + """ + expected_data = { + 'handleEnd': 7, + 'handleStart': 7, + 'mediaIn': 30.000000000000004, + 'mediaOut': 36.70769965924555, + 'speed': -0.699999988079071, + 'versionData': { + 'handleEnd': 7, + 'handleStart': 7, + 'retime': True, + 'speed': -0.699999988079071, + 'timewarps': [] + } + } + + _check_expected_retimed_values( + "qt_reverse_speed_0_7.json", + expected_data, + handle_start=10, + handle_end=10, + ) + def test_movie_frozen_frame(): """ From c166bf0514952bcef3e722d68f4c67cac47c0a92 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Jan 2025 21:55:34 +0800 Subject: [PATCH 279/463] add sbsar as the families --- client/ayon_core/plugins/publish/collect_resources_path.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_resources_path.py b/client/ayon_core/plugins/publish/collect_resources_path.py index 7a80d0054c..2e5b296228 100644 --- a/client/ayon_core/plugins/publish/collect_resources_path.py +++ b/client/ayon_core/plugins/publish/collect_resources_path.py @@ -66,7 +66,8 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): "yeticacheUE", "tycache", "usd", - "oxrig" + "oxrig", + "sbsar", ] def process(self, instance): From 81bb74c7ea0e0c46a17f8bc16e54f0044fb81066 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 20 Jan 2025 18:07:18 +0100 Subject: [PATCH 280/463] Apply suggestions from code review Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hooks/pre_add_last_workfile_arg.py | 2 +- client/ayon_core/hooks/pre_ocio_hook.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hooks/pre_add_last_workfile_arg.py b/client/ayon_core/hooks/pre_add_last_workfile_arg.py index a931fb0cbe..5b10357632 100644 --- a/client/ayon_core/hooks/pre_add_last_workfile_arg.py +++ b/client/ayon_core/hooks/pre_add_last_workfile_arg.py @@ -30,7 +30,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook): "wrap", "openrv", "cinema4d", - "silhouette" + "silhouette", } launch_types = {LaunchTypes.local} diff --git a/client/ayon_core/hooks/pre_ocio_hook.py b/client/ayon_core/hooks/pre_ocio_hook.py index dd81cf053e..8765dbd0b2 100644 --- a/client/ayon_core/hooks/pre_ocio_hook.py +++ b/client/ayon_core/hooks/pre_ocio_hook.py @@ -21,7 +21,7 @@ class OCIOEnvHook(PreLaunchHook): "resolve", "openrv", "cinema4d", - "silhouette" + "silhouette", } launch_types = set() From 827651a4a669fefe8823db7095315d5ebb96599d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 21 Jan 2025 12:20:50 +0100 Subject: [PATCH 281/463] Update client/ayon_core/style/style.css Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/style/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index fa26605354..a5e54453cc 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -24,7 +24,7 @@ Enabled vs Disabled logic in most of stylesheets font-weight: 450; outline: none; - /* Fix icons in BorisFX Silhouette */ + /* Define icon size to fix size issues for most of DCCs */ icon-size: 16px; } From 41045c1092fce14032a09a2a4dd4608eddd8a71f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 21 Jan 2025 12:52:02 +0100 Subject: [PATCH 282/463] Add missing argument in docstring --- client/ayon_core/pipeline/staging_dir.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/staging_dir.py b/client/ayon_core/pipeline/staging_dir.py index 2004096bd0..1cb2979415 100644 --- a/client/ayon_core/pipeline/staging_dir.py +++ b/client/ayon_core/pipeline/staging_dir.py @@ -158,6 +158,7 @@ def get_staging_dir_info( logger (Optional[logging.Logger]): Logger instance. prefix (Optional[str]) Optional prefix for staging dir name. suffix (Optional[str]): Optional suffix for staging dir name. + username (Optional[str]): AYON Username. Returns: Optional[StagingDir]: Staging dir info data From 2712260d08c9d8d777dfea9dbe7cfc4ec735f644 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 21 Jan 2025 14:49:22 +0000 Subject: [PATCH 283/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index b0ada09e7c..e90676d739 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.13+dev" +__version__ = "1.0.14" diff --git a/package.py b/package.py index 03b69d4c5c..bb38b431b1 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.13+dev" +version = "1.0.14" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 5e42aa7093..2496e3fa34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.13+dev" +version = "1.0.14" description = "" authors = ["Ynput Team "] readme = "README.md" From dec3dd2178130a0f197aeb4147c6eb1e290f84fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 21 Jan 2025 14:50:15 +0000 Subject: [PATCH 284/463] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 54f5d68b98..c0ab04abef 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,20 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.0.14 + - 1.0.13 + - 1.0.12 + - 1.0.11 + - 1.0.10 + - 1.0.9 + - 1.0.8 + - 1.0.7 + - 1.0.6 + - 1.0.5 + - 1.0.4 + - 1.0.3 + - 1.0.2 + - 1.0.1 - 1.0.0 - 0.4.4 - 0.4.3 From cdf7b743e8e8f38a3ccdf22ab0ec3ecb126e835d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:56:12 +0100 Subject: [PATCH 285/463] bump version to '1.0.15-dev' --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index e90676d739..2775cb606a 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.14" +__version__ = "1.0.15-dev" diff --git a/package.py b/package.py index bb38b431b1..af3342f3f2 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.14" +version = "1.0.15-dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 2496e3fa34..e040ce986f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.14" +version = "1.0.15-dev" description = "" authors = ["Ynput Team "] readme = "README.md" From bb2d38602e8abb9b83f923355145d9b7534a6cc8 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 21 Jan 2025 21:03:41 +0100 Subject: [PATCH 286/463] Restrict source range to available range. --- client/ayon_core/pipeline/editorial.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 7076b31ed9..933d1f1758 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -458,7 +458,10 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): # adjust range if needed media_in_trimmed = min(media_in_trimmed, min(frame_range)) + media_in_trimmed = max(media_in_trimmed, media_in) + media_out_trimmed = max(media_out_trimmed, max(frame_range)) + media_out_trimmed = min(media_out_trimmed, media_out) # adjust available handles if needed if (media_in_trimmed - media_in) < handle_start: From 5f0aec7bbf1415b70a8729566f770187b873c897 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 22 Jan 2025 14:23:27 +0100 Subject: [PATCH 287/463] Rework tw computation. --- client/ayon_core/pipeline/editorial.py | 15 ++++++--------- .../editorial/test_media_range_with_retimes.py | 7 ++++--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 933d1f1758..02986753b7 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -431,11 +431,11 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): if time_warp_nodes: # Naive approach: Resolve consecutive timewarp(s) on range, # then check if plate range has to be extended beyond source range. - frame_range = [media_in_trimmed] - in_frame = media_in_trimmed + time_scalar - while in_frame <= media_out_trimmed: - frame_range.append(in_frame) + in_frame = media_in_trimmed + frame_range = [in_frame] + for _ in range(otio_clip.source_range.duration.to_frames() - 1): in_frame += time_scalar + frame_range.append(in_frame) for tw_idx, tw in enumerate(time_warp_nodes): for idx, frame_number in enumerate(frame_range): @@ -457,11 +457,8 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): frame_range[idx] = frame_range[new_idx] # adjust range if needed - media_in_trimmed = min(media_in_trimmed, min(frame_range)) - media_in_trimmed = max(media_in_trimmed, media_in) - - media_out_trimmed = max(media_out_trimmed, max(frame_range)) - media_out_trimmed = min(media_out_trimmed, media_out) + media_in_trimmed = max(min(frame_range), media_in) + media_out_trimmed = min(max(frame_range), media_out) # adjust available handles if needed if (media_in_trimmed - media_in) < handle_start: diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 9ff2a7fdb2..730e6b24cd 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -219,8 +219,8 @@ def test_movie_timewarp(): expected_data = { 'handleEnd': 10, 'handleStart': 0, - 'mediaIn': 948850, - 'mediaOut': 948860, + 'mediaIn': 948852, + 'mediaOut': 948858, 'speed': 1.0, 'versionData': {'handleEnd': 10, 'handleStart': 0, @@ -249,6 +249,7 @@ def test_movie_timewarp(): } } + import pdb ; pdb.set_trace() _check_expected_retimed_values( "qt_timewarp.json", expected_data, @@ -588,7 +589,7 @@ def test_img_sequence_2X_speed_timewarp(): timewarp to get from 948854 to 948874 """ expected_data = { - 'mediaIn': 948850, + 'mediaIn': 948854, 'mediaOut': 948874, 'handleStart': 0, 'handleEnd': 20, From 874abdb38c633cdea1887c0435802077a9cb8bd4 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 22 Jan 2025 15:36:25 +0100 Subject: [PATCH 288/463] Remove pdb. --- .../pipeline/editorial/test_media_range_with_retimes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 730e6b24cd..a1d609b56e 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -249,7 +249,6 @@ def test_movie_timewarp(): } } - import pdb ; pdb.set_trace() _check_expected_retimed_values( "qt_timewarp.json", expected_data, From 2569015b9fe1a5b0baf5ee44539417ff0e678b3d Mon Sep 17 00:00:00 2001 From: Liam Hoflay Date: Wed, 22 Jan 2025 16:44:45 +0000 Subject: [PATCH 289/463] changing localhost for 0.0.0.0 in client/ayon_core/tools/tray/webserver/server.py --- client/ayon_core/tools/tray/webserver/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/tray/webserver/server.py b/client/ayon_core/tools/tray/webserver/server.py index d2a9b0fc6b..d9b3b857f5 100644 --- a/client/ayon_core/tools/tray/webserver/server.py +++ b/client/ayon_core/tools/tray/webserver/server.py @@ -28,7 +28,7 @@ def find_free_port( exclude_ports (list, tuple, set): List of ports that won't be checked form entered range. host (str): Host where will check for free ports. Set to - "localhost" by default. + "0.0.0.0" by default. """ if port_from is None: port_from = 8079 @@ -42,7 +42,7 @@ def find_free_port( # Default host is localhost but it is possible to look for other hosts if host is None: - host = "localhost" + host = "0.0.0.0" found_port = None while True: @@ -78,7 +78,7 @@ class WebServerManager: self._log = None self.port = port or 8079 - self.host = host or "localhost" + self.host = host or "0.0.0.0" self.on_stop_callbacks = [] From 66da9fa39a2f141895364031b4ad4ed7d4d02c3c Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 22 Jan 2025 20:27:55 +0100 Subject: [PATCH 290/463] Add tests to handle fractional speed. --- client/ayon_core/pipeline/editorial.py | 18 +- .../resources/img_seq_reverse_speed_0_7.json | 235 ++++++++++++++++++ .../test_media_range_with_retimes.py | 29 +++ 3 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_0_7.json diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 02986753b7..434fff0105 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -1,6 +1,7 @@ import os import re import clique +import math import opentimelineio as otio from opentimelineio import opentime as _ot @@ -397,12 +398,13 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): if is_input_sequence: src_in = conformed_source_range.start_time - src_duration = conformed_source_range.duration - + src_duration = math.ceil(otio_clip.source_range.duration.value * abs(time_scalar)) retimed_duration = otio.opentime.RationalTime( - src_duration.value * abs(time_scalar), - src_duration.rate + src_duration, + otio_clip.source_range.duration.rate ) + retimed_duration = retimed_duration.rescaled_to(src_in.rate) + trim_range = otio.opentime.TimeRange( start_time=src_in, duration=retimed_duration, @@ -478,16 +480,16 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): "retime": True, "speed": time_scalar, "timewarps": time_warp_nodes, - "handleStart": round(handle_start), - "handleEnd": round(handle_end) + "handleStart": math.ceil(handle_start), + "handleEnd": math.ceil(handle_end) } } returning_dict = { "mediaIn": media_in_trimmed, "mediaOut": media_out_trimmed, - "handleStart": round(handle_start), - "handleEnd": round(handle_end), + "handleStart": math.ceil(handle_start), + "handleEnd": math.ceil(handle_end), "speed": time_scalar } diff --git a/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_0_7.json b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_0_7.json new file mode 100644 index 0000000000..0939161817 --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/img_seq_reverse_speed_0_7.json @@ -0,0 +1,235 @@ +{ + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "img_seq_revsh0010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 41.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 1040.0 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "Speed", + "effect_name": "LinearTimeWarp", + "time_scalar": -0.7 + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "clip_index": "e7fede03-d769-4827-a014-35b50170e914", + "flame_sub_products": { + "io.ayon.creators.flame.plate": { + "active": true, + "clipName": "{sequence}{shot}", + "clipRename": true, + "clipVariant": "", + "clip_index": "e7fede03-d769-4827-a014-35b50170e914", + "countFrom": 10, + "countSteps": 10, + "creator_attributes": { + "parentInstance": "/test_robin/img_seq_rev/img_seq_revsh0010 shot", + "review": false, + "reviewableSource": "clip_media" + }, + "creator_identifier": "io.ayon.creators.flame.plate", + "episode": "ep01", + "export_audio": false, + "folder": "test_robin", + "folderName": "img_seq_revsh0010", + "folderPath": "/test_robin/img_seq_rev/img_seq_revsh0010", + "handleEnd": 5, + "handleStart": 5, + "heroTrack": true, + "hierarchy": "test_robin/img_seq_rev", + "hierarchyData": { + "episode": "ep01", + "folder": "test_robin", + "sequence": "img_seq_rev", + "track": "noname1" + }, + "id": "pyblish.avalon.instance", + "includeHandles": false, + "instance_id": "a06107cd-49ad-48cb-a84f-67f53dfd58ef", + "label": "/test_robin/img_seq_rev/img_seq_revsh0010 plateNoname1", + "newAssetPublishing": true, + "newHierarchyIntegration": true, + "parent_instance_id": "896c2dad-03a6-4a18-97f5-ecf8f00a6180", + "parents": [ + { + "entity_name": "test_robin", + "folder_type": "folder" + }, + { + "entity_name": "img_seq_rev", + "folder_type": "sequence" + } + ], + "productName": "plateNoname1", + "productType": "plate", + "publish": true, + "publish_attributes": {}, + "retimedFramerange": true, + "retimedHandles": true, + "reviewTrack": null, + "reviewableSource": null, + "segmentIndex": true, + "sequence": "img_seq_rev", + "shot": "sh####", + "sourceResolution": false, + "task": null, + "track": "{_track_}", + "useShotName": false, + "use_selection": true, + "vSyncOn": false, + "vSyncTrack": "*", + "variant": "noname1", + "workfileFrameStart": 1001 + }, + "io.ayon.creators.flame.shot": { + "active": true, + "clipName": "{sequence}{shot}", + "clipRename": true, + "clipVariant": "", + "clip_index": "e7fede03-d769-4827-a014-35b50170e914", + "countFrom": 10, + "countSteps": 10, + "creator_attributes": { + "clipDuration": 41, + "clipIn": 1, + "clipOut": 41, + "fps": "from_selection", + "frameEnd": 1042, + "frameStart": 1001, + "handleEnd": 5, + "handleStart": 5, + "includeHandles": false, + "retimedFramerange": true, + "retimedHandles": true, + "sourceIn": 1068, + "sourceOut": 1040, + "workfileFrameStart": 1001 + }, + "creator_identifier": "io.ayon.creators.flame.shot", + "episode": "ep01", + "export_audio": false, + "folder": "test_robin", + "folderName": "img_seq_revsh0010", + "folderPath": "/test_robin/img_seq_rev/img_seq_revsh0010", + "handleEnd": 5, + "handleStart": 5, + "heroTrack": true, + "hierarchy": "test_robin/img_seq_rev", + "hierarchyData": { + "episode": "ep01", + "folder": "test_robin", + "sequence": "img_seq_rev", + "track": "noname1" + }, + "id": "pyblish.avalon.instance", + "includeHandles": false, + "instance_id": "896c2dad-03a6-4a18-97f5-ecf8f00a6180", + "label": "/test_robin/img_seq_rev/img_seq_revsh0010 shot", + "newAssetPublishing": true, + "newHierarchyIntegration": true, + "parents": [ + { + "entity_name": "test_robin", + "folder_type": "folder" + }, + { + "entity_name": "img_seq_rev", + "folder_type": "sequence" + } + ], + "productName": "shotMain", + "productType": "shot", + "publish": true, + "publish_attributes": {}, + "retimedFramerange": true, + "retimedHandles": true, + "reviewTrack": null, + "reviewableSource": null, + "segmentIndex": true, + "sequence": "img_seq_rev", + "shot": "sh####", + "sourceResolution": false, + "task": null, + "track": "{_track_}", + "useShotName": false, + "use_selection": true, + "vSyncOn": false, + "vSyncTrack": "*", + "variant": "main", + "workfileFrameStart": 1001 + } + }, + "publish": true + }, + "name": "AYONData", + "color": "CYAN", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 1.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 22.0 + } + }, + "comment": "" + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "isSequence": true, + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 101.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 25.0, + "value": 1000.0 + } + }, + "available_image_bounds": null, + "target_url_base": "/home/ynput/CODE/testing_flame/test_data/sample_media_robin/Samples media/img_sequence/tif/", + "name_prefix": "output.", + "name_suffix": ".tif", + "start_frame": 1000, + "frame_step": 1, + "rate": 25.0, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index a1d609b56e..ab67d49e22 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -438,6 +438,35 @@ def test_img_sequence_reverse_speed_from_24_to_23_976fps(): ) +def test_img_sequence_reverse_speed_0_7(): + """ + Img sequence clip + available files = 1000-1100 24fps + source_range = 1040-1081 25fps + """ + expected_data = { + 'mediaIn': 1040, + 'mediaOut': 1068, + 'handleStart': 4, + 'handleEnd': 4, + 'speed': -0.7, + 'versionData': { + 'retime': True, + 'speed': -0.7, + 'timewarps': [], + 'handleStart': 4, + 'handleEnd': 4 + } + } + + _check_expected_retimed_values( + "img_seq_reverse_speed_0_7.json", + expected_data, + handle_start=5, + handle_end=5, + ) + + def test_img_sequence_2x_speed(): """ Img sequence clip From 133bb32c8154234761b6e798528d41a121f275ea Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 22 Jan 2025 20:52:15 +0100 Subject: [PATCH 291/463] Fix lint. --- client/ayon_core/pipeline/editorial.py | 30 ++++++++++++++----- .../test_media_range_with_retimes.py | 10 +++---- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 434fff0105..2ecc786581 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -398,7 +398,10 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): if is_input_sequence: src_in = conformed_source_range.start_time - src_duration = math.ceil(otio_clip.source_range.duration.value * abs(time_scalar)) + src_duration = math.ceil( + otio_clip.source_range.duration.value + * abs(time_scalar) + ) retimed_duration = otio.opentime.RationalTime( src_duration, otio_clip.source_range.duration.rate @@ -422,9 +425,15 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): # compute retimed range media_in_trimmed = conformed_source_range.start_time.value + offset_in - offset_duration = conformed_source_range.duration.value * abs(time_scalar) - offset_duration -= 1 # duration 1 frame -> freeze frame -> end = start + 0 - offset_duration = max(0, offset_duration) # negative duration = frozen frame + offset_duration = ( + conformed_source_range.duration.value + * abs(time_scalar) + ) + + # duration 1 frame -> freeze frame -> end = start + 0 + offset_duration -= 1 + # negative duration = frozen frame + offset_duration = max(0, offset_duration) media_out_trimmed = media_in_trimmed + offset_duration media_in = available_range.start_time.value @@ -443,17 +452,22 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): for idx, frame_number in enumerate(frame_range): # First timewarp, apply on media range if tw_idx == 0: - frame_range[idx] = round(frame_number + tw["lookup"][idx] * time_scalar) + frame_range[idx] = round( + frame_number + + (tw["lookup"][idx] * time_scalar) + ) # Consecutive timewarp, apply on the previous result else: new_idx = round(idx + tw["lookup"][idx]) if not 0 <= new_idx < len(frame_range): # TODO: implementing this would need to actually have - # retiming engine resolve process within AYON, resolving wraps - # as curves, then projecting those into the previous media_range. + # retiming engine resolve process within AYON, + # resolving wraps as curves, then projecting + # those into the previous media_range. raise NotImplementedError( - "Unsupported consecutive timewarps (out of computed range)" + "Unsupported consecutive timewarps " + "(out of computed range)" ) frame_range[idx] = frame_range[new_idx] diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index ab67d49e22..fbab60623f 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -501,7 +501,7 @@ def test_img_sequence_2x_speed_resolve(): """ Img sequence clip available files = 0-99 24fps - source_range = 38-49 24fps + source_range = 38-49 24fps speed = 2.0 """ expected_data = { @@ -531,7 +531,7 @@ def test_img_sequence_frozen_frame(): """ Img sequence clip available files = 948674-948974 25fps - source_range = 909990.8339241028 + source_range = 909990.8339241028 - 909995.8339241028 23.976fps speed = 0.0 """ @@ -562,7 +562,7 @@ def test_img_sequence_timewarp_beyond_range(): """ Img sequence clip available files = 948674-948974 25fps - source_range = 909990.8339241028 + source_range = 909990.8339241028 - 909995.8339241028 23.976fps timewarp to get from 948845 to 948870 """ @@ -611,7 +611,7 @@ def test_img_sequence_2X_speed_timewarp(): """ Img sequence clip available files = 948674-948974 25fps - source_range = 909990.8339241028 + source_range = 909990.8339241028 - 909995.8339241028 23.976fps speed: 200% timewarp to get from 948854 to 948874 @@ -662,7 +662,7 @@ def test_img_sequence_multiple_timewarps(): """ Img sequence clip available files = 948674-948974 25fps - source_range = 909990.8339241028 + source_range = 909990.8339241028 - 909995.8339241028 23.976fps multiple timewarps to get from 948842 to 948864 """ From 8dc243f2fef0e523bf639b2ccf53539eb7c487bb Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 22 Jan 2025 21:03:41 +0100 Subject: [PATCH 292/463] Address feedback from PR. --- client/ayon_core/pipeline/editorial.py | 29 ++++++++++--------- .../publish/collect_otio_subset_resources.py | 6 ++-- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 2ecc786581..fc962300d8 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -430,10 +430,10 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): * abs(time_scalar) ) - # duration 1 frame -> freeze frame -> end = start + 0 - offset_duration -= 1 - # negative duration = frozen frame - offset_duration = max(0, offset_duration) + # Offset duration by 1 for media out frame + # - only if duration is not single frame (start frame != end frame) + if offset_duration > 0: + offset_duration -= 1 media_out_trimmed = media_in_trimmed + offset_duration media_in = available_range.start_time.value @@ -460,17 +460,18 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): else: new_idx = round(idx + tw["lookup"][idx]) - if not 0 <= new_idx < len(frame_range): - # TODO: implementing this would need to actually have - # retiming engine resolve process within AYON, - # resolving wraps as curves, then projecting - # those into the previous media_range. - raise NotImplementedError( - "Unsupported consecutive timewarps " - "(out of computed range)" - ) + if 0 <= new_idx < len(frame_range): + frame_range[idx] = frame_range[new_idx] + continue - frame_range[idx] = frame_range[new_idx] + # TODO: implementing this would need to actually have + # retiming engine resolve process within AYON, + # resolving wraps as curves, then projecting + # those into the previous media_range. + raise NotImplementedError( + "Unsupported consecutive timewarps " + "(out of computed range)" + ) # adjust range if needed media_in_trimmed = max(min(frame_range), media_in) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index bfcf5a71bb..deb51f62a5 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -188,7 +188,7 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, collection=collection) else: - filename, = tuple(collection) + filename = tuple(collection)[0] self.log.debug(filename) # TODO: discuss this, it erases frame number. @@ -200,8 +200,8 @@ class CollectOtioSubsetResources( and "review" in instance.data["families"] ): review_repre = self._create_representation( - frame_start, frame_end, collection=collection, - delete=True, review=True) + frame_start, frame_end, collection=collection, + delete=True, review=True) else: _trim = False From b2bbec42ba2d068076d7526e96e84e9c47ae839e Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 22 Jan 2025 21:04:54 +0100 Subject: [PATCH 293/463] Fix lint. --- client/ayon_core/pipeline/editorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index fc962300d8..6e5e2ec67a 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -432,7 +432,7 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): # Offset duration by 1 for media out frame # - only if duration is not single frame (start frame != end frame) - if offset_duration > 0: + if offset_duration > 0: offset_duration -= 1 media_out_trimmed = media_in_trimmed + offset_duration From 27145add3aefa23bf34040f7e307307bd473fd00 Mon Sep 17 00:00:00 2001 From: Liam Hoflay Date: Thu, 23 Jan 2025 10:25:13 +0000 Subject: [PATCH 294/463] making 127.0.0.1 rather than 0.0.0.0 --- client/ayon_core/tools/tray/webserver/server.py | 6 +++--- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/tray/webserver/server.py b/client/ayon_core/tools/tray/webserver/server.py index d9b3b857f5..70d1fc8c0f 100644 --- a/client/ayon_core/tools/tray/webserver/server.py +++ b/client/ayon_core/tools/tray/webserver/server.py @@ -28,7 +28,7 @@ def find_free_port( exclude_ports (list, tuple, set): List of ports that won't be checked form entered range. host (str): Host where will check for free ports. Set to - "0.0.0.0" by default. + "127.0.0.1" by default. """ if port_from is None: port_from = 8079 @@ -42,7 +42,7 @@ def find_free_port( # Default host is localhost but it is possible to look for other hosts if host is None: - host = "0.0.0.0" + host = "127.0.0.1" found_port = None while True: @@ -78,7 +78,7 @@ class WebServerManager: self._log = None self.port = port or 8079 - self.host = host or "0.0.0.0" + self.host = host or "127.0.0.1" self.on_stop_callbacks = [] diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 2775cb606a..fabbb4f0e9 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.15-dev" +__version__ = "1.0.14-bk.1" diff --git a/package.py b/package.py index af3342f3f2..db5e0b80dc 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.15-dev" +version = "1.0.14-bk.1" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index e040ce986f..dc550a1eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.15-dev" +version = "1.0.14-bk.1" description = "" authors = ["Ynput Team "] readme = "README.md" From 6708abaa43c1e22ea2435f450c03ca5c2e6cd500 Mon Sep 17 00:00:00 2001 From: Liam Hoflay Date: Thu, 23 Jan 2025 11:19:48 +0000 Subject: [PATCH 295/463] version revert --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index fabbb4f0e9..2775cb606a 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.14-bk.1" +__version__ = "1.0.15-dev" diff --git a/package.py b/package.py index db5e0b80dc..af3342f3f2 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.14-bk.1" +version = "1.0.15-dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index dc550a1eda..e040ce986f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.14-bk.1" +version = "1.0.15-dev" description = "" authors = ["Ynput Team "] readme = "README.md" From ffec35f69f140a426ee3198ef9de6d01a7c11840 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 23 Jan 2025 17:26:32 +0100 Subject: [PATCH 296/463] Add Silhouette host to defaults of few validators --- server/settings/publish_plugins.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 1bf2e853cf..2cca9cd66e 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1033,7 +1033,8 @@ DEFAULT_PUBLISH_VALUES = { "maya", "nuke", "photoshop", - "substancepainter" + "substancepainter", + "silhouette" ], "enabled": True, "optional": False, @@ -1053,7 +1054,8 @@ DEFAULT_PUBLISH_VALUES = { "harmony", "photoshop", "aftereffects", - "fusion" + "fusion", + "silhouette" ], "enabled": True, "optional": True, From 375378e74cc7cddb1d4e83bbc90c1bfd14d52693 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:18:27 +0100 Subject: [PATCH 297/463] auto-fill project entity if is not passed in --- client/ayon_core/pipeline/create/creator_plugins.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 445b41cb4b..ce109e3a37 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -562,6 +562,9 @@ class BaseCreator(ABC): instance ) + if not project_entity: + project_entity = self.create_context.get_current_project_entity() + return get_product_name( project_name, task_name, From 8b4f5ec42a210c08f22bba61abc09d1d0f9dd047 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:21:22 +0100 Subject: [PATCH 298/463] added one more check if current project is the project name passed in --- client/ayon_core/pipeline/create/creator_plugins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index ce109e3a37..fca671d546 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -562,7 +562,8 @@ class BaseCreator(ABC): instance ) - if not project_entity: + cur_project_name = self.create_context.get_current_project_name() + if not project_entity and project_name == cur_project_name: project_entity = self.create_context.get_current_project_entity() return get_product_name( From 509c6a84d3004882cdff9b45e8cb31b18465db7e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:24:02 +0100 Subject: [PATCH 299/463] set minimum size of content widget to be able to stretch it's size --- client/ayon_core/tools/publisher/widgets/report_page.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index b7afcf470a..2684f85356 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1332,6 +1332,8 @@ class InstancesLogsView(QtWidgets.QFrame): content_widget = QtWidgets.QWidget(content_wrap_widget) content_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + content_widget.setMinimumSize(80, 80) + content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(8, 8, 8, 8) content_layout.setSpacing(15) From c2f3d8b114ecd93871029144bc81a77ad841758e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:31:06 +0100 Subject: [PATCH 300/463] use 'AYON_INSTANCE_ID' by default instead of 'AVALON_' --- client/ayon_core/pipeline/create/legacy_create.py | 4 ++-- client/ayon_core/pipeline/create/structures.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/create/legacy_create.py b/client/ayon_core/pipeline/create/legacy_create.py index ec9b23ac62..f6427d9bd1 100644 --- a/client/ayon_core/pipeline/create/legacy_create.py +++ b/client/ayon_core/pipeline/create/legacy_create.py @@ -9,7 +9,7 @@ import os import logging import collections -from ayon_core.pipeline.constants import AVALON_INSTANCE_ID +from ayon_core.pipeline.constants import AYON_INSTANCE_ID from .product_name import get_product_name @@ -34,7 +34,7 @@ class LegacyCreator: # Default data self.data = collections.OrderedDict() # TODO use 'AYON_INSTANCE_ID' when all hosts support it - self.data["id"] = AVALON_INSTANCE_ID + self.data["id"] = AYON_INSTANCE_ID self.data["productType"] = self.product_type self.data["folderPath"] = folder_path self.data["productName"] = name diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index a1a4d5f8ef..a45e053cca 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -492,7 +492,7 @@ class CreatedInstance: item_id = data.get("id") # TODO use only 'AYON_INSTANCE_ID' when all hosts support it if item_id not in {AYON_INSTANCE_ID, AVALON_INSTANCE_ID}: - item_id = AVALON_INSTANCE_ID + item_id = AYON_INSTANCE_ID self._data["id"] = item_id self._data["productType"] = product_type self._data["productName"] = product_name From f90bc8f6b7c68eb79f9b199238e4f7ee1d28d8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 24 Jan 2025 14:42:11 +0100 Subject: [PATCH 301/463] Update client/ayon_core/plugins/publish/collect_otio_frame_ranges.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/collect_otio_frame_ranges.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 7c31b6445a..83dbec1fe9 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -37,6 +37,7 @@ def validate_otio_clip(instance, logger): return False return True + class CollectOtioFrameRanges(pyblish.api.InstancePlugin): """Collect basic timeline frame ranges from OTIO clip. From 971c4aef43b5dad3f74b8f8ff321ef66c2927381 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 24 Jan 2025 14:52:25 +0100 Subject: [PATCH 302/463] Refactor OTIO frame range collection plugin Merged multiple plugins into a single one for collecting OTIO frame ranges. - Unified handling of timeline, source media, and retimed clip ranges. - Updated class name and docstrings for clarity. - Simplified process method to streamline range collection logic. --- .../publish/collect_otio_frame_ranges.py | 127 ++++++------------ 1 file changed, 40 insertions(+), 87 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 83dbec1fe9..917b9ad206 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -1,20 +1,15 @@ -"""Plugins for collecting OTIO frame ranges and related timing information. +"""Plugin for collecting OTIO frame ranges and related timing information. -This module contains three plugins: -- CollectOtioFrameRanges: Collects basic timeline frame ranges -- CollectOtioSourceRanges: Collects source media frame ranges -- CollectOtioRetimedRanges: Handles retimed clip frame ranges +This module contains a unified plugin that handles: +- Basic timeline frame ranges +- Source media frame ranges +- Retimed clip frame ranges """ from pprint import pformat +import opentimelineio as otio import pyblish.api - -try: - import opentimelineio as otio -except ImportError: - raise RuntimeError("OpenTimelineIO is not installed.") - from ayon_core.pipeline.editorial import ( get_media_range_with_retimes, otio_range_to_frame_range, @@ -38,16 +33,21 @@ def validate_otio_clip(instance, logger): return True -class CollectOtioFrameRanges(pyblish.api.InstancePlugin): - """Collect basic timeline frame ranges from OTIO clip. +class CollectOtioRanges(pyblish.api.InstancePlugin): + """Collect all OTIO-related frame ranges and timing information. - This plugin extracts and stores basic timeline frame ranges including - handles from the OTIO clip. + This plugin handles collection of: + - Basic timeline frame ranges with handles + - Source media frame ranges with handles + - Retimed clip frame ranges Requires: otioClip (otio.schema.Clip): OTIO clip object workfileFrameStart (int): Starting frame of work file + Optional: + shotDurationFromSource (int): Duration from source if retimed + Provides: frameStart (int): Start frame in timeline frameEnd (int): End frame in timeline @@ -55,24 +55,41 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): clipOut (int): Clip out point clipInH (int): Clip in point with handles clipOutH (int): Clip out point with handles + sourceStart (int): Source media start frame + sourceEnd (int): Source media end frame + sourceStartH (int): Source media start frame with handles + sourceEndH (int): Source media end frame with handles """ - label = "Collect OTIO Frame Ranges" + label = "Collect OTIO Ranges" order = pyblish.api.CollectorOrder - 0.08 families = ["shot", "clip"] - hosts = ["resolve", "hiero", "flame", "traypublisher"] def process(self, instance): - """Process the instance to collect frame ranges. + """Process the instance to collect all frame ranges. Args: instance: The instance to process """ - if not validate_otio_clip(instance, self.log): return otio_clip = instance.data["otioClip"] + + # Collect timeline ranges if workfile start frame is available + if "workfileFrameStart" in instance.data: + self._collect_timeline_ranges(instance, otio_clip) + + # Collect source ranges if clip has available range + if hasattr(otio_clip, 'available_range') and otio_clip.available_range(): + self._collect_source_ranges(instance, otio_clip) + + # Handle retimed ranges if source duration is available + if "shotDurationFromSource" in instance.data: + self._collect_retimed_ranges(instance, otio_clip) + + def _collect_timeline_ranges(self, instance, otio_clip): + """Collect basic timeline frame ranges.""" workfile_start = instance.data["workfileFrameStart"] # Get timeline ranges @@ -98,40 +115,8 @@ class CollectOtioFrameRanges(pyblish.api.InstancePlugin): instance.data.update(data) self.log.debug(f"Added frame ranges: {pformat(data)}") - -class CollectOtioSourceRanges(pyblish.api.InstancePlugin): - """Collect source media frame ranges from OTIO clip. - - This plugin extracts and stores source media frame ranges including - handles from the OTIO clip. - - Requires: - otioClip (otio.schema.Clip): OTIO clip object - - Provides: - sourceStart (int): Source media start frame - sourceEnd (int): Source media end frame - sourceStartH (int): Source media start frame with handles - sourceEndH (int): Source media end frame with handles - """ - - label = "Collect Source OTIO Frame Ranges" - order = pyblish.api.CollectorOrder - 0.07 - families = ["shot", "clip"] - hosts = ["hiero", "flame"] - - def process(self, instance): - """Process the instance to collect source frame ranges. - - Args: - instance: The instance to process - """ - - if not validate_otio_clip(instance, self.log): - return - - otio_clip = instance.data["otioClip"] - + def _collect_source_ranges(self, instance, otio_clip): + """Collect source media frame ranges.""" # Get source ranges otio_src_range = otio_clip.source_range otio_available_range = otio_clip.available_range() @@ -156,41 +141,9 @@ class CollectOtioSourceRanges(pyblish.api.InstancePlugin): instance.data.update(data) self.log.debug(f"Added source ranges: {pformat(data)}") - -class CollectOtioRetimedRanges(pyblish.api.InstancePlugin): - """Update frame ranges for retimed clips. - - This plugin updates the frame end value for retimed clips. - - Requires: - otioClip (otio.schema.Clip): OTIO clip object - workfileFrameStart (int): Starting frame of work file - shotDurationFromSource (Optional[int]): Duration from source if retimed - - Provides: - frameEnd (int): Updated end frame for retimed clips - """ - - label = "Collect Retimed OTIO Frame Ranges" - order = pyblish.api.CollectorOrder - 0.06 - families = ["shot", "clip"] - hosts = ["hiero", "flame"] - - def process(self, instance): - """Process the instance to handle retimed clips. - - Args: - instance: The instance to process - """ - if not validate_otio_clip(instance, self.log): - return - + def _collect_retimed_ranges(self, instance, otio_clip): + """Handle retimed clip frame ranges.""" workfile_source_duration = instance.data.get("shotDurationFromSource") - if not workfile_source_duration: - self.log.debug("No source duration found, skipping retime handling.") - return - - otio_clip = instance.data["otioClip"] frame_start = instance.data["frameStart"] # Handle retimed clip frame range From c380ebfedf03e77e4744df42db75eb527ae7663f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:09:03 +0100 Subject: [PATCH 303/463] use 'QTextEdit' for log message --- client/ayon_core/style/style.css | 2 + .../tools/publisher/widgets/report_page.py | 58 ++++++++++++++++--- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/style/style.css b/client/ayon_core/style/style.css index a5e54453cc..0e19702d53 100644 --- a/client/ayon_core/style/style.css +++ b/client/ayon_core/style/style.css @@ -1171,6 +1171,8 @@ ValidationArtistMessage QLabel { #PublishLogMessage { font-family: "Noto Sans Mono"; + border: none; + padding: 0; } #PublishInstanceLogsLabel { diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 2684f85356..58c78c0b06 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1117,6 +1117,54 @@ class LogIconFrame(QtWidgets.QFrame): painter.end() +class LogItemMessage(QtWidgets.QTextEdit): + def __init__(self, msg, parent): + super().__init__(msg, parent) + + self.setObjectName("PublishLogMessage") + self.setReadOnly(True) + self.setFrameStyle(QtWidgets.QFrame.NoFrame) + self.setLineWidth(0) + self.setMidLineWidth(0) + pal = self.palette() + pal.setColor(QtGui.QPalette.Base, QtCore.Qt.transparent) + self.setPalette(pal) + self.setContentsMargins(0, 0, 0, 0) + viewport = self.viewport() + viewport.setContentsMargins(0, 0, 0, 0) + + self.setTextInteractionFlags( + QtCore.Qt.TextBrowserInteraction) + self.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) + self.setLineWrapMode(QtWidgets.QTextEdit.WidgetWidth) + self.setWordWrapMode( + QtGui.QTextOption.WrapMode.WrapAtWordBoundaryOrAnywhere + ) + self.setSizePolicy( + QtWidgets.QSizePolicy.Preferred, + QtWidgets.QSizePolicy.Maximum + ) + document = self.document() + document.documentLayout().documentSizeChanged.connect( + self._adjust_minimum_size + ) + document.setDocumentMargin(0.0) + self._height = None + + def _adjust_minimum_size(self, size): + self._height = size.height() + (2 * self.frameWidth()) + self.updateGeometry() + + def sizeHint(self): + size = super().sizeHint() + if self._height is not None: + size.setHeight(self._height) + return size + + def minimumSizeHint(self): + return self.sizeHint() + + class LogItemWidget(QtWidgets.QWidget): log_level_to_flag = { 10: LOG_DEBUG_VISIBLE, @@ -1132,12 +1180,7 @@ class LogItemWidget(QtWidgets.QWidget): type_flag, level_n = self._get_log_info(log) icon_label = LogIconFrame( self, log["type"], level_n, log.get("is_validation_error")) - message_label = QtWidgets.QLabel(log["msg"].rstrip(), self) - message_label.setObjectName("PublishLogMessage") - message_label.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction) - message_label.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) - message_label.setWordWrap(True) + message_label = LogItemMessage(log["msg"].rstrip(), self) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) @@ -1290,6 +1333,7 @@ class InstanceLogsWidget(QtWidgets.QWidget): label_widget = QtWidgets.QLabel(instance.label, self) label_widget.setObjectName("PublishInstanceLogsLabel") + label_widget.setWordWrap(True) logs_grid = LogsWithIconsView(instance.logs, self) layout = QtWidgets.QVBoxLayout(self) @@ -1329,10 +1373,10 @@ class InstancesLogsView(QtWidgets.QFrame): content_wrap_widget = QtWidgets.QWidget(scroll_area) content_wrap_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + content_wrap_widget.setMinimumWidth(80) content_widget = QtWidgets.QWidget(content_wrap_widget) content_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) - content_widget.setMinimumSize(80, 80) content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(8, 8, 8, 8) From 22546fd9c695a5e02a61252414a153b76ee9921a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 24 Jan 2025 17:10:33 +0100 Subject: [PATCH 304/463] remove whitespaces --- client/ayon_core/tools/publisher/widgets/report_page.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index 58c78c0b06..c0c1120fc9 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1164,7 +1164,7 @@ class LogItemMessage(QtWidgets.QTextEdit): def minimumSizeHint(self): return self.sizeHint() - + class LogItemWidget(QtWidgets.QWidget): log_level_to_flag = { 10: LOG_DEBUG_VISIBLE, From aadd107975137ae4fcc611e93057bc2532850bb5 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 27 Jan 2025 13:04:00 +0100 Subject: [PATCH 305/463] Remove unecessary offsets. --- client/ayon_core/pipeline/editorial.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index 6e5e2ec67a..e6e6294a81 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -344,8 +344,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): # modifiers time_scalar = 1. - offset_in = 0 - offset_out = 0 time_warp_nodes = [] # Check for speed effects and adjust playback speed accordingly @@ -379,17 +377,12 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): # add to timewarp nodes time_warp_nodes.append(tw_node) - # multiply by time scalar - offset_in *= time_scalar - offset_out *= time_scalar - # scale handles handle_start *= abs(time_scalar) handle_end *= abs(time_scalar) # flip offset and handles if reversed speed if time_scalar < 0: - offset_in, offset_out = offset_out, offset_in handle_start, handle_end = handle_end, handle_start # If media source is an image sequence, returned @@ -423,7 +416,7 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): else: # compute retimed range - media_in_trimmed = conformed_source_range.start_time.value + offset_in + media_in_trimmed = conformed_source_range.start_time.value offset_duration = ( conformed_source_range.duration.value From 521e50619ecda3e8001b1280c2e46952d8c3a2de Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 27 Jan 2025 13:55:52 +0100 Subject: [PATCH 306/463] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- server/settings/publish_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 2cca9cd66e..18e7d67f90 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1034,7 +1034,7 @@ DEFAULT_PUBLISH_VALUES = { "nuke", "photoshop", "substancepainter", - "silhouette" + "silhouette", ], "enabled": True, "optional": False, @@ -1055,7 +1055,7 @@ DEFAULT_PUBLISH_VALUES = { "photoshop", "aftereffects", "fusion", - "silhouette" + "silhouette", ], "enabled": True, "optional": True, From 83b7c3d4429244126d984dc20bc7bcf5a663ab3e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:00:54 +0100 Subject: [PATCH 307/463] fix new-line character --- client/ayon_core/tools/publisher/widgets/report_page.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/report_page.py b/client/ayon_core/tools/publisher/widgets/report_page.py index c0c1120fc9..1e46e7e52c 100644 --- a/client/ayon_core/tools/publisher/widgets/report_page.py +++ b/client/ayon_core/tools/publisher/widgets/report_page.py @@ -1119,7 +1119,10 @@ class LogIconFrame(QtWidgets.QFrame): class LogItemMessage(QtWidgets.QTextEdit): def __init__(self, msg, parent): - super().__init__(msg, parent) + super().__init__(parent) + + # Set as plain text to propagate new line characters + self.setPlainText(msg) self.setObjectName("PublishLogMessage") self.setReadOnly(True) From 3782105fc0c7b45edc11b32f3a09f26c2d542347 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:36:17 +0100 Subject: [PATCH 308/463] EnumDef allows placeholder to be set --- client/ayon_core/lib/attribute_definitions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e8327a45b6..e750c00a27 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -554,12 +554,18 @@ class EnumDef(AbstractAttrDef): """ type = "enum" + type_attributes = [ + "multiselection", + "placeholder", + ] + def __init__( self, key: str, items: "EnumItemsInputType", default: "Union[str, List[Any]]" = None, multiselection: Optional[bool] = False, + placeholder: Optional[str] = None, **kwargs ): if not items: @@ -587,6 +593,7 @@ class EnumDef(AbstractAttrDef): self.items: List["EnumItemDict"] = items self._item_values: Set[Any] = item_values_set self.multiselection: bool = multiselection + self.placeholder: Optional[str] = placeholder def convert_value(self, value): if not self.multiselection: @@ -612,7 +619,6 @@ class EnumDef(AbstractAttrDef): def serialize(self): data = super().serialize() data["items"] = copy.deepcopy(self.items) - data["multiselection"] = self.multiselection return data @staticmethod From 29dbc8b8cf50cd72825d61964d17e601733c47d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:37:14 +0100 Subject: [PATCH 309/463] set the placeholder on widgets --- client/ayon_core/tools/attribute_defs/widgets.py | 6 +++++- .../tools/utils/multiselection_combobox.py | 4 ++-- client/ayon_core/tools/utils/widgets.py | 13 ++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 93f63730f5..635855863c 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -640,11 +640,15 @@ class EnumAttrWidget(_BaseAttrDefWidget): return self.attr_def.multiselection def _ui_init(self): + placeholder = self.attr_def.placeholder if self.multiselection: - input_widget = MultiSelectionComboBox(self) + input_widget = MultiSelectionComboBox( + self, placeholder=placeholder + ) else: input_widget = CustomTextComboBox(self) + input_widget.set_placeholder(placeholder) combo_delegate = QtWidgets.QStyledItemDelegate(input_widget) input_widget.setItemDelegate(combo_delegate) self._combo_delegate = combo_delegate diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index 34361fca17..b90838267b 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -61,7 +61,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): self._block_mouse_release_timer = QtCore.QTimer(self, singleShot=True) self._initial_mouse_pos = None self._separator = separator - self._placeholder_text = placeholder + self._placeholder_text = placeholder or "" delegate = ComboItemDelegate(self) self.setItemDelegate(delegate) @@ -74,7 +74,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): return self._placeholder_text def set_placeholder_text(self, text): - self._placeholder_text = text + self._placeholder_text = text or "" self._update_size_hint() def set_custom_text(self, text): diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 4c2b418c41..8d5a11b811 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -54,7 +54,7 @@ class ComboBox(QtWidgets.QComboBox): """ def __init__(self, *args, **kwargs): - super(ComboBox, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) delegate = QtWidgets.QStyledItemDelegate() self.setItemDelegate(delegate) self.setFocusPolicy(QtCore.Qt.StrongFocus) @@ -63,7 +63,7 @@ class ComboBox(QtWidgets.QComboBox): def wheelEvent(self, event): if self.hasFocus(): - return super(ComboBox, self).wheelEvent(event) + return super().wheelEvent(event) class CustomTextComboBox(ComboBox): @@ -71,7 +71,14 @@ class CustomTextComboBox(ComboBox): def __init__(self, *args, **kwargs): self._custom_text = None - super(CustomTextComboBox, self).__init__(*args, **kwargs) + self._placeholder = placeholder + super().__init__(*args, **kwargs) + + def set_placeholder(self, placeholder: Optional[str]): + if placeholder == self._placeholder: + return + self.lineEdit().setPlaceholderText(placeholder) + self.repaint() def set_custom_text(self, text=None): if self._custom_text != text: From 4c7176c7521827c4f48f2694ba3337d2fe0bbcf7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:40:03 +0100 Subject: [PATCH 310/463] remove placeholder from single selection combobox --- client/ayon_core/tools/attribute_defs/widgets.py | 4 +--- client/ayon_core/tools/utils/widgets.py | 7 ------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 635855863c..201fd5be48 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -640,15 +640,13 @@ class EnumAttrWidget(_BaseAttrDefWidget): return self.attr_def.multiselection def _ui_init(self): - placeholder = self.attr_def.placeholder if self.multiselection: input_widget = MultiSelectionComboBox( - self, placeholder=placeholder + self, placeholder=self.attr_def.placeholder ) else: input_widget = CustomTextComboBox(self) - input_widget.set_placeholder(placeholder) combo_delegate = QtWidgets.QStyledItemDelegate(input_widget) input_widget.setItemDelegate(combo_delegate) self._combo_delegate = combo_delegate diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 8d5a11b811..059a06648b 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -71,15 +71,8 @@ class CustomTextComboBox(ComboBox): def __init__(self, *args, **kwargs): self._custom_text = None - self._placeholder = placeholder super().__init__(*args, **kwargs) - def set_placeholder(self, placeholder: Optional[str]): - if placeholder == self._placeholder: - return - self.lineEdit().setPlaceholderText(placeholder) - self.repaint() - def set_custom_text(self, text=None): if self._custom_text != text: self._custom_text = text From a312c463842bbeae1053054503f80168faa7abd3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:40:14 +0100 Subject: [PATCH 311/463] added placeholder to docstring --- client/ayon_core/lib/attribute_definitions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e750c00a27..c901e0f03e 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -550,6 +550,8 @@ class EnumDef(AbstractAttrDef): passed items or list of values for multiselection. multiselection (Optional[bool]): If True, multiselection is allowed. Output is list of selected items. + placeholder (Optional[str]): Placeholder for UI purposes, only for + multiselection enumeration. """ type = "enum" From 5684c941deb0f2f62cf71f69667f786217ded612 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:51:17 +0100 Subject: [PATCH 312/463] 'CreatedInstance' allows to pass in transient data --- .../ayon_core/pipeline/create/structures.py | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/create/structures.py b/client/ayon_core/pipeline/create/structures.py index a45e053cca..17bb85b720 100644 --- a/client/ayon_core/pipeline/create/structures.py +++ b/client/ayon_core/pipeline/create/structures.py @@ -1,6 +1,7 @@ import copy import collections from uuid import uuid4 +import typing from typing import Optional, Dict, List, Any from ayon_core.lib.attribute_definitions import ( @@ -17,6 +18,9 @@ from ayon_core.pipeline import ( from .exceptions import ImmutableKeyError from .changes import TrackChangesItem +if typing.TYPE_CHECKING: + from .creator_plugins import BaseCreator + class ConvertorItem: """Item representing convertor plugin. @@ -444,10 +448,11 @@ class CreatedInstance: def __init__( self, - product_type, - product_name, - data, - creator, + product_type: str, + product_name: str, + data: Dict[str, Any], + creator: "BaseCreator", + transient_data: Optional[Dict[str, Any]] = None, ): self._creator = creator creator_identifier = creator.identifier @@ -462,7 +467,9 @@ class CreatedInstance: self._members = [] # Data that can be used for lifetime of object - self._transient_data = {} + if transient_data is None: + transient_data = {} + self._transient_data = transient_data # Create a copy of passed data to avoid changing them on the fly data = copy.deepcopy(data or {}) @@ -787,16 +794,26 @@ class CreatedInstance: self._create_context.instance_create_attr_defs_changed(self.id) @classmethod - def from_existing(cls, instance_data, creator): + def from_existing( + cls, + instance_data: Dict[str, Any], + creator: "BaseCreator", + transient_data: Optional[Dict[str, Any]] = None, + ) -> "CreatedInstance": """Convert instance data from workfile to CreatedInstance. Args: instance_data (Dict[str, Any]): Data in a structure ready for 'CreatedInstance' object. creator (BaseCreator): Creator plugin which is creating the - instance of for which the instance belong. - """ + instance of for which the instance belongs. + transient_data (Optional[dict[str, Any]]): Instance transient + data. + Returns: + CreatedInstance: Instance object. + + """ instance_data = copy.deepcopy(instance_data) product_type = instance_data.get("productType") @@ -809,7 +826,11 @@ class CreatedInstance: product_name = instance_data.get("subset") return cls( - product_type, product_name, instance_data, creator + product_type, + product_name, + instance_data, + creator, + transient_data=transient_data, ) def attribute_value_changed(self, key, changes): From f5bd7a9172f7a69aab66cecddb73ba973e03b50b Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 28 Jan 2025 15:22:06 +0100 Subject: [PATCH 313/463] Apply frame offset to timewarp to handle source frame offset. --- client/ayon_core/pipeline/editorial.py | 32 +++++++ .../test_media_range_with_retimes.py | 88 +++++++++---------- 2 files changed, 76 insertions(+), 44 deletions(-) diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index e6e6294a81..8b6cfc52f1 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -441,6 +441,15 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): in_frame += time_scalar frame_range.append(in_frame) + # Different editorial DCC might have different TimeWarp logic. + # The following logic assumes that the "lookup" list values are + # frame offsets relative to the current source frame number. + # + # media_source_range |______1_____|______2______|______3______| + # + # media_retimed_range |______2_____|______2______|______3______| + # + # TimeWarp lookup +1 0 0 for tw_idx, tw in enumerate(time_warp_nodes): for idx, frame_number in enumerate(frame_range): # First timewarp, apply on media range @@ -467,9 +476,32 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): ) # adjust range if needed + media_in_trimmed_before_tw = media_in_trimmed media_in_trimmed = max(min(frame_range), media_in) media_out_trimmed = min(max(frame_range), media_out) + # If TimeWarp changes the first frame of the soure range, + # we need to offset the first TimeWarp values accordingly. + # + # expected_range |______2_____|______2______|______3______| + # + # EDITORIAL + # media_source_range |______1_____|______2______|______3______| + # + # TimeWarp lookup +1 0 0 + # + # EXTRACTED PLATE + # plate_range |______2_____|______3______|_ _ _ _ _ _ _| + # + # expected TimeWarp 0 -1 -1 + if media_in_trimmed != media_in_trimmed_before_tw: + offset = media_in_trimmed_before_tw - media_in_trimmed + offset *= 1.0 / time_scalar + time_warp_nodes[0]["lookup"] = [ + value + offset + for value in time_warp_nodes[0]["lookup"] + ] + # adjust available handles if needed if (media_in_trimmed - media_in) < handle_start: handle_start = max(0, media_in_trimmed - media_in) diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index fbab60623f..112d00b3e4 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -231,17 +231,17 @@ def test_movie_timewarp(): 'Class': 'TimeWarp', 'length': 4.0, 'lookup': [ - 2.0, - 1.8959999809265136, - 1.767999971389771, - 1.59199997138977, - 1.3439999809265135, - 1.0, - 0.5440000181198119, - -0.007999974250793684, - -0.6319999756813051, - -1.3039999847412114, - -2.0 + 0.0, + -0.10400001907348644, + -0.23200002861022906, + -0.4080000286102301, + -0.6560000190734865, + -1.0, + -1.455999981880188, + -2.0079999742507937, + -2.631999975681305, + -3.3039999847412114, + -4.0 ], 'name': 'TimeWarp2' } @@ -581,17 +581,17 @@ def test_img_sequence_timewarp_beyond_range(): 'Class': 'TimeWarp', 'length': 1.0, 'lookup': [ - -5.0, - -3.9440000305175777, - -2.852000034332275, - -1.6880000228881844, - -0.4160000076293944, - 1.0, - 2.5839999923706056, - 4.311999977111817, - 6.147999965667726, - 8.055999969482421, - 10.0 + 0.0, + 1.0559999694824223, + 2.147999965667725, + 3.3119999771118156, + 4.583999992370606, + 6.0, + 7.583999992370606, + 9.311999977111817, + 11.147999965667726, + 13.055999969482421, + 15.0 ], 'name': 'TimeWarp3' } @@ -632,17 +632,17 @@ def test_img_sequence_2X_speed_timewarp(): 'Class': 'TimeWarp', 'length': 4.0, 'lookup': [ - 2.0, - 1.7039999923706055, - 1.431999991416931, - 1.2079999942779531, - 1.055999998092652, - 1.0, - 1.056000007629395, - 1.208000022888184, - 1.432000034332276, - 1.7040000305175766, - 2.0 + 0.0, + -0.2960000076293945, + -0.568000008583069, + -0.7920000057220469, + -0.944000001907348, + -1.0, + -0.9439999923706051, + -0.791999977111816, + -0.5679999656677239, + -0.29599996948242335, + 0.0 ], 'name': 'TimeWarp6' } @@ -682,17 +682,17 @@ def test_img_sequence_multiple_timewarps(): 'Class': 'TimeWarp', 'length': 1.0, 'lookup': [ - -5.0, - -3.9440000305175777, - -2.852000034332275, - -1.6880000228881844, - -0.4160000076293944, - 1.0, - 2.5839999923706056, - 4.311999977111817, - 6.147999965667726, - 8.055999969482421, - 10.0 + 0.0, + 1.0559999694824223, + 2.147999965667725, + 3.3119999771118156, + 4.583999992370606, + 6.0, + 7.583999992370606, + 9.311999977111817, + 11.147999965667726, + 13.055999969482421, + 15.0 ], 'name': 'TimeWarp3' }, From d25c4701d1ec09f1aa81b9a80b356e79e3215456 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 29 Jan 2025 12:04:38 +0100 Subject: [PATCH 314/463] Fix Version follow up from Workfile set by hosts. --- .../ayon_core/pipeline/create/creator_plugins.py | 15 +++++++++++++-- .../plugins/publish/collect_scene_version.py | 7 +++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index 445b41cb4b..f29d67f263 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -858,13 +858,24 @@ class Creator(BaseCreator): ["CollectAnatomyInstanceData"] ["follow_workfile_version"] ) + follow_version_hosts = ( + publish_settings + ["CollectSceneVersion"] + ["hosts"] + ) + + current_host = create_ctx.host.name + follow_workfile_version = ( + follow_workfile_version and + current_host in follow_version_hosts + ) # Gather version number provided from the instance. + current_workfile = create_ctx.get_current_workfile_path() version = instance.get("version") # If follow workfile, gather version from workfile path. - if version is None and follow_workfile_version: - current_workfile = self.create_context.get_current_workfile_path() + if version is None and follow_workfile_version and current_workfile: workfile_version = get_version_from_path(current_workfile) version = int(workfile_version) diff --git a/client/ayon_core/plugins/publish/collect_scene_version.py b/client/ayon_core/plugins/publish/collect_scene_version.py index 8d643062bc..8758bc2181 100644 --- a/client/ayon_core/plugins/publish/collect_scene_version.py +++ b/client/ayon_core/plugins/publish/collect_scene_version.py @@ -46,6 +46,13 @@ class CollectSceneVersion(pyblish.api.ContextPlugin): self.log.debug("Skipping for headless publishing") return + if context.data["hostName"] not in self.hosts: + self.log.debug( + f"Host {context.data['hostName']} is" + " not setup for following version." + ) + return + if not context.data.get('currentFile'): self.log.error("Cannot get current workfile path. " "Make sure your scene is saved.") From 3731e1226a840996f68dd61579f8bb0a5ba38063 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 29 Jan 2025 12:36:00 +0100 Subject: [PATCH 315/463] Fix lint + log message. --- client/ayon_core/pipeline/create/creator_plugins.py | 2 +- client/ayon_core/plugins/publish/collect_scene_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index f29d67f263..a7f191bc44 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -866,7 +866,7 @@ class Creator(BaseCreator): current_host = create_ctx.host.name follow_workfile_version = ( - follow_workfile_version and + follow_workfile_version and current_host in follow_version_hosts ) diff --git a/client/ayon_core/plugins/publish/collect_scene_version.py b/client/ayon_core/plugins/publish/collect_scene_version.py index 8758bc2181..fcd57f4110 100644 --- a/client/ayon_core/plugins/publish/collect_scene_version.py +++ b/client/ayon_core/plugins/publish/collect_scene_version.py @@ -49,7 +49,7 @@ class CollectSceneVersion(pyblish.api.ContextPlugin): if context.data["hostName"] not in self.hosts: self.log.debug( f"Host {context.data['hostName']} is" - " not setup for following version." + " not setup for collecting scene version." ) return From 1b109d761feb890a732b0995ead852c0a54db0e5 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 29 Jan 2025 14:03:23 +0100 Subject: [PATCH 316/463] Fix host refresh in publish/lib.py --- client/ayon_core/pipeline/publish/lib.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 25495ed38b..62a68bc841 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -460,8 +460,19 @@ def filter_pyblish_plugins(plugins): ) apply_plugin_settings_automatically(plugin, plugin_settins, log) + # Pyblish already operated a filter based on host. + # But applying settings might have changed "hosts" + # value in plugin so re-filter. + plugin_hosts = getattr(plugin, "hosts", None) + if ( + plugin_hosts + and "*" not in plugin_hosts + and host_name not in plugin_hosts + ): + plugins.remove(plugin) + # Remove disabled plugins - if getattr(plugin, "enabled", True) is False: + elif getattr(plugin, "enabled", True) is False: plugins.remove(plugin) From 3f691607e54274370747c84abeb358a837f079aa Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 29 Jan 2025 19:33:43 +0100 Subject: [PATCH 317/463] Fix [clip_media] reviewable. --- .../plugins/publish/collect_otio_review.py | 19 ++++++++++++++----- .../publish/collect_otio_subset_resources.py | 17 ----------------- .../plugins/publish/extract_otio_review.py | 3 +++ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 4708b0a97c..36ef8f46d8 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -36,6 +36,16 @@ class CollectOtioReview(pyblish.api.InstancePlugin): # optionally get `reviewTrack` review_track_name = instance.data.get("reviewTrack") + # [clip_media] setting: + # Extract current clip source range as reviewable. + # Flag review content from otio_clip. + if not review_track_name and "review" in instance.data["families"]: + otio_review_clips = [otio_clip] + + # skip if no review track available + elif not review_track_name: + return + # generate range in parent otio_tl_range = otio_clip.range_in_parent() @@ -43,13 +53,12 @@ class CollectOtioReview(pyblish.api.InstancePlugin): clip_frame_end = int( otio_tl_range.start_time.value + otio_tl_range.duration.value) - # skip if no review track available - if not review_track_name: - return - # loop all tracks and match with name in `reviewTrack` for track in otio_timeline.tracks: - if review_track_name != track.name: + if ( + review_track_name is None + or review_track_name != track.name + ): continue # process correct track diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index deb51f62a5..d07c956856 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -195,13 +195,6 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename) - if ( - not instance.data.get("otioReviewClips") - and "review" in instance.data["families"] - ): - review_repre = self._create_representation( - frame_start, frame_end, collection=collection, - delete=True, review=True) else: _trim = False @@ -217,13 +210,6 @@ class CollectOtioSubsetResources( repre = self._create_representation( frame_start, frame_end, file=filename, trim=_trim) - if ( - not instance.data.get("otioReviewClips") - and "review" in instance.data["families"] - ): - review_repre = self._create_representation( - frame_start, frame_end, - file=filename, delete=True, review=True) instance.data["originalDirname"] = self.staging_dir @@ -236,9 +222,6 @@ class CollectOtioSubsetResources( instance.data["representations"].append(repre) - # add review representation to instance data - if review_repre: - instance.data["representations"].append(review_repre) self.log.debug(instance.data) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index 275c3e7c58..c2788af77c 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -320,6 +320,9 @@ class ExtractOTIOReview( end = max(collection.indexes) files = [f for f in collection] + # single frame sequence + if len(files) == 1: + files = files[0] ext = collection.format("{tail}") representation_data.update({ "name": ext[1:], From fd63c97f4e9baf75027895c9460b548ed453ed8a Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 29 Jan 2025 19:56:34 +0100 Subject: [PATCH 318/463] Address feedback from PR. --- client/ayon_core/pipeline/publish/lib.py | 7 +----- .../plugins/publish/collect_scene_version.py | 25 +------------------ 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 62a68bc841..9adfc5e9e2 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -463,12 +463,7 @@ def filter_pyblish_plugins(plugins): # Pyblish already operated a filter based on host. # But applying settings might have changed "hosts" # value in plugin so re-filter. - plugin_hosts = getattr(plugin, "hosts", None) - if ( - plugin_hosts - and "*" not in plugin_hosts - and host_name not in plugin_hosts - ): + if not pyblish.plugin.host_is_compatible(plugin): plugins.remove(plugin) # Remove disabled plugins diff --git a/client/ayon_core/plugins/publish/collect_scene_version.py b/client/ayon_core/plugins/publish/collect_scene_version.py index fcd57f4110..7979b66abe 100644 --- a/client/ayon_core/plugins/publish/collect_scene_version.py +++ b/client/ayon_core/plugins/publish/collect_scene_version.py @@ -14,23 +14,7 @@ class CollectSceneVersion(pyblish.api.ContextPlugin): order = pyblish.api.CollectorOrder label = 'Collect Scene Version' # configurable in Settings - hosts = [ - "aftereffects", - "blender", - "celaction", - "fusion", - "harmony", - "hiero", - "houdini", - "maya", - "max", - "nuke", - "photoshop", - "resolve", - "tvpaint", - "motionbuilder", - "substancepainter" - ] + hosts = ["*"] # in some cases of headless publishing (for example webpublisher using PS) # you want to ignore version from name and let integrate use next version @@ -46,13 +30,6 @@ class CollectSceneVersion(pyblish.api.ContextPlugin): self.log.debug("Skipping for headless publishing") return - if context.data["hostName"] not in self.hosts: - self.log.debug( - f"Host {context.data['hostName']} is" - " not setup for collecting scene version." - ) - return - if not context.data.get('currentFile'): self.log.error("Cannot get current workfile path. " "Make sure your scene is saved.") From be5eb08d2b6ff0ee9dae3592e8c157835e414b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 30 Jan 2025 10:57:21 +0100 Subject: [PATCH 319/463] Update client/ayon_core/plugins/publish/collect_otio_frame_ranges.py Co-authored-by: Robin De Lillo --- .../ayon_core/plugins/publish/collect_otio_frame_ranges.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 917b9ad206..5d306138eb 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -123,10 +123,7 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): otio_src_range_handles = otio_range_with_handles(otio_src_range, instance) # Get source available start frame - src_starting_from = otio.opentime.to_frames( - otio_available_range.start_time, - otio_available_range.start_time.rate - ) + src_starting_from = otio_available_range.to_frames() # Convert to frames src_start, src_end = otio_range_to_frame_range(otio_src_range) From f91baa0e1ebd17d171b23eaeac9096569654a71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 30 Jan 2025 10:58:04 +0100 Subject: [PATCH 320/463] Update client/ayon_core/plugins/publish/collect_otio_frame_ranges.py Co-authored-by: Robin De Lillo --- client/ayon_core/plugins/publish/collect_otio_frame_ranges.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 5d306138eb..0187858be8 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -101,8 +101,7 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): tl_start_h, tl_end_h = otio_range_to_frame_range(otio_tl_range_handles) frame_start = workfile_start - frame_end = frame_start + otio.opentime.to_frames( - otio_tl_range.duration, otio_tl_range.duration.rate) - 1 + frame_end = frame_start + otio_tl_range.duration.to_frames() - 1 data = { "frameStart": frame_start, From 888e81fac81f2bbe6fe759c9e34b0787aca26e4b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 11:12:37 +0100 Subject: [PATCH 321/463] Fix OTIO frame range handling and add compatibility - Added backward compatibility for Hiero OTIO exporter. - Implemented rounding for floating rates to avoid precision issues. - Rescaled source ranges based on available range rates. - Updated calculations for source start, end, and handles. --- .../publish/collect_otio_frame_ranges.py | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 0187858be8..f93301d0f6 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -119,20 +119,46 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): # Get source ranges otio_src_range = otio_clip.source_range otio_available_range = otio_clip.available_range() - otio_src_range_handles = otio_range_with_handles(otio_src_range, instance) - # Get source available start frame - src_starting_from = otio_available_range.to_frames() - - # Convert to frames - src_start, src_end = otio_range_to_frame_range(otio_src_range) - src_start_h, src_end_h = otio_range_to_frame_range(otio_src_range_handles) + # Backward-compatibility for Hiero OTIO exporter. + # NTSC compatibility might introduce floating rates, when these are + # not exactly the same (23.976 vs 23.976024627685547) + # this will cause precision issue in computation. + # Currently round to 2 decimals for comparison, + # but this should always rescale after that. + rounded_av_rate = round(otio_available_range.start_time.rate, 2) + rounded_src_rate = round(otio_src_range.start_time.rate, 2) + if rounded_av_rate != rounded_src_rate: + conformed_src_in = otio_src_range.start_time.rescaled_to( + otio_available_range.start_time.rate + ) + conformed_src_duration = otio_src_range.duration.rescaled_to( + otio_available_range.duration.rate + ) + conformed_source_range = otio.opentime.TimeRange( + start_time=conformed_src_in, + duration=conformed_src_duration + ) + else: + conformed_source_range = otio_src_range + source_start = conformed_source_range.start_time + source_end = source_start + conformed_source_range.duration + handle_start = otio.opentime.RationalTime( + instance.data.get("handleStart", 0), + source_start.rate + ) + handle_end = otio.opentime.RationalTime( + instance.data.get("handleEnd", 0), + source_start.rate + ) + source_start_h = source_start - handle_start + source_end_h = source_end + handle_end data = { - "sourceStart": src_starting_from + src_start, - "sourceEnd": src_starting_from + src_end - 1, - "sourceStartH": src_starting_from + src_start_h, - "sourceEndH": src_starting_from + src_end_h - 1, + "sourceStart": source_start.to_frames(), + "sourceEnd": source_end.to_frames() - 1, + "sourceStartH": source_start_h.to_frames(), + "sourceEndH": source_end_h.to_frames() - 1, } instance.data.update(data) self.log.debug(f"Added source ranges: {pformat(data)}") From de82d8b60cb944148bba9047e89c04196de5658a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 11:26:46 +0100 Subject: [PATCH 322/463] Add handling for clips without available ranges - Added a check for available range in OTIO clips. - Improved logging to inform when a clip has no available range. - Adjusted source range collection logic based on availability. --- .../plugins/publish/collect_otio_frame_ranges.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index f93301d0f6..34c12a1cf3 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -80,8 +80,18 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): if "workfileFrameStart" in instance.data: self._collect_timeline_ranges(instance, otio_clip) + has_available_range = False + # Traypublisher Simple or Advanced editorial publishing is + # working with otio clips which are having no available range + # because they are not having any media references. + try: + otio_clip.available_range() + has_available_range = True + except otio._otio.CannotComputeAvailableRangeError: + self.log.info("Clip has no available range") + # Collect source ranges if clip has available range - if hasattr(otio_clip, 'available_range') and otio_clip.available_range(): + if has_available_range: self._collect_source_ranges(instance, otio_clip) # Handle retimed ranges if source duration is available From ca03c4d86d2c08b347f4363030ec32b0a96b7721 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Jan 2025 12:05:39 +0100 Subject: [PATCH 323/463] Add support for `optional_tooltip` attribute on `OptionalPyblishPluginMixin` --- client/ayon_core/pipeline/publish/publish_plugins.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 57215eff68..94c0307ca0 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -304,8 +304,11 @@ class OptionalPyblishPluginMixin(AYONPyblishPluginMixin): active = getattr(cls, "active", True) # Return boolean stored under 'active' key with label of the class name label = cls.label or cls.__name__ + # Allow exposing tooltip from class with `optional_tooltip` attribute + tooltip = getattr(cls, "optional_tooltip", None) + return [ - BoolDef("active", default=active, label=label) + BoolDef("active", default=active, label=label, tooltip=tooltip) ] def is_active(self, data): From 64b6729eec537862bf05a515837936c5dff6b9a5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Jan 2025 12:40:53 +0100 Subject: [PATCH 324/463] Expose `optional_tooltip` directly as attribute on the `OptionalPyblishPluginMixin` for better auto-complete in IDEs --- client/ayon_core/pipeline/publish/publish_plugins.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 94c0307ca0..65a5a474ea 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -292,6 +292,9 @@ class OptionalPyblishPluginMixin(AYONPyblishPluginMixin): ``` """ + # Allow exposing tooltip from class with `optional_tooltip` attribute + optional_tooltip: Optional[str] = None + @classmethod def get_attribute_defs(cls): """Attribute definitions based on plugin's optional attribute.""" @@ -304,11 +307,12 @@ class OptionalPyblishPluginMixin(AYONPyblishPluginMixin): active = getattr(cls, "active", True) # Return boolean stored under 'active' key with label of the class name label = cls.label or cls.__name__ - # Allow exposing tooltip from class with `optional_tooltip` attribute - tooltip = getattr(cls, "optional_tooltip", None) return [ - BoolDef("active", default=active, label=label, tooltip=tooltip) + BoolDef("active", + default=active, + label=label, + tooltip=cls.optional_tooltip) ] def is_active(self, data): From 22769ef460a8e0a9c9972fc6a4fa90d4e7f2d549 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 15:05:32 +0100 Subject: [PATCH 325/463] Refactor frame range handling for clarity - Improved readability by breaking long lines into multiple lines. - Removed unused variable related to shot duration from source. - Cleaned up code structure in the frame range collection process. --- .../ayon_core/plugins/publish/collect_otio_frame_ranges.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 34c12a1cf3..8a8af29ca9 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -104,11 +104,13 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): # Get timeline ranges otio_tl_range = otio_clip.range_in_parent() - otio_tl_range_handles = otio_range_with_handles(otio_tl_range, instance) + otio_tl_range_handles = otio_range_with_handles( + otio_tl_range, instance) # Convert to frames tl_start, tl_end = otio_range_to_frame_range(otio_tl_range) - tl_start_h, tl_end_h = otio_range_to_frame_range(otio_tl_range_handles) + tl_start_h, tl_end_h = otio_range_to_frame_range( + otio_tl_range_handles) frame_start = workfile_start frame_end = frame_start + otio_tl_range.duration.to_frames() - 1 @@ -175,7 +177,6 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): def _collect_retimed_ranges(self, instance, otio_clip): """Handle retimed clip frame ranges.""" - workfile_source_duration = instance.data.get("shotDurationFromSource") frame_start = instance.data["frameStart"] # Handle retimed clip frame range From e1438ed597550ee5b55e46efab3354838a42088a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 31 Jan 2025 23:30:04 +0100 Subject: [PATCH 326/463] Update client/ayon_core/pipeline/publish/publish_plugins.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/publish/publish_plugins.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/publish/publish_plugins.py b/client/ayon_core/pipeline/publish/publish_plugins.py index 65a5a474ea..cc6887e762 100644 --- a/client/ayon_core/pipeline/publish/publish_plugins.py +++ b/client/ayon_core/pipeline/publish/publish_plugins.py @@ -309,10 +309,12 @@ class OptionalPyblishPluginMixin(AYONPyblishPluginMixin): label = cls.label or cls.__name__ return [ - BoolDef("active", - default=active, - label=label, - tooltip=cls.optional_tooltip) + BoolDef( + "active", + default=active, + label=label, + tooltip=cls.optional_tooltip, + ) ] def is_active(self, data): From 5c53d201244b1bf5d5914a23c936472b594fb6d9 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 3 Feb 2025 10:51:59 +0100 Subject: [PATCH 327/463] Address feedback from PR. --- client/ayon_core/pipeline/create/creator_plugins.py | 5 +++-- client/ayon_core/pipeline/publish/lib.py | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/pipeline/create/creator_plugins.py b/client/ayon_core/pipeline/create/creator_plugins.py index a7f191bc44..69cd3894af 100644 --- a/client/ayon_core/pipeline/create/creator_plugins.py +++ b/client/ayon_core/pipeline/create/creator_plugins.py @@ -877,10 +877,11 @@ class Creator(BaseCreator): # If follow workfile, gather version from workfile path. if version is None and follow_workfile_version and current_workfile: workfile_version = get_version_from_path(current_workfile) - version = int(workfile_version) + if workfile_version is not None: + version = int(workfile_version) # Fill-up version with next version available. - elif version is None: + if version is None: versions = self.get_next_versions_for_instances( [instance] ) diff --git a/client/ayon_core/pipeline/publish/lib.py b/client/ayon_core/pipeline/publish/lib.py index 9adfc5e9e2..cc5f67c74b 100644 --- a/client/ayon_core/pipeline/publish/lib.py +++ b/client/ayon_core/pipeline/publish/lib.py @@ -460,14 +460,14 @@ def filter_pyblish_plugins(plugins): ) apply_plugin_settings_automatically(plugin, plugin_settins, log) + # Remove disabled plugins + if getattr(plugin, "enabled", True) is False: + plugins.remove(plugin) + # Pyblish already operated a filter based on host. # But applying settings might have changed "hosts" # value in plugin so re-filter. - if not pyblish.plugin.host_is_compatible(plugin): - plugins.remove(plugin) - - # Remove disabled plugins - elif getattr(plugin, "enabled", True) is False: + elif not pyblish.plugin.host_is_compatible(plugin): plugins.remove(plugin) From 0293d74618c1a266215ca3051cb7d56da58f741c Mon Sep 17 00:00:00 2001 From: Robin De Lillo Date: Mon, 3 Feb 2025 11:25:34 +0100 Subject: [PATCH 328/463] Update client/ayon_core/plugins/publish/collect_otio_review.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/collect_otio_review.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 36ef8f46d8..24f2f6c3e6 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -55,10 +55,11 @@ class CollectOtioReview(pyblish.api.InstancePlugin): # loop all tracks and match with name in `reviewTrack` for track in otio_timeline.tracks: - if ( - review_track_name is None - or review_track_name != track.name - ): + # Skip the loop + if review_track_name is None: + break + + if review_track_name != track.name: continue # process correct track From e12fa847c959cd4af1145810dcf74a38433232c2 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 3 Feb 2025 11:29:34 +0100 Subject: [PATCH 329/463] Address feedback from PR. --- client/ayon_core/plugins/publish/collect_otio_review.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_review.py b/client/ayon_core/plugins/publish/collect_otio_review.py index 24f2f6c3e6..064d4e3f3b 100644 --- a/client/ayon_core/plugins/publish/collect_otio_review.py +++ b/client/ayon_core/plugins/publish/collect_otio_review.py @@ -55,10 +55,12 @@ class CollectOtioReview(pyblish.api.InstancePlugin): # loop all tracks and match with name in `reviewTrack` for track in otio_timeline.tracks: - # Skip the loop + + # No review track defined, skip the loop if review_track_name is None: break + # Not current review track, skip it. if review_track_name != track.name: continue From 07ccade3a4062f10ae5a038a614b0f3260e406d6 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 3 Feb 2025 14:54:29 +0100 Subject: [PATCH 330/463] Fix lint. --- .../ayon_core/plugins/publish/collect_otio_subset_resources.py | 1 - client/ayon_core/plugins/publish/extract_otio_review.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index d07c956856..f1fa6a817d 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -158,7 +158,6 @@ class CollectOtioSubsetResources( self.log.info( "frame_start-frame_end: {}-{}".format(frame_start, frame_end)) - review_repre = None if is_sequence: # file sequence way diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index c2788af77c..2461195b27 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -322,7 +322,7 @@ class ExtractOTIOReview( files = [f for f in collection] # single frame sequence if len(files) == 1: - files = files[0] + files = files[0] ext = collection.format("{tail}") representation_data.update({ "name": ext[1:], From b048f0aa3bae1e6c3b963ab89b88b804e0208c2a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:29:01 +0100 Subject: [PATCH 331/463] added helper method 'get_template_data' to create context --- client/ayon_core/pipeline/create/context.py | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index e29971415d..36a05725a6 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -29,6 +29,7 @@ from ayon_core.lib.events import QueuedEventSystem from ayon_core.lib.attribute_definitions import get_default_values from ayon_core.host import IPublishHost, IWorkfileHost from ayon_core.pipeline import Anatomy +from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.plugin_discover import DiscoverResult from .exceptions import ( @@ -480,6 +481,36 @@ class CreateContext: self.get_current_project_name()) return self._current_project_settings + def get_template_data( + self, folder_path: Optional[str], task_name: Optional[str] + ) -> dict[str, Any]: + """Prepare template data for given context. + + Method is using cached entities and settings to prepare template data. + + Args: + folder_path (Optional[str]): Folder path. + task_name (Optional[str]): Task name. + + Returns: + dict[str, Any]: Template data. + + """ + project_entity = self.get_current_project_entity() + folder_entity = task_entity = None + if folder_path: + folder_entity = self.get_folder_entity(folder_path) + if task_name and folder_entity: + task_entity = self.get_task_entity(folder_path, task_name) + + return get_template_data( + project_entity, + folder_entity, + task_entity, + host_name=self.host_name, + settings=self.get_current_project_settings(), + ) + @property def context_has_changed(self): """Host context has changed. From cc1ec148656a7afbf0546d35cae93350742c886f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:46:52 +0100 Subject: [PATCH 332/463] fix index validation --- client/ayon_core/lib/path_templates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/path_templates.py b/client/ayon_core/lib/path_templates.py index 057889403c..9e3e455a6c 100644 --- a/client/ayon_core/lib/path_templates.py +++ b/client/ayon_core/lib/path_templates.py @@ -587,8 +587,8 @@ class FormattingPart: if sub_key < 0: sub_key = len(value) + sub_key - invalid = 0 > sub_key < len(data) - if invalid: + valid = 0 <= sub_key < len(value) + if not valid: used_keys.append(sub_key) missing_key = True break From 43b18910bac326c00d0784b764641398738aca6b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:51:08 +0100 Subject: [PATCH 333/463] fix typehint --- client/ayon_core/pipeline/create/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index 36a05725a6..c169df67df 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -483,7 +483,7 @@ class CreateContext: def get_template_data( self, folder_path: Optional[str], task_name: Optional[str] - ) -> dict[str, Any]: + ) -> Dict[str, Any]: """Prepare template data for given context. Method is using cached entities and settings to prepare template data. From c21b95679f72099c93f02e3e55a7af3bafad402f Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 4 Feb 2025 11:51:01 +0000 Subject: [PATCH 334/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 2775cb606a..da29c02004 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.0.15-dev" +__version__ = "1.1.0" diff --git a/package.py b/package.py index af3342f3f2..fe0c7dbd18 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.0.15-dev" +version = "1.1.0" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index e040ce986f..32d101cc22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.0.15-dev" +version = "1.1.0" description = "" authors = ["Ynput Team "] readme = "README.md" From c7899fb3be31d215207c5e0cccf01a43e73408ff Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 4 Feb 2025 11:51:45 +0000 Subject: [PATCH 335/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index da29c02004..909ecd7a3c 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.0" +__version__ = "1.1.0+dev" diff --git a/package.py b/package.py index fe0c7dbd18..0b888f5c33 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.0" +version = "1.1.0+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 32d101cc22..32822391c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.0" +version = "1.1.0+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 516dd2d7cedfe1309aa033ce335d4c7130f3c693 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 4 Feb 2025 15:07:05 +0100 Subject: [PATCH 336/463] Escape & on Windows in shell using ^& in `run_subprocess` --- client/ayon_core/lib/execute.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/ayon_core/lib/execute.py b/client/ayon_core/lib/execute.py index 95696fd272..35e931a5fc 100644 --- a/client/ayon_core/lib/execute.py +++ b/client/ayon_core/lib/execute.py @@ -122,6 +122,16 @@ def run_subprocess(*args, **kwargs): ) args = (new_arg, ) + # Escape & on Windows in shell using ^& + if ( + kwargs.get("shell") is True + and len(args) == 1 + and isinstance(args[0], str) + and platform.system().lower() == "windows" + ): + new_arg = args[0].replace("&", "^&") + args = (new_arg, ) + # Get environents from kwarg or use current process environments if were # not passed. env = kwargs.get("env") or os.environ From ed0f5c8d7f300a984c139ad7849779c4e781b456 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 4 Feb 2025 15:20:28 +0100 Subject: [PATCH 337/463] Merge escape if checks + include `COMSPEC` check on Windows --- client/ayon_core/lib/execute.py | 36 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/client/ayon_core/lib/execute.py b/client/ayon_core/lib/execute.py index 35e931a5fc..516ea958f5 100644 --- a/client/ayon_core/lib/execute.py +++ b/client/ayon_core/lib/execute.py @@ -108,31 +108,29 @@ def run_subprocess(*args, **kwargs): | getattr(subprocess, "CREATE_NO_WINDOW", 0) ) - # Escape parentheses for bash + # Escape special characters in certain shells if ( kwargs.get("shell") is True and len(args) == 1 and isinstance(args[0], str) - and os.getenv("SHELL") in ("/bin/bash", "/bin/sh") ): - new_arg = ( - args[0] - .replace("(", "\\(") - .replace(")", "\\)") - ) - args = (new_arg, ) + # Escape parentheses for bash + if os.getenv("SHELL") in ("/bin/bash", "/bin/sh"): + new_arg = ( + args[0] + .replace("(", "\\(") + .replace(")", "\\)") + ) + args = (new_arg,) + # Escape & on Windows in shell with `cmd.exe` using ^& + elif ( + platform.system().lower() == "windows" + and os.getenv("COMSPEC").endswith("cmd.exe") + ): + new_arg = args[0].replace("&", "^&") + args = (new_arg, ) - # Escape & on Windows in shell using ^& - if ( - kwargs.get("shell") is True - and len(args) == 1 - and isinstance(args[0], str) - and platform.system().lower() == "windows" - ): - new_arg = args[0].replace("&", "^&") - args = (new_arg, ) - - # Get environents from kwarg or use current process environments if were + # Get environments from kwarg or use current process environments if were # not passed. env = kwargs.get("env") or os.environ # Make sure environment contains only strings From bfcfa264d97d2527905f2dc00df73c75b1c50a90 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 5 Feb 2025 16:57:59 +0800 Subject: [PATCH 338/463] add substance designer as being part of the hosts in extract thumbnail --- client/ayon_core/plugins/publish/extract_thumbnail.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 8ae18f4abf..bd2f7eb0ae 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -35,6 +35,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): "resolve", "traypublisher", "substancepainter", + "substancedesigner", "nuke", "aftereffects", "unreal", From e13de28a24c2a223fe4903421f802a4ba7fcef08 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:24:07 +0100 Subject: [PATCH 339/463] draw placeholder with disabled color --- client/ayon_core/tools/utils/multiselection_combobox.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index b90838267b..e8bc688234 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -208,7 +208,8 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): draw_text = False if draw_text: option.currentText = combotext - option.palette.setCurrentColorGroup(QtGui.QPalette.Disabled) + # Draw text as disabled -> to mimic placeholder color + option.state &= ~QtWidgets.QStyle.State_Enabled painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option) return From 71454b6fa4e31ce08028b08b813974c942f0ece6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:54:56 +0100 Subject: [PATCH 340/463] usa same placeholder color as elsewhere --- .../tools/utils/multiselection_combobox.py | 65 ++++++++++++------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index e8bc688234..7a8c4c2fcc 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -1,5 +1,7 @@ from qtpy import QtCore, QtGui, QtWidgets +from ayon_core.style import get_objected_colors + from .lib import ( checkstate_int_to_enum, checkstate_enum_to_int, @@ -49,11 +51,12 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): item_spacing = 5 item_bg_color = QtGui.QColor("#31424e") + _placeholder_color = None def __init__( self, parent=None, placeholder="", separator=", ", **kwargs ): - super(MultiSelectionComboBox, self).__init__(parent=parent, **kwargs) + super().__init__(parent=parent, **kwargs) self.setObjectName("MultiSelectionComboBox") self.setFocusPolicy(QtCore.Qt.StrongFocus) @@ -206,21 +209,23 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): combotext = self._placeholder_text else: draw_text = False - if draw_text: - option.currentText = combotext - # Draw text as disabled -> to mimic placeholder color - option.state &= ~QtWidgets.QStyle.State_Enabled - painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option) - return - font_metricts = self.fontMetrics() + lines = self._lines + if draw_text: + color = self._get_placeholder_color() + pen = painter.pen() + pen.setColor(color) + painter.setPen(pen) + lines = {0: [combotext]} + + font_metrics = self.fontMetrics() if self._item_height is None: self.updateGeometry() self.update() return - for line, items in self._lines.items(): + for line, items in lines.items(): top_y = ( option.rect.top() + (line * self._item_height) @@ -228,7 +233,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): ) left_x = option.rect.left() + self.left_offset for item in items: - label_rect = font_metricts.boundingRect(item) + label_rect = font_metrics.boundingRect(item) label_height = label_rect.height() label_rect.moveTop(top_y) @@ -238,22 +243,23 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): label_rect.width() + self.left_right_padding ) - bg_rect = QtCore.QRectF(label_rect) - bg_rect.setWidth( - label_rect.width() + self.left_right_padding - ) - left_x = bg_rect.right() + self.item_spacing + if not draw_text: + bg_rect = QtCore.QRectF(label_rect) + bg_rect.setWidth( + label_rect.width() + self.left_right_padding + ) + left_x = bg_rect.right() + self.item_spacing + + bg_rect.setHeight(label_height + (2 * self.top_bottom_padding)) + bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins) + + path = QtGui.QPainterPath() + path.addRoundedRect(bg_rect, 5, 5) + + painter.fillPath(path, self.item_bg_color) label_rect.moveLeft(label_rect.x() + self.left_right_padding) - bg_rect.setHeight(label_height + (2 * self.top_bottom_padding)) - bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins) - - path = QtGui.QPainterPath() - path.addRoundedRect(bg_rect, 5, 5) - - painter.fillPath(path, self.item_bg_color) - painter.drawText( label_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, @@ -288,11 +294,11 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): line = 0 self._lines = {line: []} - font_metricts = self.fontMetrics() + font_metrics = self.fontMetrics() default_left_x = 0 + self.left_offset left_x = int(default_left_x) for item in items: - rect = font_metricts.boundingRect(item) + rect = font_metrics.boundingRect(item) width = rect.width() + (2 * self.left_right_padding) right_x = left_x + width if right_x > total_width: @@ -383,3 +389,12 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): return event.ignore() return super(MultiSelectionComboBox, self).keyPressEvent(event) + + @classmethod + def _get_placeholder_color(cls): + if cls._placeholder_color is None: + color_obj = get_objected_colors("font") + color = color_obj.get_qcolor() + color.setAlpha(67) + cls._placeholder_color = color + return cls._placeholder_color From e30b89e36d23a97a01f7fe22086c5b4f8d1291d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:09:12 +0100 Subject: [PATCH 341/463] fix formatting --- client/ayon_core/tools/utils/multiselection_combobox.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index 7a8c4c2fcc..7bd7a76abc 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -47,7 +47,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): top_bottom_padding = 2 left_right_padding = 3 left_offset = 4 - top_bottom_margins = 2 + top_bottom_margins = 1 item_spacing = 5 item_bg_color = QtGui.QColor("#31424e") @@ -250,7 +250,9 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): ) left_x = bg_rect.right() + self.item_spacing - bg_rect.setHeight(label_height + (2 * self.top_bottom_padding)) + bg_rect.setHeight( + label_height + (2 * self.top_bottom_padding) + ) bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins) path = QtGui.QPainterPath() From 8a76ca9af74b7bb013017cd70f4a28eeb96927af Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 6 Feb 2025 14:20:56 +0100 Subject: [PATCH 342/463] Hide AutoCreator and HiddenCreator Creators from the 'CreatePlaceholder' lists --- .../pipeline/workfile/workfile_template_builder.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index 4412e4489b..f607f18431 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -54,6 +54,7 @@ from ayon_core.pipeline.plugin_discover import ( from ayon_core.pipeline.create import ( discover_legacy_creator_plugins, CreateContext, + HiddenCreator ) _NOT_SET = object() @@ -309,7 +310,12 @@ class AbstractTemplateBuilder(ABC): self._creators_by_name = creators_by_name def _collect_creators(self): - self._creators_by_name = dict(self.create_context.creators) + self._creators_by_name = { + name: creator for name, creator + in self.create_context.manual_creators.items() + # Do not list HiddenCreator even though it is a 'manual creator' + if not isinstance(creator, HiddenCreator) + } def get_creators_by_name(self): if self._creators_by_name is None: From d97cff327ac61ef329f3446fc1472f6cf63f0378 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 6 Feb 2025 14:21:15 +0100 Subject: [PATCH 343/463] Cosmetics --- client/ayon_core/pipeline/workfile/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index f607f18431..af3694e6e6 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -54,7 +54,7 @@ from ayon_core.pipeline.plugin_discover import ( from ayon_core.pipeline.create import ( discover_legacy_creator_plugins, CreateContext, - HiddenCreator + HiddenCreator, ) _NOT_SET = object() From c9e2b05636cd87ec094bd478324535fd324fad81 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 6 Feb 2025 14:24:17 +0100 Subject: [PATCH 344/463] Update client/ayon_core/pipeline/workfile/workfile_template_builder.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../ayon_core/pipeline/workfile/workfile_template_builder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/workfile/workfile_template_builder.py b/client/ayon_core/pipeline/workfile/workfile_template_builder.py index af3694e6e6..27da278c5e 100644 --- a/client/ayon_core/pipeline/workfile/workfile_template_builder.py +++ b/client/ayon_core/pipeline/workfile/workfile_template_builder.py @@ -311,7 +311,8 @@ class AbstractTemplateBuilder(ABC): def _collect_creators(self): self._creators_by_name = { - name: creator for name, creator + identifier: creator + for identifier, creator in self.create_context.manual_creators.items() # Do not list HiddenCreator even though it is a 'manual creator' if not isinstance(creator, HiddenCreator) From ece0631c680891800a98afd391b1b9f129af3721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 6 Feb 2025 14:29:05 +0100 Subject: [PATCH 345/463] :dog: update ruff action --- .github/workflows/pr_linting.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_linting.yml b/.github/workflows/pr_linting.yml index 3d2431b69a..896d5b7f4d 100644 --- a/.github/workflows/pr_linting.yml +++ b/.github/workflows/pr_linting.yml @@ -21,4 +21,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: chartboost/ruff-action@v1 + - uses: astral-sh/ruff-action@v1 + with: + changed-files: "true" From e0507d99d2829fd6296878899468146f92b5de0d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 6 Feb 2025 22:20:27 +0800 Subject: [PATCH 346/463] add substance designer into the template --- server/settings/tools.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index 96851be1da..0aaa88a8b0 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -484,6 +484,17 @@ DEFAULT_TOOLS_VALUES = { "task_types": [], "tasks": [], "template": "{folder[name]}_{variant}" + }, + { + "product_types": [ + "textureSet" + ], + "hosts": [ + "substancedesigner" + ], + "task_types": [], + "tasks": [], + "template": "T_{folder[name]}{variant}" } ], "filter_creator_profiles": [] @@ -548,10 +559,13 @@ DEFAULT_TOOLS_VALUES = { }, { "product_types": [ - "simpleUnrealTexture" + "simpleUnrealTexture", + "image", + "textures" ], "hosts": [ - "standalonepublisher" + "standalonepublisher", + "substancedesigner" ], "task_types": [], "task_names": [], @@ -595,10 +609,13 @@ DEFAULT_TOOLS_VALUES = { "hero_template_name_profiles": [ { "product_types": [ - "simpleUnrealTexture" + "simpleUnrealTexture", + "image", + "textures" ], "hosts": [ - "standalonepublisher" + "standalonepublisher", + "substancedesigner" ], "task_types": [], "task_names": [], From c6f5988aae75a1fb514f20ec56bb50985b145bea Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:13:29 +0100 Subject: [PATCH 347/463] use same font size for placeholder as elsewhere --- .../tools/utils/multiselection_combobox.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index 7bd7a76abc..a6198abb51 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -210,22 +210,36 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): else: draw_text = False - lines = self._lines if draw_text: color = self._get_placeholder_color() pen = painter.pen() pen.setColor(color) painter.setPen(pen) - lines = {0: [combotext]} - font_metrics = self.fontMetrics() + left_x = option.rect.left() + self.left_offset + + font = self.font() + # This is hardcoded point size from styles + font.setPointSize(10) + painter.setFont(font) + + label_rect = QtCore.QRect(option.rect) + label_rect.moveLeft(left_x) + + painter.drawText( + label_rect, + QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, + combotext + ) + return if self._item_height is None: self.updateGeometry() self.update() return - for line, items in lines.items(): + font_metrics = self.fontMetrics() + for line, items in self._lines.items(): top_y = ( option.rect.top() + (line * self._item_height) From a409d55b5904525797f0520c4a872a7776ad1123 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 7 Feb 2025 16:53:57 +0800 Subject: [PATCH 348/463] big roy's comment - separate template for substancedesigner from standalonepublisher --- server/settings/tools.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index 0aaa88a8b0..32c72e7a98 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -559,12 +559,21 @@ DEFAULT_TOOLS_VALUES = { }, { "product_types": [ - "simpleUnrealTexture", - "image", - "textures" + "simpleUnrealTexture" + ], + "hosts": [ + "standalonepublisher" + ], + "task_types": [], + "task_names": [], + "template_name": "simpleUnrealTexture" + }, + { + "product_types": [ + "image", + "textures", ], "hosts": [ - "standalonepublisher", "substancedesigner" ], "task_types": [], @@ -609,12 +618,21 @@ DEFAULT_TOOLS_VALUES = { "hero_template_name_profiles": [ { "product_types": [ - "simpleUnrealTexture", + "simpleUnrealTexture" + ], + "hosts": [ + "standalonepublisher" + ], + "task_types": [], + "task_names": [], + "template_name": "simpleUnrealTextureHero" + }, + { + "product_types": [ "image", "textures" ], "hosts": [ - "standalonepublisher", "substancedesigner" ], "task_types": [], From cdf7af65bf9ecad7fe6bb1fee7760ab22bf0cc6b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:20:56 +0100 Subject: [PATCH 349/463] pass 'AYON_USE_STAGING' instead of 'AYON_DEFAULT_SETTINGS_VARIANT' --- client/ayon_core/plugins/publish/collect_farm_env_variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index ee88985905..2a58b580cd 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -32,7 +32,7 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): for key in [ "AYON_BUNDLE_NAME", - "AYON_DEFAULT_SETTINGS_VARIANT", + "AYON_USE_STAGING", "AYON_IN_TESTS", # NOTE Not sure why workdir is needed? "AYON_WORKDIR", From ccd5e447842b632ffad3f0ec352db8d6ccedf799 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:52:03 +0100 Subject: [PATCH 350/463] keep filling 'AYON_DEFAULT_SETTINGS_VARIANT' for now --- client/ayon_core/plugins/publish/collect_farm_env_variables.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 2a58b580cd..7ee3356cee 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -36,6 +36,8 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): "AYON_IN_TESTS", # NOTE Not sure why workdir is needed? "AYON_WORKDIR", + # DEPRECATED remove when deadline stops using it (added in 1.1.1) + "AYON_DEFAULT_SETTINGS_VARIANT", ]: value = os.getenv(key) if value: From 4b51f6479f391e853642a24059193fa3c244a772 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 7 Feb 2025 12:16:03 +0100 Subject: [PATCH 351/463] change added version --- client/ayon_core/plugins/publish/collect_farm_env_variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/collect_farm_env_variables.py b/client/ayon_core/plugins/publish/collect_farm_env_variables.py index 7ee3356cee..2782ea86ac 100644 --- a/client/ayon_core/plugins/publish/collect_farm_env_variables.py +++ b/client/ayon_core/plugins/publish/collect_farm_env_variables.py @@ -36,7 +36,7 @@ class CollectCoreJobEnvVars(pyblish.api.ContextPlugin): "AYON_IN_TESTS", # NOTE Not sure why workdir is needed? "AYON_WORKDIR", - # DEPRECATED remove when deadline stops using it (added in 1.1.1) + # DEPRECATED remove when deadline stops using it (added in 1.1.2) "AYON_DEFAULT_SETTINGS_VARIANT", ]: value = os.getenv(key) From a711f346fbf19de3d59b930ecc0d4f9a2b196f8e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 10 Feb 2025 17:49:07 +0100 Subject: [PATCH 352/463] don't use 'six' --- .../tools/pyblish_pype/vendor/qtawesome/iconic_font.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py index c25739aff8..ce95f9e74f 100644 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py +++ b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py @@ -5,7 +5,6 @@ from __future__ import print_function import json import os -import six from qtpy import QtCore, QtGui @@ -152,7 +151,7 @@ class IconicFont(QtCore.QObject): def hook(obj): result = {} for key in obj: - result[key] = six.unichr(int(obj[key], 16)) + result[key] = chr(int(obj[key], 16)) return result if directory is None: From ac54c441ecf2d28892e3bd115598bbda459dace8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 10 Feb 2025 19:01:24 +0100 Subject: [PATCH 353/463] remove six from pyblish pype --- client/ayon_core/tools/pyblish_pype/model.py | 5 ++--- client/ayon_core/tools/pyblish_pype/util.py | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/pyblish_pype/model.py b/client/ayon_core/tools/pyblish_pype/model.py index 3a402f386e..44f951fe14 100644 --- a/client/ayon_core/tools/pyblish_pype/model.py +++ b/client/ayon_core/tools/pyblish_pype/model.py @@ -31,7 +31,6 @@ from . import settings, util from .awesome import tags as awesome from qtpy import QtCore, QtGui import qtawesome -from six import text_type from .constants import PluginStates, InstanceStates, GroupStates, Roles @@ -985,7 +984,7 @@ class TerminalModel(QtGui.QStandardItemModel): record_item = record else: record_item = { - "label": text_type(record.msg), + "label": str(record.msg), "type": "record", "levelno": record.levelno, "threadName": record.threadName, @@ -993,7 +992,7 @@ class TerminalModel(QtGui.QStandardItemModel): "filename": record.filename, "pathname": record.pathname, "lineno": record.lineno, - "msg": text_type(record.msg), + "msg": str(record.msg), "msecs": record.msecs, "levelname": record.levelname } diff --git a/client/ayon_core/tools/pyblish_pype/util.py b/client/ayon_core/tools/pyblish_pype/util.py index d24b07a409..081f7775d5 100644 --- a/client/ayon_core/tools/pyblish_pype/util.py +++ b/client/ayon_core/tools/pyblish_pype/util.py @@ -10,7 +10,6 @@ import sys import collections from qtpy import QtCore -from six import text_type import pyblish.api root = os.path.dirname(__file__) @@ -64,7 +63,7 @@ def u_print(msg, **kwargs): **kwargs: Keyword argument for `print` function. """ - if isinstance(msg, text_type): + if isinstance(msg, str): encoding = None try: encoding = os.getenv('PYTHONIOENCODING', sys.stdout.encoding) From 6bb4937c0aa7f36f872c04fc5d34c52fc4fa3fe8 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 11 Feb 2025 10:39:35 +0100 Subject: [PATCH 354/463] Consolidate frame range detection from collect_otio_frame_ranges plugin. --- .../publish/collect_otio_frame_ranges.py | 74 +- .../resources/timeline/timeline.json | 2054 +++++++++++++++++ .../test_collect_otio_frame_ranges.py | 129 ++ 3 files changed, 2234 insertions(+), 23 deletions(-) create mode 100644 tests/client/ayon_core/pipeline/editorial/resources/timeline/timeline.json create mode 100644 tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 917b9ad206..f0192e473d 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -101,8 +101,7 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): tl_start_h, tl_end_h = otio_range_to_frame_range(otio_tl_range_handles) frame_start = workfile_start - frame_end = frame_start + otio.opentime.to_frames( - otio_tl_range.duration, otio_tl_range.duration.rate) - 1 + frame_end = frame_start + otio_tl_range.duration.to_frames() - 1 data = { "frameStart": frame_start, @@ -120,39 +119,68 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): # Get source ranges otio_src_range = otio_clip.source_range otio_available_range = otio_clip.available_range() - otio_src_range_handles = otio_range_with_handles(otio_src_range, instance) - # Get source available start frame - src_starting_from = otio.opentime.to_frames( - otio_available_range.start_time, - otio_available_range.start_time.rate + # Backward-compatibility for Hiero OTIO exporter. + # NTSC compatibility might introduce floating rates, when these are + # not exactly the same (23.976 vs 23.976024627685547) + # this will cause precision issue in computation. + # Currently round to 2 decimals for comparison, + # but this should always rescale after that. + rounded_av_rate = round(otio_available_range.start_time.rate, 2) + rounded_src_rate = round(otio_src_range.start_time.rate, 2) + if rounded_av_rate != rounded_src_rate: + conformed_src_in = otio_src_range.start_time.rescaled_to( + otio_available_range.start_time.rate + ) + conformed_src_duration = otio_src_range.duration.rescaled_to( + otio_available_range.duration.rate + ) + conformed_source_range = otio.opentime.TimeRange( + start_time=conformed_src_in, + duration=conformed_src_duration + ) + else: + conformed_source_range = otio_src_range + + source_start = conformed_source_range.start_time + source_end = source_start + conformed_source_range.duration + handle_start = otio.opentime.RationalTime( + instance.data.get("handleStart", 0), + source_start.rate ) - - # Convert to frames - src_start, src_end = otio_range_to_frame_range(otio_src_range) - src_start_h, src_end_h = otio_range_to_frame_range(otio_src_range_handles) - + handle_end = otio.opentime.RationalTime( + instance.data.get("handleEnd", 0), + source_start.rate + ) + source_start_h = source_start - handle_start + source_end_h = source_end + handle_end data = { - "sourceStart": src_starting_from + src_start, - "sourceEnd": src_starting_from + src_end - 1, - "sourceStartH": src_starting_from + src_start_h, - "sourceEndH": src_starting_from + src_end_h - 1, + "sourceStart": source_start.to_frames(), + "sourceEnd": source_end.to_frames() - 1, + "sourceStartH": source_start_h.to_frames(), + "sourceEndH": source_end_h.to_frames() - 1, } instance.data.update(data) self.log.debug(f"Added source ranges: {pformat(data)}") def _collect_retimed_ranges(self, instance, otio_clip): """Handle retimed clip frame ranges.""" - workfile_source_duration = instance.data.get("shotDurationFromSource") - frame_start = instance.data["frameStart"] - - # Handle retimed clip frame range retimed_attributes = get_media_range_with_retimes(otio_clip, 0, 0) self.log.debug(f"Retimed attributes: {retimed_attributes}") + frame_start = instance.data["frameStart"] media_in = int(retimed_attributes["mediaIn"]) media_out = int(retimed_attributes["mediaOut"]) - frame_end = frame_start + (media_out - media_in) + 1 + frame_end = frame_start + (media_out - media_in) - instance.data["frameEnd"] = frame_end - self.log.debug(f"Updated frameEnd for retimed clip: {frame_end}") + data = { + "frameStart": frame_start, + "frameEnd": frame_end, + "sourceStart": media_in, + "sourceEnd": media_out, + "sourceStartH": media_in - int(retimed_attributes["handleStart"]), + "sourceEndH": media_out + int(retimed_attributes["handleEnd"]), + } + + instance.data.update(data) + self.log.debug(f"Updated retimed values: {data}") diff --git a/tests/client/ayon_core/pipeline/editorial/resources/timeline/timeline.json b/tests/client/ayon_core/pipeline/editorial/resources/timeline/timeline.json new file mode 100644 index 0000000000..03ed87569b --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/timeline/timeline.json @@ -0,0 +1,2054 @@ +{ + "OTIO_SCHEMA": "Timeline.1", + "metadata": { + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "dt3", + "foundry.timeline.samplerate": "48000", + "openpype.project.lutSetting16Bit": "ACES - ACEScc", + "openpype.project.lutSetting8Bit": "Output - Rec.709", + "openpype.project.lutSettingFloat": "ACES - ACES2065-1", + "openpype.project.lutSettingLog": "ACES - ACEScc", + "openpype.project.lutSettingViewer": "ACES/Rec.709", + "openpype.project.lutSettingWorkingSpace": "ACES - ACEScg", + "openpype.project.lutUseOCIOForExport": true, + "openpype.project.ocioConfigName": "", + "openpype.project.ocioConfigPath": "C:/Program Files/Nuke12.2v3/plugins/OCIOConfigs/configs/aces_1.1/config.ocio", + "openpype.project.useOCIOEnvironmentOverride": true, + "openpype.timeline.height": 1080, + "openpype.timeline.pixelAspect": 1, + "openpype.timeline.width": 1920 + }, + "name": "sq001", + "global_start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86400.0 + }, + "tracks": { + "OTIO_SCHEMA": "Stack.1", + "metadata": {}, + "name": "tracks", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "reference", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "referenceclip_mediash010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 24.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86400.08874841638 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "referencesq01sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 52.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86424.08877306872 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "referencesq01sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 51.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86476.08882648213 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "referencesq01sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 65.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86527.08887886834 + } + }, + "effects": [], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": false, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/sq01/referencesq01sh010\", \"task\": null, \"clip_index\": \"8185DC63-DE17-F143-817B-B34C00CECDDF\", \"hierarchy\": \"shots/sq01\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"sq01\", \"track\": \"reference\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"sq01\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"sq01\", \"track\": \"reference\"}, \"heroTrack\": true, \"uuid\": \"5fa79821-2a65-4f3e-aec5-05471b0f145e\", \"reviewTrack\": null, \"folderName\": \"referencesq01sh010\", \"label\": \"/shots/sq01/referencesq01sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"813286be-1492-47e2-aa4e-a192fdc4294e\", \"creator_attributes\": {\"fps\": \"from_selection\", \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1066, \"clipIn\": 127, \"clipOut\": 191, \"clipDuration\": 65, \"sourceIn\": 127.0, \"sourceOut\": 191.0}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateReference\", \"active\": false, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"reference\", \"folderPath\": \"/shots/sq01/referencesq01sh010\", \"task\": null, \"clip_index\": \"8185DC63-DE17-F143-817B-B34C00CECDDF\", \"hierarchy\": \"shots/sq01\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"sq01\", \"track\": \"reference\", \"shot\": \"sh010\", \"reviewableSource\": null, \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"sq01\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"sq01\", \"track\": \"reference\"}, \"heroTrack\": true, \"uuid\": \"5fa79821-2a65-4f3e-aec5-05471b0f145e\", \"reviewTrack\": null, \"folderName\": \"referencesq01sh010\", \"parent_instance_id\": \"813286be-1492-47e2-aa4e-a192fdc4294e\", \"label\": \"/shots/sq01/referencesq01sh010 plateReference\", \"newHierarchyIntegration\": true, \"instance_id\": \"7a9ef903-ec0c-4c0c-9b84-5d5a8cf8e72c\", \"creator_attributes\": {\"parentInstance\": \"/shots/sq01/referencesq01sh010 shotMain\", \"review\": false, \"reviewableSource\": \"clip_media\", \"publish_effects\": true}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"8185DC63-DE17-F143-817B-B34C00CECDDF\"}", + "label": "AYONdata_1fa7d197", + "note": "AYON data container" + }, + "name": "AYONdata_1fa7d197", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sq001", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 24.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86592.08894563509 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Video" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "P01", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Gap.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 24.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 52.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86535.08888708579 + } + }, + "effects": [], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "family": "task", + "hiero_source_type": "TrackItem", + "label": "comp", + "note": "Compositing", + "type": "Compositing" + }, + "name": "comp", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "60", + "foundry.source.filename": "MER_sq001_sh010_P01.%04d.exr 997-1056", + "foundry.source.filesize": "", + "foundry.source.fragments": "60", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh010_P01/MER_sq001_sh010_P01.%04d.exr 997-1056", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "MER_sq001_sh010_P01.%04d.exr 997-1056", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "997", + "foundry.source.timecode": "86531", + "foundry.source.umid": "1bf7437a-b446-440c-07c5-7cae7acf4f5e", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1920", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "60", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "8", + "media.exr.compressionName": "DWAA", + "media.exr.dataWindow": "0,0,1919,1079", + "media.exr.displayWindow": "0,0,1919,1079", + "media.exr.dwaCompressionLevel": "90", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2022-04-21 11:56:03", + "media.input.filename": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh010_P01/MER_sq001_sh010_P01.0997.exr", + "media.input.filereader": "exr", + "media.input.filesize": "1217052", + "media.input.frame": "1", + "media.input.frame_rate": "23.976", + "media.input.height": "1080", + "media.input.mtime": "2022-03-06 10:14:37", + "media.input.timecode": "01:00:05:11", + "media.input.width": "1920", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "ffffffffffffffff", + "media.nuke.version": "12.2v3", + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 60.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86531.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh010_P01\\", + "name_prefix": "MER_sq001_sh010_P01.", + "name_suffix": ".exr", + "start_frame": 997, + "frame_step": 1, + "rate": 23.976, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 51.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 172800.17749683277 + } + }, + "effects": [], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "family": "task", + "hiero_source_type": "TrackItem", + "label": "comp", + "note": "Compositing", + "type": "Compositing" + }, + "name": "comp", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + }, + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"ayon.create.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/test_align/sh010\", \"task\": null, \"clip_index\": \"FD37E3CA-F66B-1749-80CD-212210AB1C28\", \"hierarchy\": \"shots/test_align\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\", \"shot\": \"sh010\", \"reviewableSource\": \"reference\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"test_align\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\"}, \"heroTrack\": true, \"uuid\": \"e2bcf862-b8b9-4c2c-806f-5ba6e227f782\", \"reviewTrack\": \"reference\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/test_align/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"888195e5-f432-4032-9ef5-3e3b7897f80d\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1052, \"clipIn\": 76, \"clipOut\": 126, \"clipDuration\": 51, \"sourceIn\": 0.0, \"sourceOut\": 50.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"ayon.create.instance\", \"productType\": \"plate\", \"productName\": \"plateP01\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"P01\", \"folderPath\": \"/shots/test_align/sh010\", \"task\": null, \"clip_index\": \"FD37E3CA-F66B-1749-80CD-212210AB1C28\", \"hierarchy\": \"shots/test_align\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\", \"shot\": \"sh010\", \"reviewableSource\": \"reference\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"test_align\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\"}, \"heroTrack\": true, \"uuid\": \"e2bcf862-b8b9-4c2c-806f-5ba6e227f782\", \"reviewTrack\": \"reference\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"888195e5-f432-4032-9ef5-3e3b7897f80d\", \"label\": \"/shots/test_align/sh010 plateP01\", \"newHierarchyIntegration\": true, \"instance_id\": \"16ae41aa-20c4-4c63-97c6-7666e4d1d30b\", \"creator_attributes\": {\"parentInstance\": \"/shots/test_align/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"reference\", \"publish_effects\": true}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.audio\": {\"id\": \"ayon.create.instance\", \"productType\": \"audio\", \"productName\": \"audioMain\", \"active\": false, \"creator_identifier\": \"io.ayon.creators.hiero.audio\", \"variant\": \"main\", \"folderPath\": \"/shots/test_align/sh010\", \"task\": null, \"clip_index\": \"FD37E3CA-F66B-1749-80CD-212210AB1C28\", \"hierarchy\": \"shots/test_align\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\", \"shot\": \"sh010\", \"reviewableSource\": \"reference\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"test_align\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\"}, \"heroTrack\": true, \"uuid\": \"e2bcf862-b8b9-4c2c-806f-5ba6e227f782\", \"reviewTrack\": \"reference\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"888195e5-f432-4032-9ef5-3e3b7897f80d\", \"label\": \"/shots/test_align/sh010 audioMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"4f31e750-f665-4ef7-8fab-578ebc606d7e\", \"creator_attributes\": {\"parentInstance\": \"/shots/test_align/sh010 shotMain\", \"review\": true}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"FD37E3CA-F66B-1749-80CD-212210AB1C28\"}", + "label": "AYONdata_86163a19", + "note": "AYON data container" + }, + "name": "AYONdata_86163a19", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "59", + "foundry.source.filename": "MER_sq001_sh020_P01.%04d.exr 997-1055", + "foundry.source.filesize": "", + "foundry.source.fragments": "59", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P01/MER_sq001_sh020_P01.%04d.exr 997-1055", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "MER_sq001_sh020_P01.%04d.exr 997-1055", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "997", + "foundry.source.timecode": "172800", + "foundry.source.umid": "1bf7437a-b446-440c-07c5-7cae7acf4f5e", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1920", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "59", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "8", + "media.exr.compressionName": "DWAA", + "media.exr.dataWindow": "0,0,1919,1079", + "media.exr.displayWindow": "0,0,1919,1079", + "media.exr.dwaCompressionLevel": "90", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2022-04-21 11:56:03", + "media.input.filename": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P01/MER_sq001_sh020_P01.0997.exr", + "media.input.filereader": "exr", + "media.input.filesize": "1235182", + "media.input.frame": "1", + "media.input.frame_rate": "23.976", + "media.input.height": "1080", + "media.input.mtime": "2022-03-06 10:14:41", + "media.input.timecode": "02:00:00:00", + "media.input.width": "1920", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "ffffffffffffffff", + "media.nuke.version": "12.2v3", + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 59.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 172800.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P01\\", + "name_prefix": "MER_sq001_sh020_P01.", + "name_suffix": ".exr", + "start_frame": 997, + "frame_step": 1, + "rate": 23.976, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "P01default_twsh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 65.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 345623.3550172907 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "TimeEffect.1", + "metadata": { + "length": 1.0, + "lookup": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "name": "TimeWarp1", + "effect_name": "TimeWarp" + } + ], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "family": "task", + "hiero_source_type": "TrackItem", + "label": "comp", + "note": "Compositing", + "type": "Compositing" + }, + "name": "comp", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 1556, + "ayon.source.pixelAspect": 2.0, + "ayon.source.width": 1828, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "73", + "foundry.source.filename": "MER_sq001_sh040_P01.%04d.exr 997-1069", + "foundry.source.filesize": "", + "foundry.source.fragments": "73", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "1556", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh040_P01/MER_sq001_sh040_P01.%04d.exr 997-1069", + "foundry.source.pixelAspect": "2", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "MER_sq001_sh040_P01.%04d.exr 997-1069", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "997", + "foundry.source.timecode": "345619", + "foundry.source.umid": "1bf7437a-b446-440c-07c5-7cae7acf4f5e", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1828", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "73", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "9", + "media.exr.compressionName": "DWAB", + "media.exr.dataWindow": "0,0,1827,1555", + "media.exr.displayWindow": "0,0,1827,1555", + "media.exr.dwaCompressionLevel": "80", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "2", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2022-04-21 11:56:05", + "media.input.filename": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh040_P01/MER_sq001_sh040_P01.0997.exr", + "media.input.filereader": "exr", + "media.input.filesize": "1170604", + "media.input.frame": "1", + "media.input.frame_rate": "23.976", + "media.input.height": "1556", + "media.input.mtime": "2022-03-30 13:47:47", + "media.input.timecode": "04:00:00:19", + "media.input.width": "1828", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "2a4", + "media.nuke.version": "12.2v3", + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 73.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 345619.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh040_P01\\", + "name_prefix": "MER_sq001_sh040_P01.", + "name_suffix": ".exr", + "start_frame": 997, + "frame_step": 1, + "rate": 23.976, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Video" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "P02", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Gap.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 76.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 51.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 1.0000010271807451 + } + }, + "effects": [], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.plate\": {\"id\": \"ayon.create.instance\", \"productType\": \"plate\", \"productName\": \"plateP02\", \"active\": false, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"P02\", \"folderPath\": \"/shots/test_align/sh010\", \"task\": null, \"clip_index\": \"881A3D65-A052-DC45-9D3B-304990BD6488\", \"hierarchy\": \"shots/test_align\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P02\", \"shot\": \"sh020\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"test_align\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\"}, \"heroTrack\": false, \"uuid\": \"b7416739-1102-4dfd-bac3-771d43018b84\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"parent_instance_id\": \"888195e5-f432-4032-9ef5-3e3b7897f80d\", \"label\": \"/shots/test_align/sh010 plateP02\", \"newHierarchyIntegration\": true, \"instance_id\": \"6cc84e25-e2fa-4f31-9901-85d75e8fd36a\", \"creator_attributes\": {\"parentInstance\": \"/shots/test_align/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"clip_media\", \"publish_effects\": true}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"881A3D65-A052-DC45-9D3B-304990BD6488\"}", + "label": "AYONdata_05836436", + "note": "AYON data container" + }, + "name": "AYONdata_05836436", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACEScg", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 2048, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACEScg", + "foundry.source.duration": "200", + "foundry.source.filename": "MER_sq001_sh020_P02.%04d.exr 1-200", + "foundry.source.filesize": "", + "foundry.source.fragments": "200", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P02/MER_sq001_sh020_P02.%04d.exr 1-200", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "MER_sq001_sh020_P02.%04d.exr 1-200", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "1", + "foundry.source.timecode": "1", + "foundry.source.umid": "bdfbe576-124a-4200-a1c9-daa2dcc3e952", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "2048", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACEScg", + "foundry.timeline.duration": "200", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAQAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "A:{1 0 1 1},B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "9", + "media.exr.compressionName": "DWAB", + "media.exr.dataWindow": "0,358,2047,904", + "media.exr.displayWindow": "0,0,2047,1079", + "media.exr.dwaCompressionLevel": "80", + "media.exr.lineOrder": "0", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2022-04-21 11:56:03", + "media.input.filename": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P02/MER_sq001_sh020_P02.0001.exr", + "media.input.filereader": "exr", + "media.input.filesize": "453070", + "media.input.frame": "1", + "media.input.height": "1080", + "media.input.mtime": "2022-03-30 11:29:25", + "media.input.width": "2048", + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 200.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 1.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/projects/AY01_VFX_demo/resources/plates/MER_sq001_sh020_P02\\", + "name_prefix": "MER_sq001_sh020_P02.", + "name_suffix": ".exr", + "start_frame": 1, + "frame_step": 1, + "rate": 23.976, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Video" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "P03", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Gap.1", + "metadata": {}, + "name": "", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 76.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + }, + "effects": [], + "markers": [], + "enabled": true + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "img_sequence_exr", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 26.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 87311.69068479538 + } + }, + "effects": [], + "markers": [ + { + "OTIO_SCHEMA": "Marker.2", + "metadata": { + "applieswhole": "1", + "hiero_source_type": "TrackItem", + "json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.plate\": {\"id\": \"ayon.create.instance\", \"productType\": \"plate\", \"productName\": \"plateP03\", \"active\": false, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"P03\", \"folderPath\": \"/shots/test_align/sh010\", \"task\": null, \"clip_index\": \"70A463D1-4FDD-E843-90D9-A2FC3978B06B\", \"hierarchy\": \"shots/test_align\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P03\", \"shot\": \"sh030\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"test_align\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"test_align\", \"track\": \"P01\"}, \"heroTrack\": false, \"uuid\": \"cd05c022-94ff-4527-bd3e-1533a8347f99\", \"reviewTrack\": null, \"folderName\": \"sh010\", \"parent_instance_id\": \"888195e5-f432-4032-9ef5-3e3b7897f80d\", \"label\": \"/shots/test_align/sh010 plateP03\", \"newHierarchyIntegration\": true, \"instance_id\": \"fb5ea749-0f9b-43a0-b2b5-6dadc6f6af7e\", \"creator_attributes\": {\"parentInstance\": \"/shots/test_align/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"clip_media\", \"publish_effects\": true}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"70A463D1-4FDD-E843-90D9-A2FC3978B06B\"}", + "label": "AYONdata_0b6cdbd7", + "note": "AYON data container" + }, + "name": "AYONdata_0b6cdbd7", + "color": "RED", + "marked_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 0.0 + } + } + } + ], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ImageSequenceReference.1", + "metadata": { + "ayon.source.colorspace": "ACES - ACES2065-1", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 956, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "ACES - ACES2065-1", + "foundry.source.duration": "84", + "foundry.source.filename": "output.%04d.exr 1000-1083", + "foundry.source.filesize": "", + "foundry.source.fragments": "84", + "foundry.source.framerate": "24", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_embedded_tc/output.%04d.exr 1000-1083", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shortfilename": "output.%04d.exr 1000-1083", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "1000", + "foundry.source.timecode": "87399", + "foundry.source.umid": "3cd0643b-4ee3-4d94-46dd-7aac61829c84", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "956", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "ACES - ACES2065-1", + "foundry.timeline.duration": "84", + "foundry.timeline.framerate": "24", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=", + "foundry.timeline.samplerate": "Invalid", + "isSequence": true, + "media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}", + "media.exr.compression": "2", + "media.exr.compressionName": "Zip (1 scanline)", + "media.exr.dataWindow": "0,0,955,684", + "media.exr.displayWindow": "0,0,955,719", + "media.exr.lineOrder": "0", + "media.exr.nuke.input.frame_rate": "24", + "media.exr.nuke.input.timecode": "01:00:41:15", + "media.exr.pixelAspectRatio": "1", + "media.exr.screenWindowCenter": "0,0", + "media.exr.screenWindowWidth": "1", + "media.exr.type": "scanlineimage", + "media.exr.version": "1", + "media.input.bitsperchannel": "16-bit half float", + "media.input.ctime": "2024-09-18 08:28:26", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_embedded_tc/output.1000.exr", + "media.input.filereader": "exr", + "media.input.filesize": "457525", + "media.input.frame": "1", + "media.input.frame_rate": "24", + "media.input.height": "720", + "media.input.mtime": "2024-09-18 08:28:26", + "media.input.timecode": "01:00:41:15", + "media.input.width": "956", + "media.nuke.full_layer_names": "0", + "media.nuke.node_hash": "f6b6ac187e7c550c", + "media.nuke.version": "15.0v5", + "padding": 4 + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 84.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 87399.0 + } + }, + "available_image_bounds": null, + "target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_embedded_tc\\", + "name_prefix": "output.", + "name_suffix": ".exr", + "start_frame": 1000, + "frame_step": 1, + "rate": 24.0, + "frame_zero_padding": 4, + "missing_frame_policy": "error" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Video" + }, + { + "OTIO_SCHEMA": "Track.1", + "metadata": {}, + "name": "Audio", + "source_range": null, + "effects": [], + "markers": [], + "enabled": true, + "children": [ + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sq001", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 24.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86400.08874841638 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh010", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 52.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86424.08877306872 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sh020", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 51.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86476.08882648213 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sq001", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 65.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86527.08887886834 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + }, + { + "OTIO_SCHEMA": "Clip.2", + "metadata": {}, + "name": "sq001", + "source_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 24.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976024627685547, + "value": 86592.08894563509 + } + }, + "effects": [], + "markers": [], + "enabled": true, + "media_references": { + "DEFAULT_MEDIA": { + "OTIO_SCHEMA": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Output - Rec.709", + "ayon.source.height": 720, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1280, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "default", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.mov64_decode_video_levels": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "ProRes422(LT)", + "foundry.source.audio": "", + "foundry.source.audiobitdepth": "4", + "foundry.source.colourtransform": "Output - Rec.709", + "foundry.source.duration": "216", + "foundry.source.filename": "sq001.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "23.98", + "foundry.source.fullpath": "", + "foundry.source.height": "720", + "foundry.source.layers": "colour", + "foundry.source.numaudiochannels": "2", + "foundry.source.originalsamplerate": "48000", + "foundry.source.path": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "48000", + "foundry.source.shoottime": "3732770621", + "foundry.source.shortfilename": "sq001.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "86400", + "foundry.source.timecodedropframe": "0", + "foundry.source.type": "QuickTime ProRes422(LT)", + "foundry.source.umid": "db4a2c91-784e-394f-95e1-7bb75b709c7a", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1280", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Output - Rec.709", + "foundry.timeline.duration": "216", + "foundry.timeline.framerate": "23.98", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.readParams": "AAAAAQAAAAAAAAAHAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAABzAAAAGW1vdjY0X2RlY29kZV92aWRlb19sZXZlbHMAAAAFaW50MzIAAAAAAAAAFm1vdjY0X2ZpcnN0X3RyYWNrX29ubHkAAAAEYm9vbAEAAAASbW92NjRfeWNiY3JfbWF0cml4AAAABWludDMyAAAAAAAAAAdkZWNvZGVyAAAABWludDMyAAAAAAAAABdtb3Y2NF9tYXRjaF9tZXRhX2Zvcm1hdAAAAARib29sAQAAAA9tb3Y2NF9ub19wcmVmaXgAAAAEYm9vbAA=", + "foundry.timeline.samplerate": "48000" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 216.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 23.976, + "value": 86400.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/projects/AY01_VFX_demo/resources/reference/sq001.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" + } + ], + "kind": "Audio" + } + ] + } +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py b/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py new file mode 100644 index 0000000000..d895e9888f --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py @@ -0,0 +1,129 @@ +import os +import mock + +import opentimelineio as otio + +from ayon_core.plugins.publish import collect_otio_frame_ranges + + +_RESOURCE_DIR = os.path.join( + os.path.dirname(__file__), + "resources", + "timeline" +) + + +class MockInstance(): + """ Mock pyblish instance for testing purpose. + """ + def __init__(self, data: dict): + self.data = data + self.context = self + + +def _check_expected_frame_range_values( + clip_name: str, + expected_data: dict, + handle_start: int = 10, + handle_end: int = 10, + retimed: bool = False, +): + file_path = os.path.join(_RESOURCE_DIR, "timeline.json") + otio_timeline = otio.schema.Timeline.from_json_file(file_path) + + for otio_clip in otio_timeline.find_clips(): + if otio_clip.name == clip_name: + break + + instance_data = { + "otioClip": otio_clip, + "handleStart": handle_start, + "handleEnd": handle_end, + "workfileFrameStart": 1001, + } + if retimed: + instance_data["shotDurationFromSource"] = True + + instance = MockInstance(instance_data) + + processor = collect_otio_frame_ranges.CollectOtioRanges() + processor.process(instance) + + # Assert expected data is subset of edited instance. + assert expected_data.items() <= instance.data.items() + + +def test_movie_with_timecode(): + """ + Movie clip (with embedded timecode) + available_range = 86531-86590 23.976fps + source_range = 86535-86586 23.976fps + """ + expected_data = { + 'frameStart': 1001, + 'frameEnd': 1052, + 'clipIn': 24, + 'clipOut': 75, + 'clipInH': 14, + 'clipOutH': 85, + 'sourceStart': 86535, + 'sourceStartH': 86525, + 'sourceEnd': 86586, + 'sourceEndH': 86596, + } + + _check_expected_frame_range_values( + "sh010", + expected_data, + ) + + +def test_image_sequence(): + """ + EXR image sequence. + available_range = 87399-87482 24fps + source_range = 87311-87336 23.976fps + """ + expected_data = { + 'frameStart': 1001, + 'frameEnd': 1026, + 'clipIn': 76, + 'clipOut': 101, + 'clipInH': 66, + 'clipOutH': 111, + 'sourceStart': 87399, + 'sourceStartH': 87389, + 'sourceEnd': 87424, + 'sourceEndH': 87434, + } + + _check_expected_frame_range_values( + "img_sequence_exr", + expected_data, + ) + +def test_media_retimed(): + """ + EXR image sequence. + available_range = 345619-345691 23.976fps + source_range = 345623-345687 23.976fps + TimeWarp = frozen frame. + """ + expected_data = { + 'frameStart': 1001, + 'frameEnd': 1065, + 'clipIn': 127, + 'clipOut': 191, + 'clipInH': 117, + 'clipOutH': 201, + 'sourceStart': 1001, + 'sourceStartH': 1001, + 'sourceEnd': 1065, + 'sourceEndH': 1065, + } + + _check_expected_frame_range_values( + "P01default_twsh010", + expected_data, + retimed=True, + ) From d0364cbec3814d0b58ce2397b68628b91c442293 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Tue, 11 Feb 2025 10:47:12 +0100 Subject: [PATCH 355/463] Fix lint. --- .../ayon_core/plugins/publish/collect_otio_frame_ranges.py | 5 ++++- .../pipeline/editorial/test_collect_otio_frame_ranges.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py index 7dc4af273a..0a4efc2172 100644 --- a/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py +++ b/client/ayon_core/plugins/publish/collect_otio_frame_ranges.py @@ -104,7 +104,10 @@ class CollectOtioRanges(pyblish.api.InstancePlugin): # Get timeline ranges otio_tl_range = otio_clip.range_in_parent() - otio_tl_range_handles = otio_range_with_handles(otio_tl_range, instance) + otio_tl_range_handles = otio_range_with_handles( + otio_tl_range, + instance + ) # Convert to frames tl_start, tl_end = otio_range_to_frame_range(otio_tl_range) diff --git a/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py b/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py index d895e9888f..20f0c05804 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py +++ b/tests/client/ayon_core/pipeline/editorial/test_collect_otio_frame_ranges.py @@ -1,5 +1,4 @@ import os -import mock import opentimelineio as otio From 18c1ef04e60b433cf1e1fb9d89d87f6418d4f689 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:44:03 +0100 Subject: [PATCH 356/463] use platformdirs instead of appdirs --- client/ayon_core/lib/local_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/local_settings.py b/client/ayon_core/lib/local_settings.py index 08030ae87e..eff0068f00 100644 --- a/client/ayon_core/lib/local_settings.py +++ b/client/ayon_core/lib/local_settings.py @@ -9,7 +9,7 @@ from datetime import datetime from abc import ABC, abstractmethod from functools import lru_cache -import appdirs +import platformdirs import ayon_api _PLACEHOLDER = object() @@ -17,7 +17,7 @@ _PLACEHOLDER = object() def _get_ayon_appdirs(*args): return os.path.join( - appdirs.user_data_dir("AYON", "Ynput"), + platformdirs.user_data_dir("AYON", "Ynput"), *args ) From 1d8d417e53856f1be8d1f88b066710468504ec50 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:53:51 +0100 Subject: [PATCH 357/463] added acre functionality to lib functions --- client/ayon_core/lib/env_tools.py | 255 +++++++++++++++++++++++++++++- 1 file changed, 252 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/lib/env_tools.py b/client/ayon_core/lib/env_tools.py index 25bcbf7c1b..6ed67d7270 100644 --- a/client/ayon_core/lib/env_tools.py +++ b/client/ayon_core/lib/env_tools.py @@ -1,7 +1,39 @@ +from __future__ import annotations import os +import re +import platform +import typing +import collections +from string import Formatter +from typing import Optional + +if typing.TYPE_CHECKING: + from typing import Union, Literal + + PlatformName = Literal["windows", "linux", "darwin"] + EnvValue = Union[str, list[str], dict[str, str], dict[str, list[str]]] + +Results = collections.namedtuple( + "Results", + ["sorted", "cyclic"] +) -def env_value_to_bool(env_key=None, value=None, default=False): +class CycleError(ValueError): + """Raised when a cycle is detected in dynamic env variables compute.""" + pass + + +class DynamicKeyClashError(Exception): + """Raised when dynamic key clashes with an existing key.""" + pass + + +def env_value_to_bool( + env_key: Optional[str] = None, + value: Optional[str] = None, + default: bool = False, +) -> bool: """Convert environment variable value to boolean. Function is based on value of the environemt variable. Value is lowered @@ -11,6 +43,7 @@ def env_value_to_bool(env_key=None, value=None, default=False): bool: If value match to one of ["true", "yes", "1"] result if True but if value match to ["false", "no", "0"] result is False else default value is returned. + """ if value is None and env_key is None: return default @@ -27,7 +60,11 @@ def env_value_to_bool(env_key=None, value=None, default=False): return default -def get_paths_from_environ(env_key=None, env_value=None, return_first=False): +def get_paths_from_environ( + env_key: Optional[str] = None, + env_value: Optional[str] = None, + return_first: bool = False, +) -> Optional[Union[str, list[str]]]: """Return existing paths from specific environment variable. Args: @@ -38,7 +75,8 @@ def get_paths_from_environ(env_key=None, env_value=None, return_first=False): paths. `None` or empty list returned if nothing found. Returns: - str, list, None: Result of found path/s. + Optional[Union[str, list[str]]]: Result of found path/s. + """ existing_paths = [] if not env_key and not env_value: @@ -69,3 +107,214 @@ def get_paths_from_environ(env_key=None, env_value=None, return_first=False): return None # Return all existing paths from environment variable return existing_paths + + +def parse_env_variables_structure( + env: dict[str, EnvValue], + platform_name: Optional[PlatformName] = None +) -> dict[str, str]: + """Parse environment for platform-specific values and paths as lists. + + Args: + env (dict): The source environment to read. + platform_name (Optional[PlatformName]): Name of platform to parse for. + Defaults to current platform. + + Returns: + dict: The flattened environment for a platform. + + """ + platform_name = platform_name or platform.system().lower() + + result = {} + for variable, value in env.items(): + # Platform specific values + if isinstance(value, dict): + value = value.get(platform_name) + + # Allow to have lists as values in the tool data + if isinstance(value, (list, tuple)): + value = os.pathsep.join(value) + + if not value: + continue + + if not isinstance(value, str): + raise TypeError(f"Expected 'str' got '{type(value)}'") + + result[variable] = value + + return result + + +def _topological_sort(dependency_pairs): + """Sort values subject to dependency constraints""" + num_heads = collections.defaultdict(int) # num arrows pointing in + tails = collections.defaultdict(list) # list of arrows going out + heads = [] # unique list of heads in order first seen + for h, t in dependency_pairs: + num_heads[t] += 1 + if h in tails: + tails[h].append(t) + else: + tails[h] = [t] + heads.append(h) + + ordered = [h for h in heads if h not in num_heads] + for h in ordered: + for t in tails[h]: + num_heads[t] -= 1 + if not num_heads[t]: + ordered.append(t) + cyclic = [n for n, heads in num_heads.items() if heads] + return Results(ordered, cyclic) + + +def _partial_format( + s: str, + data: dict[str, str], + missing: Optional[str] = None, +) -> str: + """Return string `s` formatted by `data` allowing a partial format + + Arguments: + s (str): The string that will be formatted + data (dict): The dictionary used to format with. + + Example: + >>> _partial_format("{d} {a} {b} {c} {d}", {'b': "and", 'd': "left"}) + 'left {a} and {c} left' + """ + + if missing is None: + missing = "{{{key}}}" + + class FormatDict(dict): + """This supports partial formatting. + + Missing keys are replaced with the return value of __missing__. + + """ + + def __missing__(self, key): + return missing.format(key=key) + + formatter = Formatter() + mapping = FormatDict(**data) + try: + f = formatter.vformat(s, (), mapping) + except Exception: + r_token = re.compile(r"({.*?})") + matches = re.findall(r_token, s) + f = s + for m in matches: + try: + f = re.sub(m, m.format(**data), f) + except (KeyError, ValueError): + continue + return f + + +def compute_env_variables_structure( + env: dict[str, str], + fill_dynamic_keys: bool = True, +) -> dict[str, str]: + """Compute the result from recursive dynamic environment. + + Note: Keys that are not present in the data will remain unformatted as the + original keys. So they can be formatted against the current user + environment when merging. So {"A": "{key}"} will remain {key} if not + present in the dynamic environment. + + """ + env = env.copy() + + # Collect dependencies + dependencies = [] + for key, value in env.items(): + try: + dependent_keys = re.findall("{(.+?)}", value) + for dependency in dependent_keys: + # Ignore direct references to itself because + # we don't format with itself anyway + if dependency == key: + continue + + dependencies.append((key, dependency)) + except Exception: + dependencies.append((key, value)) + + result = _topological_sort(dependencies) + + # Check cycle + if result.cyclic: + raise CycleError(f"A cycle is detected on: {result.cyclic}") + + # Format dynamic values + for key in reversed(result.sorted): + if key in env: + if not isinstance(env[key], str): + continue + data = env.copy() + data.pop(key) # format without itself + env[key] = _partial_format(env[key], data=data) + + # Format cyclic values + for key in result.cyclic: + if key in env: + if not isinstance(env[key], str): + continue + data = env.copy() + data.pop(key) # format without itself + env[key] = _partial_format(env[key], data=data) + + # Format dynamic keys + if fill_dynamic_keys: + formatted = {} + for key, value in env.items(): + if not isinstance(value, str): + formatted[key] = value + continue + + new_key = _partial_format(key, data=env) + if new_key in formatted: + raise DynamicKeyClashError( + f"Key clashes on: {new_key} (source: {key})" + ) + + formatted[new_key] = value + env = formatted + + return env + + +def merge_env_variables( + src_env: dict[str, str], + dst_env: dict[str, str], + missing: Optional[str] = None, +): + """Merge the tools environment with the 'current_env'. + + This finalizes the join with a current environment by formatting the + remainder of dynamic variables with that from the current environment. + + Remaining missing variables result in an empty value. + + Args: + src_env (dict): The dynamic environment + dst_env (dict): The target environment variables mapping to merge + the dynamic environment into. + missing (str): Argument passed to '_partial_format' during merging. + `None` should keep missing keys unchanged. + + Returns: + dict: The resulting environment after the merge. + + """ + result = dst_env.copy() + for key, value in src_env.items(): + result[key] = _partial_format( + str(value), data=dst_env, missing=missing + ) + + return result From b0927595a26a39f7b7c4f8c91a3fec3ca6bb590e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:54:24 +0100 Subject: [PATCH 358/463] use new functions in cli.py --- client/ayon_core/cli.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 6b4a1f824f..1287534bbf 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -8,7 +8,6 @@ from pathlib import Path import warnings import click -import acre from ayon_core import AYON_CORE_ROOT from ayon_core.addon import AddonsManager @@ -18,6 +17,11 @@ from ayon_core.lib import ( is_running_from_build, Logger, ) +from ayon_core.lib.env_tools import ( + parse_env_variables_structure, + compute_env_variables_structure, + merge_env_variables, +) @@ -240,14 +244,13 @@ def _set_global_environments() -> None: # first resolve general environment because merge doesn't expect # values to be list. # TODO: switch to AYON environment functions - merged_env = acre.merge( - acre.compute(acre.parse(general_env), cleanup=False), + merged_env = merge_env_variables( + compute_env_variables_structure( + parse_env_variables_structure(general_env) + ), dict(os.environ) ) - env = acre.compute( - merged_env, - cleanup=False - ) + env = compute_env_variables_structure(merged_env) os.environ.clear() os.environ.update(env) @@ -263,8 +266,8 @@ def _set_addons_environments(addons_manager): # Merge environments with current environments and update values if module_envs := addons_manager.collect_global_environments(): - parsed_envs = acre.parse(module_envs) - env = acre.merge(parsed_envs, dict(os.environ)) + parsed_envs = parse_env_variables_structure(module_envs) + env = merge_env_variables(parsed_envs, dict(os.environ)) os.environ.clear() os.environ.update(env) From 74443c92e71c3e79cbe7dcc61528703b0ed8faad Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:37:00 +0100 Subject: [PATCH 359/463] small tweaks --- client/ayon_core/cli.py | 11 +-- client/ayon_core/lib/env_tools.py | 151 +++++++++++++++--------------- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index 1287534bbf..d7cd3ba7f5 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -239,15 +239,12 @@ def version(build): def _set_global_environments() -> None: """Set global AYON environments.""" - general_env = get_general_environments() + # First resolve general environment + general_env = parse_env_variables_structure(get_general_environments()) - # first resolve general environment because merge doesn't expect - # values to be list. - # TODO: switch to AYON environment functions + # Merge environments with current environments and update values merged_env = merge_env_variables( - compute_env_variables_structure( - parse_env_variables_structure(general_env) - ), + compute_env_variables_structure(general_env), dict(os.environ) ) env = compute_env_variables_structure(merged_env) diff --git a/client/ayon_core/lib/env_tools.py b/client/ayon_core/lib/env_tools.py index 6ed67d7270..c71350869e 100644 --- a/client/ayon_core/lib/env_tools.py +++ b/client/ayon_core/lib/env_tools.py @@ -13,11 +13,6 @@ if typing.TYPE_CHECKING: PlatformName = Literal["windows", "linux", "darwin"] EnvValue = Union[str, list[str], dict[str, str], dict[str, list[str]]] -Results = collections.namedtuple( - "Results", - ["sorted", "cyclic"] -) - class CycleError(ValueError): """Raised when a cycle is detected in dynamic env variables compute.""" @@ -124,7 +119,8 @@ def parse_env_variables_structure( dict: The flattened environment for a platform. """ - platform_name = platform_name or platform.system().lower() + if platform_name is None: + platform_name = platform.system().lower() result = {} for variable, value in env.items(): @@ -147,72 +143,94 @@ def parse_env_variables_structure( return result -def _topological_sort(dependency_pairs): - """Sort values subject to dependency constraints""" +def _topological_sort( + dependencies: dict[str, set[str]] +) -> tuple[list[str], list[str]]: + """Sort values subject to dependency constraints. + + Args: + dependencies (dict[str, set[str]): Mapping of environment variable + keys to a set of keys they depend on. + + Returns: + tuple[list[str], list[str]]: A tuple of two lists. The first list + contains the ordered keys in which order should be environment + keys filled, the second list contains the keys that would cause + cyclic fill of values. + + """ num_heads = collections.defaultdict(int) # num arrows pointing in tails = collections.defaultdict(list) # list of arrows going out heads = [] # unique list of heads in order first seen - for h, t in dependency_pairs: - num_heads[t] += 1 - if h in tails: - tails[h].append(t) - else: - tails[h] = [t] - heads.append(h) + for head, tail_values in dependencies.items(): + for tail_value in tail_values: + num_heads[tail_value] += 1 + if head not in tails: + heads.append(head) + tails[head].append(tail_value) - ordered = [h for h in heads if h not in num_heads] - for h in ordered: - for t in tails[h]: - num_heads[t] -= 1 - if not num_heads[t]: - ordered.append(t) - cyclic = [n for n, heads in num_heads.items() if heads] - return Results(ordered, cyclic) + ordered = [head for head in heads if head not in num_heads] + for head in ordered: + for tail in tails[head]: + num_heads[tail] -= 1 + if not num_heads[tail]: + ordered.append(tail) + cyclic = [tail for tail, heads in num_heads.items() if heads] + return ordered, cyclic + + +class _PartialFormatDict(dict): + """This supports partial formatting. + + Missing keys are replaced with the return value of __missing__. + + """ + def __init__(self, **kwargs): + super().__init__(**kwargs) + self._missing_template: str = "{{{key}}}" + + def set_missing_template(self, template: str): + self._missing_template = template + + def __missing__(self, key: str) -> str: + return self._missing_template.format(key=key) def _partial_format( - s: str, + value: str, data: dict[str, str], - missing: Optional[str] = None, + missing_template: Optional[str] = None, ) -> str: """Return string `s` formatted by `data` allowing a partial format Arguments: - s (str): The string that will be formatted + value (str): The string that will be formatted data (dict): The dictionary used to format with. + missing_template (Optional[str]): The template to use when a key is + missing from the data. If `None`, the key will remain unformatted. Example: >>> _partial_format("{d} {a} {b} {c} {d}", {'b': "and", 'd': "left"}) 'left {a} and {c} left' + """ - if missing is None: - missing = "{{{key}}}" - - class FormatDict(dict): - """This supports partial formatting. - - Missing keys are replaced with the return value of __missing__. - - """ - - def __missing__(self, key): - return missing.format(key=key) + mapping = _PartialFormatDict(**data) + if missing_template is not None: + mapping.set_missing_template(missing_template) formatter = Formatter() - mapping = FormatDict(**data) try: - f = formatter.vformat(s, (), mapping) + output = formatter.vformat(value, (), mapping) except Exception: r_token = re.compile(r"({.*?})") - matches = re.findall(r_token, s) - f = s - for m in matches: + output = value + for match in re.findall(r_token, value): try: - f = re.sub(m, m.format(**data), f) - except (KeyError, ValueError): + output = re.sub(match, match.format(**data), output) + except (KeyError, ValueError, IndexError): continue - return f + return output def compute_env_variables_structure( @@ -230,28 +248,22 @@ def compute_env_variables_structure( env = env.copy() # Collect dependencies - dependencies = [] + dependencies = collections.defaultdict(set) for key, value in env.items(): - try: - dependent_keys = re.findall("{(.+?)}", value) - for dependency in dependent_keys: - # Ignore direct references to itself because - # we don't format with itself anyway - if dependency == key: - continue + dependent_keys = re.findall("{(.+?)}", value) + for dependent_key in dependent_keys: + # Ignore reference to itself or key is not in env + if dependent_key != key and dependent_key in env: + dependencies[key].add(dependent_key) - dependencies.append((key, dependency)) - except Exception: - dependencies.append((key, value)) - - result = _topological_sort(dependencies) + ordered, cyclic = _topological_sort(dependencies) # Check cycle - if result.cyclic: - raise CycleError(f"A cycle is detected on: {result.cyclic}") + if cyclic: + raise CycleError(f"A cycle is detected on: {cyclic}") # Format dynamic values - for key in reversed(result.sorted): + for key in reversed(ordered): if key in env: if not isinstance(env[key], str): continue @@ -259,15 +271,6 @@ def compute_env_variables_structure( data.pop(key) # format without itself env[key] = _partial_format(env[key], data=data) - # Format cyclic values - for key in result.cyclic: - if key in env: - if not isinstance(env[key], str): - continue - data = env.copy() - data.pop(key) # format without itself - env[key] = _partial_format(env[key], data=data) - # Format dynamic keys if fill_dynamic_keys: formatted = {} @@ -291,7 +294,7 @@ def compute_env_variables_structure( def merge_env_variables( src_env: dict[str, str], dst_env: dict[str, str], - missing: Optional[str] = None, + missing_template: Optional[str] = None, ): """Merge the tools environment with the 'current_env'. @@ -304,7 +307,7 @@ def merge_env_variables( src_env (dict): The dynamic environment dst_env (dict): The target environment variables mapping to merge the dynamic environment into. - missing (str): Argument passed to '_partial_format' during merging. + missing_template (str): Argument passed to '_partial_format' during merging. `None` should keep missing keys unchanged. Returns: @@ -314,7 +317,7 @@ def merge_env_variables( result = dst_env.copy() for key, value in src_env.items(): result[key] = _partial_format( - str(value), data=dst_env, missing=missing + str(value), dst_env, missing_template ) return result From 1d7036ffed2ddbb2610212a999ce8492f2d15386 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 15:49:48 +0100 Subject: [PATCH 360/463] formatting fixes --- client/ayon_core/lib/env_tools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/lib/env_tools.py b/client/ayon_core/lib/env_tools.py index c71350869e..c1bfe0c292 100644 --- a/client/ayon_core/lib/env_tools.py +++ b/client/ayon_core/lib/env_tools.py @@ -295,7 +295,7 @@ def merge_env_variables( src_env: dict[str, str], dst_env: dict[str, str], missing_template: Optional[str] = None, -): +) -> dict[str, str]: """Merge the tools environment with the 'current_env'. This finalizes the join with a current environment by formatting the @@ -307,11 +307,11 @@ def merge_env_variables( src_env (dict): The dynamic environment dst_env (dict): The target environment variables mapping to merge the dynamic environment into. - missing_template (str): Argument passed to '_partial_format' during merging. - `None` should keep missing keys unchanged. + missing_template (str): Argument passed to '_partial_format' during + merging. `None` should keep missing keys unchanged. Returns: - dict: The resulting environment after the merge. + dict[str, str]: The resulting environment after the merge. """ result = dst_env.copy() From 6a57b4c4504bb19987a3e10bcf8f167e4ae121b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:13:16 +0100 Subject: [PATCH 361/463] use odd numbers for frame size --- client/ayon_core/tools/utils/nice_checkbox.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/utils/nice_checkbox.py b/client/ayon_core/tools/utils/nice_checkbox.py index 06845c397a..b47e3e62e6 100644 --- a/client/ayon_core/tools/utils/nice_checkbox.py +++ b/client/ayon_core/tools/utils/nice_checkbox.py @@ -328,6 +328,9 @@ class NiceCheckbox(QtWidgets.QFrame): if frame_rect.width() < 0 or frame_rect.height() < 0: return + frame_rect.setLeft(frame_rect.x() + (frame_rect.width() % 2)) + frame_rect.setTop(frame_rect.y() + (frame_rect.height() % 2)) + painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) @@ -364,11 +367,16 @@ class NiceCheckbox(QtWidgets.QFrame): margin_size_c = 0 checkbox_rect = QtCore.QRect( - frame_rect.x() + margin_size_c, - frame_rect.y() + margin_size_c, - frame_rect.width() - (margin_size_c * 2), - frame_rect.height() - (margin_size_c * 2) + frame_rect.x(), + frame_rect.y(), + frame_rect.width(), + frame_rect.height() ) + if margin_size_c: + checkbox_rect.adjust( + margin_size_c, margin_size_c, + -margin_size_c, -margin_size_c + ) if checkbox_rect.width() > checkbox_rect.height(): radius = floor(checkbox_rect.height() * 0.5) From 21187bb28cdc76655e562f474b65d6245de449c7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:13:25 +0100 Subject: [PATCH 362/463] use NoPen instead of transparent --- client/ayon_core/tools/utils/nice_checkbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/nice_checkbox.py b/client/ayon_core/tools/utils/nice_checkbox.py index b47e3e62e6..3d9d63b6bc 100644 --- a/client/ayon_core/tools/utils/nice_checkbox.py +++ b/client/ayon_core/tools/utils/nice_checkbox.py @@ -383,7 +383,7 @@ class NiceCheckbox(QtWidgets.QFrame): else: radius = floor(checkbox_rect.width() * 0.5) - painter.setPen(QtCore.Qt.transparent) + painter.setPen(QtCore.Qt.NoPen) painter.setBrush(bg_color) painter.drawRoundedRect(checkbox_rect, radius, radius) From cbaefffabc756a7922481263fe12a335467e5d44 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 18:55:04 +0100 Subject: [PATCH 363/463] multiselection EnumDef allows empty items --- client/ayon_core/lib/attribute_definitions.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e8327a45b6..f3de4fc943 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -562,17 +562,18 @@ class EnumDef(AbstractAttrDef): multiselection: Optional[bool] = False, **kwargs ): - if not items: - raise ValueError(( - "Empty 'items' value. {} must have" + if multiselection is None: + multiselection = False + + if not items and not multiselection: + raise ValueError( + f"Empty 'items' value. {self.__class__.__name__} must have" " defined values on initialization." - ).format(self.__class__.__name__)) + ) items = self.prepare_enum_items(items) item_values = [item["value"] for item in items] item_values_set = set(item_values) - if multiselection is None: - multiselection = False if multiselection: if default is None: From 8a27bd5bc3a680d144e23c1f4a3f4a08ac3957ae Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:34:36 +0100 Subject: [PATCH 364/463] implemented 'PlaceholderPlainTextEdit' --- client/ayon_core/tools/utils/__init__.py | 2 ++ client/ayon_core/tools/utils/widgets.py | 41 +++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/tools/utils/__init__.py b/client/ayon_core/tools/utils/__init__.py index 4714e76ea3..9206af9beb 100644 --- a/client/ayon_core/tools/utils/__init__.py +++ b/client/ayon_core/tools/utils/__init__.py @@ -5,6 +5,7 @@ from .widgets import ( ComboBox, CustomTextComboBox, PlaceholderLineEdit, + PlaceholderPlainTextEdit, ElideLabel, HintedLineEdit, ExpandingTextEdit, @@ -89,6 +90,7 @@ __all__ = ( "ComboBox", "CustomTextComboBox", "PlaceholderLineEdit", + "PlaceholderPlainTextEdit", "ElideLabel", "HintedLineEdit", "ExpandingTextEdit", diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 059a06648b..1074b6d4fb 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -88,23 +88,48 @@ class CustomTextComboBox(ComboBox): painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option) -class PlaceholderLineEdit(QtWidgets.QLineEdit): - """Set placeholder color of QLineEdit in Qt 5.12 and higher.""" - def __init__(self, *args, **kwargs): - super(PlaceholderLineEdit, self).__init__(*args, **kwargs) - # Change placeholder palette color - if hasattr(QtGui.QPalette, "PlaceholderText"): - filter_palette = self.palette() +class _Cache: + _placeholder_color = None + + @classmethod + def get_placeholder_color(cls): + if cls._placeholder_color is None: color_obj = get_objected_colors("font") color = color_obj.get_qcolor() color.setAlpha(67) + cls._placeholder_color = color + return cls._placeholder_color + + +class PlaceholderLineEdit(QtWidgets.QLineEdit): + """Set placeholder color of QLineEdit in Qt 5.12 and higher.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Change placeholder palette color + if hasattr(QtGui.QPalette, "PlaceholderText"): + filter_palette = self.palette() filter_palette.setColor( QtGui.QPalette.PlaceholderText, - color + _Cache.get_placeholder_color() ) self.setPalette(filter_palette) +class PlaceholderPlainTextEdit(QtWidgets.QPlainTextEdit): + """Set placeholder color of QPlainTextEdit in Qt 5.12 and higher.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Change placeholder palette color + if hasattr(QtGui.QPalette, "PlaceholderText"): + viewport = self.viewport() + filter_palette = viewport.palette() + filter_palette.setColor( + QtGui.QPalette.PlaceholderText, + _Cache.get_placeholder_color() + ) + viewport.setPalette(filter_palette) + + class ElideLabel(QtWidgets.QLabel): """Label which elide text. From bfbc8baa3cb5ec684099d61a2a101fedbb30d9c9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:34:51 +0100 Subject: [PATCH 365/463] use placeholder widgets in attribute definitions --- client/ayon_core/tools/attribute_defs/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 201fd5be48..e4f1ddec51 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -22,6 +22,8 @@ from ayon_core.tools.utils import ( FocusSpinBox, FocusDoubleSpinBox, MultiSelectionComboBox, + PlaceholderLineEdit, + PlaceholderPlainTextEdit, set_style_property, ) from ayon_core.tools.utils import NiceCheckbox @@ -502,9 +504,9 @@ class TextAttrWidget(_BaseAttrDefWidget): self.multiline = self.attr_def.multiline if self.multiline: - input_widget = QtWidgets.QPlainTextEdit(self) + input_widget = PlaceholderPlainTextEdit(self) else: - input_widget = QtWidgets.QLineEdit(self) + input_widget = PlaceholderLineEdit(self) # Override context menu event to add revert to default action input_widget.contextMenuEvent = self._input_widget_context_event From 416657eefa0aa5294ea70ebd6a20e14214f55243 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:05:55 +0100 Subject: [PATCH 366/463] added empty item --- .../ayon_core/tools/attribute_defs/widgets.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 93f63730f5..32671eade7 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -2,7 +2,7 @@ import copy import typing from typing import Optional -from qtpy import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore, QtGui from ayon_core.lib.attribute_definitions import ( AbstractAttrDef, @@ -655,6 +655,9 @@ class EnumAttrWidget(_BaseAttrDefWidget): for item in self.attr_def.items: input_widget.addItem(item["label"], item["value"]) + if not self.attr_def.items: + self._add_empty_item(input_widget) + idx = input_widget.findData(self.attr_def.default) if idx >= 0: input_widget.setCurrentIndex(idx) @@ -671,6 +674,20 @@ class EnumAttrWidget(_BaseAttrDefWidget): input_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) input_widget.customContextMenuRequested.connect(self._on_context_menu) + def _add_empty_item(self, input_widget): + model = input_widget.model() + if not isinstance(model, QtGui.QStandardItemModel): + return + + root_item = model.invisibleRootItem() + + empty_item = QtGui.QStandardItem() + empty_item.setData("< No items to select >", QtCore.Qt.DisplayRole) + empty_item.setData("", QtCore.Qt.UserRole) + empty_item.setFlags(QtCore.Qt.NoItemFlags) + + root_item.appendRow(empty_item) + def _on_context_menu(self, pos): menu = QtWidgets.QMenu(self) From f0943753c40565dcb1217b571eaba108b0c5b2b7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:08:44 +0100 Subject: [PATCH 367/463] set column stretch for pre-create attributes --- client/ayon_core/tools/publisher/widgets/precreate_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/precreate_widget.py b/client/ayon_core/tools/publisher/widgets/precreate_widget.py index 5ad203d370..b786fea3b5 100644 --- a/client/ayon_core/tools/publisher/widgets/precreate_widget.py +++ b/client/ayon_core/tools/publisher/widgets/precreate_widget.py @@ -85,6 +85,8 @@ class AttributesWidget(QtWidgets.QWidget): layout.setContentsMargins(0, 0, 0, 0) layout.setHorizontalSpacing(INPUTS_LAYOUT_HSPACING) layout.setVerticalSpacing(INPUTS_LAYOUT_VSPACING) + layout.setColumnStretch(0, 0) + layout.setColumnStretch(1, 1) self._layout = layout From c6093f72946898ddff80010f48509bb3a75e429c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:31:28 +0100 Subject: [PATCH 368/463] use task label if is filled --- .../tools/common_models/hierarchy.py | 28 +++++++++++++++---- client/ayon_core/tools/utils/tasks_widget.py | 2 +- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/common_models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py index 6bccb0f468..323a433a4f 100644 --- a/client/ayon_core/tools/common_models/hierarchy.py +++ b/client/ayon_core/tools/common_models/hierarchy.py @@ -1,12 +1,18 @@ +from __future__ import annotations + import time import collections import contextlib +import typing from abc import ABC, abstractmethod import ayon_api from ayon_core.lib import NestedCacheItem +if typing.TYPE_CHECKING: + from typing import Union + HIERARCHY_MODEL_SENDER = "hierarchy.model" @@ -82,19 +88,26 @@ class TaskItem: Args: task_id (str): Task id. name (str): Name of task. + name (Union[str, None]): Task label. task_type (str): Type of task. parent_id (str): Parent folder id. """ def __init__( - self, task_id, name, task_type, parent_id + self, + task_id: str, + name: str, + label: Union[str, None], + task_type: str, + parent_id: str, ): self.task_id = task_id self.name = name + self.label = label self.task_type = task_type self.parent_id = parent_id - self._label = None + self._full_label = None @property def id(self): @@ -107,15 +120,16 @@ class TaskItem: return self.task_id @property - def label(self): + def full_label(self): """Label of task item for UI. Returns: str: Label of task item. """ - if self._label is None: - self._label = "{} ({})".format(self.name, self.task_type) + if self._full_label is None: + label = self.label or self.name + self._full_label = f"{label} ({self.task_type})" return self._label def to_data(self): @@ -128,6 +142,7 @@ class TaskItem: return { "task_id": self.task_id, "name": self.name, + "label": self.label, "parent_id": self.parent_id, "task_type": self.task_type, } @@ -159,6 +174,7 @@ def _get_task_items_from_tasks(tasks): output.append(TaskItem( task["id"], task["name"], + task["label"], task["type"], folder_id )) @@ -368,7 +384,7 @@ class HierarchyModel(object): sender (Union[str, None]): Who requested the task item. Returns: - Union[TaskItem, None]: Task item found by name and folder id. + Optional[TaskItem]: Task item found by name and folder id. """ for task_item in self.get_task_items(project_name, folder_id, sender): diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index bba7b93925..87a4c3db3b 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -270,7 +270,7 @@ class TasksQtModel(QtGui.QStandardItemModel): task_type_item_by_name, task_type_icon_cache ) - item.setData(task_item.label, QtCore.Qt.DisplayRole) + item.setData(task_item.full_label, QtCore.Qt.DisplayRole) item.setData(name, ITEM_NAME_ROLE) item.setData(task_item.id, ITEM_ID_ROLE) item.setData(task_item.task_type, TASK_TYPE_ROLE) From 94a32c0a061f5aef2e851f2eea422c63fa1ca9b2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:47:58 +0100 Subject: [PATCH 369/463] fix used variable name --- client/ayon_core/tools/common_models/hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/common_models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py index 323a433a4f..edff8471b0 100644 --- a/client/ayon_core/tools/common_models/hierarchy.py +++ b/client/ayon_core/tools/common_models/hierarchy.py @@ -130,7 +130,7 @@ class TaskItem: if self._full_label is None: label = self.label or self.name self._full_label = f"{label} ({self.task_type})" - return self._label + return self._full_label def to_data(self): """Converts task item to data. From e1b0680ba47540adbc09a7c886806915cf74f4ea Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 16:58:55 +0100 Subject: [PATCH 370/463] Added basic tests for env parse and compute --- tests/client/ayon_core/lib/test_env_tools.py | 126 +++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tests/client/ayon_core/lib/test_env_tools.py diff --git a/tests/client/ayon_core/lib/test_env_tools.py b/tests/client/ayon_core/lib/test_env_tools.py new file mode 100644 index 0000000000..396d430376 --- /dev/null +++ b/tests/client/ayon_core/lib/test_env_tools.py @@ -0,0 +1,126 @@ +import platform +import unittest +from unittest.mock import patch + +from ayon_core.lib.env_tools import ( + CycleError, + DynamicKeyClashError, + parse_env_variables_structure, + compute_env_variables_structure, +) + +COMPUTE_SRC_ENV = { + "COMPUTE_VERSION": "1.0.0", + # Will be available only for darwin + "COMPUTE_ONE_PLATFORM": { + "darwin": "Compute macOs", + }, + "COMPUTE_LOCATION": { + "darwin": "/compute-app-{COMPUTE_VERSION}", + "linux": "/usr/compute-app-{COMPUTE_VERSION}", + "windows": "C:/Program Files/compute-app-{COMPUTE_VERSION}" + }, + "PATH_LIST": { + "darwin": ["{COMPUTE_LOCATION}/bin", "{COMPUTE_LOCATION}/bin2"], + "linux": ["{COMPUTE_LOCATION}/bin", "{COMPUTE_LOCATION}/bin2"], + "windows": ["{COMPUTE_LOCATION}/bin", "{COMPUTE_LOCATION}/bin2"], + }, + "PATH_STR": { + "darwin": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", + "linux": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", + "windows": "{COMPUTE_LOCATION}/bin;{COMPUTE_LOCATION}/bin2", + }, +} + +PARSE_RESULT_WINDOWS = { + "COMPUTE_VERSION": "1.0.0", + "COMPUTE_LOCATION": "C:/Program Files/compute-app-{COMPUTE_VERSION}", + "PATH_LIST": "{COMPUTE_LOCATION}/bin;{COMPUTE_LOCATION}/bin2", + "PATH_STR": "{COMPUTE_LOCATION}/bin;{COMPUTE_LOCATION}/bin2", +} + +PARSE_RESULT_LINUX = { + "COMPUTE_VERSION": "1.0.0", + "COMPUTE_LOCATION": "/usr/compute-app-{COMPUTE_VERSION}", + "PATH_LIST": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", + "PATH_STR": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", +} + +PARSE_RESULT_DARWIN = { + "COMPUTE_VERSION": "1.0.0", + "COMPUTE_ONE_PLATFORM": "Compute macOs", + "COMPUTE_LOCATION": "/compute-app-{COMPUTE_VERSION}", + "PATH_LIST": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", + "PATH_STR": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", +} + +COMPUTE_RESULT_WINDOWS = { + "COMPUTE_VERSION": "1.0.0", + "COMPUTE_LOCATION": "C:/Program Files/compute-app-1.0.0", + "PATH_LIST": "C:/Program Files/compute-app-1.0.0/bin;C:/Program Files/compute-app-1.0.0/bin2", + "PATH_STR": "C:/Program Files/compute-app-1.0.0/bin;C:/Program Files/compute-app-1.0.0/bin2" +} + +COMPUTE_RESULT_LINUX = { + "COMPUTE_VERSION": "1.0.0", + "COMPUTE_LOCATION": "/usr/compute-app-1.0.0", + "PATH_LIST": "/usr/compute-app-1.0.0/bin:/usr/compute-app-1.0.0/bin2", + "PATH_STR": "/usr/compute-app-1.0.0/bin:/usr/compute-app-1.0.0/bin2" +} + +COMPUTE_RESULT_DARWIN = { + "COMPUTE_VERSION": "1.0.0", + "COMPUTE_ONE_PLATFORM": "Compute macOs", + "COMPUTE_LOCATION": "/compute-app-1.0.0", + "PATH_LIST": "/compute-app-1.0.0/bin:/compute-app-1.0.0/bin2", + "PATH_STR": "/compute-app-1.0.0/bin:/compute-app-1.0.0/bin2" +} + + +class EnvParseCompute(unittest.TestCase): + def test_parse_env(self): + with patch("platform.system", return_value="windows"): + result = parse_env_variables_structure(COMPUTE_SRC_ENV) + assert result == PARSE_RESULT_WINDOWS + + with patch("platform.system", return_value="linux"): + result = parse_env_variables_structure(COMPUTE_SRC_ENV) + assert result == PARSE_RESULT_LINUX + + with patch("platform.system", return_value="darwin"): + result = parse_env_variables_structure(COMPUTE_SRC_ENV) + assert result == PARSE_RESULT_DARWIN + + def test_compute_env(self): + with patch("platform.system", return_value="windows"): + result = compute_env_variables_structure( + parse_env_variables_structure(COMPUTE_SRC_ENV) + ) + assert result == COMPUTE_RESULT_WINDOWS + + with patch("platform.system", return_value="linux"): + result = compute_env_variables_structure( + parse_env_variables_structure(COMPUTE_SRC_ENV) + ) + assert result == COMPUTE_RESULT_LINUX + + with patch("platform.system", return_value="darwin"): + result = compute_env_variables_structure( + parse_env_variables_structure(COMPUTE_SRC_ENV) + ) + assert result == COMPUTE_RESULT_DARWIN + + def test_cycle_error(self): + with self.assertRaises(CycleError): + compute_env_variables_structure({ + "KEY_1": "{KEY_2}", + "KEY_2": "{KEY_1}", + }) + + def test_dynamic_key_error(self): + with self.assertRaises(DynamicKeyClashError): + compute_env_variables_structure({ + "KEY_A": "Occupied", + "SUBKEY": "A", + "KEY_{SUBKEY}": "Resolves as occupied key", + }) From f28cfe4c0ea013d7e43abfe3bc96f6f5b26c1a2c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 16:59:20 +0100 Subject: [PATCH 371/463] modify code to be able run tests --- client/ayon_core/lib/env_tools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/lib/env_tools.py b/client/ayon_core/lib/env_tools.py index c1bfe0c292..b02966fac2 100644 --- a/client/ayon_core/lib/env_tools.py +++ b/client/ayon_core/lib/env_tools.py @@ -122,6 +122,9 @@ def parse_env_variables_structure( if platform_name is None: platform_name = platform.system().lower() + # Separator based on OS 'os.pathsep' is ';' on Windows and ':' on Unix + sep = ";" if platform_name == "windows" else ":" + result = {} for variable, value in env.items(): # Platform specific values @@ -130,7 +133,7 @@ def parse_env_variables_structure( # Allow to have lists as values in the tool data if isinstance(value, (list, tuple)): - value = os.pathsep.join(value) + value = sep.join(value) if not value: continue From f09ece485f5c7462b89e88c0cc58ca40269cdad7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 16:59:49 +0100 Subject: [PATCH 372/463] removed unused import --- tests/client/ayon_core/lib/test_env_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/client/ayon_core/lib/test_env_tools.py b/tests/client/ayon_core/lib/test_env_tools.py index 396d430376..5bc63a6158 100644 --- a/tests/client/ayon_core/lib/test_env_tools.py +++ b/tests/client/ayon_core/lib/test_env_tools.py @@ -1,4 +1,3 @@ -import platform import unittest from unittest.mock import patch From 7e5f9f27d133f75ce8933b11fdde534ad5fc73bf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:00:43 +0100 Subject: [PATCH 373/463] added separators --- tests/client/ayon_core/lib/test_env_tools.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/client/ayon_core/lib/test_env_tools.py b/tests/client/ayon_core/lib/test_env_tools.py index 5bc63a6158..7c9ff26d6f 100644 --- a/tests/client/ayon_core/lib/test_env_tools.py +++ b/tests/client/ayon_core/lib/test_env_tools.py @@ -8,6 +8,7 @@ from ayon_core.lib.env_tools import ( compute_env_variables_structure, ) +# --- Test data --- COMPUTE_SRC_ENV = { "COMPUTE_VERSION": "1.0.0", # Will be available only for darwin @@ -31,6 +32,8 @@ COMPUTE_SRC_ENV = { }, } +# --- RESULTS --- +# --- Parse results --- PARSE_RESULT_WINDOWS = { "COMPUTE_VERSION": "1.0.0", "COMPUTE_LOCATION": "C:/Program Files/compute-app-{COMPUTE_VERSION}", @@ -53,6 +56,7 @@ PARSE_RESULT_DARWIN = { "PATH_STR": "{COMPUTE_LOCATION}/bin:{COMPUTE_LOCATION}/bin2", } +# --- Compute results --- COMPUTE_RESULT_WINDOWS = { "COMPUTE_VERSION": "1.0.0", "COMPUTE_LOCATION": "C:/Program Files/compute-app-1.0.0", From 02b22797170ce8a2326864dfa4fd4dc44d696c6f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:06:21 +0100 Subject: [PATCH 374/463] match typehints in arguments --- client/ayon_core/lib/env_tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/lib/env_tools.py b/client/ayon_core/lib/env_tools.py index b02966fac2..bc788a082d 100644 --- a/client/ayon_core/lib/env_tools.py +++ b/client/ayon_core/lib/env_tools.py @@ -63,9 +63,9 @@ def get_paths_from_environ( """Return existing paths from specific environment variable. Args: - env_key (str): Environment key where should look for paths. - env_value (str): Value of environment variable. Argument `env_key` is - skipped if this argument is entered. + env_key (Optional[str]): Environment key where should look for paths. + env_value (Optional[str]): Value of environment variable. + Argument `env_key` is skipped if this argument is entered. return_first (bool): Return first found value or return list of found paths. `None` or empty list returned if nothing found. From 17399b8f4a6b2cb53db950fd2cccd477307346ff Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:06:47 +0100 Subject: [PATCH 375/463] fix formatting --- tests/client/ayon_core/lib/test_env_tools.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/client/ayon_core/lib/test_env_tools.py b/tests/client/ayon_core/lib/test_env_tools.py index 7c9ff26d6f..38aac822bf 100644 --- a/tests/client/ayon_core/lib/test_env_tools.py +++ b/tests/client/ayon_core/lib/test_env_tools.py @@ -60,8 +60,14 @@ PARSE_RESULT_DARWIN = { COMPUTE_RESULT_WINDOWS = { "COMPUTE_VERSION": "1.0.0", "COMPUTE_LOCATION": "C:/Program Files/compute-app-1.0.0", - "PATH_LIST": "C:/Program Files/compute-app-1.0.0/bin;C:/Program Files/compute-app-1.0.0/bin2", - "PATH_STR": "C:/Program Files/compute-app-1.0.0/bin;C:/Program Files/compute-app-1.0.0/bin2" + "PATH_LIST": ( + "C:/Program Files/compute-app-1.0.0/bin" + ";C:/Program Files/compute-app-1.0.0/bin2" + ), + "PATH_STR": ( + "C:/Program Files/compute-app-1.0.0/bin" + ";C:/Program Files/compute-app-1.0.0/bin2" + ) } COMPUTE_RESULT_LINUX = { From b09bcdada8e6f842eee0a4cd202f102607d0c1c8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Feb 2025 17:17:00 +0100 Subject: [PATCH 376/463] remove trailing spaces --- tests/client/ayon_core/lib/test_env_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/client/ayon_core/lib/test_env_tools.py b/tests/client/ayon_core/lib/test_env_tools.py index 38aac822bf..e7aea7fd7d 100644 --- a/tests/client/ayon_core/lib/test_env_tools.py +++ b/tests/client/ayon_core/lib/test_env_tools.py @@ -58,7 +58,7 @@ PARSE_RESULT_DARWIN = { # --- Compute results --- COMPUTE_RESULT_WINDOWS = { - "COMPUTE_VERSION": "1.0.0", + "COMPUTE_VERSION": "1.0.0", "COMPUTE_LOCATION": "C:/Program Files/compute-app-1.0.0", "PATH_LIST": ( "C:/Program Files/compute-app-1.0.0/bin" From a78ee95070cac951c90ef4d3d727de8e656424a5 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 17 Feb 2025 10:51:12 +0000 Subject: [PATCH 377/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 909ecd7a3c..a93ec35297 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.0+dev" +__version__ = "1.1.1" diff --git a/package.py b/package.py index 0b888f5c33..a33fc9d77c 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.0+dev" +version = "1.1.1" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 32822391c8..bfe36e9f5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.0+dev" +version = "1.1.1" description = "" authors = ["Ynput Team "] readme = "README.md" From a83f1ca5ade16afb5a14de4c5150dfdde6ea089f Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 17 Feb 2025 10:51:52 +0000 Subject: [PATCH 378/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index a93ec35297..f2e82af12b 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.1" +__version__ = "1.1.1+dev" diff --git a/package.py b/package.py index a33fc9d77c..b9629d6c51 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.1" +version = "1.1.1+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index bfe36e9f5a..87fe9708dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.1" +version = "1.1.1+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 777e03d88456acb0e5a6638ce34d70c52943112c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 17 Feb 2025 14:33:13 +0100 Subject: [PATCH 379/463] add required dependencies for running tests --- poetry.lock | 712 ++++++++++++++++++++++++++++++------------------- pyproject.toml | 9 +- 2 files changed, 445 insertions(+), 276 deletions(-) diff --git a/poetry.lock b/poetry.lock index be5a3b2c2c..2d040a5f91 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "appdirs" @@ -6,37 +6,59 @@ version = "1.4.4" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] +[[package]] +name = "attrs" +version = "25.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, + {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, +] + +[package.extras] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] + [[package]] name = "ayon-python-api" -version = "1.0.1" +version = "1.0.12" description = "AYON Python API" optional = false python-versions = "*" +groups = ["dev"] files = [ - {file = "ayon-python-api-1.0.1.tar.gz", hash = "sha256:6a53af84903317e2097f3c6bba0094e90d905d6670fb9c7d3ad3aa9de6552bc1"}, - {file = "ayon_python_api-1.0.1-py3-none-any.whl", hash = "sha256:d4b649ac39c9003cdbd60f172c0d35f05d310fba3a0649b6d16300fe67f967d6"}, + {file = "ayon-python-api-1.0.12.tar.gz", hash = "sha256:8e4c03436df8afdda4c6ad4efce436068771995bb0153a90e003364afa0e7f55"}, + {file = "ayon_python_api-1.0.12-py3-none-any.whl", hash = "sha256:65f61c2595dd6deb26fed5e3fda7baef887f475fa4b21df12513646ddccf4a7d"}, ] [package.dependencies] appdirs = ">=1,<2" requests = ">=2.27.1" -six = ">=1.15" -Unidecode = ">=1.2.0" +Unidecode = ">=1.3.0" [[package]] name = "certifi" -version = "2024.2.2" +version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, ] [[package]] @@ -45,6 +67,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -52,118 +75,139 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] +[[package]] +name = "clique" +version = "2.0.0" +description = "Manage collections with common numerical component" +optional = false +python-versions = ">=3.0, <4.0" +groups = ["dev"] +files = [ + {file = "clique-2.0.0-py2.py3-none-any.whl", hash = "sha256:45e2a4c6078382e0b217e5e369494279cf03846d95ee601f93290bed5214c22e"}, + {file = "clique-2.0.0.tar.gz", hash = "sha256:6e1115dbf21b1726f4b3db9e9567a662d6bdf72487c4a0a1f8cb7f10cf4f4754"}, +] + +[package.extras] +dev = ["lowdown (>=0.2.0,<1)", "pytest (>=2.3.5,<5)", "pytest-cov (>=2,<3)", "pytest-runner (>=2.7,<3)", "sphinx (>=2,<4)", "sphinx-rtd-theme (>=0.1.6,<1)"] +doc = ["lowdown (>=0.2.0,<1)", "sphinx (>=2,<4)", "sphinx-rtd-theme (>=0.1.6,<1)"] +test = ["pytest (>=2.3.5,<5)", "pytest-cov (>=2,<3)", "pytest-runner (>=2.7,<3)"] + [[package]] name = "codespell" -version = "2.2.6" -description = "Codespell" +version = "2.4.1" +description = "Fix common misspellings in text files" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "codespell-2.2.6-py3-none-any.whl", hash = "sha256:9ee9a3e5df0990604013ac2a9f22fa8e57669c827124a2e961fe8a1da4cacc07"}, - {file = "codespell-2.2.6.tar.gz", hash = "sha256:a8c65d8eb3faa03deabab6b3bbe798bea72e1799c7e9e955d57eca4096abcff9"}, + {file = "codespell-2.4.1-py3-none-any.whl", hash = "sha256:3dadafa67df7e4a3dbf51e0d7315061b80d265f9552ebd699b3dd6834b47e425"}, + {file = "codespell-2.4.1.tar.gz", hash = "sha256:299fcdcb09d23e81e35a671bbe746d5ad7e8385972e65dbb833a2eaac33c01e5"}, ] [package.extras] dev = ["Pygments", "build", "chardet", "pre-commit", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli", "twine"] hard-encoding-detection = ["chardet"] -toml = ["tomli"] +toml = ["tomli ; python_version < \"3.11\""] types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] [[package]] @@ -172,6 +216,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -179,24 +225,26 @@ files = [ [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" +groups = ["dev"] files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -204,29 +252,31 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.1" +version = "3.17.0" description = "A platform independent file lock." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, + {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "identify" -version = "2.5.35" +version = "2.6.7" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, + {file = "identify-2.6.7-py2.py3-none-any.whl", hash = "sha256:155931cb617a401807b09ecec6635d6c692d180090a1cedca8ef7d58ba5b6aa0"}, + {file = "identify-2.6.7.tar.gz", hash = "sha256:3fa266b42eba321ee0b2bb0936a6a6b9e36a1351cbb69055b3082f4193035684"}, ] [package.extras] @@ -234,75 +284,149 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.6" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" +groups = ["dev"] files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] -name = "nodeenv" -version = "1.8.0" -description = "Node.js virtual environment builder" +name = "mock" +version = "5.1.0" +description = "Rolling backport of unittest.mock for all Pythons" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = ">=3.6" +groups = ["dev"] files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "mock-5.1.0-py3-none-any.whl", hash = "sha256:18c694e5ae8a208cdb3d2c20a993ca1a7b0efa258c247a1e565150f477f83744"}, + {file = "mock-5.1.0.tar.gz", hash = "sha256:5e96aad5ccda4718e0a229ed94b2024df75cc2d55575ba5762d31f5767b8767d"}, ] -[package.dependencies] -setuptools = "*" +[package.extras] +build = ["blurb", "twine", "wheel"] +docs = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "opentimelineio" +version = "0.17.0" +description = "Editorial interchange format and API" +optional = false +python-versions = "!=3.9.0,>=3.7" +groups = ["dev"] +files = [ + {file = "OpenTimelineIO-0.17.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:2dd31a570cabfd6227c1b1dd0cc038da10787492c26c55de058326e21fe8a313"}, + {file = "OpenTimelineIO-0.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a1da5d4803d1ba5e846b181a9e0f4a392c76b9acc5e08947772bc086f2ebfc0"}, + {file = "OpenTimelineIO-0.17.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3527977aec8202789a42d60e1e0dc11b4154f585ef72921760445f43e7967a00"}, + {file = "OpenTimelineIO-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3aafb4c50455832ed2627c2cac654b896473a5c1f8348ddc07c10be5cfbd59"}, + {file = "OpenTimelineIO-0.17.0-cp310-cp310-win32.whl", hash = "sha256:fee45af9f6330773893cd0858e92f8256bb5bde4229b44a76f03e59a9fb1b1b6"}, + {file = "OpenTimelineIO-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:d51887619689c21d67cc4b11b1088f99ae44094513315e7a144be00f1393bfa8"}, + {file = "OpenTimelineIO-0.17.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:cbf05c3e8c0187969f79e91f7495d1f0dc3609557874d8e601ba2e072c70ddb1"}, + {file = "OpenTimelineIO-0.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d3430c3f4e88c5365d7b6afbee920b0815b62ecf141abe44cd739c9eedc04284"}, + {file = "OpenTimelineIO-0.17.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1912345227b0bd1654c7153863eadbcee60362aa46340678e576e5d2aa3106a"}, + {file = "OpenTimelineIO-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51e06eb11a868d970c1534e39faf916228d5163bf3598076d408d8f393ab0bd4"}, + {file = "OpenTimelineIO-0.17.0-cp311-cp311-win32.whl", hash = "sha256:5c3a3f4780b25a8c1a80d788becba691d12b629069ad8783d0db21027639276f"}, + {file = "OpenTimelineIO-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c8726b33af30ba42928972192311ea0f986edbbd5f74651bada182d4fe805c"}, + {file = "OpenTimelineIO-0.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:9a9af4105a088c0ab131780e49db268db7e37871aac33db842de6b2b16f14e39"}, + {file = "OpenTimelineIO-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8e653ad1dd3b85f5c312a742dc24b61b330964aa391dc5bc072fe8b9c85adff1"}, + {file = "OpenTimelineIO-0.17.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a77823c27a1b93c6b87682372c3734ac5fddc10bfe53875e657d43c60fb885"}, + {file = "OpenTimelineIO-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4f4efcf3ddd81b62c4feb49a0bcc309b50ffeb6a8c48ab173d169a029006f4d"}, + {file = "OpenTimelineIO-0.17.0-cp312-cp312-win32.whl", hash = "sha256:9872ab74a20bb2bb3a50af04e80fe9238998d67d6be4e30e45aebe25d3eefac6"}, + {file = "OpenTimelineIO-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:c83b78be3312d3152d7e07ab32b0086fe220acc2a5b035b70ad69a787c0ece62"}, + {file = "OpenTimelineIO-0.17.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:0e671a6f2a1f772445bb326c7640dc977cfc3db589fe108a783a0311939cfac8"}, + {file = "OpenTimelineIO-0.17.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b931a3189b4ce064f06f15a89fe08ef4de01f7dcf0abc441fe2e02ef2a3311bb"}, + {file = "OpenTimelineIO-0.17.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923cb54d806c981cf1e91916c3e57fba5664c22f37763dd012bad5a5a7bd4db4"}, + {file = "OpenTimelineIO-0.17.0-cp37-cp37m-win32.whl", hash = "sha256:8e16598c5084dcb21df3d83978b0e5f72300af9edd4cdcb85e3b0ba5da0df4e8"}, + {file = "OpenTimelineIO-0.17.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7eed5033494888fb3f802af50e60559e279b2f398802748872903c2f54efd2c9"}, + {file = "OpenTimelineIO-0.17.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:118baa22b9227da5003bee653601a68686ae2823682dcd7d13c88178c63081c3"}, + {file = "OpenTimelineIO-0.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:43389eacdee2169de454e1c79ecfea82f54a9e73b67151427a9b621349a22b7f"}, + {file = "OpenTimelineIO-0.17.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17659b1e6aa42ed617a942f7a2bfc6ecc375d0464ec127ce9edf896278ecaee9"}, + {file = "OpenTimelineIO-0.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d5ea8cfbebf3c9013cc680eef5be48bffb515aafa9dc31e99bf66052a4ca3d"}, + {file = "OpenTimelineIO-0.17.0-cp38-cp38-win32.whl", hash = "sha256:cc67c74eb4b73bc0f7d135d3ff3dbbd86b2d451a9b142690a8d1631ad79c46f2"}, + {file = "OpenTimelineIO-0.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:69b39079bee6fa4aff34c6ad6544df394bc7388483fa5ce958ecd16e243a53ad"}, + {file = "OpenTimelineIO-0.17.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a33554894dea17c22feec0201991e705c2c90a679ba2a012a0c558a7130df711"}, + {file = "OpenTimelineIO-0.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6b1ad3b3155370245b851b2f7b60006b2ebbb5bb76dd0fdc49bb4dce73fa7d96"}, + {file = "OpenTimelineIO-0.17.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:030454a9c0e9e82e5a153119f9afb8f3f4e64a3b27f80ac0dcde44b029fd3f3f"}, + {file = "OpenTimelineIO-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce64376a28919533bd4f744ff8885118abefa73f78fd408f95fa7a9489855b6"}, + {file = "OpenTimelineIO-0.17.0-cp39-cp39-win32.whl", hash = "sha256:fa8cdceb25f9003c3c0b5b32baef2c764949d88b867161ddc6f44f48f6bbfa4a"}, + {file = "OpenTimelineIO-0.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:fbcf8a000cd688633c8dc5d22e91912013c67c674329eba603358e3b54da32bf"}, + {file = "opentimelineio-0.17.0.tar.gz", hash = "sha256:10ef324e710457e9977387cd9ef91eb24a9837bfb370aec3330f9c0f146cea85"}, +] + +[package.extras] +dev = ["check-manifest", "coverage (>=4.5)", "flake8 (>=3.5)", "urllib3 (>=1.24.3)"] +view = ["PySide2 (>=5.11,<6.0) ; platform_machine == \"x86_64\"", "PySide6 (>=6.2,<7.0) ; platform_machine == \"aarch64\""] [[package]] name = "packaging" -version = "24.0" +version = "24.2" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -311,13 +435,14 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.6.2" +version = "3.8.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"}, - {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"}, + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, ] [package.dependencies] @@ -327,15 +452,28 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "pyblish-base" +version = "1.8.12" +description = "Plug-in driven automation framework for content" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "pyblish-base-1.8.12.tar.gz", hash = "sha256:ebc184eb038864380555227a8b58055dd24ece7e6ef7f16d33416c718512871b"}, + {file = "pyblish_base-1.8.12-py2.py3-none-any.whl", hash = "sha256:2cbe956bfbd4175a2d7d22b344cd345800f4d4437153434ab658fc12646a11e8"}, +] + [[package]] name = "pytest" -version = "8.1.1" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -343,97 +481,103 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.4,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-print" -version = "1.0.0" +version = "1.0.2" description = "pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout)" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "pytest_print-1.0.0-py3-none-any.whl", hash = "sha256:23484f42b906b87e31abd564761efffeb0348a6f83109fb857ee6e8e5df42b69"}, - {file = "pytest_print-1.0.0.tar.gz", hash = "sha256:1fcde9945fba462227a8959271369b10bb7a193be8452162707e63cd60875ca0"}, + {file = "pytest_print-1.0.2-py3-none-any.whl", hash = "sha256:3ae7891085dddc3cd697bd6956787240107fe76d6b5cdcfcd782e33ca6543de9"}, + {file = "pytest_print-1.0.2.tar.gz", hash = "sha256:2780350a7bbe7117f99c5d708dc7b0431beceda021b1fd3f11200670d7f33679"}, ] [package.dependencies] -pytest = ">=7.4" +pytest = ">=8.3.2" [package.extras] -test = ["covdefaults (>=2.3)", "coverage (>=7.3)", "pytest-mock (>=3.11.1)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "pytest-mock (>=3.14)"] [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -448,66 +592,83 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.3.3" +version = "0.3.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:973a0e388b7bc2e9148c7f9be8b8c6ae7471b9be37e1cc732f8f44a6f6d7720d"}, - {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfa60d23269d6e2031129b053fdb4e5a7b0637fc6c9c0586737b962b2f834493"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eca7ff7a47043cf6ce5c7f45f603b09121a7cc047447744b029d1b719278eb5"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7d3f6762217c1da954de24b4a1a70515630d29f71e268ec5000afe81377642d"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b24c19e8598916d9c6f5a5437671f55ee93c212a2c4c569605dc3842b6820386"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5a6cbf216b69c7090f0fe4669501a27326c34e119068c1494f35aaf4cc683778"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352e95ead6964974b234e16ba8a66dad102ec7bf8ac064a23f95371d8b198aab"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d6ab88c81c4040a817aa432484e838aaddf8bfd7ca70e4e615482757acb64f8"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79bca3a03a759cc773fca69e0bdeac8abd1c13c31b798d5bb3c9da4a03144a9f"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2700a804d5336bcffe063fd789ca2c7b02b552d2e323a336700abb8ae9e6a3f8"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fd66469f1a18fdb9d32e22b79f486223052ddf057dc56dea0caaf1a47bdfaf4e"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45817af234605525cdf6317005923bf532514e1ea3d9270acf61ca2440691376"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0da458989ce0159555ef224d5b7c24d3d2e4bf4c300b85467b08c3261c6bc6a8"}, - {file = "ruff-0.3.3-py3-none-win32.whl", hash = "sha256:f2831ec6a580a97f1ea82ea1eda0401c3cdf512cf2045fa3c85e8ef109e87de0"}, - {file = "ruff-0.3.3-py3-none-win_amd64.whl", hash = "sha256:be90bcae57c24d9f9d023b12d627e958eb55f595428bafcb7fec0791ad25ddfc"}, - {file = "ruff-0.3.3-py3-none-win_arm64.whl", hash = "sha256:0171aab5fecdc54383993389710a3d1227f2da124d76a2784a7098e818f92d61"}, - {file = "ruff-0.3.3.tar.gz", hash = "sha256:38671be06f57a2f8aba957d9f701ea889aa5736be806f18c0cd03d6ff0cbca8d"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, ] [[package]] -name = "setuptools" -version = "69.2.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" +name = "semver" +version = "3.0.4" +description = "Python helper for Semantic Versioning (https://semver.org)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" +groups = ["dev"] files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746"}, + {file = "semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602"}, ] [[package]] name = "tomli" -version = "2.0.1" +version = "2.2.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -516,6 +677,7 @@ version = "1.3.8" description = "ASCII transliterations of Unicode text" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39"}, {file = "Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4"}, @@ -523,30 +685,32 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["dev"] files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.25.1" +version = "20.29.2" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, + {file = "virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a"}, + {file = "virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728"}, ] [package.dependencies] @@ -555,10 +719,10 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = ">=3.9.1,<3.10" -content-hash = "1bb724694792fbc2b3c05e3355e6c25305d9f4034eb7b1b4b1791ee95427f8d2" +content-hash = "0a399d239c49db714c1166c20286fdd5cd62faf12e45ab85833c4d6ea7a04a2a" diff --git a/pyproject.toml b/pyproject.toml index 87fe9708dc..9833902c16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,12 +9,12 @@ version = "1.1.1+dev" description = "" authors = ["Ynput Team "] readme = "README.md" +package-mode = false [tool.poetry.dependencies] python = ">=3.9.1,<3.10" - -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] # test dependencies pytest = "^8.0" pytest-print = "^1.0" @@ -24,6 +24,11 @@ ruff = "^0.3.3" pre-commit = "^3.6.2" codespell = "^2.2.6" semver = "^3.0.2" +mock = "^5.0.0" +attrs = "^25.0.0" +pyblish-base = "^1.8.7" +clique = "^2.0.0" +opentimelineio = "^0.17.0" [tool.ruff] From ea905daeca9b9b35b75329d263adef37cbb74449 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 17 Feb 2025 14:38:01 +0100 Subject: [PATCH 380/463] added commands to run tests --- tools/manage.ps1 | 11 +++++++++++ tools/manage.sh | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/tools/manage.ps1 b/tools/manage.ps1 index 9a9a9a2eff..8324277713 100755 --- a/tools/manage.ps1 +++ b/tools/manage.ps1 @@ -240,6 +240,13 @@ function Run-From-Code { & $Poetry $RunArgs @arguments } +function Run-Tests { + $Poetry = "$RepoRoot\.poetry\bin\poetry.exe" + $RunArgs = @( "run", "pytest", "$($RepoRoot)/tests") + + & $Poetry $RunArgs @arguments +} + function Write-Help { <# .SYNOPSIS @@ -256,6 +263,7 @@ function Write-Help { Write-Info -Text " ruff-fix ", "Run Ruff fix for the repository" -Color White, Cyan Write-Info -Text " codespell ", "Run codespell check for the repository" -Color White, Cyan Write-Info -Text " run ", "Run a poetry command in the repository environment" -Color White, Cyan + Write-Info -Text " run-tests ", "Run ayon-core tests" -Color White, Cyan Write-Host "" } @@ -280,6 +288,9 @@ function Resolve-Function { } elseif ($FunctionName -eq "run") { Set-Cwd Run-From-Code + } elseif ($FunctionName -eq "runtests") { + Set-Cwd + Run-Tests } else { Write-Host "Unknown function ""$FunctionName""" Write-Help diff --git a/tools/manage.sh b/tools/manage.sh index 6b0a4d6978..86ae7155c5 100755 --- a/tools/manage.sh +++ b/tools/manage.sh @@ -158,6 +158,7 @@ default_help() { echo -e " ${BWhite}ruff-fix${RST} ${BCyan}Run Ruff fix for the repository${RST}" echo -e " ${BWhite}codespell${RST} ${BCyan}Run codespell check for the repository${RST}" echo -e " ${BWhite}run${RST} ${BCyan}Run a poetry command in the repository environment${RST}" + echo -e " ${BWhite}run-tests${RST} ${BCyan}Run ayon-core tests${RST}" echo "" } @@ -182,6 +183,12 @@ run_command () { "$POETRY_HOME/bin/poetry" run "$@" } +run_tests () { + echo -e "${BIGreen}>>>${RST} Running tests..." + shift; # will remove first arg ("run-tests") from the "$@" + "$POETRY_HOME/bin/poetry" run pytest ./tests +} + main () { detect_python || return 1 @@ -218,6 +225,10 @@ main () { run_command "$@" || return_code=$? exit $return_code ;; + "runtests") + run_tests "$@" || return_code=$? + exit $return_code + ;; esac if [ "$function_name" != "" ]; then From 8c8205a4e6406e07b131433ce00c1fb3014cb85c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Feb 2025 16:11:51 +0100 Subject: [PATCH 381/463] Fix doubled quotes around outer elements --- .../plugins/publish/collect_anatomy_instance_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index 354d877b62..a86ef3f24c 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -116,10 +116,10 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): if not_found_folder_paths: joined_folder_paths = ", ".join( - ["\"{}\"".format(path) for path in not_found_folder_paths] + [f"\"{path}\"" for path in not_found_folder_paths] ) self.log.warning(( - "Not found folder entities with paths \"{}\"." + "Not found folder entities with paths {}." ).format(joined_folder_paths)) def fill_missing_task_entities(self, context, project_name): From 5564863371d896f60cf8459ffeeed7f7f5848c45 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Feb 2025 16:12:15 +0100 Subject: [PATCH 382/463] Cosmetics --- .../plugins/publish/collect_anatomy_instance_data.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index a86ef3f24c..677ebb04a2 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -118,9 +118,9 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): joined_folder_paths = ", ".join( [f"\"{path}\"" for path in not_found_folder_paths] ) - self.log.warning(( - "Not found folder entities with paths {}." - ).format(joined_folder_paths)) + self.log.warning( + f"Not found folder entities with paths {joined_folder_paths}." + ) def fill_missing_task_entities(self, context, project_name): self.log.debug("Querying task entities for instances.") From 279df284ff390e9a5458ebf3da9274bf3c992067 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 17 Feb 2025 16:22:36 +0100 Subject: [PATCH 383/463] Change to debug log, because it's not very nice artist-facing information --- client/ayon_core/plugins/publish/extract_otio_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index 2461195b27..7a9a020ff0 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -286,7 +286,7 @@ class ExtractOTIOReview( ) instance.data["representations"].append(representation) - self.log.info("Adding representation: {}".format(representation)) + self.log.debug("Adding representation: {}".format(representation)) def _create_representation(self, start, duration): """ From daea1fae80b6f9df4dd88125531ee1a69e3900d3 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 17 Feb 2025 16:33:29 +0100 Subject: [PATCH 384/463] Add unit tests as part of CI/CD --- .github/workflows/pr_unittests.yaml | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/pr_unittests.yaml diff --git a/.github/workflows/pr_unittests.yaml b/.github/workflows/pr_unittests.yaml new file mode 100644 index 0000000000..a701180133 --- /dev/null +++ b/.github/workflows/pr_unittests.yaml @@ -0,0 +1,31 @@ +name: 📇 Code Linting + +on: + push: + branches: [ develop ] + pull_request: + branches: [ develop ] + + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number}} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.9' + - name: Install requirements + run: ./tools/manage.sh create-env + - name: Run tests + run: ./tools/manage.sh run-tests From 5ee08a10dc64bf561cf6f1b811dd4f31d4630ea2 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 17 Feb 2025 16:34:32 +0100 Subject: [PATCH 385/463] Add unit tests as part of CI/CD --- .github/workflows/pr_unittests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_unittests.yaml b/.github/workflows/pr_unittests.yaml index a701180133..424135785c 100644 --- a/.github/workflows/pr_unittests.yaml +++ b/.github/workflows/pr_unittests.yaml @@ -1,4 +1,4 @@ -name: 📇 Code Linting +name: Run Unit Tests on: push: From 901ec4673824e23902e2212a142d96700903288c Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Mon, 17 Feb 2025 16:37:55 +0100 Subject: [PATCH 386/463] Change emoji. --- .github/workflows/pr_unittests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_unittests.yaml b/.github/workflows/pr_unittests.yaml index 424135785c..9dcb1f301f 100644 --- a/.github/workflows/pr_unittests.yaml +++ b/.github/workflows/pr_unittests.yaml @@ -1,4 +1,4 @@ -name: Run Unit Tests +name: 💯 Run Unit Tests on: push: From ac93b5a34b9c6d8a23fd741fbba356f575ea8ef3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:36:21 +0100 Subject: [PATCH 387/463] multiselect combobox enhancements --- .../tools/loader/ui/_multicombobox.py | 381 ++++++++++++++++-- .../tools/loader/ui/statuses_combo.py | 234 ++--------- 2 files changed, 381 insertions(+), 234 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/_multicombobox.py b/client/ayon_core/tools/loader/ui/_multicombobox.py index 9efe57ef0f..393272fdf9 100644 --- a/client/ayon_core/tools/loader/ui/_multicombobox.py +++ b/client/ayon_core/tools/loader/ui/_multicombobox.py @@ -1,7 +1,10 @@ +from __future__ import annotations +import typing from typing import List, Tuple, Optional, Iterable, Any from qtpy import QtWidgets, QtCore, QtGui +from ayon_core.tools.utils import get_qt_icon from ayon_core.tools.utils.lib import ( checkstate_int_to_enum, checkstate_enum_to_int, @@ -11,14 +14,269 @@ from ayon_core.tools.utils.constants import ( UNCHECKED_INT, ITEM_IS_USER_TRISTATE, ) +if typing.TYPE_CHECKING: + from ayon_core.tools.loader.abstract import FrontendLoaderController VALUE_ITEM_TYPE = 0 STANDARD_ITEM_TYPE = 1 SEPARATOR_ITEM_TYPE = 2 +VALUE_ITEM_SUBTYPE = 0 +SELECT_ALL_SUBTYPE = 1 +DESELECT_ALL_SUBTYPE = 2 +SWAP_STATE_SUBTYPE = 3 + + +class BaseQtModel(QtGui.QStandardItemModel): + _empty_icon = None + + def __init__( + self, + item_type_role: int, + item_subtype_role: int, + empty_values_label: str, + controller: FrontendLoaderController, + ): + self._item_type_role = item_type_role + self._item_subtype_role = item_subtype_role + self._empty_values_label = empty_values_label + self._controller = controller + + self._last_project = None + + self._select_project_item = None + self._empty_values_item = None + + self._select_all_item = None + self._deselect_all_item = None + self._swap_states_item = None + + super().__init__() + + self.refresh(None) + + def _get_standard_items(self) -> list[QtGui.QStandardItem]: + raise NotImplementedError( + "'_get_standard_items' is not implemented" + f" for {self.__class__}" + ) + + def _clear_standard_items(self): + raise NotImplementedError( + "'_clear_standard_items' is not implemented" + f" for {self.__class__}" + ) + + def _prepare_new_value_items( + self, project_name: str, project_changed: bool + ) -> tuple[ + list[QtGui.QStandardItem], list[QtGui.QStandardItem] + ]: + raise NotImplementedError( + "'_prepare_new_value_items' is not implemented" + f" for {self.__class__}" + ) + + def refresh(self, project_name: Optional[str]): + # New project was selected + project_changed = False + if project_name != self._last_project: + self._last_project = project_name + project_changed = True + + if project_name is None: + self._add_select_project_item() + return + + value_items, items_to_remove = self._prepare_new_value_items( + project_name, project_changed + ) + if not value_items: + self._add_empty_values_item() + return + + self._remove_empty_items() + + root_item = self.invisibleRootItem() + for row_idx, value_item in enumerate(value_items): + if value_item.row() == row_idx: + continue + if value_item.row() >= 0: + root_item.takeRow(value_item.row()) + root_item.insertRow(row_idx, value_item) + + for item in items_to_remove: + root_item.removeRow(item.row()) + + self._add_selection_items() + + def setData(self, index, value, role): + if role == QtCore.Qt.CheckStateRole and index.isValid(): + item_subtype = index.data(self._item_subtype_role) + if item_subtype == SELECT_ALL_SUBTYPE: + for item in self._get_standard_items(): + item.setCheckState(QtCore.Qt.Checked) + return True + if item_subtype == DESELECT_ALL_SUBTYPE: + for item in self._get_standard_items(): + item.setCheckState(QtCore.Qt.Unchecked) + return True + if item_subtype == SWAP_STATE_SUBTYPE: + for item in self._get_standard_items(): + current_state = item.checkState() + item.setCheckState( + QtCore.Qt.Checked + if current_state == QtCore.Qt.Unchecked + else QtCore.Qt.Unchecked + ) + return True + return super().setData(index, value, role) + + @classmethod + def _get_empty_icon(cls): + if cls._empty_icon is None: + pix = QtGui.QPixmap(1, 1) + pix.fill(QtCore.Qt.transparent) + cls._empty_icon = QtGui.QIcon(pix) + return cls._empty_icon + + def _init_default_items(self): + if self._empty_values_item is not None: + return + + empty_values_item = QtGui.QStandardItem(self._empty_values_label) + select_project_item = QtGui.QStandardItem("Select project...") + + select_all_item = QtGui.QStandardItem("Select all") + deselect_all_item = QtGui.QStandardItem("Deselect all") + swap_states_item = QtGui.QStandardItem("Swap") + + for item in ( + empty_values_item, + select_project_item, + select_all_item, + deselect_all_item, + swap_states_item, + ): + item.setData(STANDARD_ITEM_TYPE, self._item_type_role) + + select_all_item.setIcon(get_qt_icon({ + "type": "material-symbols", + "name": "done_all", + "color": "white" + })) + deselect_all_item.setIcon(get_qt_icon({ + "type": "material-symbols", + "name": "remove_done", + "color": "white" + })) + swap_states_item.setIcon(get_qt_icon({ + "type": "material-symbols", + "name": "swap_horiz", + "color": "white" + })) + + for item in ( + empty_values_item, + select_project_item, + ): + item.setFlags(QtCore.Qt.NoItemFlags) + + for item, item_type in ( + (select_all_item, SELECT_ALL_SUBTYPE), + (deselect_all_item, DESELECT_ALL_SUBTYPE), + (swap_states_item, SWAP_STATE_SUBTYPE), + ): + item.setData(item_type, self._item_subtype_role) + + for item in ( + select_all_item, + deselect_all_item, + swap_states_item, + ): + item.setFlags( + QtCore.Qt.ItemIsEnabled + | QtCore.Qt.ItemIsSelectable + | QtCore.Qt.ItemIsUserCheckable + ) + + self._empty_values_item = empty_values_item + self._select_project_item = select_project_item + + self._select_all_item = select_all_item + self._deselect_all_item = deselect_all_item + self._swap_states_item = swap_states_item + + def _get_empty_values_item(self): + self._init_default_items() + return self._empty_values_item + + def _get_select_project_item(self): + self._init_default_items() + return self._select_project_item + + def _get_empty_items(self): + self._init_default_items() + return [ + self._empty_values_item, + self._select_project_item, + ] + + def _get_selection_items(self): + self._init_default_items() + return [ + self._select_all_item, + self._deselect_all_item, + self._swap_states_item, + ] + + def _get_default_items(self): + return self._get_empty_items() + self._get_selection_items() + + def _add_select_project_item(self): + item = self._get_select_project_item() + if item.row() < 0: + self._remove_items() + root_item = self.invisibleRootItem() + root_item.appendRow(item) + + def _add_empty_values_item(self): + item = self._get_empty_values_item() + if item.row() < 0: + self._remove_items() + root_item = self.invisibleRootItem() + root_item.appendRow(item) + + def _add_selection_items(self): + root_item = self.invisibleRootItem() + items = self._get_selection_items() + for item in self._get_selection_items(): + row = item.row() + if row >= 0: + root_item.takeRow(row) + root_item.appendRows(items) + + def _remove_items(self): + root_item = self.invisibleRootItem() + for item in self._get_default_items(): + if item.row() < 0: + continue + root_item.takeRow(item.row()) + + root_item.removeRows(0, root_item.rowCount()) + self._clear_standard_items() + + def _remove_empty_items(self): + root_item = self.invisibleRootItem() + for item in self._get_empty_items(): + if item.row() < 0: + continue + root_item.takeRow(item.row()) + class CustomPaintDelegate(QtWidgets.QStyledItemDelegate): """Delegate showing status name and short name.""" + _empty_icon = None _checked_value = checkstate_enum_to_int(QtCore.Qt.Checked) _checked_bg_color = QtGui.QColor("#2C3B4C") @@ -38,6 +296,14 @@ class CustomPaintDelegate(QtWidgets.QStyledItemDelegate): self._icon_role = icon_role self._item_type_role = item_type_role + @classmethod + def _get_empty_icon(cls): + if cls._empty_icon is None: + pix = QtGui.QPixmap(1, 1) + pix.fill(QtCore.Qt.transparent) + cls._empty_icon = QtGui.QIcon(pix) + return cls._empty_icon + def paint(self, painter, option, index): item_type = None if self._item_type_role is not None: @@ -70,6 +336,9 @@ class CustomPaintDelegate(QtWidgets.QStyledItemDelegate): if option.state & QtWidgets.QStyle.State_Open: state = QtGui.QIcon.On icon = self._get_index_icon(index) + if icon is None or icon.isNull(): + icon = self._get_empty_icon() + option.features |= QtWidgets.QStyleOptionViewItem.HasDecoration # Disable visible check indicator @@ -241,6 +510,10 @@ class CustomPaintMultiselectComboBox(QtWidgets.QComboBox): QtCore.Qt.Key_Home, QtCore.Qt.Key_End, } + _top_bottom_margins = 1 + _top_bottom_padding = 2 + _left_right_padding = 3 + _item_bg_color = QtGui.QColor("#31424e") def __init__( self, @@ -433,14 +706,14 @@ class CustomPaintMultiselectComboBox(QtWidgets.QComboBox): idxs = self._get_checked_idx() # draw the icon and text - draw_text = True + draw_items = False combotext = None if self._custom_text is not None: combotext = self._custom_text elif not idxs: combotext = self._placeholder_text else: - draw_text = False + draw_items = True content_field_rect = self.style().subControlRect( QtWidgets.QStyle.CC_ComboBox, @@ -448,7 +721,9 @@ class CustomPaintMultiselectComboBox(QtWidgets.QComboBox): QtWidgets.QStyle.SC_ComboBoxEditField ).adjusted(1, 0, -1, 0) - if draw_text: + if draw_items: + self._paint_items(painter, idxs, content_field_rect) + else: color = option.palette.color(QtGui.QPalette.Text) color.setAlpha(67) pen = painter.pen() @@ -459,15 +734,12 @@ class CustomPaintMultiselectComboBox(QtWidgets.QComboBox): QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, combotext ) - else: - self._paint_items(painter, idxs, content_field_rect) painter.end() def _paint_items(self, painter, indexes, content_rect): origin_rect = QtCore.QRect(content_rect) - metrics = self.fontMetrics() model = self.model() available_width = content_rect.width() total_used_width = 0 @@ -482,31 +754,80 @@ class CustomPaintMultiselectComboBox(QtWidgets.QComboBox): continue icon = index.data(self._icon_role) - # TODO handle this case - if icon is None or icon.isNull(): - continue + text = index.data(self._text_role) + valid_icon = icon is not None and not icon.isNull() + if valid_icon: + sizes = icon.availableSizes() + if sizes: + valid_icon = any(size.width() > 1 for size in sizes) - icon_rect = QtCore.QRect(content_rect) - diff = icon_rect.height() - metrics.height() - if diff < 0: - diff = 0 - top_offset = diff // 2 - bottom_offset = diff - top_offset - icon_rect.adjust(0, top_offset, 0, -bottom_offset) - icon_rect.setWidth(metrics.height()) - icon.paint( - painter, - icon_rect, - QtCore.Qt.AlignCenter, - QtGui.QIcon.Normal, - QtGui.QIcon.On - ) - content_rect.setLeft(icon_rect.right() + spacing) - if total_used_width > 0: - total_used_width += spacing - total_used_width += icon_rect.width() - if total_used_width > available_width: - break + if valid_icon: + metrics = self.fontMetrics() + icon_rect = QtCore.QRect(content_rect) + diff = icon_rect.height() - metrics.height() + if diff < 0: + diff = 0 + top_offset = diff // 2 + bottom_offset = diff - top_offset + icon_rect.adjust(0, top_offset, 0, -bottom_offset) + used_width = metrics.height() + if total_used_width > 0: + total_used_width += spacing + total_used_width += used_width + if total_used_width > available_width: + break + + icon_rect.setWidth(used_width) + icon.paint( + painter, + icon_rect, + QtCore.Qt.AlignCenter, + QtGui.QIcon.Normal, + QtGui.QIcon.On + ) + content_rect.setLeft(icon_rect.right() + spacing) + + elif text: + bg_height = ( + content_rect.height() + - (2 * self._top_bottom_margins) + ) + font_height = bg_height - (2 * self._top_bottom_padding) + + bg_top_y = content_rect.y() + self._top_bottom_margins + + font = self.font() + font.setPixelSize(font_height) + metrics = QtGui.QFontMetrics(font) + painter.setFont(font) + + label_rect = metrics.boundingRect(text) + + bg_width = label_rect.width() + (2 * self._left_right_padding) + if total_used_width > 0: + total_used_width += spacing + total_used_width += bg_width + if total_used_width > available_width: + break + + bg_rect = QtCore.QRectF(label_rect) + bg_rect.moveTop(bg_top_y) + bg_rect.moveLeft(content_rect.left()) + bg_rect.setWidth(bg_width) + bg_rect.setHeight(bg_height) + + label_rect.moveTop(bg_top_y) + label_rect.moveLeft( + content_rect.left() + self._left_right_padding + ) + + path = QtGui.QPainterPath() + path.addRoundedRect(bg_rect, 5, 5) + + painter.fillPath(path, self._item_bg_color) + painter.drawText(label_rect, QtCore.Qt.AlignCenter, text) + + content_rect.setLeft(bg_rect.right() + spacing) painter.restore() diff --git a/client/ayon_core/tools/loader/ui/statuses_combo.py b/client/ayon_core/tools/loader/ui/statuses_combo.py index 9fe7ab62a5..2f034d00de 100644 --- a/client/ayon_core/tools/loader/ui/statuses_combo.py +++ b/client/ayon_core/tools/loader/ui/statuses_combo.py @@ -1,4 +1,4 @@ -from typing import List, Dict +from __future__ import annotations from qtpy import QtCore, QtGui @@ -7,7 +7,7 @@ from ayon_core.tools.common_models import StatusItem from ._multicombobox import ( CustomPaintMultiselectComboBox, - STANDARD_ITEM_TYPE, + BaseQtModel, ) STATUS_ITEM_TYPE = 0 @@ -24,62 +24,43 @@ ITEM_TYPE_ROLE = QtCore.Qt.UserRole + 5 ITEM_SUBTYPE_ROLE = QtCore.Qt.UserRole + 6 -class StatusesQtModel(QtGui.QStandardItemModel): +class StatusesQtModel(BaseQtModel): def __init__(self, controller): - self._controller = controller - self._items_by_name: Dict[str, QtGui.QStandardItem] = {} - self._icons_by_name_n_color: Dict[str, QtGui.QIcon] = {} - self._last_project = None + self._items_by_name: dict[str, QtGui.QStandardItem] = {} + self._icons_by_name_n_color: dict[str, QtGui.QIcon] = {} + super().__init__( + ITEM_TYPE_ROLE, + ITEM_SUBTYPE_ROLE, + "No statuses...", + controller, + ) - self._select_project_item = None - self._empty_statuses_item = None + def _get_standard_items(self) -> list[QtGui.QStandardItem]: + return list(self._items_by_name.values()) - self._select_all_item = None - self._deselect_all_item = None - self._swap_states_item = None + def _clear_standard_items(self): + self._items_by_name.clear() - super().__init__() - - self.refresh(None) - - def get_placeholder_text(self): - return self._placeholder - - def refresh(self, project_name): - # New project was selected - # status filter is reset to show all statuses - uncheck_all = False - if project_name != self._last_project: - self._last_project = project_name - uncheck_all = True - - if project_name is None: - self._add_select_project_item() - return - - status_items: List[StatusItem] = ( + def _prepare_new_value_items( + self, project_name: str, project_changed: bool + ): + status_items: list[StatusItem] = ( self._controller.get_project_status_items( project_name, sender=STATUSES_FILTER_SENDER ) ) + items = [] + items_to_remove = [] if not status_items: - self._add_empty_statuses_item() - return + return items, items_to_remove - self._remove_empty_items() - - items_to_remove = set(self._items_by_name) - root_item = self.invisibleRootItem() + names_to_remove = set(self._items_by_name) for row_idx, status_item in enumerate(status_items): name = status_item.name if name in self._items_by_name: - is_new = False item = self._items_by_name[name] - if uncheck_all: - item.setCheckState(QtCore.Qt.Unchecked) - items_to_remove.discard(name) + names_to_remove.discard(name) else: - is_new = True item = QtGui.QStandardItem() item.setData(ITEM_SUBTYPE_ROLE, STATUS_ITEM_TYPE) item.setCheckState(QtCore.Qt.Unchecked) @@ -100,36 +81,14 @@ class StatusesQtModel(QtGui.QStandardItemModel): if item.data(role) != value: item.setData(value, role) - if is_new: - root_item.insertRow(row_idx, item) + if project_changed: + item.setCheckState(QtCore.Qt.Unchecked) + items.append(item) - for name in items_to_remove: - item = self._items_by_name.pop(name) - root_item.removeRow(item.row()) + for name in names_to_remove: + items_to_remove.append(self._items_by_name.pop(name)) - self._add_selection_items() - - def setData(self, index, value, role): - if role == QtCore.Qt.CheckStateRole and index.isValid(): - item_type = index.data(ITEM_SUBTYPE_ROLE) - if item_type == SELECT_ALL_TYPE: - for item in self._items_by_name.values(): - item.setCheckState(QtCore.Qt.Checked) - return True - if item_type == DESELECT_ALL_TYPE: - for item in self._items_by_name.values(): - item.setCheckState(QtCore.Qt.Unchecked) - return True - if item_type == SWAP_STATE_TYPE: - for item in self._items_by_name.values(): - current_state = item.checkState() - item.setCheckState( - QtCore.Qt.Checked - if current_state == QtCore.Qt.Unchecked - else QtCore.Qt.Unchecked - ) - return True - return super().setData(index, value, role) + return items, items_to_remove def _get_icon(self, status_item: StatusItem) -> QtGui.QIcon: name = status_item.name @@ -147,139 +106,6 @@ class StatusesQtModel(QtGui.QStandardItemModel): self._icons_by_name_n_color[unique_id] = icon return icon - def _init_default_items(self): - if self._empty_statuses_item is not None: - return - - empty_statuses_item = QtGui.QStandardItem("No statuses...") - select_project_item = QtGui.QStandardItem("Select project...") - - select_all_item = QtGui.QStandardItem("Select all") - deselect_all_item = QtGui.QStandardItem("Deselect all") - swap_states_item = QtGui.QStandardItem("Swap") - - for item in ( - empty_statuses_item, - select_project_item, - select_all_item, - deselect_all_item, - swap_states_item, - ): - item.setData(STANDARD_ITEM_TYPE, ITEM_TYPE_ROLE) - - select_all_item.setIcon(get_qt_icon({ - "type": "material-symbols", - "name": "done_all", - "color": "white" - })) - deselect_all_item.setIcon(get_qt_icon({ - "type": "material-symbols", - "name": "remove_done", - "color": "white" - })) - swap_states_item.setIcon(get_qt_icon({ - "type": "material-symbols", - "name": "swap_horiz", - "color": "white" - })) - - for item in ( - empty_statuses_item, - select_project_item, - ): - item.setFlags(QtCore.Qt.NoItemFlags) - - for item, item_type in ( - (select_all_item, SELECT_ALL_TYPE), - (deselect_all_item, DESELECT_ALL_TYPE), - (swap_states_item, SWAP_STATE_TYPE), - ): - item.setData(item_type, ITEM_SUBTYPE_ROLE) - - for item in ( - select_all_item, - deselect_all_item, - swap_states_item, - ): - item.setFlags( - QtCore.Qt.ItemIsEnabled - | QtCore.Qt.ItemIsSelectable - | QtCore.Qt.ItemIsUserCheckable - ) - - self._empty_statuses_item = empty_statuses_item - self._select_project_item = select_project_item - - self._select_all_item = select_all_item - self._deselect_all_item = deselect_all_item - self._swap_states_item = swap_states_item - - def _get_empty_statuses_item(self): - self._init_default_items() - return self._empty_statuses_item - - def _get_select_project_item(self): - self._init_default_items() - return self._select_project_item - - def _get_empty_items(self): - self._init_default_items() - return [ - self._empty_statuses_item, - self._select_project_item, - ] - - def _get_selection_items(self): - self._init_default_items() - return [ - self._select_all_item, - self._deselect_all_item, - self._swap_states_item, - ] - - def _get_default_items(self): - return self._get_empty_items() + self._get_selection_items() - - def _add_select_project_item(self): - item = self._get_select_project_item() - if item.row() < 0: - self._remove_items() - root_item = self.invisibleRootItem() - root_item.appendRow(item) - - def _add_empty_statuses_item(self): - item = self._get_empty_statuses_item() - if item.row() < 0: - self._remove_items() - root_item = self.invisibleRootItem() - root_item.appendRow(item) - - def _add_selection_items(self): - root_item = self.invisibleRootItem() - items = self._get_selection_items() - for item in self._get_selection_items(): - row = item.row() - if row >= 0: - root_item.takeRow(row) - root_item.appendRows(items) - - def _remove_items(self): - root_item = self.invisibleRootItem() - for item in self._get_default_items(): - if item.row() < 0: - continue - root_item.takeRow(item.row()) - - root_item.removeRows(0, root_item.rowCount()) - self._items_by_name.clear() - - def _remove_empty_items(self): - root_item = self.invisibleRootItem() - for item in self._get_empty_items(): - if item.row() < 0: - continue - root_item.takeRow(item.row()) - class StatusesCombobox(CustomPaintMultiselectComboBox): def __init__(self, controller, parent): From 5317d2817df9f2a2ecedf5b7565a069fb0e0ef94 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:37:08 +0100 Subject: [PATCH 388/463] converted product types view to combobox --- .../tools/loader/ui/product_types_widget.py | 278 ++++++------------ .../tools/loader/ui/products_widget.py | 42 +-- client/ayon_core/tools/loader/ui/window.py | 36 ++- 3 files changed, 136 insertions(+), 220 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/product_types_widget.py b/client/ayon_core/tools/loader/ui/product_types_widget.py index 9b1bf6326f..ff2a70a7fa 100644 --- a/client/ayon_core/tools/loader/ui/product_types_widget.py +++ b/client/ayon_core/tools/loader/ui/product_types_widget.py @@ -1,57 +1,66 @@ from qtpy import QtWidgets, QtGui, QtCore -from ayon_core.tools.utils import get_qt_icon +from ._multicombobox import ( + CustomPaintMultiselectComboBox, + BaseQtModel, +) + +STATUS_ITEM_TYPE = 0 +SELECT_ALL_TYPE = 1 +DESELECT_ALL_TYPE = 2 +SWAP_STATE_TYPE = 3 PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 1 +ITEM_TYPE_ROLE = QtCore.Qt.UserRole + 2 +ITEM_SUBTYPE_ROLE = QtCore.Qt.UserRole + 3 -class ProductTypesQtModel(QtGui.QStandardItemModel): - refreshed = QtCore.Signal() - filter_changed = QtCore.Signal() - +class ProductTypesQtModel(BaseQtModel): def __init__(self, controller): - super(ProductTypesQtModel, self).__init__() - self._controller = controller - self._reset_filters_on_refresh = True self._refreshing = False self._bulk_change = False - self._last_project = None self._items_by_name = {} - controller.register_event_callback( - "controller.reset.finished", - self._on_controller_reset_finish, + super().__init__( + item_type_role=ITEM_TYPE_ROLE, + item_subtype_role=ITEM_SUBTYPE_ROLE, + empty_values_label="No product types...", + controller=controller, ) def is_refreshing(self): return self._refreshing - def get_filter_info(self): - """Product types filtering info. - - Returns: - dict[str, bool]: Filtering value by product type name. False value - means to hide product type. - """ - - return { - name: item.checkState() == QtCore.Qt.Checked - for name, item in self._items_by_name.items() - } - def refresh(self, project_name): self._refreshing = True + super().refresh(project_name) + + self._reset_filters_on_refresh = False + self._refreshing = False + + def reset_product_types_filter_on_refresh(self): + self._reset_filters_on_refresh = True + + def _get_standard_items(self) -> list[QtGui.QStandardItem]: + return list(self._items_by_name.values()) + + def _clear_standard_items(self): + self._items_by_name.clear() + + def _prepare_new_value_items(self, project_name: str, _: bool) -> tuple[ + list[QtGui.QStandardItem], list[QtGui.QStandardItem] + ]: product_type_items = self._controller.get_product_type_items( project_name) self._last_project = project_name - items_to_remove = set(self._items_by_name.keys()) - new_items = [] + names_to_remove = set(self._items_by_name.keys()) + items = [] items_filter_required = {} for product_type_item in product_type_items: name = product_type_item.name - items_to_remove.discard(name) + names_to_remove.discard(name) item = self._items_by_name.get(name) # Apply filter to new items or if filters reset is requested filter_required = self._reset_filters_on_refresh @@ -61,15 +70,13 @@ class ProductTypesQtModel(QtGui.QStandardItemModel): item.setData(name, PRODUCT_TYPE_ROLE) item.setEditable(False) item.setCheckable(True) - new_items.append(item) self._items_by_name[name] = item + items.append(item) + if filter_required: items_filter_required[name] = item - icon = get_qt_icon(product_type_item.icon) - item.setData(icon, QtCore.Qt.DecorationRole) - if items_filter_required: product_types_filter = self._controller.get_product_types_filter() for product_type, item in items_filter_required.items(): @@ -77,180 +84,77 @@ class ProductTypesQtModel(QtGui.QStandardItemModel): int(product_type in product_types_filter.product_types) + int(product_types_filter.is_allow_list) ) - state = ( + item.setCheckState( QtCore.Qt.Checked if matching % 2 == 0 else QtCore.Qt.Unchecked ) - item.setCheckState(state) - root_item = self.invisibleRootItem() - if new_items: - root_item.appendRows(new_items) + items_to_remove = [] + for name in names_to_remove: + items_to_remove.append( + self._items_by_name.pop(name) + ) - for name in items_to_remove: - item = self._items_by_name.pop(name) - root_item.removeRow(item.row()) + # Uncheck all if all are checked (same result) + if all( + item.checkState() == QtCore.Qt.Checked + for item in items + ): + for item in items: + item.setCheckState(QtCore.Qt.Unchecked) - self._reset_filters_on_refresh = False - self._refreshing = False - self.refreshed.emit() - - def reset_product_types_filter_on_refresh(self): - self._reset_filters_on_refresh = True - - def setData(self, index, value, role=None): - checkstate_changed = False - if role is None: - role = QtCore.Qt.EditRole - elif role == QtCore.Qt.CheckStateRole: - checkstate_changed = True - output = super(ProductTypesQtModel, self).setData(index, value, role) - if checkstate_changed and not self._bulk_change: - self.filter_changed.emit() - return output - - def change_state_for_all(self, checked): - if self._items_by_name: - self.change_states(checked, self._items_by_name.keys()) - - def change_states(self, checked, product_types): - product_types = set(product_types) - if not product_types: - return - - if checked is None: - state = None - elif checked: - state = QtCore.Qt.Checked - else: - state = QtCore.Qt.Unchecked - - self._bulk_change = True - - changed = False - for product_type in product_types: - item = self._items_by_name.get(product_type) - if item is None: - continue - new_state = state - item_checkstate = item.checkState() - if new_state is None: - if item_checkstate == QtCore.Qt.Checked: - new_state = QtCore.Qt.Unchecked - else: - new_state = QtCore.Qt.Checked - elif item_checkstate == new_state: - continue - changed = True - item.setCheckState(new_state) - - self._bulk_change = False - - if changed: - self.filter_changed.emit() - - def _on_controller_reset_finish(self): - self.refresh(self._last_project) + return items, items_to_remove -class ProductTypesView(QtWidgets.QListView): - filter_changed = QtCore.Signal() - +class ProductTypesCombobox(CustomPaintMultiselectComboBox): def __init__(self, controller, parent): - super(ProductTypesView, self).__init__(parent) - - self.setSelectionMode( - QtWidgets.QAbstractItemView.ExtendedSelection + self._controller = controller + model = ProductTypesQtModel(controller) + super().__init__( + PRODUCT_TYPE_ROLE, + PRODUCT_TYPE_ROLE, + QtCore.Qt.ForegroundRole, + QtCore.Qt.DecorationRole, + item_type_role=ITEM_TYPE_ROLE, + model=model, + parent=parent ) - self.setAlternatingRowColors(True) - self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - - product_types_model = ProductTypesQtModel(controller) - product_types_proxy_model = QtCore.QSortFilterProxyModel() - product_types_proxy_model.setSourceModel(product_types_model) - - self.setModel(product_types_proxy_model) - - product_types_model.refreshed.connect(self._on_refresh_finished) - product_types_model.filter_changed.connect(self._on_filter_change) - self.customContextMenuRequested.connect(self._on_context_menu) + self.set_placeholder_text("Product types filter...") + self._model = model + self._last_project_name = None + self._fully_disabled_filter = False controller.register_event_callback( "selection.project.changed", self._on_project_change ) - - self._controller = controller - self._refresh_product_types_filter = False - - self._product_types_model = product_types_model - self._product_types_proxy_model = product_types_proxy_model - - def get_filter_info(self): - return self._product_types_model.get_filter_info() + controller.register_event_callback( + "projects.refresh.finished", + self._on_projects_refresh + ) + self.setToolTip("Product types filter") + self.value_changed.connect( + self._on_product_type_filter_change + ) def reset_product_types_filter_on_refresh(self): - self._product_types_model.reset_product_types_filter_on_refresh() + self._model.reset_product_types_filter_on_refresh() + + def _on_product_type_filter_change(self): + lines = ["Product types filter"] + for item in self.get_value_info(): + status_name, enabled = item + lines.append(f"{'✔' if enabled else '☐'} {status_name}") + + self.setToolTip("\n".join(lines)) def _on_project_change(self, event): project_name = event["project_name"] - self._product_types_model.refresh(project_name) + self._last_project_name = project_name + self._model.refresh(project_name) - def _on_refresh_finished(self): - # Apply product types filter on first show - self.filter_changed.emit() - - def _on_filter_change(self): - if not self._product_types_model.is_refreshing(): - self.filter_changed.emit() - - def _change_selection_state(self, checkstate): - selection_model = self.selectionModel() - product_types = { - index.data(PRODUCT_TYPE_ROLE) - for index in selection_model.selectedIndexes() - } - product_types.discard(None) - self._product_types_model.change_states(checkstate, product_types) - - def _on_enable_all(self): - self._product_types_model.change_state_for_all(True) - - def _on_disable_all(self): - self._product_types_model.change_state_for_all(False) - - def _on_context_menu(self, pos): - menu = QtWidgets.QMenu(self) - - # Add enable all action - action_check_all = QtWidgets.QAction(menu) - action_check_all.setText("Enable All") - action_check_all.triggered.connect(self._on_enable_all) - # Add disable all action - action_uncheck_all = QtWidgets.QAction(menu) - action_uncheck_all.setText("Disable All") - action_uncheck_all.triggered.connect(self._on_disable_all) - - menu.addAction(action_check_all) - menu.addAction(action_uncheck_all) - - # Get mouse position - global_pos = self.viewport().mapToGlobal(pos) - menu.exec_(global_pos) - - def event(self, event): - if event.type() == QtCore.QEvent.KeyPress: - if event.key() == QtCore.Qt.Key_Space: - self._change_selection_state(None) - return True - - if event.key() == QtCore.Qt.Key_Backspace: - self._change_selection_state(False) - return True - - if event.key() == QtCore.Qt.Key_Return: - self._change_selection_state(True) - return True - - return super(ProductTypesView, self).event(event) + def _on_projects_refresh(self): + if self._last_project_name: + self._model.refresh(self._last_project_name) + self._on_product_type_filter_change() diff --git a/client/ayon_core/tools/loader/ui/products_widget.py b/client/ayon_core/tools/loader/ui/products_widget.py index 748a1b5fb8..41a49b8ae1 100644 --- a/client/ayon_core/tools/loader/ui/products_widget.py +++ b/client/ayon_core/tools/loader/ui/products_widget.py @@ -1,4 +1,6 @@ +from __future__ import annotations import collections +from typing import Optional from qtpy import QtWidgets, QtCore @@ -36,7 +38,7 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): def __init__(self, parent=None): super().__init__(parent) - self._product_type_filters = {} + self._product_type_filters = None self._statuses_filter = None self._ascending_sort = True @@ -46,6 +48,8 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): return set(self._statuses_filter) def set_product_type_filters(self, product_type_filters): + if self._product_type_filters == product_type_filters: + return self._product_type_filters = product_type_filters self.invalidateFilter() @@ -59,28 +63,32 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): source_model = self.sourceModel() index = source_model.index(source_row, 0, source_parent) - product_types_s = source_model.data(index, PRODUCT_TYPE_ROLE) - product_types = [] - if product_types_s: - product_types = product_types_s.split("|") - - for product_type in product_types: - if not self._product_type_filters.get(product_type, True): - return False - - if not self._accept_row_by_statuses(index): + if not self._accept_row_by_role_value( + index, self._product_type_filters, PRODUCT_TYPE_ROLE + ): return False + + if not self._accept_row_by_role_value( + index, self._statuses_filter, STATUS_NAME_FILTER_ROLE + ): + return False + return super().filterAcceptsRow(source_row, source_parent) - def _accept_row_by_statuses(self, index): - if self._statuses_filter is None: + def _accept_row_by_role_value( + self, + index: QtCore.QModelIndex, + filter_value: Optional[set[str]], + role: int + ): + if filter_value is None: return True - if not self._statuses_filter: + if not filter_value: return False - status_s = index.data(STATUS_NAME_FILTER_ROLE) + status_s = index.data(role) for status in status_s.split("|"): - if status in self._statuses_filter: + if status in filter_value: return True return False @@ -120,7 +128,7 @@ class ProductsWidget(QtWidgets.QWidget): 90, # Product type 130, # Folder label 60, # Version - 100, # Status + 100, # Status 125, # Time 75, # Author 75, # Frames diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index 31c9908b23..31d88d4ed7 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -15,7 +15,7 @@ from ayon_core.tools.loader.control import LoaderController from .folders_widget import LoaderFoldersWidget from .products_widget import ProductsWidget -from .product_types_widget import ProductTypesView +from .product_types_widget import ProductTypesCombobox from .product_group_dialog import ProductGroupDialog from .info_widget import InfoWidget from .repres_widget import RepresentationsWidget @@ -164,8 +164,6 @@ class LoaderWindow(QtWidgets.QWidget): folders_widget = LoaderFoldersWidget(controller, context_widget) - product_types_widget = ProductTypesView(controller, context_splitter) - context_layout = QtWidgets.QVBoxLayout(context_widget) context_layout.setContentsMargins(0, 0, 0, 0) context_layout.addWidget(context_top_widget, 0) @@ -173,7 +171,6 @@ class LoaderWindow(QtWidgets.QWidget): context_layout.addWidget(folders_widget, 1) context_splitter.addWidget(context_widget) - context_splitter.addWidget(product_types_widget) context_splitter.setStretchFactor(0, 65) context_splitter.setStretchFactor(1, 35) @@ -185,6 +182,10 @@ class LoaderWindow(QtWidgets.QWidget): products_filter_input = PlaceholderLineEdit(products_inputs_widget) products_filter_input.setPlaceholderText("Product name filter...") + product_types_filter_combo = ProductTypesCombobox( + controller, products_inputs_widget + ) + product_status_filter_combo = StatusesCombobox(controller, self) product_group_checkbox = QtWidgets.QCheckBox( @@ -196,6 +197,7 @@ class LoaderWindow(QtWidgets.QWidget): products_inputs_layout = QtWidgets.QHBoxLayout(products_inputs_widget) products_inputs_layout.setContentsMargins(0, 0, 0, 0) products_inputs_layout.addWidget(products_filter_input, 1) + products_inputs_layout.addWidget(product_types_filter_combo, 1) products_inputs_layout.addWidget(product_status_filter_combo, 1) products_inputs_layout.addWidget(product_group_checkbox, 0) @@ -244,12 +246,12 @@ class LoaderWindow(QtWidgets.QWidget): folders_filter_input.textChanged.connect( self._on_folder_filter_change ) - product_types_widget.filter_changed.connect( - self._on_product_type_filter_change - ) products_filter_input.textChanged.connect( self._on_product_filter_change ) + product_types_filter_combo.value_changed.connect( + self._on_product_type_filter_change + ) product_status_filter_combo.value_changed.connect( self._on_status_filter_change ) @@ -304,9 +306,8 @@ class LoaderWindow(QtWidgets.QWidget): self._folders_filter_input = folders_filter_input self._folders_widget = folders_widget - self._product_types_widget = product_types_widget - self._products_filter_input = products_filter_input + self._product_types_filter_combo = product_types_filter_combo self._product_status_filter_combo = product_status_filter_combo self._product_group_checkbox = product_group_checkbox self._products_widget = products_widget @@ -335,7 +336,7 @@ class LoaderWindow(QtWidgets.QWidget): self._controller.reset() def showEvent(self, event): - super(LoaderWindow, self).showEvent(event) + super().showEvent(event) if self._first_show: self._on_first_show() @@ -343,9 +344,13 @@ class LoaderWindow(QtWidgets.QWidget): self._show_timer.start() def closeEvent(self, event): - super(LoaderWindow, self).closeEvent(event) + super().closeEvent(event) - self._product_types_widget.reset_product_types_filter_on_refresh() + ( + self + ._product_types_filter_combo + .reset_product_types_filter_on_refresh() + ) self._reset_on_show = True @@ -363,7 +368,7 @@ class LoaderWindow(QtWidgets.QWidget): event.setAccepted(True) return - super(LoaderWindow, self).keyPressEvent(event) + super().keyPressEvent(event) def _on_first_show(self): self._first_show = False @@ -428,9 +433,8 @@ class LoaderWindow(QtWidgets.QWidget): self._products_widget.set_statuses_filter(status_names) def _on_product_type_filter_change(self): - self._products_widget.set_product_type_filter( - self._product_types_widget.get_filter_info() - ) + product_types = self._product_types_filter_combo.get_value() + self._products_widget.set_product_type_filter(product_types) def _on_merged_products_selection_change(self): items = self._products_widget.get_selected_merged_products() From b586bf0ad604e02a424b1470bd5edea81944e7b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:38:32 +0100 Subject: [PATCH 389/463] renamed 'product_types_widget' to 'product_types_combo' --- .../ui/{product_types_widget.py => product_types_combo.py} | 0 client/ayon_core/tools/loader/ui/window.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename client/ayon_core/tools/loader/ui/{product_types_widget.py => product_types_combo.py} (100%) diff --git a/client/ayon_core/tools/loader/ui/product_types_widget.py b/client/ayon_core/tools/loader/ui/product_types_combo.py similarity index 100% rename from client/ayon_core/tools/loader/ui/product_types_widget.py rename to client/ayon_core/tools/loader/ui/product_types_combo.py diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index 31d88d4ed7..31de0cf4e5 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -15,7 +15,7 @@ from ayon_core.tools.loader.control import LoaderController from .folders_widget import LoaderFoldersWidget from .products_widget import ProductsWidget -from .product_types_widget import ProductTypesCombobox +from .product_types_combo import ProductTypesCombobox from .product_group_dialog import ProductGroupDialog from .info_widget import InfoWidget from .repres_widget import RepresentationsWidget From b9cc8c350733412589781a3eb58bd24fabe2e1f3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Feb 2025 17:28:10 +0100 Subject: [PATCH 390/463] Move the profile defaults to settings --- .../extract_usd_layer_contributions.py | 44 ++-------- server/settings/publish_plugins.py | 81 +++++++++++++++++++ 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py index 6f08df790f..bf4ca70afa 100644 --- a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py +++ b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py @@ -1,7 +1,7 @@ from operator import attrgetter import dataclasses import os -from typing import Dict +from typing import Any, Dict, List import pyblish.api try: @@ -282,6 +282,9 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "fx": 500, "lighting": 600, } + # Default profiles to set certain instance attribute defaults based on + # profiles in settings + profiles: List[Dict[str, Any]] = [] @classmethod def apply_settings(cls, project_settings): @@ -299,6 +302,8 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, if contribution_layers: cls.contribution_layers = contribution_layers + cls.profiles = plugin_settings.get("profiles", []) + def process(self, instance): attr_values = self.get_attr_values_from_data(instance.data) @@ -465,41 +470,8 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, return [] # Set default target layer based on product type - # TODO: Define profiles in settings - profiles = [ - { - "productType": "model", - "contribution_layer": "model", - "contribution_apply_as_variant": True, - "contribution_target_product": "usdAsset" - }, - { - "productType": "look", - "contribution_layer": "look", - "contribution_apply_as_variant": True, - "contribution_target_product": "usdAsset" - }, - { - "productType": "groom", - "contribution_layer": "groom", - "contribution_apply_as_variant": True, - "contribution_target_product": "usdAsset" - }, - { - "productType": "rig", - "contribution_layer": "rig", - "contribution_apply_as_variant": True, - "contribution_target_product": "usdShot" - }, - { - "productType": "usd", - "contribution_layer": "assembly", - "contribution_apply_as_variant": False, - "contribution_target_product": "usdShot" - }, - ] - profile = filter_profiles(profiles, { - "productType": instance.data["productType"] + profile = filter_profiles(cls.profiles, { + "product_types": instance.data["productType"] }) if not profile: profile = {} diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 18e7d67f90..462b1e2861 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -68,6 +68,47 @@ class ContributionLayersModel(BaseSettingsModel): "layer on top.") +class CollectUSDLayerContributionsProfileModel(BaseSettingsModel): + """Profiles to define instance attribute defaults for USD contribution.""" + _layout = "expanded" + product_types: list[str] = SettingsField( + default_factory=list, + title="Product types", + description=( + "The product types to match this profile to. When matched, the" + " settings below would apply to the instance as default" + " attributes." + ), + ) + contribution_layer: str = SettingsField( + "", + title="Contribution Department Layer", + description=( + "The default contribution layer to apply the contribution to when" + " matching this profile. The layer name should be in the" + " 'Department Layer Orders' list to get a sensible order." + ), + ) + contribution_apply_as_variant: bool = SettingsField( + True, + title="Apply as variant", + description=( + "The default 'Apply as variant' state for instances matching this" + " profile. Usually enabled for asset contributions and disabled" + " for shot contributions." + ), + ) + contribution_target_product: str = SettingsField( + "usdAsset", + title="Target Product", + description=( + "The default destination product name to apply the contribution to" + " when matching this profile." + " Usually e.g. 'usdAsset' or 'usdShot'." + ), + ) + + class CollectUSDLayerContributionsModel(BaseSettingsModel): enabled: bool = SettingsField(True, title="Enabled") contribution_layers: list[ContributionLayersModel] = SettingsField( @@ -77,6 +118,14 @@ class CollectUSDLayerContributionsModel(BaseSettingsModel): "ordering inside the USD contribution workflow." ) ) + profiles: list[CollectUSDLayerContributionsProfileModel] = SettingsField( + default_factory=list, + title="Profiles", + description=( + "Define attribute defaults for USD Contributions on publish" + " instances." + ) + ) @validator("contribution_layers") def validate_unique_outputs(cls, value): @@ -1017,6 +1066,38 @@ DEFAULT_PUBLISH_VALUES = { {"name": "fx", "order": 500}, {"name": "lighting", "order": 600}, ], + "profiles": [ + { + "product_types": ["model"], + "contribution_layer": "model", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "product_types": ["look"], + "contribution_layer": "look", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "product_types": ["groom"], + "contribution_layer": "groom", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "product_types": ["rig"], + "contribution_layer": "rig", + "contribution_apply_as_variant": True, + "contribution_target_product": "usdAsset" + }, + { + "product_types": ["usd"], + "contribution_layer": "assembly", + "contribution_apply_as_variant": False, + "contribution_target_product": "usdShot" + }, + ] }, "ValidateEditorialAssetName": { "enabled": True, From 602faa81dc75323cc34797ea1e4de5aab0b2e778 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Feb 2025 17:41:52 +0100 Subject: [PATCH 391/463] Add Task Types profiles filtering for defaults (note that it uses current context, not the task type from the instance) --- .../publish/extract_usd_layer_contributions.py | 4 +++- server/settings/publish_plugins.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py index bf4ca70afa..a2698b03de 100644 --- a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py +++ b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py @@ -470,8 +470,10 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, return [] # Set default target layer based on product type + current_context_task_type = create_context.get_current_task_type() profile = filter_profiles(cls.profiles, { - "product_types": instance.data["productType"] + "product_types": instance.data["productType"], + "task_types": current_context_task_type }) if not profile: profile = {} diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 462b1e2861..d10a6b7507 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -79,6 +79,17 @@ class CollectUSDLayerContributionsProfileModel(BaseSettingsModel): " settings below would apply to the instance as default" " attributes." ), + section="Filter" + ) + task_types: list[str] = SettingsField( + default_factory=list, + title="Task Types", + enum_resolver=task_types_enum, + description=( + "The current create context task type to filter against. This" + " allows to filter the profile to only be valid if currently " + " creating from within that task type." + ), ) contribution_layer: str = SettingsField( "", @@ -88,6 +99,7 @@ class CollectUSDLayerContributionsProfileModel(BaseSettingsModel): " matching this profile. The layer name should be in the" " 'Department Layer Orders' list to get a sensible order." ), + section="Instance attribute defaults", ) contribution_apply_as_variant: bool = SettingsField( True, @@ -1069,30 +1081,35 @@ DEFAULT_PUBLISH_VALUES = { "profiles": [ { "product_types": ["model"], + "task_types": [], "contribution_layer": "model", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" }, { "product_types": ["look"], + "task_types": [], "contribution_layer": "look", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" }, { "product_types": ["groom"], + "task_types": [], "contribution_layer": "groom", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" }, { "product_types": ["rig"], + "task_types": [], "contribution_layer": "rig", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" }, { "product_types": ["usd"], + "task_types": [], "contribution_layer": "assembly", "contribution_apply_as_variant": False, "contribution_target_product": "usdShot" From 38c4a5d4e3fcb36b97df430b78f43a12cba1cacf Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:13:20 +0100 Subject: [PATCH 392/463] added controller functions --- client/ayon_core/tools/loader/abstract.py | 26 +++++++++++++++++-- client/ayon_core/tools/loader/control.py | 14 ++++++++++ .../tools/loader/models/selection.py | 18 +++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 0b790dfbbd..82703d028f 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -717,8 +717,30 @@ class FrontendLoaderController(_BaseLoaderController): Returns: list[str]: Selected folder ids. - """ + """ + pass + + @abstractmethod + def get_selected_task_ids(self): + """Get selected task ids. + + The information is based on last selection from UI. + + Returns: + list[str]: Selected folder ids. + + """ + pass + + @abstractmethod + def set_selected_tasks(self, task_ids): + """Set selected tasks. + + Args: + task_ids (Iterable[str]): Selected task ids. + + """ pass @abstractmethod @@ -729,8 +751,8 @@ class FrontendLoaderController(_BaseLoaderController): Returns: list[str]: Selected version ids. - """ + """ pass @abstractmethod diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 16cf7c31c7..8f8e7c2b15 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -198,6 +198,14 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): def get_folder_items(self, project_name, sender=None): return self._hierarchy_model.get_folder_items(project_name, sender) + def get_task_items(self, project_name, folder_ids, sender=None): + output = [] + for folder_id in folder_ids: + output.extend(self._hierarchy_model.get_task_items( + project_name, folder_id, sender + )) + return output + def get_product_items(self, project_name, folder_ids, sender=None): return self._products_model.get_product_items( project_name, folder_ids, sender) @@ -299,6 +307,12 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): def set_selected_folders(self, folder_ids): self._selection_model.set_selected_folders(folder_ids) + def get_selected_task_ids(self): + return self._selection_model.get_selected_task_ids() + + def set_selected_tasks(self, task_ids): + self._selection_model.set_selected_tasks(task_ids) + def get_selected_version_ids(self): return self._selection_model.get_selected_version_ids() diff --git a/client/ayon_core/tools/loader/models/selection.py b/client/ayon_core/tools/loader/models/selection.py index 326ff835f6..04add26f86 100644 --- a/client/ayon_core/tools/loader/models/selection.py +++ b/client/ayon_core/tools/loader/models/selection.py @@ -14,6 +14,7 @@ class SelectionModel(object): self._project_name = None self._folder_ids = set() + self._task_ids = set() self._version_ids = set() self._representation_ids = set() @@ -48,6 +49,23 @@ class SelectionModel(object): self.event_source ) + def get_selected_task_ids(self): + return self._task_ids + + def set_selected_tasks(self, task_ids): + if task_ids == self._task_ids: + return + + self._task_ids = task_ids + self._controller.emit_event( + "selection.tasks.changed", + { + "project_name": self._project_name, + "task_ids": task_ids, + }, + self.event_source + ) + def get_selected_version_ids(self): return self._version_ids From 61314835099be9afaa789a2b119096045b1379aa Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:13:53 +0100 Subject: [PATCH 393/463] added task id to VersionItem --- client/ayon_core/tools/loader/abstract.py | 5 +++++ client/ayon_core/tools/loader/models/products.py | 1 + 2 files changed, 6 insertions(+) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index 82703d028f..a9578032a2 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -108,6 +108,7 @@ class VersionItem: version (int): Version. Can be negative when is hero version. is_hero (bool): Is hero version. product_id (str): Product id. + task_id (Union[str, None]): Task id. thumbnail_id (Union[str, None]): Thumbnail id. published_time (Union[str, None]): Published time in format '%Y%m%dT%H%M%SZ'. @@ -127,6 +128,7 @@ class VersionItem: version, is_hero, product_id, + task_id, thumbnail_id, published_time, author, @@ -140,6 +142,7 @@ class VersionItem: ): self.version_id = version_id self.product_id = product_id + self.task_id = task_id self.thumbnail_id = thumbnail_id self.version = version self.is_hero = is_hero @@ -161,6 +164,7 @@ class VersionItem: and self.version == other.version and self.version_id == other.version_id and self.product_id == other.product_id + and self.task_id == other.task_id ) def __ne__(self, other): @@ -198,6 +202,7 @@ class VersionItem: return { "version_id": self.version_id, "product_id": self.product_id, + "task_id": self.task_id, "thumbnail_id": self.thumbnail_id, "version": self.version, "is_hero": self.is_hero, diff --git a/client/ayon_core/tools/loader/models/products.py b/client/ayon_core/tools/loader/models/products.py index 58eab0cabe..34acc0550c 100644 --- a/client/ayon_core/tools/loader/models/products.py +++ b/client/ayon_core/tools/loader/models/products.py @@ -55,6 +55,7 @@ def version_item_from_entity(version): version=version_num, is_hero=is_hero, product_id=version["productId"], + task_id=version["taskId"], thumbnail_id=version["thumbnailId"], published_time=published_time, author=author, From 22c86fcf692d08079b9f7437d642baa5316cd173 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:14:03 +0100 Subject: [PATCH 394/463] added 'get_task_items' to abstract methods --- client/ayon_core/tools/loader/abstract.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index a9578032a2..bdd8f057a1 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -541,6 +541,21 @@ class FrontendLoaderController(_BaseLoaderController): """ pass + @abstractmethod + def get_task_items(self, project_name, folder_ids, sender=None): + """Task items for folder ids. + + Args: + project_name (str): Project name. + folder_ids (Iterable[str]): Folder ids. + sender (Optional[str]): Sender who requested the items. + + Returns: + list[TaskItem]: List of task items. + + """ + pass + @abstractmethod def get_project_status_items(self, project_name, sender=None): """Items for all projects available on server. From 9cd7fe6253552e4a7f5045baef505449684680f6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:14:17 +0100 Subject: [PATCH 395/463] products widget can filter by task ids --- .../tools/loader/ui/products_model.py | 56 ++++++++++--------- .../tools/loader/ui/products_widget.py | 19 +++++++ 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/products_model.py b/client/ayon_core/tools/loader/ui/products_model.py index 3571788134..cebae9bca7 100644 --- a/client/ayon_core/tools/loader/ui/products_model.py +++ b/client/ayon_core/tools/loader/ui/products_model.py @@ -12,34 +12,35 @@ GROUP_TYPE_ROLE = QtCore.Qt.UserRole + 1 MERGED_COLOR_ROLE = QtCore.Qt.UserRole + 2 FOLDER_LABEL_ROLE = QtCore.Qt.UserRole + 3 FOLDER_ID_ROLE = QtCore.Qt.UserRole + 4 -PRODUCT_ID_ROLE = QtCore.Qt.UserRole + 5 -PRODUCT_NAME_ROLE = QtCore.Qt.UserRole + 6 -PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 7 -PRODUCT_TYPE_ICON_ROLE = QtCore.Qt.UserRole + 8 -PRODUCT_IN_SCENE_ROLE = QtCore.Qt.UserRole + 9 -VERSION_ID_ROLE = QtCore.Qt.UserRole + 10 -VERSION_HERO_ROLE = QtCore.Qt.UserRole + 11 -VERSION_NAME_ROLE = QtCore.Qt.UserRole + 12 -VERSION_NAME_EDIT_ROLE = QtCore.Qt.UserRole + 13 -VERSION_PUBLISH_TIME_ROLE = QtCore.Qt.UserRole + 14 -VERSION_STATUS_NAME_ROLE = QtCore.Qt.UserRole + 15 -VERSION_STATUS_SHORT_ROLE = QtCore.Qt.UserRole + 16 -VERSION_STATUS_COLOR_ROLE = QtCore.Qt.UserRole + 17 -VERSION_STATUS_ICON_ROLE = QtCore.Qt.UserRole + 18 -VERSION_AUTHOR_ROLE = QtCore.Qt.UserRole + 19 -VERSION_FRAME_RANGE_ROLE = QtCore.Qt.UserRole + 20 -VERSION_DURATION_ROLE = QtCore.Qt.UserRole + 21 -VERSION_HANDLES_ROLE = QtCore.Qt.UserRole + 22 -VERSION_STEP_ROLE = QtCore.Qt.UserRole + 23 -VERSION_AVAILABLE_ROLE = QtCore.Qt.UserRole + 24 -VERSION_THUMBNAIL_ID_ROLE = QtCore.Qt.UserRole + 25 -ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 26 -REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 27 -REPRESENTATIONS_COUNT_ROLE = QtCore.Qt.UserRole + 28 -SYNC_ACTIVE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 29 -SYNC_REMOTE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 30 +TASK_ID_ROLE = QtCore.Qt.UserRole + 5 +PRODUCT_ID_ROLE = QtCore.Qt.UserRole + 6 +PRODUCT_NAME_ROLE = QtCore.Qt.UserRole + 7 +PRODUCT_TYPE_ROLE = QtCore.Qt.UserRole + 8 +PRODUCT_TYPE_ICON_ROLE = QtCore.Qt.UserRole + 9 +PRODUCT_IN_SCENE_ROLE = QtCore.Qt.UserRole + 10 +VERSION_ID_ROLE = QtCore.Qt.UserRole + 11 +VERSION_HERO_ROLE = QtCore.Qt.UserRole + 12 +VERSION_NAME_ROLE = QtCore.Qt.UserRole + 13 +VERSION_NAME_EDIT_ROLE = QtCore.Qt.UserRole + 14 +VERSION_PUBLISH_TIME_ROLE = QtCore.Qt.UserRole + 15 +VERSION_STATUS_NAME_ROLE = QtCore.Qt.UserRole + 16 +VERSION_STATUS_SHORT_ROLE = QtCore.Qt.UserRole + 17 +VERSION_STATUS_COLOR_ROLE = QtCore.Qt.UserRole + 18 +VERSION_STATUS_ICON_ROLE = QtCore.Qt.UserRole + 19 +VERSION_AUTHOR_ROLE = QtCore.Qt.UserRole + 20 +VERSION_FRAME_RANGE_ROLE = QtCore.Qt.UserRole + 21 +VERSION_DURATION_ROLE = QtCore.Qt.UserRole + 22 +VERSION_HANDLES_ROLE = QtCore.Qt.UserRole + 23 +VERSION_STEP_ROLE = QtCore.Qt.UserRole + 24 +VERSION_AVAILABLE_ROLE = QtCore.Qt.UserRole + 25 +VERSION_THUMBNAIL_ID_ROLE = QtCore.Qt.UserRole + 26 +ACTIVE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 27 +REMOTE_SITE_ICON_ROLE = QtCore.Qt.UserRole + 28 +REPRESENTATIONS_COUNT_ROLE = QtCore.Qt.UserRole + 29 +SYNC_ACTIVE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 30 +SYNC_REMOTE_SITE_AVAILABILITY = QtCore.Qt.UserRole + 31 -STATUS_NAME_FILTER_ROLE = QtCore.Qt.UserRole + 31 +STATUS_NAME_FILTER_ROLE = QtCore.Qt.UserRole + 32 class ProductsModel(QtGui.QStandardItemModel): @@ -368,6 +369,7 @@ class ProductsModel(QtGui.QStandardItemModel): """ model_item.setData(version_item.version_id, VERSION_ID_ROLE) + model_item.setData(version_item.task_id, TASK_ID_ROLE) model_item.setData(version_item.version, VERSION_NAME_ROLE) model_item.setData(version_item.is_hero, VERSION_HERO_ROLE) model_item.setData( diff --git a/client/ayon_core/tools/loader/ui/products_widget.py b/client/ayon_core/tools/loader/ui/products_widget.py index 41a49b8ae1..c9a2335538 100644 --- a/client/ayon_core/tools/loader/ui/products_widget.py +++ b/client/ayon_core/tools/loader/ui/products_widget.py @@ -17,6 +17,7 @@ from .products_model import ( GROUP_TYPE_ROLE, MERGED_COLOR_ROLE, FOLDER_ID_ROLE, + TASK_ID_ROLE, PRODUCT_ID_ROLE, VERSION_ID_ROLE, VERSION_STATUS_NAME_ROLE, @@ -40,6 +41,7 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): self._product_type_filters = None self._statuses_filter = None + self._task_ids_filter = None self._ascending_sort = True def get_statuses_filter(self): @@ -47,6 +49,12 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): return None return set(self._statuses_filter) + def set_tasks_filters(self, task_ids_filter): + if self._task_ids_filter == task_ids_filter: + return + self._task_ids_filter = task_ids_filter + self.invalidateFilter() + def set_product_type_filters(self, product_type_filters): if self._product_type_filters == product_type_filters: return @@ -62,6 +70,8 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): def filterAcceptsRow(self, source_row, source_parent): source_model = self.sourceModel() index = source_model.index(source_row, 0, source_parent) + if not self._accept_task_ids_filter(index): + return False if not self._accept_row_by_role_value( index, self._product_type_filters, PRODUCT_TYPE_ROLE @@ -75,6 +85,12 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): return super().filterAcceptsRow(source_row, source_parent) + def _accept_task_ids_filter(self, index): + if not self._task_ids_filter: + return True + task_id = index.data(TASK_ID_ROLE) + return task_id in self._task_ids_filter + def _accept_row_by_role_value( self, index: QtCore.QModelIndex, @@ -254,6 +270,9 @@ class ProductsWidget(QtWidgets.QWidget): """ self._products_proxy_model.setFilterFixedString(name) + def set_tasks_filters(self, task_ids): + self._products_proxy_model.set_tasks_filters(task_ids) + def set_statuses_filter(self, status_names): """Set filter of version statuses. From c6b2ab3f22ef42daad9a0f8f5d9d84c14f01d581 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:15:08 +0100 Subject: [PATCH 396/463] added tasks widget for tasks filtering --- .../ayon_core/tools/loader/ui/tasks_widget.py | 346 ++++++++++++++++++ client/ayon_core/tools/loader/ui/window.py | 13 + 2 files changed, 359 insertions(+) create mode 100644 client/ayon_core/tools/loader/ui/tasks_widget.py diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py new file mode 100644 index 0000000000..3bf69d9b15 --- /dev/null +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -0,0 +1,346 @@ +import collections +import hashlib + +from qtpy import QtWidgets, QtCore, QtGui + +from ayon_core.tools.utils import ( + RecursiveSortFilterProxyModel, + DeselectableTreeView, + TasksQtModel, + TASKS_MODEL_SENDER_NAME, +) +from ayon_core.tools.utils.tasks_widget import ( + ITEM_ID_ROLE, + ITEM_NAME_ROLE, + PARENT_ID_ROLE, + TASK_TYPE_ROLE, +) +from ayon_core.tools.utils.lib import RefreshThread + + +class LoaderTasksQtModel(TasksQtModel): + column_labels = [ + "Task name", + "Task type", + ] + + def __init__(self, controller): + super().__init__(controller) + + self.setColumnCount(len(self.column_labels)) + for idx, label in enumerate(self.column_labels): + self.setHeaderData(idx, QtCore.Qt.Horizontal, label) + + self._items_by_id = {} + self._groups_by_name = {} + self._last_folder_ids = set() + + def refresh(self): + """Refresh tasks for selected folders.""" + + self._refresh(self._last_project_name, self._last_folder_ids) + + def set_context(self, project_name, folder_ids): + self._refresh(project_name, folder_ids) + + # Mark some functions from 'TasksQtModel' as not implemented + def get_index_by_name(self, task_name): + raise NotImplementedError( + "Method 'get_index_by_name' is not implemented." + ) + + def get_last_folder_id(self): + raise NotImplementedError( + "Method 'get_last_folder_id' is not implemented." + ) + + + def _refresh(self, project_name, folder_ids): + self._is_refreshing = True + self._last_project_name = project_name + self._last_folder_ids = folder_ids + if not folder_ids: + self._add_invalid_selection_item() + self._current_refresh_thread = None + self._is_refreshing = False + self.refreshed.emit() + return + + thread_id = hashlib.sha256( + "|".join(sorted(folder_ids)).encode() + ).hexdigest() + thread = self._refresh_threads.get(thread_id) + if thread is not None: + self._current_refresh_thread = thread + return + thread = RefreshThread( + thread_id, + self._thread_getter, + project_name, + folder_ids + ) + self._current_refresh_thread = thread + self._refresh_threads[thread.id] = thread + thread.refresh_finished.connect(self._on_refresh_thread) + thread.start() + + def _thread_getter(self, project_name, folder_ids): + task_items = self._controller.get_task_items( + project_name, folder_ids, sender=TASKS_MODEL_SENDER_NAME + ) + task_type_items = {} + if hasattr(self._controller, "get_task_type_items"): + task_type_items = self._controller.get_task_type_items( + project_name, sender=TASKS_MODEL_SENDER_NAME + ) + return task_items, task_type_items + + def _on_refresh_thread(self, thread_id): + """Callback when refresh thread is finished. + + Technically can be running multiple refresh threads at the same time, + to avoid using values from wrong thread, we check if thread id is + current refresh thread id. + + Tasks are stored by name, so if a folder has same task name as + previously selected folder it keeps the selection. + + Args: + thread_id (str): Thread id. + """ + + # Make sure to remove thread from '_refresh_threads' dict + thread = self._refresh_threads.pop(thread_id) + if ( + self._current_refresh_thread is None + or thread_id != self._current_refresh_thread.id + ): + return + + self._fill_data_from_thread(thread) + + root_item = self.invisibleRootItem() + self._has_content = root_item.rowCount() > 0 + self._current_refresh_thread = None + self._is_refreshing = False + self.refreshed.emit() + + def _clear_items(self): + self._items_by_id = {} + self._groups_by_name = {} + super()._clear_items() + + def _fill_data_from_thread(self, thread): + task_items, task_type_items = thread.get_result() + # Task items are refreshed + if task_items is None: + return + + # No tasks are available on folder + if not task_items: + self._add_empty_task_item() + return + self._remove_invalid_items() + + task_type_item_by_name = { + task_type_item.name: task_type_item + for task_type_item in task_type_items + } + task_type_icon_cache = {} + current_ids = set() + items_by_name = collections.defaultdict(list) + for task_item in task_items: + task_id = task_item.task_id + current_ids.add(task_id) + item = self._items_by_id.get(task_id) + if item is None: + item = QtGui.QStandardItem() + item.setColumnCount(self.columnCount()) + item.setEditable(False) + self._items_by_id[task_id] = item + + icon = self._get_task_item_icon( + task_item, + task_type_item_by_name, + task_type_icon_cache + ) + name = task_item.name + item.setData(name, QtCore.Qt.DisplayRole) + item.setData(name, ITEM_NAME_ROLE) + item.setData(task_item.id, ITEM_ID_ROLE) + item.setData(task_item.task_type, TASK_TYPE_ROLE) + item.setData(task_item.parent_id, PARENT_ID_ROLE) + item.setData(icon, QtCore.Qt.DecorationRole) + + items_by_name[name].append(item) + + root_item = self.invisibleRootItem() + + for task_id in set(self._items_by_id) - current_ids: + item = self._items_by_id.pop(task_id) + parent = item.parent() + if parent is None: + parent = root_item + parent.removeRow(item.row()) + + used_group_names = set() + new_root_items = [] + for name, items in items_by_name.items(): + # Make sure item is not parented + # - this is laziness to avoid re-parenting items which does + # complicate the code with no benefit + for item in items: + parent = item.parent() + # If item is in root then model is not None, and + # if parent is set then model is None + if parent is None and item.model() is None: + continue + + if parent is None: + # We can skip when task stays un-grouped + if len(items) == 1: + continue + parent = root_item + + parent.takeRow(item.row()) + + if len(items) == 1: + new_root_items.extend(items) + continue + + used_group_names.add(name) + group_item = self._groups_by_name.get(name) + if group_item is None: + group_item = QtGui.QStandardItem() + group_item.setData(name, QtCore.Qt.DisplayRole) + group_item.setEditable(False) + group_item.setColumnCount(self.columnCount()) + self._groups_by_name[name] = group_item + new_root_items.append(group_item) + + # Use icon from first item + first_item_icon = items[0].data(QtCore.Qt.DecorationRole) + task_ids = [ + item.data(ITEM_ID_ROLE) + for item in items + ] + + group_item.setData(first_item_icon, QtCore.Qt.DecorationRole) + group_item.setData("|".join(task_ids), ITEM_ID_ROLE) + + group_item.appendRows(items) + + for name in set(self._groups_by_name.keys()) - used_group_names: + group_item = self._groups_by_name.pop(name) + root_item.removeRow(group_item.row()) + + if new_root_items: + root_item.appendRows(new_root_items) + + def data(self, index, role=None): + if not index.isValid(): + return None + + if role is None: + role = QtCore.Qt.DisplayRole + + col = index.column() + if col != 0: + index = self.index(index.row(), 0, index.parent()) + + if col == 1: + if role == QtCore.Qt.DisplayRole: + role = TASK_TYPE_ROLE + else: + return None + + return super().data(index, role) + + +class LoaderTasksWidget(QtWidgets.QWidget): + refreshed = QtCore.Signal() + + def __init__(self, controller, parent): + super().__init__(parent) + + tasks_view = DeselectableTreeView(self) + # tasks_view.setHeaderHidden(True) + tasks_view.setSelectionMode( + QtWidgets.QAbstractItemView.ExtendedSelection) + tasks_view_header = tasks_view.header() + tasks_view_header.setStretchLastSection(False) + + tasks_model = LoaderTasksQtModel(controller) + tasks_proxy_model = RecursiveSortFilterProxyModel() + tasks_proxy_model.setSourceModel(tasks_model) + tasks_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + + tasks_view.setModel(tasks_proxy_model) + tasks_view_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(tasks_view, 1) + + controller.register_event_callback( + "selection.folders.changed", + self._on_folders_selection_changed, + ) + controller.register_event_callback( + "tasks.refresh.finished", + self._on_tasks_refresh_finished + ) + + selection_model = tasks_view.selectionModel() + selection_model.selectionChanged.connect(self._on_selection_change) + + tasks_model.refreshed.connect(self._on_model_refresh) + + self._controller = controller + self._tasks_view = tasks_view + self._tasks_model = tasks_model + self._tasks_proxy_model = tasks_proxy_model + + def set_name_filter(self, name): + """Set filter of folder name. + + Args: + name (str): The string filter. + + """ + self._tasks_proxy_model.setFilterFixedString(name) + if name: + self._tasks_view.expandAll() + + def refresh(self): + self._tasks_model.refresh() + + def _clear(self): + self._tasks_model.clear() + + def _on_tasks_refresh_finished(self, event): + if event["sender"] != TASKS_MODEL_SENDER_NAME: + self._set_project_name(event["project_name"]) + + def _on_folders_selection_changed(self, event): + project_name = event["project_name"] + folder_ids = event["folder_ids"] + self._tasks_model.set_context(project_name, folder_ids) + + def _on_model_refresh(self): + self._tasks_proxy_model.sort(0) + self.refreshed.emit() + + def _get_selected_item_ids(self): + selection_model = self._tasks_view.selectionModel() + item_ids = set() + for index in selection_model.selectedIndexes(): + item_id = index.data(ITEM_ID_ROLE) + if item_id is None: + continue + item_ids |= set(item_id.split("|")) + return item_ids + + def _on_selection_change(self): + item_ids = self._get_selected_item_ids() + self._controller.set_selected_tasks(item_ids) diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index 31de0cf4e5..b54e8aab42 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -14,6 +14,7 @@ from ayon_core.tools.utils import ProjectsCombobox from ayon_core.tools.loader.control import LoaderController from .folders_widget import LoaderFoldersWidget +from .tasks_widget import LoaderTasksWidget from .products_widget import ProductsWidget from .product_types_combo import ProductTypesCombobox from .product_group_dialog import ProductGroupDialog @@ -170,7 +171,10 @@ class LoaderWindow(QtWidgets.QWidget): context_layout.addWidget(folders_filter_input, 0) context_layout.addWidget(folders_widget, 1) + tasks_widget = LoaderTasksWidget(controller, context_widget) + context_splitter.addWidget(context_widget) + context_splitter.addWidget(tasks_widget) context_splitter.setStretchFactor(0, 65) context_splitter.setStretchFactor(1, 35) @@ -282,6 +286,10 @@ class LoaderWindow(QtWidgets.QWidget): "selection.folders.changed", self._on_folders_selection_changed, ) + controller.register_event_callback( + "selection.tasks.changed", + self._on_tasks_selection_change, + ) controller.register_event_callback( "selection.versions.changed", self._on_versions_selection_changed, @@ -306,6 +314,8 @@ class LoaderWindow(QtWidgets.QWidget): self._folders_filter_input = folders_filter_input self._folders_widget = folders_widget + self._tasks_widget = tasks_widget + self._products_filter_input = products_filter_input self._product_types_filter_combo = product_types_filter_combo self._product_status_filter_combo = product_status_filter_combo @@ -428,6 +438,9 @@ class LoaderWindow(QtWidgets.QWidget): def _on_product_filter_change(self, text): self._products_widget.set_name_filter(text) + def _on_tasks_selection_change(self, event): + self._products_widget.set_tasks_filters(event["task_ids"]) + def _on_status_filter_change(self): status_names = self._product_status_filter_combo.get_value() self._products_widget.set_statuses_filter(status_names) From de504e8c8555031dad766dbb25521ee814bcabec Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:43:57 +0100 Subject: [PATCH 397/463] enhanced 'DeselectableTreeView' --- client/ayon_core/tools/utils/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/views.py b/client/ayon_core/tools/utils/views.py index d8ae94bf0c..d69be9b6a9 100644 --- a/client/ayon_core/tools/utils/views.py +++ b/client/ayon_core/tools/utils/views.py @@ -7,7 +7,6 @@ class DeselectableTreeView(QtWidgets.QTreeView): """A tree view that deselects on clicking on an empty area in the view""" def mousePressEvent(self, event): - index = self.indexAt(event.pos()) if not index.isValid(): # clear the selection @@ -15,7 +14,14 @@ class DeselectableTreeView(QtWidgets.QTreeView): # clear the current index self.setCurrentIndex(QtCore.QModelIndex()) - QtWidgets.QTreeView.mousePressEvent(self, event) + elif ( + self.selectionModel().isSelected(index) + and len(self.selectionModel().selectedRows()) == 1 + and event.modifiers() == QtCore.Qt.NoModifier + ): + event.setModifiers(QtCore.Qt.ControlModifier) + + super().mousePressEvent(event) class TreeView(QtWidgets.QTreeView): From 1c4cf7f6373ddbd980975f27d3dc91b3d6474c7e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:44:08 +0100 Subject: [PATCH 398/463] better formatting --- client/ayon_core/tools/loader/ui/tasks_widget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index 3bf69d9b15..e4fb7d1199 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -266,7 +266,8 @@ class LoaderTasksWidget(QtWidgets.QWidget): tasks_view = DeselectableTreeView(self) # tasks_view.setHeaderHidden(True) tasks_view.setSelectionMode( - QtWidgets.QAbstractItemView.ExtendedSelection) + QtWidgets.QAbstractItemView.ExtendedSelection + ) tasks_view_header = tasks_view.header() tasks_view_header.setStretchLastSection(False) From 8f6799002e4854a385cd7b1fdb21a9f0a0fb73a0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:59:14 +0100 Subject: [PATCH 399/463] task filtering also happens per version item --- .../tools/loader/ui/products_delegates.py | 43 ++++++++++++++++--- .../tools/loader/ui/products_widget.py | 13 ++++-- client/ayon_core/tools/loader/ui/window.py | 2 +- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/products_delegates.py b/client/ayon_core/tools/loader/ui/products_delegates.py index fba9b5b3ca..8cece4687f 100644 --- a/client/ayon_core/tools/loader/ui/products_delegates.py +++ b/client/ayon_core/tools/loader/ui/products_delegates.py @@ -19,6 +19,7 @@ from .products_model import ( ) STATUS_NAME_ROLE = QtCore.Qt.UserRole + 1 +TASK_ID_ROLE = QtCore.Qt.UserRole + 2 class VersionsModel(QtGui.QStandardItemModel): @@ -48,6 +49,7 @@ class VersionsModel(QtGui.QStandardItemModel): item.setData(version_id, QtCore.Qt.UserRole) self._items_by_id[version_id] = item item.setData(version_item.status, STATUS_NAME_ROLE) + item.setData(version_item.task_id, TASK_ID_ROLE) if item.row() != idx: root_item.insertRow(idx, item) @@ -57,17 +59,30 @@ class VersionsFilterModel(QtCore.QSortFilterProxyModel): def __init__(self): super().__init__() self._status_filter = None + self._task_ids_filter = None def filterAcceptsRow(self, row, parent): - if self._status_filter is None: - return True + if self._status_filter is not None: + if not self._status_filter: + return False - if not self._status_filter: - return False + index = self.sourceModel().index(row, 0, parent) + status = index.data(STATUS_NAME_ROLE) + if status not in self._status_filter: + return False - index = self.sourceModel().index(row, 0, parent) - status = index.data(STATUS_NAME_ROLE) - return status in self._status_filter + if self._task_ids_filter: + index = self.sourceModel().index(row, 0, parent) + task_id = index.data(TASK_ID_ROLE) + if task_id not in self._task_ids_filter: + return False + return True + + def set_tasks_filter(self, task_ids): + if self._task_ids_filter == task_ids: + return + self._task_ids_filter = task_ids + self.invalidateFilter() def set_statuses_filter(self, status_names): if self._status_filter == status_names: @@ -101,6 +116,13 @@ class VersionComboBox(QtWidgets.QComboBox): def get_product_id(self): return self._product_id + def set_tasks_filter(self, task_ids): + self._proxy_model.set_tasks_filter(task_ids) + if self.count() == 0: + return + if self.currentIndex() != 0: + self.setCurrentIndex(0) + def set_statuses_filter(self, status_names): self._proxy_model.set_statuses_filter(status_names) if self.count() == 0: @@ -149,6 +171,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): super().__init__(*args, **kwargs) self._editor_by_id: Dict[str, VersionComboBox] = {} + self._task_ids_filter = None self._statuses_filter = None def displayText(self, value, locale): @@ -156,6 +179,11 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): return "N/A" return format_version(value) + def set_tasks_filter(self, task_ids): + self._task_ids_filter = set(task_ids) + for widget in self._editor_by_id.values(): + widget.set_tasks_filter(task_ids) + def set_statuses_filter(self, status_names): self._statuses_filter = set(status_names) for widget in self._editor_by_id.values(): @@ -239,6 +267,7 @@ class VersionDelegate(QtWidgets.QStyledItemDelegate): version_id = index.data(VERSION_ID_ROLE) editor.update_versions(versions, version_id) + editor.set_tasks_filter(self._task_ids_filter) editor.set_statuses_filter(self._statuses_filter) def setModelData(self, editor, model, index): diff --git a/client/ayon_core/tools/loader/ui/products_widget.py b/client/ayon_core/tools/loader/ui/products_widget.py index c9a2335538..94d95b9026 100644 --- a/client/ayon_core/tools/loader/ui/products_widget.py +++ b/client/ayon_core/tools/loader/ui/products_widget.py @@ -49,7 +49,7 @@ class ProductsProxyModel(RecursiveSortFilterProxyModel): return None return set(self._statuses_filter) - def set_tasks_filters(self, task_ids_filter): + def set_tasks_filter(self, task_ids_filter): if self._task_ids_filter == task_ids_filter: return self._task_ids_filter = task_ids_filter @@ -270,8 +270,15 @@ class ProductsWidget(QtWidgets.QWidget): """ self._products_proxy_model.setFilterFixedString(name) - def set_tasks_filters(self, task_ids): - self._products_proxy_model.set_tasks_filters(task_ids) + def set_tasks_filter(self, task_ids): + """Set filter of version tasks. + + Args: + task_ids (set[str]): Task ids. + + """ + self._version_delegate.set_tasks_filter(task_ids) + self._products_proxy_model.set_tasks_filter(task_ids) def set_statuses_filter(self, status_names): """Set filter of version statuses. diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index b54e8aab42..b846484c39 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -439,7 +439,7 @@ class LoaderWindow(QtWidgets.QWidget): self._products_widget.set_name_filter(text) def _on_tasks_selection_change(self, event): - self._products_widget.set_tasks_filters(event["task_ids"]) + self._products_widget.set_tasks_filter(event["task_ids"]) def _on_status_filter_change(self): status_names = self._product_status_filter_combo.get_value() From 4d7285c5b1db76da02528a47d7f70ff83c3ddeb3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Feb 2025 19:10:36 +0100 Subject: [PATCH 400/463] added folder label to task view --- client/ayon_core/tools/loader/abstract.py | 14 ++++++++ client/ayon_core/tools/loader/control.py | 12 +++++++ .../ayon_core/tools/loader/ui/tasks_widget.py | 34 ++++++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index bdd8f057a1..d527428196 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -556,6 +556,20 @@ class FrontendLoaderController(_BaseLoaderController): """ pass + @abstractmethod + def get_folder_labels(self, project_name, folder_ids): + """Get folder labels for folder ids. + + Args: + project_name (str): Project name. + folder_ids (Iterable[str]): Folder ids. + + Returns: + dict[str, Optional[str]]: Folder labels by folder id. + + """ + pass + @abstractmethod def get_project_status_items(self, project_name, sender=None): """Items for all projects available on server. diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 8f8e7c2b15..089435140e 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -206,6 +206,18 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): )) return output + def get_folder_labels(self, project_name, folder_ids): + folder_items_by_id = self._hierarchy_model.get_folder_items_by_id( + project_name, folder_ids + ) + output = {} + for folder_id, folder_item in folder_items_by_id.items(): + label = None + if folder_item is not None: + label = folder_item.label + output[folder_id] = label + return output + def get_product_items(self, project_name, folder_ids, sender=None): return self._products_model.get_product_items( project_name, folder_ids, sender) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index e4fb7d1199..3a48c2fbf7 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -17,11 +17,15 @@ from ayon_core.tools.utils.tasks_widget import ( ) from ayon_core.tools.utils.lib import RefreshThread +# Role that can't clash with default 'tasks_widget' roles +FOLDER_LABEL_ROLE = QtCore.Qt.UserRole + 100 + class LoaderTasksQtModel(TasksQtModel): column_labels = [ "Task name", "Task type", + "Folder" ] def __init__(self, controller): @@ -93,7 +97,14 @@ class LoaderTasksQtModel(TasksQtModel): task_type_items = self._controller.get_task_type_items( project_name, sender=TASKS_MODEL_SENDER_NAME ) - return task_items, task_type_items + folder_ids = { + task_item.parent_id + for task_item in task_items + } + folder_labels_by_id = self._controller.get_folder_labels( + project_name, folder_ids + ) + return task_items, task_type_items, folder_labels_by_id def _on_refresh_thread(self, thread_id): """Callback when refresh thread is finished. @@ -131,7 +142,7 @@ class LoaderTasksQtModel(TasksQtModel): super()._clear_items() def _fill_data_from_thread(self, thread): - task_items, task_type_items = thread.get_result() + task_items, task_type_items, folder_labels_by_id = thread.get_result() # Task items are refreshed if task_items is None: return @@ -165,11 +176,15 @@ class LoaderTasksQtModel(TasksQtModel): task_type_icon_cache ) name = task_item.name + folder_id = task_item.parent_id + folder_label = folder_labels_by_id.get(folder_id) + item.setData(name, QtCore.Qt.DisplayRole) item.setData(name, ITEM_NAME_ROLE) item.setData(task_item.id, ITEM_ID_ROLE) item.setData(task_item.task_type, TASK_TYPE_ROLE) - item.setData(task_item.parent_id, PARENT_ID_ROLE) + item.setData(folder_id, PARENT_ID_ROLE) + item.setData(folder_label, FOLDER_LABEL_ROLE) item.setData(icon, QtCore.Qt.DecorationRole) items_by_name[name].append(item) @@ -254,6 +269,12 @@ class LoaderTasksQtModel(TasksQtModel): else: return None + if col == 2: + if role == QtCore.Qt.DisplayRole: + role = FOLDER_LABEL_ROLE + else: + return None + return super().data(index, role) @@ -277,7 +298,11 @@ class LoaderTasksWidget(QtWidgets.QWidget): tasks_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) tasks_view.setModel(tasks_proxy_model) - tasks_view_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + # Hide folder column by default + tasks_view.setColumnHidden(2, True) + tasks_view_header.setSectionResizeMode( + 0, QtWidgets.QHeaderView.Stretch + ) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) @@ -326,6 +351,7 @@ class LoaderTasksWidget(QtWidgets.QWidget): def _on_folders_selection_changed(self, event): project_name = event["project_name"] folder_ids = event["folder_ids"] + self._tasks_view.setColumnHidden(2, len(folder_ids) == 1) self._tasks_model.set_context(project_name, folder_ids) def _on_model_refresh(self): From 4b44a1bc1c8084f691a5abd6c7612ce6548ab215 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:51:03 +0100 Subject: [PATCH 401/463] remove unused import --- client/ayon_core/tools/loader/ui/product_types_combo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/loader/ui/product_types_combo.py b/client/ayon_core/tools/loader/ui/product_types_combo.py index ff2a70a7fa..fe136ff04c 100644 --- a/client/ayon_core/tools/loader/ui/product_types_combo.py +++ b/client/ayon_core/tools/loader/ui/product_types_combo.py @@ -1,4 +1,4 @@ -from qtpy import QtWidgets, QtGui, QtCore +from qtpy import QtGui, QtCore from ._multicombobox import ( CustomPaintMultiselectComboBox, From 6947680cc2b6f4834121df89c274ffede8531ae3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:51:15 +0100 Subject: [PATCH 402/463] make sure items are only selectable in tasks widget --- client/ayon_core/tools/loader/ui/tasks_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index 3a48c2fbf7..f3ad42ea15 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -58,6 +58,8 @@ class LoaderTasksQtModel(TasksQtModel): "Method 'get_last_folder_id' is not implemented." ) + def flags(self, _index): + return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def _refresh(self, project_name, folder_ids): self._is_refreshing = True From 57c813004928e2a3bbb2845aa5dd090086d2bc58 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 20 Feb 2025 11:20:25 +0000 Subject: [PATCH 403/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index f2e82af12b..c4606e65f2 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.1+dev" +__version__ = "1.1.2" diff --git a/package.py b/package.py index b9629d6c51..990d712056 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.1+dev" +version = "1.1.2" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 9833902c16..01a8aa24ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.1+dev" +version = "1.1.2" description = "" authors = ["Ynput Team "] readme = "README.md" From c8a7a766180f59915cb50f015c62b613626e607f Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 20 Feb 2025 11:21:05 +0000 Subject: [PATCH 404/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index c4606e65f2..7f3228e769 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.2" +__version__ = "1.1.2+dev" diff --git a/package.py b/package.py index 990d712056..89276751b0 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.2" +version = "1.1.2+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 01a8aa24ef..98692d396f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.2" +version = "1.1.2+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From fe58f6150f33b67f779321f0c821dcac2564c90c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 10:20:33 +0100 Subject: [PATCH 405/463] ignore failed initialization of create plugin --- client/ayon_core/pipeline/create/context.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index c169df67df..24557234f4 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -755,11 +755,19 @@ class CreateContext: ).format(creator_class.host_name, self.host_name)) continue - creator = creator_class( - project_settings, - self, - self.headless - ) + # TODO report initialization error + try: + creator = creator_class( + project_settings, + self, + self.headless + ) + except Exception: + self.log.error( + f"Failed to initialize plugin: {creator_class}", + exc_info=True + ) + continue if not creator.enabled: disabled_creators[creator_identifier] = creator From 66285949a5beabadfa1cf9654610cfa9a2568788 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:02:00 +0100 Subject: [PATCH 406/463] take rows instead of removing them --- client/ayon_core/tools/utils/tasks_widget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 87a4c3db3b..41dc46c16a 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -53,7 +53,8 @@ class TasksQtModel(QtGui.QStandardItemModel): self._has_content = False self._remove_invalid_items() root_item = self.invisibleRootItem() - root_item.removeRows(0, root_item.rowCount()) + while root_item.rowCount() != 0: + root_item.takeRow(0) def refresh(self): """Refresh tasks for last project and folder.""" From ef0106346f53530285d5244ed2e8c20d4c951135 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:02:37 +0100 Subject: [PATCH 407/463] flags consider item flags --- client/ayon_core/tools/loader/ui/tasks_widget.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index f3ad42ea15..f13f352f3a 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -58,8 +58,10 @@ class LoaderTasksQtModel(TasksQtModel): "Method 'get_last_folder_id' is not implemented." ) - def flags(self, _index): - return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable + def flags(self, index): + if index.column() != 0: + index = self.index(index.row(), 0, index.parent()) + return super().flags(index) def _refresh(self, project_name, folder_ids): self._is_refreshing = True From c5043aec8e58c6576a5185e89ddfcd70a3d1f4b5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:03:21 +0100 Subject: [PATCH 408/463] fix fill items from thread --- .../ayon_core/tools/loader/ui/tasks_widget.py | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index f13f352f3a..0b9f1f6008 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -195,34 +195,26 @@ class LoaderTasksQtModel(TasksQtModel): root_item = self.invisibleRootItem() - for task_id in set(self._items_by_id) - current_ids: - item = self._items_by_id.pop(task_id) - parent = item.parent() - if parent is None: - parent = root_item - parent.removeRow(item.row()) + # Make sure item is not parented + # - this is laziness to avoid re-parenting items which does + # complicate the code with no benefit + queue = collections.deque() + queue.append((None, root_item)) + while queue: + (parent, item) = queue.popleft() + if not item.hasChildren(): + if parent: + parent.takeRow(item.row()) + continue + + for row in range(item.rowCount()): + queue.append((item, item.child(row, 0))) + + queue.append((parent, item)) used_group_names = set() new_root_items = [] for name, items in items_by_name.items(): - # Make sure item is not parented - # - this is laziness to avoid re-parenting items which does - # complicate the code with no benefit - for item in items: - parent = item.parent() - # If item is in root then model is not None, and - # if parent is set then model is None - if parent is None and item.model() is None: - continue - - if parent is None: - # We can skip when task stays un-grouped - if len(items) == 1: - continue - parent = root_item - - parent.takeRow(item.row()) - if len(items) == 1: new_root_items.extend(items) continue @@ -235,7 +227,6 @@ class LoaderTasksQtModel(TasksQtModel): group_item.setEditable(False) group_item.setColumnCount(self.columnCount()) self._groups_by_name[name] = group_item - new_root_items.append(group_item) # Use icon from first item first_item_icon = items[0].data(QtCore.Qt.DecorationRole) @@ -249,9 +240,14 @@ class LoaderTasksQtModel(TasksQtModel): group_item.appendRows(items) - for name in set(self._groups_by_name.keys()) - used_group_names: - group_item = self._groups_by_name.pop(name) - root_item.removeRow(group_item.row()) + new_root_items.append(group_item) + + # Remove unused caches + for task_id in set(self._items_by_id) - current_ids: + self._items_by_id.pop(task_id) + + for name in set(self._groups_by_name) - used_group_names: + self._groups_by_name.pop(name) if new_root_items: root_item.appendRows(new_root_items) From ec41b30665220e995c5906931e94324bf154f860 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:04:28 +0100 Subject: [PATCH 409/463] validate host for 'IPublishHost' interface instead of 'ILoadHost' --- client/ayon_core/tools/utils/host_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/host_tools.py b/client/ayon_core/tools/utils/host_tools.py index 1eff746b9e..3d356555f3 100644 --- a/client/ayon_core/tools/utils/host_tools.py +++ b/client/ayon_core/tools/utils/host_tools.py @@ -7,7 +7,7 @@ import os import pyblish.api -from ayon_core.host import ILoadHost +from ayon_core.host import ILoadHost, IPublishHost from ayon_core.lib import Logger from ayon_core.pipeline import registered_host @@ -236,7 +236,7 @@ class HostToolsHelper: from ayon_core.tools.publisher.window import PublisherWindow host = registered_host() - ILoadHost.validate_load_methods(host) + IPublishHost.validate_publish_methods(host) publisher_window = PublisherWindow( controller=controller, From 8ad5489d68ceaec2eb528702f6d9789a3b0cc069 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:12:25 +0100 Subject: [PATCH 410/463] base tasks widget defines column labels using default methods --- client/ayon_core/tools/utils/tasks_widget.py | 22 +++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 41dc46c16a..30846e6cda 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -24,9 +24,14 @@ class TasksQtModel(QtGui.QStandardItemModel): """ _default_task_icon = None refreshed = QtCore.Signal() + column_labels = ["Tasks"] def __init__(self, controller): - super(TasksQtModel, self).__init__() + super().__init__() + + self.setColumnCount(len(self.column_labels)) + for idx, label in enumerate(self.column_labels): + self.setHeaderData(idx, QtCore.Qt.Horizontal, label) self._controller = controller @@ -337,19 +342,6 @@ class TasksQtModel(QtGui.QStandardItemModel): return self._has_content - def headerData(self, section, orientation, role): - # Show nice labels in the header - if ( - role == QtCore.Qt.DisplayRole - and orientation == QtCore.Qt.Horizontal - ): - if section == 0: - return "Tasks" - - return super(TasksQtModel, self).headerData( - section, orientation, role - ) - class TasksWidget(QtWidgets.QWidget): """Tasks widget. @@ -366,7 +358,7 @@ class TasksWidget(QtWidgets.QWidget): selection_changed = QtCore.Signal() def __init__(self, controller, parent, handle_expected_selection=False): - super(TasksWidget, self).__init__(parent) + super().__init__(parent) tasks_view = DeselectableTreeView(self) tasks_view.setIndentation(0) From 62084ac684fb0cc1b5bb703321161ceef773edc3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:13:09 +0100 Subject: [PATCH 411/463] remove duplicated code from super class --- client/ayon_core/tools/loader/ui/tasks_widget.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index 0b9f1f6008..9f5a17ac6e 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -31,10 +31,6 @@ class LoaderTasksQtModel(TasksQtModel): def __init__(self, controller): super().__init__(controller) - self.setColumnCount(len(self.column_labels)) - for idx, label in enumerate(self.column_labels): - self.setHeaderData(idx, QtCore.Qt.Horizontal, label) - self._items_by_id = {} self._groups_by_name = {} self._last_folder_ids = set() From 592fac87f7614100c8591641713ce2598040e51d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:50:51 +0100 Subject: [PATCH 412/463] tasks header widget defines width of first column --- client/ayon_core/tools/loader/ui/tasks_widget.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index 9f5a17ac6e..14840e852f 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -281,12 +281,9 @@ class LoaderTasksWidget(QtWidgets.QWidget): super().__init__(parent) tasks_view = DeselectableTreeView(self) - # tasks_view.setHeaderHidden(True) tasks_view.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection ) - tasks_view_header = tasks_view.header() - tasks_view_header.setStretchLastSection(False) tasks_model = LoaderTasksQtModel(controller) tasks_proxy_model = RecursiveSortFilterProxyModel() @@ -296,9 +293,6 @@ class LoaderTasksWidget(QtWidgets.QWidget): tasks_view.setModel(tasks_proxy_model) # Hide folder column by default tasks_view.setColumnHidden(2, True) - tasks_view_header.setSectionResizeMode( - 0, QtWidgets.QHeaderView.Stretch - ) main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) @@ -323,6 +317,15 @@ class LoaderTasksWidget(QtWidgets.QWidget): self._tasks_model = tasks_model self._tasks_proxy_model = tasks_proxy_model + self._fisrt_show = True + + def showEvent(self, event): + super().showEvent(event) + if self._fisrt_show: + self._fisrt_show = False + header_widget = self._tasks_view.header() + header_widget.resizeSection(0, 200) + def set_name_filter(self, name): """Set filter of folder name. From acd446bcef27710e98395ca3af22a6c67f487ef8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:51:03 +0100 Subject: [PATCH 413/463] added no tasks item to task selection --- .../ayon_core/tools/loader/ui/tasks_widget.py | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index 14840e852f..4841af75dd 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -3,6 +3,7 @@ import hashlib from qtpy import QtWidgets, QtCore, QtGui +from ayon_core.style import get_default_entity_icon_color from ayon_core.tools.utils import ( RecursiveSortFilterProxyModel, DeselectableTreeView, @@ -15,10 +16,11 @@ from ayon_core.tools.utils.tasks_widget import ( PARENT_ID_ROLE, TASK_TYPE_ROLE, ) -from ayon_core.tools.utils.lib import RefreshThread +from ayon_core.tools.utils.lib import RefreshThread, get_qt_icon # Role that can't clash with default 'tasks_widget' roles FOLDER_LABEL_ROLE = QtCore.Qt.UserRole + 100 +NO_TASKS_ID = "--no-task--" class LoaderTasksQtModel(TasksQtModel): @@ -34,6 +36,9 @@ class LoaderTasksQtModel(TasksQtModel): self._items_by_id = {} self._groups_by_name = {} self._last_folder_ids = set() + # This item is used to be able filter versions without any task + # - do not mismatch with '_empty_tasks_item' item from 'TasksQtModel' + self._no_tasks_item = None def refresh(self): """Refresh tasks for selected folders.""" @@ -59,6 +64,20 @@ class LoaderTasksQtModel(TasksQtModel): index = self.index(index.row(), 0, index.parent()) return super().flags(index) + def _get_no_tasks_item(self): + if self._no_tasks_item is None: + item = QtGui.QStandardItem("< Without task >") + icon = get_qt_icon({ + "type": "material-symbols", + "name": "indeterminate_check_box", + "color": get_default_entity_icon_color(), + }) + item.setData(icon, QtCore.Qt.DecorationRole) + item.setData(NO_TASKS_ID, ITEM_ID_ROLE) + item.setEditable(False) + self._no_tasks_item = item + return self._no_tasks_item + def _refresh(self, project_name, folder_ids): self._is_refreshing = True self._last_project_name = project_name @@ -209,7 +228,9 @@ class LoaderTasksQtModel(TasksQtModel): queue.append((parent, item)) used_group_names = set() - new_root_items = [] + new_root_items = [ + self._get_no_tasks_item() + ] for name, items in items_by_name.items(): if len(items) == 1: new_root_items.extend(items) @@ -364,7 +385,10 @@ class LoaderTasksWidget(QtWidgets.QWidget): item_id = index.data(ITEM_ID_ROLE) if item_id is None: continue - item_ids |= set(item_id.split("|")) + if item_id == NO_TASKS_ID: + item_ids.add(None) + else: + item_ids |= set(item_id.split("|")) return item_ids def _on_selection_change(self): From 8825fee96ddb437eb71c16adaa1c880292dc7310 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:17:14 +0100 Subject: [PATCH 414/463] fix task icons --- client/ayon_core/tools/loader/abstract.py | 20 ++++++++++++++++++++ client/ayon_core/tools/loader/control.py | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/client/ayon_core/tools/loader/abstract.py b/client/ayon_core/tools/loader/abstract.py index d527428196..26b476de1f 100644 --- a/client/ayon_core/tools/loader/abstract.py +++ b/client/ayon_core/tools/loader/abstract.py @@ -556,6 +556,26 @@ class FrontendLoaderController(_BaseLoaderController): """ pass + @abstractmethod + def get_task_type_items(self, project_name, sender=None): + """Task type items for a project. + + This function may trigger events with topics + 'projects.task_types.refresh.started' and + 'projects.task_types.refresh.finished' which will contain 'sender' + value in data. + That may help to avoid re-refresh of items in UI elements. + + Args: + project_name (str): Project name. + sender (str): Who requested task type items. + + Returns: + list[TaskTypeItem]: Task type information. + + """ + pass + @abstractmethod def get_folder_labels(self, project_name, folder_ids): """Get folder labels for folder ids. diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index 089435140e..7959a63edb 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -206,6 +206,11 @@ class LoaderController(BackendLoaderController, FrontendLoaderController): )) return output + def get_task_type_items(self, project_name, sender=None): + return self._projects_model.get_task_type_items( + project_name, sender + ) + def get_folder_labels(self, project_name, folder_ids): folder_items_by_id = self._hierarchy_model.get_folder_items_by_id( project_name, folder_ids From 9045c7422f6581b3d119d5f937fa2619306f3b98 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:17:39 +0100 Subject: [PATCH 415/463] Change label without task > no task --- client/ayon_core/tools/loader/ui/tasks_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index 4841af75dd..df0c5afe1f 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -66,7 +66,7 @@ class LoaderTasksQtModel(TasksQtModel): def _get_no_tasks_item(self): if self._no_tasks_item is None: - item = QtGui.QStandardItem("< Without task >") + item = QtGui.QStandardItem("No task") icon = get_qt_icon({ "type": "material-symbols", "name": "indeterminate_check_box", From 8ee87c9d26996b9e5e5dcf84df351bc14b27d9b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:17:51 +0100 Subject: [PATCH 416/463] no task is always last --- client/ayon_core/tools/loader/ui/tasks_widget.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/loader/ui/tasks_widget.py b/client/ayon_core/tools/loader/ui/tasks_widget.py index df0c5afe1f..5779fc2a01 100644 --- a/client/ayon_core/tools/loader/ui/tasks_widget.py +++ b/client/ayon_core/tools/loader/ui/tasks_widget.py @@ -295,6 +295,15 @@ class LoaderTasksQtModel(TasksQtModel): return super().data(index, role) +class LoaderTasksProxyModel(RecursiveSortFilterProxyModel): + def lessThan(self, left, right): + if left.data(ITEM_ID_ROLE) == NO_TASKS_ID: + return False + if right.data(ITEM_ID_ROLE) == NO_TASKS_ID: + return True + return super().lessThan(left, right) + + class LoaderTasksWidget(QtWidgets.QWidget): refreshed = QtCore.Signal() @@ -307,7 +316,7 @@ class LoaderTasksWidget(QtWidgets.QWidget): ) tasks_model = LoaderTasksQtModel(controller) - tasks_proxy_model = RecursiveSortFilterProxyModel() + tasks_proxy_model = LoaderTasksProxyModel() tasks_proxy_model.setSourceModel(tasks_model) tasks_proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) From c6e5a8ec11ead0e051730ed868593cf413aac7ee Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:25:01 +0100 Subject: [PATCH 417/463] initial state of product name filtering are propagated --- client/ayon_core/tools/loader/ui/product_types_combo.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/ayon_core/tools/loader/ui/product_types_combo.py b/client/ayon_core/tools/loader/ui/product_types_combo.py index fe136ff04c..91fa52b0e9 100644 --- a/client/ayon_core/tools/loader/ui/product_types_combo.py +++ b/client/ayon_core/tools/loader/ui/product_types_combo.py @@ -16,6 +16,8 @@ ITEM_SUBTYPE_ROLE = QtCore.Qt.UserRole + 3 class ProductTypesQtModel(BaseQtModel): + refreshed = QtCore.Signal() + def __init__(self, controller): self._reset_filters_on_refresh = True self._refreshing = False @@ -38,6 +40,7 @@ class ProductTypesQtModel(BaseQtModel): self._reset_filters_on_refresh = False self._refreshing = False + self.refreshed.emit() def reset_product_types_filter_on_refresh(self): self._reset_filters_on_refresh = True @@ -120,6 +123,9 @@ class ProductTypesCombobox(CustomPaintMultiselectComboBox): model=model, parent=parent ) + + model.refreshed.connect(self._on_model_refresh) + self.set_placeholder_text("Product types filter...") self._model = model self._last_project_name = None @@ -141,6 +147,9 @@ class ProductTypesCombobox(CustomPaintMultiselectComboBox): def reset_product_types_filter_on_refresh(self): self._model.reset_product_types_filter_on_refresh() + def _on_model_refresh(self): + self.value_changed.emit() + def _on_product_type_filter_change(self): lines = ["Product types filter"] for item in self.get_value_info(): From d8c023d0a166b994d9ce0be1ec772b1247bbfffb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 24 Feb 2025 15:14:32 +0100 Subject: [PATCH 418/463] Removed frame recalculation Maybe temporary commit as this naive approach won't be possible. Existing code recalculated/renamed rendered files according to values in AYON DB. This limits explicit render of selected only frames. --- client/ayon_core/plugins/publish/integrate.py | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index e8fe09bab7..16122339f6 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -691,28 +691,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Use last frame for minimum padding # - that should cover both 'udim' and 'frame' minimum padding destination_padding = len(str(destination_indexes[-1])) - if not is_udim: - # Change padding for frames if template has defined higher - # padding. - template_padding = anatomy.templates_obj.frame_padding - if template_padding > destination_padding: - destination_padding = template_padding - - # If the representation has `frameStart` set it renumbers the - # frame indices of the published collection. It will start from - # that `frameStart` index instead. Thus if that frame start - # differs from the collection we want to shift the destination - # frame indices from the source collection. - # In case source are published in place we need to - # skip renumbering - repre_frame_start = repre.get("frameStart") - if repre_frame_start is not None: - index_frame_start = int(repre_frame_start) - # Shift destination sequence to the start frame - destination_indexes = [ - index_frame_start + idx - for idx in range(len(destination_indexes)) - ] # To construct the destination template with anatomy we require # a Frame or UDIM tile set for the template data. We use the first From 95b7c61fe20ae33c78be5b90f302a8fd9529b88b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 25 Feb 2025 09:13:33 +0000 Subject: [PATCH 419/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 7f3228e769..8aff30c508 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.2+dev" +__version__ = "1.1.3" diff --git a/package.py b/package.py index 89276751b0..4f856c53c0 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.2+dev" +version = "1.1.3" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 98692d396f..48c3479a70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.2+dev" +version = "1.1.3" description = "" authors = ["Ynput Team "] readme = "README.md" From eec161ed15e451245438023c069840c1f71de43c Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 25 Feb 2025 09:14:21 +0000 Subject: [PATCH 420/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 8aff30c508..e533e08fe4 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.3" +__version__ = "1.1.3+dev" diff --git a/package.py b/package.py index 4f856c53c0..02e2f25384 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.3" +version = "1.1.3+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 48c3479a70..f065ca0c39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.3" +version = "1.1.3+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From df95e252fa18b9e3d068357506c73023c6373a2d Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Wed, 26 Feb 2025 11:09:25 +0100 Subject: [PATCH 421/463] Add SXR support for image extension. --- client/ayon_core/lib/transcoding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index e9750864ac..1fda014bd8 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -53,7 +53,7 @@ IMAGE_EXTENSIONS = { ".kra", ".logluv", ".mng", ".miff", ".nrrd", ".ora", ".pam", ".pbm", ".pgm", ".ppm", ".pnm", ".pcx", ".pgf", ".pictor", ".png", ".psd", ".psb", ".psp", ".qtvr", - ".ras", ".rgbe", ".sgi", ".tga", + ".ras", ".rgbe", ".sgi", ".sxr", ".tga", ".tif", ".tiff", ".tiff/ep", ".tiff/it", ".ufo", ".ufp", ".wbmp", ".webp", ".xr", ".xt", ".xbm", ".xcf", ".xpm", ".xwd" } From 85c33580f1258b9c80ec09d5ec39a4c22ea7d193 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 26 Feb 2025 13:02:28 +0100 Subject: [PATCH 422/463] Revert "Removed frame recalculation" This reverts commit d8c023d0a166b994d9ce0be1ec772b1247bbfffb. --- client/ayon_core/plugins/publish/integrate.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 16122339f6..e8fe09bab7 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -691,6 +691,28 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Use last frame for minimum padding # - that should cover both 'udim' and 'frame' minimum padding destination_padding = len(str(destination_indexes[-1])) + if not is_udim: + # Change padding for frames if template has defined higher + # padding. + template_padding = anatomy.templates_obj.frame_padding + if template_padding > destination_padding: + destination_padding = template_padding + + # If the representation has `frameStart` set it renumbers the + # frame indices of the published collection. It will start from + # that `frameStart` index instead. Thus if that frame start + # differs from the collection we want to shift the destination + # frame indices from the source collection. + # In case source are published in place we need to + # skip renumbering + repre_frame_start = repre.get("frameStart") + if repre_frame_start is not None: + index_frame_start = int(repre_frame_start) + # Shift destination sequence to the start frame + destination_indexes = [ + index_frame_start + idx + for idx in range(len(destination_indexes)) + ] # To construct the destination template with anatomy we require # a Frame or UDIM tile set for the template data. We use the first From 080e86ee6eb731281901b69d2cd78fa709d27f21 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 26 Feb 2025 13:11:04 +0100 Subject: [PATCH 423/463] Do frame recalculation only for not explicit_frames `explicit_frames` denotes that artist wants to render only specific frames. Could be set in Publisher UI. --- client/ayon_core/plugins/publish/integrate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index e8fe09bab7..51d6333f27 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -706,7 +706,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # In case source are published in place we need to # skip renumbering repre_frame_start = repre.get("frameStart") - if repre_frame_start is not None: + explicit_frames = instance.data.get("explicit_frames", False) + if not explicit_frames and repre_frame_start is not None: index_frame_start = int(repre_frame_start) # Shift destination sequence to the start frame destination_indexes = [ From 16e817f5d510034393ade00e96e4a45fd5dc7f48 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 26 Feb 2025 14:19:03 +0100 Subject: [PATCH 424/463] Fix propagation of explicit_frames --- client/ayon_core/pipeline/farm/pyblish_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e48d99602e..c5a70dc022 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -247,7 +247,8 @@ def create_skeleton_instance( "useSequenceForReview": data.get("useSequenceForReview", True), # map inputVersions `ObjectId` -> `str` so json supports it "inputVersions": list(map(str, data.get("inputVersions", []))), - "colorspace": data.get("colorspace") + "colorspace": data.get("colorspace"), + "explicit_frames": data.get("explicit_frames") } if data.get("renderlayer"): From d519d284a4f35b593ac38352e0b3220f7e891a78 Mon Sep 17 00:00:00 2001 From: "robin@ynput.io" Date: Thu, 27 Feb 2025 09:54:53 +0100 Subject: [PATCH 425/463] Change icon. --- .github/workflows/pr_unittests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_unittests.yaml b/.github/workflows/pr_unittests.yaml index 9dcb1f301f..811843d5e3 100644 --- a/.github/workflows/pr_unittests.yaml +++ b/.github/workflows/pr_unittests.yaml @@ -1,4 +1,4 @@ -name: 💯 Run Unit Tests +name: 🧐 Run Unit Tests on: push: From 594f1f1abb319434ede99e729be764ed2c55bd72 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Feb 2025 11:56:11 +0100 Subject: [PATCH 426/463] Fix using explicit frames in Maya AOVs --- .../pipeline/farm/pyblish_functions.py | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c5a70dc022..e9a9906d48 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -338,7 +338,7 @@ def prepare_representations( log = Logger.get_logger("farm_publishing") if frames_to_render is not None: - frames_to_render = _get_real_frames_to_render(frames_to_render) + frames_to_render = get_real_frames_to_render(frames_to_render) else: # Backwards compatibility for older logic frame_start = int(skeleton_data.get("frameStartHandle")) @@ -476,7 +476,7 @@ def prepare_representations( return representations -def _get_real_frames_to_render(frames): +def get_real_frames_to_render(frames): """Returns list of frames that should be rendered. Artists could want to selectively render only particular frames @@ -531,9 +531,14 @@ def _get_real_files_to_render(collection, frames_to_render): return [os.path.basename(file_url) for file_url in real_full_paths] -def create_instances_for_aov(instance, skeleton, aov_filter, - skip_integration_repre_list, - do_not_add_review): +def create_instances_for_aov( + instance, + skeleton, + aov_filter, + skip_integration_repre_list, + do_not_add_review, + frames_to_render=None +): """Create instances from AOVs. This will create new pyblish.api.Instances by going over expected @@ -591,7 +596,8 @@ def create_instances_for_aov(instance, skeleton, aov_filter, aov_filter, additional_color_data, skip_integration_repre_list, - do_not_add_review + do_not_add_review, + frames_to_render ) @@ -720,8 +726,15 @@ def get_product_name_and_group_from_template( return resulting_product_name, resulting_group_name -def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, - skip_integration_repre_list, do_not_add_review): +def _create_instances_for_aov( + instance, + skeleton, + aov_filter, + additional_data, + skip_integration_repre_list, + do_not_add_review, + frames_to_render=None +): """Create instance for each AOV found. This will create new instance for every AOV it can detect in expected @@ -735,7 +748,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, skip_integration_repre_list (list): list of extensions that shouldn't be published do_not_add_review (bool): explicitly disable review - + frames_to_render (str): implicit or explicit range of frames to render + this value is sent to Deadline in JobInfo.Frames Returns: list of instances @@ -755,10 +769,26 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, # go through AOVs in expected files for aov, files in expected_files[0].items(): collected_files = _collect_expected_files_for_aov(files) + staging_dir = ( + os.path.dirname(collected_files[0]) + if isinstance(collected_files, (list, tuple)) + else os.path.dirname(collected_files) + ) + + if frames_to_render is not None: + frames_to_render = get_real_frames_to_render(frames_to_render) + collections, _ = clique.assemble(collected_files) + collected_files = _get_real_files_to_render( + collections[0], frames_to_render) + else: + frame_start = int(skeleton.get("frameStartHandle")) + frame_end = int(skeleton.get("frameEndHandle")) + frames_to_render = list(range(frame_start, frame_end + 1)) - expected_filepath = collected_files if isinstance(collected_files, (list, tuple)): - expected_filepath = collected_files[0] + expected_filepath = os.path.join(staging_dir, collected_files[0]) + else: + expected_filepath = os.path.join(staging_dir, expected_filepath) dynamic_data = { "aov": aov, @@ -814,10 +844,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, dynamic_data=dynamic_data ) - staging = os.path.dirname(expected_filepath) - try: - staging = remap_source(staging, anatomy) + staging = remap_source(staging_dir, anatomy) except ValueError as e: log.warning(e) @@ -882,8 +910,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, "name": ext, "ext": ext, "files": collected_files, - "frameStart": int(skeleton["frameStartHandle"]), - "frameEnd": int(skeleton["frameEndHandle"]), + "frameStart": frames_to_render[0], + "frameEnd": frames_to_render[-1], # If expectedFile are absolute, we need only filenames "stagingDir": staging, "fps": new_instance.get("fps"), From 86a9613fdb849971f1f9fb2ddf16b3e28de3eca4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Feb 2025 13:06:45 +0100 Subject: [PATCH 427/463] Fix get real frames to include steps --- .../pipeline/farm/pyblish_functions.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e9a9906d48..81e10abeff 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -480,13 +480,27 @@ def get_real_frames_to_render(frames): """Returns list of frames that should be rendered. Artists could want to selectively render only particular frames + Handles formats as: + 1001 + 1002,1004 + 1003-1005 + 1001-1100x5 """ + pattern = r'(?:x|step|by|every)?(\d+)$' + frames_to_render = [] + step = 1 for frame in frames.split(","): if "-" in frame: - splitted = frame.split("-") + frame_start, frame_end = frame.split("-") + match = re.findall(pattern, frame_end) + if match: + step = int(match[0]) + frame_end = re.sub(pattern, "", frame_end) + frames_to_render.extend( - range(int(splitted[0]), int(splitted[1])+1)) + range(int(frame_start), int(frame_end) + 1, step) + ) else: frames_to_render.append(int(frame)) frames_to_render.sort() From 8347bad343e0c1053b22472af7f29b478f4917b3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Feb 2025 13:08:25 +0100 Subject: [PATCH 428/463] Fix do frame recalculate only on lists --- client/ayon_core/pipeline/farm/pyblish_functions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 81e10abeff..e484232927 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -789,7 +789,10 @@ def _create_instances_for_aov( else os.path.dirname(collected_files) ) - if frames_to_render is not None: + if ( + frames_to_render is not None + and isinstance(collected_files, (list, tuple)) # not single file + ): frames_to_render = get_real_frames_to_render(frames_to_render) collections, _ = clique.assemble(collected_files) collected_files = _get_real_files_to_render( From 1455ebb54e4b7e6d20e2e059f78aa711387f9fe2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Feb 2025 13:08:43 +0100 Subject: [PATCH 429/463] Fix wrong variable --- client/ayon_core/pipeline/farm/pyblish_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index e484232927..c64bf91371 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -805,7 +805,8 @@ def _create_instances_for_aov( if isinstance(collected_files, (list, tuple)): expected_filepath = os.path.join(staging_dir, collected_files[0]) else: - expected_filepath = os.path.join(staging_dir, expected_filepath) + expected_filepath = os.path.join(staging_dir, collected_files) + dynamic_data = { "aov": aov, From 07674c304031ec60ebea391221145467d393d31e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Feb 2025 13:09:05 +0100 Subject: [PATCH 430/463] Fix against local variable issues --- client/ayon_core/pipeline/farm/pyblish_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c64bf91371..f67eb1dd04 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -863,7 +863,7 @@ def _create_instances_for_aov( ) try: - staging = remap_source(staging_dir, anatomy) + staging_dir = remap_source(staging_dir, anatomy) except ValueError as e: log.warning(e) @@ -931,7 +931,7 @@ def _create_instances_for_aov( "frameStart": frames_to_render[0], "frameEnd": frames_to_render[-1], # If expectedFile are absolute, we need only filenames - "stagingDir": staging, + "stagingDir": staging_dir, "fps": new_instance.get("fps"), "tags": ["review"] if preview else [], "colorspaceData": { From ab3fdea704181a07c0f5da01c83cd7aa47b193ad Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 27 Feb 2025 13:17:02 +0100 Subject: [PATCH 431/463] Renamed new variable to hasExplicitFrames --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- client/ayon_core/plugins/publish/integrate.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index f67eb1dd04..5776e42ff6 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -248,7 +248,7 @@ def create_skeleton_instance( # map inputVersions `ObjectId` -> `str` so json supports it "inputVersions": list(map(str, data.get("inputVersions", []))), "colorspace": data.get("colorspace"), - "explicit_frames": data.get("explicit_frames") + "hasExplicitFrames": data.get("hasExplicitFrames") } if data.get("renderlayer"): diff --git a/client/ayon_core/plugins/publish/integrate.py b/client/ayon_core/plugins/publish/integrate.py index 51d6333f27..ae043a10a9 100644 --- a/client/ayon_core/plugins/publish/integrate.py +++ b/client/ayon_core/plugins/publish/integrate.py @@ -706,7 +706,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # In case source are published in place we need to # skip renumbering repre_frame_start = repre.get("frameStart") - explicit_frames = instance.data.get("explicit_frames", False) + explicit_frames = instance.data.get("hasExplicitFrames", False) if not explicit_frames and repre_frame_start is not None: index_frame_start = int(repre_frame_start) # Shift destination sequence to the start frame From d150377092aec9997a33c2a27504b16902495113 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 28 Feb 2025 10:08:21 +0100 Subject: [PATCH 432/463] Fix gaps in color transcode Create sequence only if no gaps. --- client/ayon_core/plugins/publish/extract_color_transcode.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 3c11a016ec..22997dc986 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -280,6 +280,10 @@ class ExtractOIIOTranscode(publish.Extractor): collection = collections[0] frames = list(collection.indexes) + real_range = list(range(frames[0], frames[-1] + 1)) + if set(frames) != set(real_range): # check for gaps + return files_to_convert + frame_str = "{}-{}#".format(frames[0], frames[-1]) file_name = "{}{}{}".format(collection.head, frame_str, collection.tail) From 0bdc2bd29d80e0d49ed50bf06b2989f50604d770 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 28 Feb 2025 10:40:30 +0100 Subject: [PATCH 433/463] Fix pattern for occurence of : --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 5776e42ff6..c0285d0446 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -486,7 +486,7 @@ def get_real_frames_to_render(frames): 1003-1005 1001-1100x5 """ - pattern = r'(?:x|step|by|every)?(\d+)$' + pattern = r"(?:|step|by|every|:?)(\d+)$" frames_to_render = [] step = 1 From af2f210e7c08d15919c6efc75a1b66d424ad3f2e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 28 Feb 2025 11:00:27 +0100 Subject: [PATCH 434/463] Fix pattern for occurence of : --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c0285d0446..4795eaac47 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -486,7 +486,7 @@ def get_real_frames_to_render(frames): 1003-1005 1001-1100x5 """ - pattern = r"(?:|step|by|every|:?)(\d+)$" + pattern = r"(?:step|by|every|x|:)(\d+)$" frames_to_render = [] step = 1 From 1ec9106f0124d6d69ed7a90429d791b5ad46267d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 4 Mar 2025 16:06:07 +0100 Subject: [PATCH 435/463] Support `list[str]` for leaf entries in `core/project_folder_structure` settings to define folder names instead of requiring dicts with empty values --- client/ayon_core/pipeline/project_folders.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 902b969457..df4353c503 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -82,6 +82,14 @@ def create_project_folders(project_name, basic_paths=None): def _list_path_items(folder_structure): output = [] + + # Allow leaf folders of the `project_folder_structure` to use a list of + # strings instead of a dictionary of keys with empty values. + if isinstance(folder_structure, list): + assert all(isinstance(item, str) for item in folder_structure) + return [folder_structure] + + # Process key, value as key for folder names and value its subfolders for key, value in folder_structure.items(): if not value: output.append(key) From b9adc29be190e9c2ecada1f64278669c53cf38ed Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 4 Mar 2025 16:06:45 +0100 Subject: [PATCH 436/463] Add some type hints --- client/ayon_core/pipeline/project_folders.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index df4353c503..a6f903701f 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -1,6 +1,7 @@ import os import re import json +from typing import Dict, Any, List, Union from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -9,7 +10,7 @@ from .anatomy import Anatomy from .template_data import get_project_template_data -def concatenate_splitted_paths(split_paths, anatomy): +def concatenate_splitted_paths(split_paths, anatomy: Anatomy): log = Logger.get_logger("concatenate_splitted_paths") pattern_array = re.compile(r"\[.*\]") output = [] @@ -47,7 +48,7 @@ def concatenate_splitted_paths(split_paths, anatomy): return output -def fill_paths(path_list, anatomy): +def fill_paths(path_list: List[str], anatomy: Anatomy): format_data = get_project_template_data(project_name=anatomy.project_name) format_data["root"] = anatomy.roots filled_paths = [] @@ -59,7 +60,7 @@ def fill_paths(path_list, anatomy): return filled_paths -def create_project_folders(project_name, basic_paths=None): +def create_project_folders(project_name: str, basic_paths=None): log = Logger.get_logger("create_project_folders") anatomy = Anatomy(project_name) if basic_paths is None: @@ -80,7 +81,8 @@ def create_project_folders(project_name, basic_paths=None): os.makedirs(path) -def _list_path_items(folder_structure): +def _list_path_items( + folder_structure: Union[Dict[str, Any], List[str]]): output = [] # Allow leaf folders of the `project_folder_structure` to use a list of @@ -107,7 +109,7 @@ def _list_path_items(folder_structure): return output -def get_project_basic_paths(project_name): +def get_project_basic_paths(project_name: str): project_settings = get_project_settings(project_name) folder_structure = ( project_settings["core"]["project_folder_structure"] From 8bf17b8e52434ffaa8dfa84c3f622af1281f8899 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 4 Mar 2025 20:15:25 +0200 Subject: [PATCH 437/463] remove usage of a deprecated tag: `thumb-nuke` --- client/ayon_core/plugins/publish/extract_thumbnail.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index bd2f7eb0ae..da429c1cd2 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -343,8 +343,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # to be published locally continue - valid = "review" in tags or "thumb-nuke" in tags - if not valid: + if "review" not in tags: continue if not repre.get("files"): From 1f415d66e3fb3461ab802be275373aaa4437767a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 00:51:46 +0100 Subject: [PATCH 438/463] Allow setting the default USD Contribution enabled state per profile in settings --- .../publish/extract_usd_layer_contributions.py | 3 ++- server/settings/publish_plugins.py | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py index a2698b03de..ec1fddc6b1 100644 --- a/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py +++ b/client/ayon_core/plugins/publish/extract_usd_layer_contributions.py @@ -479,6 +479,7 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, profile = {} # Define defaults + default_enabled = profile.get("contribution_enabled", True) default_contribution_layer = profile.get( "contribution_layer", None) default_apply_as_variant = profile.get( @@ -513,7 +514,7 @@ class CollectUSDLayerContributions(pyblish.api.InstancePlugin, "In both cases the USD data itself is free to have " "references and sublayers of its own." ), - default=True), + default=default_enabled), TextDef("contribution_target_product", label="Target product", tooltip=( diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index d10a6b7507..c9c66e65d9 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -91,6 +91,15 @@ class CollectUSDLayerContributionsProfileModel(BaseSettingsModel): " creating from within that task type." ), ) + contribution_enabled: bool = SettingsField( + True, + title="Contribution Enabled (default)", + description=( + "The default state for USD Contribution being marked enabled or" + " disabled for this profile." + ), + section="Instance attribute defaults", + ) contribution_layer: str = SettingsField( "", title="Contribution Department Layer", @@ -99,7 +108,6 @@ class CollectUSDLayerContributionsProfileModel(BaseSettingsModel): " matching this profile. The layer name should be in the" " 'Department Layer Orders' list to get a sensible order." ), - section="Instance attribute defaults", ) contribution_apply_as_variant: bool = SettingsField( True, @@ -1082,6 +1090,7 @@ DEFAULT_PUBLISH_VALUES = { { "product_types": ["model"], "task_types": [], + "contribution_enabled": True, "contribution_layer": "model", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" @@ -1089,6 +1098,7 @@ DEFAULT_PUBLISH_VALUES = { { "product_types": ["look"], "task_types": [], + "contribution_enabled": True, "contribution_layer": "look", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" @@ -1096,6 +1106,7 @@ DEFAULT_PUBLISH_VALUES = { { "product_types": ["groom"], "task_types": [], + "contribution_enabled": True, "contribution_layer": "groom", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" @@ -1103,6 +1114,7 @@ DEFAULT_PUBLISH_VALUES = { { "product_types": ["rig"], "task_types": [], + "contribution_enabled": True, "contribution_layer": "rig", "contribution_apply_as_variant": True, "contribution_target_product": "usdAsset" @@ -1110,6 +1122,7 @@ DEFAULT_PUBLISH_VALUES = { { "product_types": ["usd"], "task_types": [], + "contribution_enabled": True, "contribution_layer": "assembly", "contribution_apply_as_variant": False, "contribution_target_product": "usdShot" From 7baf208c61f992a30de62f29a3aef484207ac542 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 14:51:12 +0100 Subject: [PATCH 439/463] Fix `List[str]` entries --- client/ayon_core/pipeline/project_folders.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index a6f903701f..37197495f9 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -89,7 +89,7 @@ def _list_path_items( # strings instead of a dictionary of keys with empty values. if isinstance(folder_structure, list): assert all(isinstance(item, str) for item in folder_structure) - return [folder_structure] + return [[path] for path in folder_structure] # Process key, value as key for folder names and value its subfolders for key, value in folder_structure.items(): @@ -119,4 +119,6 @@ def get_project_basic_paths(project_name: str): if isinstance(folder_structure, str): folder_structure = json.loads(folder_structure) - return _list_path_items(folder_structure) + result = _list_path_items(folder_structure) + print(result) + return [] From 1b4f6bea20b2c6e7abac4eb5b57b9377ae78db90 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Mar 2025 15:05:50 +0100 Subject: [PATCH 440/463] Revert debug code --- client/ayon_core/pipeline/project_folders.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 37197495f9..570442ff0c 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -119,6 +119,4 @@ def get_project_basic_paths(project_name: str): if isinstance(folder_structure, str): folder_structure = json.loads(folder_structure) - result = _list_path_items(folder_structure) - print(result) - return [] + return _list_path_items(folder_structure) From 380310b017d3e9df0c50cdbd4b416f02016db01a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 10 Mar 2025 09:51:46 +0100 Subject: [PATCH 441/463] Removed optionality Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 4795eaac47..80048ae0ee 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -747,7 +747,7 @@ def _create_instances_for_aov( additional_data, skip_integration_repre_list, do_not_add_review, - frames_to_render=None + frames_to_render ): """Create instance for each AOV found. From bafd3dcf8f787bbf694d02310811837f7cd589fc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 10 Mar 2025 09:55:13 +0100 Subject: [PATCH 442/463] Update name of pattern --- client/ayon_core/pipeline/farm/pyblish_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 80048ae0ee..037e6e4c42 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -486,17 +486,17 @@ def get_real_frames_to_render(frames): 1003-1005 1001-1100x5 """ - pattern = r"(?:step|by|every|x|:)(\d+)$" + step_pattern = re.compile(r"(?:step|by|every|x|:)(\d+)$") frames_to_render = [] step = 1 for frame in frames.split(","): if "-" in frame: frame_start, frame_end = frame.split("-") - match = re.findall(pattern, frame_end) + match = step_pattern.findall(frame_end) if match: step = int(match[0]) - frame_end = re.sub(pattern, "", frame_end) + frame_end = re.sub(step_pattern, "", frame_end) frames_to_render.extend( range(int(frame_start), int(frame_end) + 1, step) From 4ea2ed3dc1c18997013ab2b56f03ef2597288d93 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 10 Mar 2025 10:18:21 +0100 Subject: [PATCH 443/463] Updated use of first_filepath --- .../pipeline/farm/pyblish_functions.py | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 037e6e4c42..b47b8eede0 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -393,7 +393,7 @@ def prepare_representations( rep = { "name": ext, "ext": ext, - "files": files, + "files": [os.path.basename(file_path) for file_path in files], "frameStart": frame_start, "frameEnd": frame_end, # If expectedFile are absolute, we need only filenames @@ -521,7 +521,7 @@ def _get_real_files_to_render(collection, frames_to_render): collection (clique.Collection): absolute paths frames_to_render (list[int]): of int 1001 Returns: - (list[str]) + (list[str]): absolut paths of files to be rendered Example: -------- @@ -541,8 +541,7 @@ def _get_real_files_to_render(collection, frames_to_render): collection.padding, indexes=included_frames ) - real_full_paths = list(real_collection) - return [os.path.basename(file_url) for file_url in real_full_paths] + return list(real_collection) def create_instances_for_aov( @@ -783,11 +782,10 @@ def _create_instances_for_aov( # go through AOVs in expected files for aov, files in expected_files[0].items(): collected_files = _collect_expected_files_for_aov(files) - staging_dir = ( - os.path.dirname(collected_files[0]) - if isinstance(collected_files, (list, tuple)) - else os.path.dirname(collected_files) - ) + first_filepath = collected_files + if isinstance(first_filepath, (list, tuple)): + first_filepath = first_filepath[0] + staging_dir = os.path.dirname(first_filepath) if ( frames_to_render is not None @@ -802,12 +800,6 @@ def _create_instances_for_aov( frame_end = int(skeleton.get("frameEndHandle")) frames_to_render = list(range(frame_start, frame_end + 1)) - if isinstance(collected_files, (list, tuple)): - expected_filepath = os.path.join(staging_dir, collected_files[0]) - else: - expected_filepath = os.path.join(staging_dir, collected_files) - - dynamic_data = { "aov": aov, "renderlayer": instance.data.get("renderlayer"), @@ -817,7 +809,7 @@ def _create_instances_for_aov( # TODO: this must be changed to be more robust. Any coincidence # of camera name in the file path will be considered as # camera name. This is not correct. - camera = [cam for cam in cameras if cam in expected_filepath] + camera = [cam for cam in cameras if cam in first_filepath] # Is there just one camera matching? # TODO: this is not true, we can have multiple cameras in the scene @@ -871,7 +863,7 @@ def _create_instances_for_aov( app = os.environ.get("AYON_HOST_NAME", "") - render_file_name = os.path.basename(expected_filepath) + render_file_name = os.path.basename(first_filepath) aov_patterns = aov_filter From ab682da2b83b1e405ed776d6b18ca8234e83d302 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 11:51:29 +0100 Subject: [PATCH 444/463] Raise ValueError instead of using assertion --- client/ayon_core/pipeline/project_folders.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 570442ff0c..7386382d56 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -88,7 +88,9 @@ def _list_path_items( # Allow leaf folders of the `project_folder_structure` to use a list of # strings instead of a dictionary of keys with empty values. if isinstance(folder_structure, list): - assert all(isinstance(item, str) for item in folder_structure) + if not all(isinstance(item, str) for item in folder_structure): + raise ValueError( + f"List items must all be strings. Got: {folder_structure}") return [[path] for path in folder_structure] # Process key, value as key for folder names and value its subfolders From eb17eebb99460963ef4888c039975ede350596b5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 12:28:49 +0100 Subject: [PATCH 445/463] Update client/ayon_core/pipeline/project_folders.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/pipeline/project_folders.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 7386382d56..1e4d807c49 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -1,3 +1,4 @@ +from __future__ import annotations import os import re import json From 8008ab02b543b2d42a0dfbb91bd6afca315d28cf Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 10 Mar 2025 12:29:45 +0100 Subject: [PATCH 446/463] Use future annotations style --- client/ayon_core/pipeline/project_folders.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/project_folders.py b/client/ayon_core/pipeline/project_folders.py index 1e4d807c49..def2af9ba1 100644 --- a/client/ayon_core/pipeline/project_folders.py +++ b/client/ayon_core/pipeline/project_folders.py @@ -2,7 +2,7 @@ from __future__ import annotations import os import re import json -from typing import Dict, Any, List, Union +from typing import Any, Union from ayon_core.settings import get_project_settings from ayon_core.lib import Logger @@ -49,7 +49,7 @@ def concatenate_splitted_paths(split_paths, anatomy: Anatomy): return output -def fill_paths(path_list: List[str], anatomy: Anatomy): +def fill_paths(path_list: list[str], anatomy: Anatomy): format_data = get_project_template_data(project_name=anatomy.project_name) format_data["root"] = anatomy.roots filled_paths = [] @@ -83,7 +83,7 @@ def create_project_folders(project_name: str, basic_paths=None): def _list_path_items( - folder_structure: Union[Dict[str, Any], List[str]]): + folder_structure: Union[dict[str, Any], list[str]]): output = [] # Allow leaf folders of the `project_folder_structure` to use a list of From 428175ee00051fc0eec0dbc00e4ac271c59c5d17 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:14:13 +0100 Subject: [PATCH 447/463] rename '_get_real_files_to_render' to '_get_real_paths_to_render' --- client/ayon_core/pipeline/farm/pyblish_functions.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index b47b8eede0..202d84f02c 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -387,13 +387,18 @@ def prepare_representations( frame_start -= 1 frames_to_render.insert(0, frame_start) - files = _get_real_files_to_render(collection, frames_to_render) + filenames = [ + os.path.basename(filepath) + for filepath in _get_real_paths_to_render( + collection, frames_to_render + ) + ] # explicitly disable review by user preview = preview and not do_not_add_review rep = { "name": ext, "ext": ext, - "files": [os.path.basename(file_path) for file_path in files], + "files": filenames, "frameStart": frame_start, "frameEnd": frame_end, # If expectedFile are absolute, we need only filenames @@ -507,7 +512,7 @@ def get_real_frames_to_render(frames): return frames_to_render -def _get_real_files_to_render(collection, frames_to_render): +def _get_real_paths_to_render(collection, frames_to_render): """Filter files with frames that should be really rendered. 'expected_files' are collected from DCC based on timeline setting. This is @@ -793,7 +798,7 @@ def _create_instances_for_aov( ): frames_to_render = get_real_frames_to_render(frames_to_render) collections, _ = clique.assemble(collected_files) - collected_files = _get_real_files_to_render( + collected_files = _get_real_paths_to_render( collections[0], frames_to_render) else: frame_start = int(skeleton.get("frameStartHandle")) From 2f47a6d35ae5743c35d1a7b20b2bb58e5ce3df0e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:14:37 +0100 Subject: [PATCH 448/463] remove out of context comment --- client/ayon_core/pipeline/farm/pyblish_functions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 202d84f02c..a9dcbcba70 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -399,10 +399,9 @@ def prepare_representations( "name": ext, "ext": ext, "files": filenames, + "stagingDir": staging, "frameStart": frame_start, "frameEnd": frame_end, - # If expectedFile are absolute, we need only filenames - "stagingDir": staging, "fps": skeleton_data.get("fps"), "tags": ["review"] if preview else [], } From 1255aa2776f6ed4a8956995d285bd28793129107 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:23:01 +0100 Subject: [PATCH 449/463] added typehints and better examples --- .../pipeline/farm/pyblish_functions.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index a9dcbcba70..36a231d45f 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -1,3 +1,4 @@ +from __future__ import annotations import copy import os import re @@ -480,15 +481,23 @@ def prepare_representations( return representations -def get_real_frames_to_render(frames): +def get_real_frames_to_render(frames: str) -> list[int]: """Returns list of frames that should be rendered. Artists could want to selectively render only particular frames + Handles formats as: - 1001 - 1002,1004 - 1003-1005 - 1001-1100x5 + - '1001' > [1001] + - '1002,1004' > [1002, 1004] + - '1003-1005' > [1003, 1004, 1005] + - '1001-1021x5' > [1001, 1006, 1011, 1016, 2021] + + Args: + frames (str): string with frames to render + + Returns: + list[int]: List of frames. + """ step_pattern = re.compile(r"(?:step|by|every|x|:)(\d+)$") From 369383045f6dc2e48b187d720d874dd512730e62 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:47:47 +0100 Subject: [PATCH 450/463] rename 'get_real_frames_to_render' to 'convert_frames_str_to_list' --- .../pipeline/farm/pyblish_functions.py | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 36a231d45f..cbc44e725e 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -339,7 +339,7 @@ def prepare_representations( log = Logger.get_logger("farm_publishing") if frames_to_render is not None: - frames_to_render = get_real_frames_to_render(frames_to_render) + frames_to_render = convert_frames_str_to_list(frames_to_render) else: # Backwards compatibility for older logic frame_start = int(skeleton_data.get("frameStartHandle")) @@ -481,19 +481,21 @@ def prepare_representations( return representations -def get_real_frames_to_render(frames: str) -> list[int]: - """Returns list of frames that should be rendered. - - Artists could want to selectively render only particular frames +def convert_frames_str_to_list(frames: str) -> list[int]: + """Convert frames definition string to frames. Handles formats as: - - '1001' > [1001] - - '1002,1004' > [1002, 1004] - - '1003-1005' > [1003, 1004, 1005] - - '1001-1021x5' > [1001, 1006, 1011, 1016, 2021] + >>> convert_frames_str_to_list('1001') + [1001] + >>> convert_frames_str_to_list('1002,1004') + [1002, 1004] + >>> convert_frames_str_to_list('1003-1005') + [1003, 1004, 1005] + >>> convert_frames_str_to_list('1001-1021x5') + [1001, 1006, 1011, 1016, 1021] Args: - frames (str): string with frames to render + frames (str): String with frames definition. Returns: list[int]: List of frames. @@ -501,7 +503,7 @@ def get_real_frames_to_render(frames: str) -> list[int]: """ step_pattern = re.compile(r"(?:step|by|every|x|:)(\d+)$") - frames_to_render = [] + output = [] step = 1 for frame in frames.split(","): if "-" in frame: @@ -511,13 +513,13 @@ def get_real_frames_to_render(frames: str) -> list[int]: step = int(match[0]) frame_end = re.sub(step_pattern, "", frame_end) - frames_to_render.extend( + output.extend( range(int(frame_start), int(frame_end) + 1, step) ) else: - frames_to_render.append(int(frame)) - frames_to_render.sort() - return frames_to_render + output.append(int(frame)) + output.sort() + return output def _get_real_paths_to_render(collection, frames_to_render): @@ -804,7 +806,7 @@ def _create_instances_for_aov( frames_to_render is not None and isinstance(collected_files, (list, tuple)) # not single file ): - frames_to_render = get_real_frames_to_render(frames_to_render) + frames_to_render = convert_frames_str_to_list(frames_to_render) collections, _ = clique.assemble(collected_files) collected_files = _get_real_paths_to_render( collections[0], frames_to_render) From 5a88698ce14bf87a6feca460724391effeab6600 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:10:02 +0100 Subject: [PATCH 451/463] rename '_get_real_paths_to_render' back to '_get_real_files_to_render' --- client/ayon_core/pipeline/farm/pyblish_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index cbc44e725e..30ca506879 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -390,7 +390,7 @@ def prepare_representations( filenames = [ os.path.basename(filepath) - for filepath in _get_real_paths_to_render( + for filepath in _get_real_files_to_render( collection, frames_to_render ) ] @@ -522,7 +522,7 @@ def convert_frames_str_to_list(frames: str) -> list[int]: return output -def _get_real_paths_to_render(collection, frames_to_render): +def _get_real_files_to_render(collection, frames_to_render): """Filter files with frames that should be really rendered. 'expected_files' are collected from DCC based on timeline setting. This is @@ -808,7 +808,7 @@ def _create_instances_for_aov( ): frames_to_render = convert_frames_str_to_list(frames_to_render) collections, _ = clique.assemble(collected_files) - collected_files = _get_real_paths_to_render( + collected_files = _get_real_files_to_render( collections[0], frames_to_render) else: frame_start = int(skeleton.get("frameStartHandle")) From 3853fab3f432c97cc0d8abd51fdd2cd03f57e10d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:10:28 +0100 Subject: [PATCH 452/463] fix return description --- client/ayon_core/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 30ca506879..407bab43b9 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -536,7 +536,7 @@ def _get_real_files_to_render(collection, frames_to_render): collection (clique.Collection): absolute paths frames_to_render (list[int]): of int 1001 Returns: - (list[str]): absolut paths of files to be rendered + list[str]: absolute paths of files to be rendered Example: -------- From 44ba00e22a3ab344a904d8164353e542335bace3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:10:35 +0100 Subject: [PATCH 453/463] better example --- .../pipeline/farm/pyblish_functions.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 407bab43b9..39b4bd528e 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -532,22 +532,23 @@ def _get_real_files_to_render(collection, frames_to_render): This range would override and filter previously prepared expected files from DCC. + Example: + >>> expected_files = clique.parse([ + >>> "foo_v01.0001.exr", + >>> "foo_v01.0002.exr", + >>> ]) + >>> frames_to_render = [1] + >>> _get_real_files_to_render(expected_files, frames_to_render) + ["foo_v01.0001.exr"] + Args: collection (clique.Collection): absolute paths frames_to_render (list[int]): of int 1001 + Returns: list[str]: absolute paths of files to be rendered - Example: - -------- - expectedFiles = [ - "foo_v01.0001.exr", - "foo_v01.0002.exr", - ] - frames_to_render = 1 - >> - ["foo_v01.0001.exr"] - only explicitly requested frame returned """ included_frames = set(collection.indexes).intersection(frames_to_render) real_collection = clique.Collection( From 74cb8574eedc9a8947f00048c14a402571bd1ba8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:11:32 +0100 Subject: [PATCH 454/463] fix typehint for 'frames_to_render' --- client/ayon_core/pipeline/farm/pyblish_functions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 39b4bd528e..0261a0c2b5 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -326,8 +326,8 @@ def prepare_representations( skip_integration_repre_list (list): exclude specific extensions, do_not_add_review (bool): explicitly skip review color_managed_plugin (publish.ColormanagedPyblishPluginMixin) - frames_to_render (str): implicit or explicit range of frames to render - this value is sent to Deadline in JobInfo.Frames + frames_to_render (str | None): implicit or explicit range of frames + to render this value is sent to Deadline in JobInfo.Frames Returns: list of representations @@ -579,6 +579,7 @@ def create_instances_for_aov( aov_filter (dict): AOV filter. skip_integration_repre_list (list): skip do_not_add_review (bool): Explicitly disable reviews + frames_to_render (str | None): Frames to render. Returns: list of pyblish.api.Instance: Instances created from @@ -777,8 +778,8 @@ def _create_instances_for_aov( skip_integration_repre_list (list): list of extensions that shouldn't be published do_not_add_review (bool): explicitly disable review - frames_to_render (str): implicit or explicit range of frames to render - this value is sent to Deadline in JobInfo.Frames + frames_to_render (str | None): implicit or explicit range of + frames to render this value is sent to Deadline in JobInfo.Frames Returns: list of instances From 987b8faf7b009c99dff4cbb8f4d7e4333b227f79 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 11 Mar 2025 16:07:36 +0100 Subject: [PATCH 455/463] Remove legacy `pyblish_pype` tool --- client/ayon_core/cli.py | 3 - .../ayon_core/tools/pyblish_pype/__init__.py | 13 - .../ayon_core/tools/pyblish_pype/__main__.py | 19 - client/ayon_core/tools/pyblish_pype/app.css | 539 ------- client/ayon_core/tools/pyblish_pype/app.py | 110 -- .../ayon_core/tools/pyblish_pype/awesome.py | 733 --------- .../ayon_core/tools/pyblish_pype/constants.py | 97 -- .../ayon_core/tools/pyblish_pype/control.py | 666 --------- .../ayon_core/tools/pyblish_pype/delegate.py | 540 ------- .../font/fontawesome/fontawesome-webfont.ttf | Bin 152364 -> 0 bytes .../pyblish_pype/font/opensans/LICENSE.txt | 202 --- .../font/opensans/OpenSans-Bold.ttf | Bin 224592 -> 0 bytes .../font/opensans/OpenSans-BoldItalic.ttf | Bin 213292 -> 0 bytes .../font/opensans/OpenSans-ExtraBold.ttf | Bin 222584 -> 0 bytes .../opensans/OpenSans-ExtraBoldItalic.ttf | Bin 213420 -> 0 bytes .../font/opensans/OpenSans-Italic.ttf | Bin 212896 -> 0 bytes .../font/opensans/OpenSans-Light.ttf | Bin 222412 -> 0 bytes .../font/opensans/OpenSans-LightItalic.ttf | Bin 213128 -> 0 bytes .../font/opensans/OpenSans-Regular.ttf | Bin 217360 -> 0 bytes .../font/opensans/OpenSans-Semibold.ttf | Bin 221328 -> 0 bytes .../font/opensans/OpenSans-SemiboldItalic.ttf | Bin 212820 -> 0 bytes .../tools/pyblish_pype/i18n/pyblish_lite.pro | 2 - .../tools/pyblish_pype/i18n/zh_CN.qm | Bin 1187 -> 0 bytes .../tools/pyblish_pype/i18n/zh_CN.ts | 96 -- .../tools/pyblish_pype/img/down_arrow.png | Bin 165 -> 0 bytes .../pyblish_pype/img/logo-extrasmall.png | Bin 2377 -> 0 bytes .../tools/pyblish_pype/img/tab-overview.png | Bin 207 -> 0 bytes .../tools/pyblish_pype/img/tab-terminal.png | Bin 197 -> 0 bytes client/ayon_core/tools/pyblish_pype/mock.py | 732 --------- client/ayon_core/tools/pyblish_pype/model.py | 1116 -------------- .../ayon_core/tools/pyblish_pype/settings.py | 30 - client/ayon_core/tools/pyblish_pype/util.py | 144 -- .../tools/pyblish_pype/vendor/__init__.py | 0 .../pyblish_pype/vendor/qtawesome/__init__.py | 39 - .../pyblish_pype/vendor/qtawesome/_version.py | 2 - .../vendor/qtawesome/animation.py | 41 - .../fonts/elusiveicons-webfont-charmap.json | 306 ---- .../qtawesome/fonts/elusiveicons-webfont.ttf | Bin 79556 -> 0 bytes .../fonts/fontawesome-webfont-charmap.json | 696 --------- .../qtawesome/fonts/fontawesome-webfont.ttf | Bin 142072 -> 0 bytes .../vendor/qtawesome/iconic_font.py | 286 ---- .../ayon_core/tools/pyblish_pype/version.py | 11 - client/ayon_core/tools/pyblish_pype/view.py | 334 ----- .../ayon_core/tools/pyblish_pype/widgets.py | 555 ------- client/ayon_core/tools/pyblish_pype/window.py | 1315 ----------------- 45 files changed, 8627 deletions(-) delete mode 100644 client/ayon_core/tools/pyblish_pype/__init__.py delete mode 100644 client/ayon_core/tools/pyblish_pype/__main__.py delete mode 100644 client/ayon_core/tools/pyblish_pype/app.css delete mode 100644 client/ayon_core/tools/pyblish_pype/app.py delete mode 100644 client/ayon_core/tools/pyblish_pype/awesome.py delete mode 100644 client/ayon_core/tools/pyblish_pype/constants.py delete mode 100644 client/ayon_core/tools/pyblish_pype/control.py delete mode 100644 client/ayon_core/tools/pyblish_pype/delegate.py delete mode 100644 client/ayon_core/tools/pyblish_pype/font/fontawesome/fontawesome-webfont.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Bold.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-BoldItalic.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBold.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBoldItalic.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Italic.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Light.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-LightItalic.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Regular.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Semibold.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-SemiboldItalic.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro delete mode 100644 client/ayon_core/tools/pyblish_pype/i18n/zh_CN.qm delete mode 100644 client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts delete mode 100644 client/ayon_core/tools/pyblish_pype/img/down_arrow.png delete mode 100644 client/ayon_core/tools/pyblish_pype/img/logo-extrasmall.png delete mode 100644 client/ayon_core/tools/pyblish_pype/img/tab-overview.png delete mode 100644 client/ayon_core/tools/pyblish_pype/img/tab-terminal.png delete mode 100644 client/ayon_core/tools/pyblish_pype/mock.py delete mode 100644 client/ayon_core/tools/pyblish_pype/model.py delete mode 100644 client/ayon_core/tools/pyblish_pype/settings.py delete mode 100644 client/ayon_core/tools/pyblish_pype/util.py delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/__init__.py delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont.ttf delete mode 100644 client/ayon_core/tools/pyblish_pype/vendor/qtawesome/iconic_font.py delete mode 100644 client/ayon_core/tools/pyblish_pype/version.py delete mode 100644 client/ayon_core/tools/pyblish_pype/view.py delete mode 100644 client/ayon_core/tools/pyblish_pype/widgets.py delete mode 100644 client/ayon_core/tools/pyblish_pype/window.py diff --git a/client/ayon_core/cli.py b/client/ayon_core/cli.py index d7cd3ba7f5..6f89a6d17d 100644 --- a/client/ayon_core/cli.py +++ b/client/ayon_core/cli.py @@ -252,7 +252,6 @@ def _set_global_environments() -> None: os.environ.update(env) # Hardcoded default values - os.environ["PYBLISH_GUI"] = "pyblish_pype" # Change scale factor only if is not set if "QT_AUTO_SCREEN_SCALE_FACTOR" not in os.environ: os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" @@ -290,8 +289,6 @@ def main(*args, **kwargs): split_paths = python_path.split(os.pathsep) additional_paths = [ - # add AYON tools for 'pyblish_pype' - os.path.join(AYON_CORE_ROOT, "tools"), # add common AYON vendor # (common for multiple Python interpreter versions) os.path.join(AYON_CORE_ROOT, "vendor", "python") diff --git a/client/ayon_core/tools/pyblish_pype/__init__.py b/client/ayon_core/tools/pyblish_pype/__init__.py deleted file mode 100644 index ef507005a5..0000000000 --- a/client/ayon_core/tools/pyblish_pype/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .version import version, version_info, __version__ - -# This must be run prior to importing the application, due to the -# application requiring a discovered copy of Qt bindings. - -from .app import show - -__all__ = [ - 'show', - 'version', - 'version_info', - '__version__' -] diff --git a/client/ayon_core/tools/pyblish_pype/__main__.py b/client/ayon_core/tools/pyblish_pype/__main__.py deleted file mode 100644 index 5fc1b44a35..0000000000 --- a/client/ayon_core/tools/pyblish_pype/__main__.py +++ /dev/null @@ -1,19 +0,0 @@ -from .app import show - - -if __name__ == '__main__': - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("--debug", action="store_true") - - args = parser.parse_args() - - if args.debug: - from . import mock - import pyblish.api - - for Plugin in mock.plugins: - pyblish.api.register_plugin(Plugin) - - show() diff --git a/client/ayon_core/tools/pyblish_pype/app.css b/client/ayon_core/tools/pyblish_pype/app.css deleted file mode 100644 index 33b6acbddb..0000000000 --- a/client/ayon_core/tools/pyblish_pype/app.css +++ /dev/null @@ -1,539 +0,0 @@ -/* Global CSS */ - -* { - outline: none; - color: #ddd; - font-family: "Open Sans"; - font-style: normal; -} - -/* General CSS */ - -QWidget { - background: #555; - background-position: center center; - background-repeat: no-repeat; - font-size: 12px; -} - -QMenu { - background-color: #555; /* sets background of the menu */ - border: 1px solid #222; -} - -QMenu::item { - /* sets background of menu item. set this to something non-transparent - if you want menu color and menu item color to be different */ - background-color: transparent; - padding: 5px; - padding-left: 30px; -} - -QMenu::item:selected { /* when user selects item using mouse or keyboard */ - background-color: #666; -} - -QDialog { - min-width: 300; - background: "#555"; -} - -QListView { - border: 0px; - background: "transparent" -} - -QTreeView { - border: 0px; - background: "transparent" -} - -QPushButton { - width: 27px; - height: 27px; - background: #555; - border: 1px solid #aaa; - border-radius: 4px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; - padding: 0px; -} - -QPushButton:pressed { - background: "#777"; -} - -QPushButton:hover { - color: white; - background: "#666"; -} - -QPushButton:disabled { - color: rgba(255, 255, 255, 50); -} - -QTextEdit, QLineEdit { - background: #555; - border: 1px solid #333; - font-size: 9pt; - color: #fff; -} - -QCheckBox { - min-width: 17px; - max-width: 17px; - border: 1px solid #222; - background: transparent; -} - -QCheckBox::indicator { - width: 15px; - height: 15px; - /*background: #444;*/ - background: transparent; - border: 1px solid #555; -} - -QCheckBox::indicator:checked { - background: #222; -} - -QComboBox { - background: #444; - color: #EEE; - font-size: 8pt; - border: 1px solid #333; - padding: 0px; -} - -QComboBox[combolist="true"]::drop-down { - background: transparent; -} - -QComboBox[combolist="true"]::down-arrow { - max-width: 0px; - width: 1px; -} - -QComboBox[combolist="true"] QAbstractItemView { - background: #555; -} - -QScrollBar:vertical { - border: none; - background: transparent; - width: 6px; - margin: 0; -} - -QScrollBar::handle:vertical { - background: #333; - border-radius: 3px; - min-height: 20px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { - height: 0px; -} - -QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical { - border: 1px solid #444; - width: 3px; - height: 3px; - background: white; -} - -QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; -} - -QToolTip { - color: #eee; - background-color: #555; - border: none; - padding: 5px; -} - -QLabel { - border-radius: 0px; -} - -QToolButton { - background-color: transparent; - margin: 0px; - padding: 0px; - border-radius: 0px; - border: none; -} - -/* Specific CSS */ -#PerspectiveToggleBtn { - border-bottom: 3px solid lightblue; - border-top: 0px; - border-radius: 0px; - border-right: 1px solid #232323; - border-left: 0px; - font-size: 26pt; - font-family: "FontAwesome"; -} - -#Terminal QComboBox::drop-down { - width: 60px; -} - -#Header { - background: #555; - border: 1px solid #444; - padding: 0px; - margin: 0px; -} - -#Header QRadioButton { - border: 3px solid "transparent"; - border-right: 1px solid #333; - left: 2px; -} - -#Header QRadioButton::indicator { - width: 65px; - height: 40px; - background-repeat: no-repeat; - background-position: center center; - image: none; -} - -#Header QRadioButton:hover { - background-color: rgba(255, 255, 255, 10); -} - -#Header QRadioButton:checked { - background-color: rgba(255, 255, 255, 20); - border-bottom: 3px solid "lightblue"; -} - -#Body { - padding: 0px; - border: 1px solid #333; - background: #444; -} - -#Body QWidget { - background: #444; -} - -#Header #TerminalTab { - background-image: url("img/tab-terminal.png"); -} - -#Header #OverviewTab { - background-image: url("img/tab-overview.png"); -} - -#ButtonWithMenu { - background: #555; - border: 1px solid #fff; - border-radius: 4px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; -} - -#ButtonWithMenu:pressed { - background: #777; -} - -#ButtonWithMenu:hover { - color: white; - background: #666; -} -#ButtonWithMenu:disabled { - background: #666; - color: #999; - border: 1px solid #999; -} - -#FooterSpacer, #FooterInfo, #HeaderSpacer { - background: transparent; -} - -#Footer { - background: #555; - min-height: 43px; -} - -#Footer[success="1"] { - background: #458056 -} - -#Footer[success="0"] { - background-color: #AA5050 -} - -#Footer QPushButton { - background: #555; - border: 1px solid #aaa; - border-radius: 4px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; - padding: 0px; -} - -#Footer QPushButton:pressed:hover { - color: #3784c5; - background: #444; -} - -#Footer QPushButton:hover { - background: #505050; - border: 2px solid #3784c5; -} - -#Footer QPushButton:disabled { - border: 1px solid #888; - background: #666; - color: #999; -} - -#ClosingPlaceholder { - background: rgba(0, 0, 0, 50); -} - -#CommentIntentWidget { - background: transparent; -} - -#CommentBox, #CommentPlaceholder { - font-family: "Open Sans"; - font-size: 8pt; - padding: 5px; - background: #444; -} - -#CommentBox { - selection-background-color: #222; -} - -#CommentBox:disabled, #CommentPlaceholder:disabled, #IntentBox:disabled { - background: #555; -} - -#CommentPlaceholder { - color: #888 -} - -#IntentBox { - background: #444; - font-size: 8pt; - padding: 5px; - min-width: 75px; - color: #EEE; -} - -#IntentBox::drop-down:button { - border: 0px; - background: transparent; -} - -#IntentBox::down-arrow { - image: url("/img/down_arrow.png"); -} - -#IntentBox::down-arrow:disabled { - image: url(); -} - -#TerminalView { - background-color: transparent; -} - -#TerminalView:item { - background-color: transparent; -} - -#TerminalView:hover { - background-color: transparent; -} - -#TerminalView:selected { - background-color: transparent; -} - -#TerminalView:item:hover { - color: #ffffff; -} - -#TerminalView:item:selected { - color: #eeeeee; -} - -#TerminalView QTextEdit { - padding:3px; - color: #aaa; - border-radius: 7px; - border-color: #222; - border-style: solid; - border-width: 2px; - background-color: #333; -} - -#TerminalView QTextEdit:hover { - background-color: #353535; -} - -#TerminalView QTextEdit:selected { - background-color: #303030; -} - -#ExpandableWidgetContent { - border: none; - background-color: #232323; - color:#eeeeee; -} - -#EllidableLabel { - font-size: 16pt; - font-weight: normal; -} - -#PerspectiveScrollContent { - border: 1px solid #333; - border-radius: 0px; -} - -#PerspectiveWidgetContent{ - padding: 0px; -} - -#PerspectiveLabel { - background-color: transparent; - border: none; -} - -#PerspectiveIndicator { - font-size: 16pt; - font-weight: normal; - padding: 5px; - background-color: #ffffff; - color: #333333; -} - -#PerspectiveIndicator[state="warning"] { - background-color: #ff9900; - color: #ffffff; -} - -#PerspectiveIndicator[state="active"] { - background-color: #99CEEE; - color: #ffffff; -} - -#PerspectiveIndicator[state="error"] { - background-color: #cc4a4a; - color: #ffffff; -} - -#PerspectiveIndicator[state="ok"] { - background-color: #69a567; - color: #ffffff; -} - -#ExpandableHeader { - background-color: transparent; - margin: 0px; - padding: 0px; - border-radius: 0px; - border: none; -} - -#ExpandableHeader QWidget { - color: #ddd; -} - -#ExpandableHeader QWidget:hover { - color: #fff; -} - -#TerminalFilterWidget QPushButton { - /* font: %(font_size_pt)spt; */ - font-family: "FontAwesome"; - text-align: center; - background-color: transparent; - border-width: 1px; - border-color: #777777; - border-style: none; - padding: 0px; - border-radius: 8px; -} -#TerminalFilterWidget QPushButton:hover { - background: #5f5f5f; - border-style: none; -} -#TerminalFilterWidget QPushButton:pressed { - background: #606060; - border-style: none; -} -#TerminalFilterWidget QPushButton:pressed:hover { - background: #626262; - border-style: none; -} - -#TerminalFilerBtn[type="info"]:checked {color: rgb(255, 255, 255);} -#TerminalFilerBtn[type="info"]:hover:pressed {color: rgba(255, 255, 255, 163);} -#TerminalFilerBtn[type="info"] {color: rgba(255, 255, 255, 63);} - -#TerminalFilerBtn[type="error"]:checked {color: rgb(255, 74, 74);} -#TerminalFilerBtn[type="error"]:hover:pressed {color: rgba(255, 74, 74, 163);} -#TerminalFilerBtn[type="error"] {color: rgba(255, 74, 74, 63);} - -#TerminalFilerBtn[type="log_debug"]:checked {color: rgb(255, 102, 232);} -#TerminalFilerBtn[type="log_debug"] {color: rgba(255, 102, 232, 63);} -#TerminalFilerBtn[type="log_debug"]:hover:pressed { - color: rgba(255, 102, 232, 163); -} - -#TerminalFilerBtn[type="log_info"]:checked {color: rgb(102, 171, 255);} -#TerminalFilerBtn[type="log_info"] {color: rgba(102, 171, 255, 63);} -#TerminalFilerBtn[type="log_info"]:hover:pressed { - color: rgba(102, 171, 255, 163); -} - -#TerminalFilerBtn[type="log_warning"]:checked {color: rgb(255, 186, 102);} -#TerminalFilerBtn[type="log_warning"] {color: rgba(255, 186, 102, 63);} -#TerminalFilerBtn[type="log_warning"]:hover:pressed { - color: rgba(255, 186, 102, 163); -} - -#TerminalFilerBtn[type="log_error"]:checked {color: rgb(255, 77, 88);} -#TerminalFilerBtn[type="log_error"] {color: rgba(255, 77, 88, 63);} -#TerminalFilerBtn[type="log_error"]:hover:pressed { - color: rgba(255, 77, 88, 163); -} - -#TerminalFilerBtn[type="log_critical"]:checked {color: rgb(255, 79, 117);} -#TerminalFilerBtn[type="log_critical"] {color: rgba(255, 79, 117, 63);} -#TerminalFilerBtn[type="log_critical"]:hover:pressed { - color: rgba(255, 79, 117, 163); -} - -#SuspendLogsBtn { - background: #444; - border: none; - border-top-right-radius: 7px; - border-bottom-right-radius: 7px; - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - font-family: "FontAwesome"; - font-size: 11pt; - color: white; - padding: 0px; -} - -#SuspendLogsBtn:hover { - background: #333; -} - -#SuspendLogsBtn:disabled { - background: #4c4c4c; -} diff --git a/client/ayon_core/tools/pyblish_pype/app.py b/client/ayon_core/tools/pyblish_pype/app.py deleted file mode 100644 index bdc204bfbd..0000000000 --- a/client/ayon_core/tools/pyblish_pype/app.py +++ /dev/null @@ -1,110 +0,0 @@ -from __future__ import print_function - -import os -import sys -import ctypes -import platform -import contextlib - -from qtpy import QtCore, QtGui, QtWidgets - -from . import control, settings, util, window - -self = sys.modules[__name__] - -# Maintain reference to currently opened window -self._window = None - - -@contextlib.contextmanager -def application(): - app = QtWidgets.QApplication.instance() - - if not app: - print("Starting new QApplication..") - app = QtWidgets.QApplication(sys.argv) - yield app - app.exec_() - else: - print("Using existing QApplication..") - yield app - if os.environ.get("PYBLISH_GUI_ALWAYS_EXEC"): - app.exec_() - - -def install_translator(app): - translator = QtCore.QTranslator(app) - translator.load(QtCore.QLocale.system(), "i18n/", - directory=util.root) - app.installTranslator(translator) - print("Installed translator") - - -def install_fonts(): - database = QtGui.QFontDatabase() - fonts = [ - "opensans/OpenSans-Bold.ttf", - "opensans/OpenSans-BoldItalic.ttf", - "opensans/OpenSans-ExtraBold.ttf", - "opensans/OpenSans-ExtraBoldItalic.ttf", - "opensans/OpenSans-Italic.ttf", - "opensans/OpenSans-Light.ttf", - "opensans/OpenSans-LightItalic.ttf", - "opensans/OpenSans-Regular.ttf", - "opensans/OpenSans-Semibold.ttf", - "opensans/OpenSans-SemiboldItalic.ttf", - "fontawesome/fontawesome-webfont.ttf" - ] - - for font in fonts: - path = util.get_asset("font", font) - - # TODO(marcus): Check if they are already installed first. - # In hosts, this will be called each time the GUI is shown, - # potentially installing a font each time. - if database.addApplicationFont(path) < 0: - print("Could not install %s" % path) - else: - print("Installed %s" % font) - - -def on_destroyed(): - """Remove internal reference to window on window destroyed""" - self._window = None - - -def show(parent=None): - with open(util.get_asset("app.css")) as f: - css = f.read() - - # Make relative paths absolute - root = util.get_asset("").replace("\\", "/") - css = css.replace("url(\"", "url(\"%s" % root) - - with application() as app: - - if platform.system().lower() == "windows": - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( - u"pyblish_pype" - ) - - install_fonts() - install_translator(app) - - if self._window is None: - ctrl = control.Controller() - self._window = window.Window(ctrl, parent) - self._window.destroyed.connect(on_destroyed) - - self._window.show() - self._window.activateWindow() - self._window.setWindowTitle(settings.WindowTitle) - - font = QtGui.QFont("Open Sans", 8, QtGui.QFont.Normal) - self._window.setFont(font) - self._window.setStyleSheet(css) - - self._window.reset() - self._window.resize(*settings.WindowSize) - - return self._window diff --git a/client/ayon_core/tools/pyblish_pype/awesome.py b/client/ayon_core/tools/pyblish_pype/awesome.py deleted file mode 100644 index c70f5b1064..0000000000 --- a/client/ayon_core/tools/pyblish_pype/awesome.py +++ /dev/null @@ -1,733 +0,0 @@ - -tags = { - "500px": u"\uf26e", - "adjust": u"\uf042", - "adn": u"\uf170", - "align-center": u"\uf037", - "align-justify": u"\uf039", - "align-left": u"\uf036", - "align-right": u"\uf038", - "amazon": u"\uf270", - "ambulance": u"\uf0f9", - "american-sign-language-interpreting": u"\uf2a3", - "anchor": u"\uf13d", - "android": u"\uf17b", - "angellist": u"\uf209", - "angle-double-down": u"\uf103", - "angle-double-left": u"\uf100", - "angle-double-right": u"\uf101", - "angle-double-up": u"\uf102", - "angle-down": u"\uf107", - "angle-left": u"\uf104", - "angle-right": u"\uf105", - "angle-up": u"\uf106", - "apple": u"\uf179", - "archive": u"\uf187", - "area-chart": u"\uf1fe", - "arrow-circle-down": u"\uf0ab", - "arrow-circle-left": u"\uf0a8", - "arrow-circle-o-down": u"\uf01a", - "arrow-circle-o-left": u"\uf190", - "arrow-circle-o-right": u"\uf18e", - "arrow-circle-o-up": u"\uf01b", - "arrow-circle-right": u"\uf0a9", - "arrow-circle-up": u"\uf0aa", - "arrow-down": u"\uf063", - "arrow-left": u"\uf060", - "arrow-right": u"\uf061", - "arrow-up": u"\uf062", - "arrows": u"\uf047", - "arrows-alt": u"\uf0b2", - "arrows-h": u"\uf07e", - "arrows-v": u"\uf07d", - "asl-interpreting (alias)": u"\uf2a3", - "assistive-listening-systems": u"\uf2a2", - "asterisk": u"\uf069", - "at": u"\uf1fa", - "audio-description": u"\uf29e", - "automobile (alias)": u"\uf1b9", - "backward": u"\uf04a", - "balance-scale": u"\uf24e", - "ban": u"\uf05e", - "bank (alias)": u"\uf19c", - "bar-chart": u"\uf080", - "bar-chart-o (alias)": u"\uf080", - "barcode": u"\uf02a", - "bars": u"\uf0c9", - "battery-0 (alias)": u"\uf244", - "battery-1 (alias)": u"\uf243", - "battery-2 (alias)": u"\uf242", - "battery-3 (alias)": u"\uf241", - "battery-4 (alias)": u"\uf240", - "battery-empty": u"\uf244", - "battery-full": u"\uf240", - "battery-half": u"\uf242", - "battery-quarter": u"\uf243", - "battery-three-quarters": u"\uf241", - "bed": u"\uf236", - "beer": u"\uf0fc", - "behance": u"\uf1b4", - "behance-square": u"\uf1b5", - "bell": u"\uf0f3", - "bell-o": u"\uf0a2", - "bell-slash": u"\uf1f6", - "bell-slash-o": u"\uf1f7", - "bicycle": u"\uf206", - "binoculars": u"\uf1e5", - "birthday-cake": u"\uf1fd", - "bitbucket": u"\uf171", - "bitbucket-square": u"\uf172", - "bitcoin (alias)": u"\uf15a", - "black-tie": u"\uf27e", - "blind": u"\uf29d", - "bluetooth": u"\uf293", - "bluetooth-b": u"\uf294", - "bold": u"\uf032", - "bolt": u"\uf0e7", - "bomb": u"\uf1e2", - "book": u"\uf02d", - "bookmark": u"\uf02e", - "bookmark-o": u"\uf097", - "braille": u"\uf2a1", - "briefcase": u"\uf0b1", - "btc": u"\uf15a", - "bug": u"\uf188", - "building": u"\uf1ad", - "building-o": u"\uf0f7", - "bullhorn": u"\uf0a1", - "bullseye": u"\uf140", - "bus": u"\uf207", - "buysellads": u"\uf20d", - "cab (alias)": u"\uf1ba", - "calculator": u"\uf1ec", - "calendar": u"\uf073", - "calendar-check-o": u"\uf274", - "calendar-minus-o": u"\uf272", - "calendar-o": u"\uf133", - "calendar-plus-o": u"\uf271", - "calendar-times-o": u"\uf273", - "camera": u"\uf030", - "camera-retro": u"\uf083", - "car": u"\uf1b9", - "caret-down": u"\uf0d7", - "caret-left": u"\uf0d9", - "caret-right": u"\uf0da", - "caret-square-o-down": u"\uf150", - "caret-square-o-left": u"\uf191", - "caret-square-o-right": u"\uf152", - "caret-square-o-up": u"\uf151", - "caret-up": u"\uf0d8", - "cart-arrow-down": u"\uf218", - "cart-plus": u"\uf217", - "cc": u"\uf20a", - "cc-amex": u"\uf1f3", - "cc-diners-club": u"\uf24c", - "cc-discover": u"\uf1f2", - "cc-jcb": u"\uf24b", - "cc-mastercard": u"\uf1f1", - "cc-paypal": u"\uf1f4", - "cc-stripe": u"\uf1f5", - "cc-visa": u"\uf1f0", - "certificate": u"\uf0a3", - "chain (alias)": u"\uf0c1", - "chain-broken": u"\uf127", - "check": u"\uf00c", - "check-circle": u"\uf058", - "check-circle-o": u"\uf05d", - "check-square": u"\uf14a", - "check-square-o": u"\uf046", - "chevron-circle-down": u"\uf13a", - "chevron-circle-left": u"\uf137", - "chevron-circle-right": u"\uf138", - "chevron-circle-up": u"\uf139", - "chevron-down": u"\uf078", - "chevron-left": u"\uf053", - "chevron-right": u"\uf054", - "chevron-up": u"\uf077", - "child": u"\uf1ae", - "chrome": u"\uf268", - "circle": u"\uf111", - "circle-o": u"\uf10c", - "circle-o-notch": u"\uf1ce", - "circle-thin": u"\uf1db", - "clipboard": u"\uf0ea", - "clock-o": u"\uf017", - "clone": u"\uf24d", - "close (alias)": u"\uf00d", - "cloud": u"\uf0c2", - "cloud-download": u"\uf0ed", - "cloud-upload": u"\uf0ee", - "cny (alias)": u"\uf157", - "code": u"\uf121", - "code-fork": u"\uf126", - "codepen": u"\uf1cb", - "codiepie": u"\uf284", - "coffee": u"\uf0f4", - "cog": u"\uf013", - "cogs": u"\uf085", - "columns": u"\uf0db", - "comment": u"\uf075", - "comment-o": u"\uf0e5", - "commenting": u"\uf27a", - "commenting-o": u"\uf27b", - "comments": u"\uf086", - "comments-o": u"\uf0e6", - "compass": u"\uf14e", - "compress": u"\uf066", - "connectdevelop": u"\uf20e", - "contao": u"\uf26d", - "copy (alias)": u"\uf0c5", - "copyright": u"\uf1f9", - "creative-commons": u"\uf25e", - "credit-card": u"\uf09d", - "credit-card-alt": u"\uf283", - "crop": u"\uf125", - "crosshairs": u"\uf05b", - "css3": u"\uf13c", - "cube": u"\uf1b2", - "cubes": u"\uf1b3", - "cut (alias)": u"\uf0c4", - "cutlery": u"\uf0f5", - "dashboard (alias)": u"\uf0e4", - "dashcube": u"\uf210", - "database": u"\uf1c0", - "deaf": u"\uf2a4", - "deafness (alias)": u"\uf2a4", - "dedent (alias)": u"\uf03b", - "delicious": u"\uf1a5", - "desktop": u"\uf108", - "deviantart": u"\uf1bd", - "diamond": u"\uf219", - "digg": u"\uf1a6", - "dollar (alias)": u"\uf155", - "dot-circle-o": u"\uf192", - "download": u"\uf019", - "dribbble": u"\uf17d", - "dropbox": u"\uf16b", - "drupal": u"\uf1a9", - "edge": u"\uf282", - "edit (alias)": u"\uf044", - "eject": u"\uf052", - "ellipsis-h": u"\uf141", - "ellipsis-v": u"\uf142", - "empire": u"\uf1d1", - "envelope": u"\uf0e0", - "envelope-o": u"\uf003", - "envelope-square": u"\uf199", - "envira": u"\uf299", - "eraser": u"\uf12d", - "eur": u"\uf153", - "euro (alias)": u"\uf153", - "exchange": u"\uf0ec", - "exclamation": u"\uf12a", - "exclamation-circle": u"\uf06a", - "exclamation-triangle": u"\uf071", - "expand": u"\uf065", - "expeditedssl": u"\uf23e", - "external-link": u"\uf08e", - "external-link-square": u"\uf14c", - "eye": u"\uf06e", - "eye-slash": u"\uf070", - "eyedropper": u"\uf1fb", - "fa (alias)": u"\uf2b4", - "facebook": u"\uf09a", - "facebook-f (alias)": u"\uf09a", - "facebook-official": u"\uf230", - "facebook-square": u"\uf082", - "fast-backward": u"\uf049", - "fast-forward": u"\uf050", - "fax": u"\uf1ac", - "feed (alias)": u"\uf09e", - "female": u"\uf182", - "fighter-jet": u"\uf0fb", - "file": u"\uf15b", - "file-archive-o": u"\uf1c6", - "file-audio-o": u"\uf1c7", - "file-code-o": u"\uf1c9", - "file-excel-o": u"\uf1c3", - "file-image-o": u"\uf1c5", - "file-movie-o (alias)": u"\uf1c8", - "file-o": u"\uf016", - "file-pdf-o": u"\uf1c1", - "file-photo-o (alias)": u"\uf1c5", - "file-picture-o (alias)": u"\uf1c5", - "file-powerpoint-o": u"\uf1c4", - "file-sound-o (alias)": u"\uf1c7", - "file-text": u"\uf15c", - "file-text-o": u"\uf0f6", - "file-video-o": u"\uf1c8", - "file-word-o": u"\uf1c2", - "file-zip-o (alias)": u"\uf1c6", - "files-o": u"\uf0c5", - "film": u"\uf008", - "filter": u"\uf0b0", - "fire": u"\uf06d", - "fire-extinguisher": u"\uf134", - "firefox": u"\uf269", - "first-order": u"\uf2b0", - "flag": u"\uf024", - "flag-checkered": u"\uf11e", - "flag-o": u"\uf11d", - "flash (alias)": u"\uf0e7", - "flask": u"\uf0c3", - "flickr": u"\uf16e", - "floppy-o": u"\uf0c7", - "folder": u"\uf07b", - "folder-o": u"\uf114", - "folder-open": u"\uf07c", - "folder-open-o": u"\uf115", - "font": u"\uf031", - "font-awesome": u"\uf2b4", - "fonticons": u"\uf280", - "fort-awesome": u"\uf286", - "forumbee": u"\uf211", - "forward": u"\uf04e", - "foursquare": u"\uf180", - "frown-o": u"\uf119", - "futbol-o": u"\uf1e3", - "gamepad": u"\uf11b", - "gavel": u"\uf0e3", - "gbp": u"\uf154", - "ge (alias)": u"\uf1d1", - "gear (alias)": u"\uf013", - "gears (alias)": u"\uf085", - "genderless": u"\uf22d", - "get-pocket": u"\uf265", - "gg": u"\uf260", - "gg-circle": u"\uf261", - "gift": u"\uf06b", - "git": u"\uf1d3", - "git-square": u"\uf1d2", - "github": u"\uf09b", - "github-alt": u"\uf113", - "github-square": u"\uf092", - "gitlab": u"\uf296", - "gittip (alias)": u"\uf184", - "glass": u"\uf000", - "glide": u"\uf2a5", - "glide-g": u"\uf2a6", - "globe": u"\uf0ac", - "google": u"\uf1a0", - "google-plus": u"\uf0d5", - "google-plus-circle (alias)": u"\uf2b3", - "google-plus-official": u"\uf2b3", - "google-plus-square": u"\uf0d4", - "google-wallet": u"\uf1ee", - "graduation-cap": u"\uf19d", - "gratipay": u"\uf184", - "group (alias)": u"\uf0c0", - "h-square": u"\uf0fd", - "hacker-news": u"\uf1d4", - "hand-grab-o (alias)": u"\uf255", - "hand-lizard-o": u"\uf258", - "hand-o-down": u"\uf0a7", - "hand-o-left": u"\uf0a5", - "hand-o-right": u"\uf0a4", - "hand-o-up": u"\uf0a6", - "hand-paper-o": u"\uf256", - "hand-peace-o": u"\uf25b", - "hand-pointer-o": u"\uf25a", - "hand-rock-o": u"\uf255", - "hand-scissors-o": u"\uf257", - "hand-spock-o": u"\uf259", - "hand-stop-o (alias)": u"\uf256", - "hard-of-hearing (alias)": u"\uf2a4", - "hashtag": u"\uf292", - "hdd-o": u"\uf0a0", - "header": u"\uf1dc", - "headphones": u"\uf025", - "heart": u"\uf004", - "heart-o": u"\uf08a", - "heartbeat": u"\uf21e", - "history": u"\uf1da", - "home": u"\uf015", - "hospital-o": u"\uf0f8", - "hotel (alias)": u"\uf236", - "hourglass": u"\uf254", - "hourglass-1 (alias)": u"\uf251", - "hourglass-2 (alias)": u"\uf252", - "hourglass-3 (alias)": u"\uf253", - "hourglass-end": u"\uf253", - "hourglass-half": u"\uf252", - "hourglass-o": u"\uf250", - "hourglass-start": u"\uf251", - "houzz": u"\uf27c", - "html5": u"\uf13b", - "i-cursor": u"\uf246", - "ils": u"\uf20b", - "image (alias)": u"\uf03e", - "inbox": u"\uf01c", - "indent": u"\uf03c", - "industry": u"\uf275", - "info": u"\uf129", - "info-circle": u"\uf05a", - "inr": u"\uf156", - "instagram": u"\uf16d", - "institution (alias)": u"\uf19c", - "internet-explorer": u"\uf26b", - "intersex (alias)": u"\uf224", - "ioxhost": u"\uf208", - "italic": u"\uf033", - "joomla": u"\uf1aa", - "jpy": u"\uf157", - "jsfiddle": u"\uf1cc", - "key": u"\uf084", - "keyboard-o": u"\uf11c", - "krw": u"\uf159", - "language": u"\uf1ab", - "laptop": u"\uf109", - "lastfm": u"\uf202", - "lastfm-square": u"\uf203", - "leaf": u"\uf06c", - "leanpub": u"\uf212", - "legal (alias)": u"\uf0e3", - "lemon-o": u"\uf094", - "level-down": u"\uf149", - "level-up": u"\uf148", - "life-bouy (alias)": u"\uf1cd", - "life-buoy (alias)": u"\uf1cd", - "life-ring": u"\uf1cd", - "life-saver (alias)": u"\uf1cd", - "lightbulb-o": u"\uf0eb", - "line-chart": u"\uf201", - "link": u"\uf0c1", - "linkedin": u"\uf0e1", - "linkedin-square": u"\uf08c", - "linux": u"\uf17c", - "list": u"\uf03a", - "list-alt": u"\uf022", - "list-ol": u"\uf0cb", - "list-ul": u"\uf0ca", - "location-arrow": u"\uf124", - "lock": u"\uf023", - "long-arrow-down": u"\uf175", - "long-arrow-left": u"\uf177", - "long-arrow-right": u"\uf178", - "long-arrow-up": u"\uf176", - "low-vision": u"\uf2a8", - "magic": u"\uf0d0", - "magnet": u"\uf076", - "mail-forward (alias)": u"\uf064", - "mail-reply (alias)": u"\uf112", - "mail-reply-all (alias)": u"\uf122", - "male": u"\uf183", - "map": u"\uf279", - "map-marker": u"\uf041", - "map-o": u"\uf278", - "map-pin": u"\uf276", - "map-signs": u"\uf277", - "mars": u"\uf222", - "mars-double": u"\uf227", - "mars-stroke": u"\uf229", - "mars-stroke-h": u"\uf22b", - "mars-stroke-v": u"\uf22a", - "maxcdn": u"\uf136", - "meanpath": u"\uf20c", - "medium": u"\uf23a", - "medkit": u"\uf0fa", - "meh-o": u"\uf11a", - "mercury": u"\uf223", - "microphone": u"\uf130", - "microphone-slash": u"\uf131", - "minus": u"\uf068", - "minus-circle": u"\uf056", - "minus-square": u"\uf146", - "minus-square-o": u"\uf147", - "mixcloud": u"\uf289", - "mobile": u"\uf10b", - "mobile-phone (alias)": u"\uf10b", - "modx": u"\uf285", - "money": u"\uf0d6", - "moon-o": u"\uf186", - "mortar-board (alias)": u"\uf19d", - "motorcycle": u"\uf21c", - "mouse-pointer": u"\uf245", - "music": u"\uf001", - "navicon (alias)": u"\uf0c9", - "neuter": u"\uf22c", - "newspaper-o": u"\uf1ea", - "object-group": u"\uf247", - "object-ungroup": u"\uf248", - "odnoklassniki": u"\uf263", - "odnoklassniki-square": u"\uf264", - "opencart": u"\uf23d", - "openid": u"\uf19b", - "opera": u"\uf26a", - "optin-monster": u"\uf23c", - "outdent": u"\uf03b", - "pagelines": u"\uf18c", - "paint-brush": u"\uf1fc", - "paper-plane": u"\uf1d8", - "paper-plane-o": u"\uf1d9", - "paperclip": u"\uf0c6", - "paragraph": u"\uf1dd", - "paste (alias)": u"\uf0ea", - "pause": u"\uf04c", - "pause-circle": u"\uf28b", - "pause-circle-o": u"\uf28c", - "paw": u"\uf1b0", - "paypal": u"\uf1ed", - "pencil": u"\uf040", - "pencil-square": u"\uf14b", - "pencil-square-o": u"\uf044", - "percent": u"\uf295", - "phone": u"\uf095", - "phone-square": u"\uf098", - "photo (alias)": u"\uf03e", - "picture-o": u"\uf03e", - "pie-chart": u"\uf200", - "pied-piper": u"\uf2ae", - "pied-piper-alt": u"\uf1a8", - "pied-piper-pp": u"\uf1a7", - "pinterest": u"\uf0d2", - "pinterest-p": u"\uf231", - "pinterest-square": u"\uf0d3", - "plane": u"\uf072", - "play": u"\uf04b", - "play-circle": u"\uf144", - "play-circle-o": u"\uf01d", - "plug": u"\uf1e6", - "plus": u"\uf067", - "plus-circle": u"\uf055", - "plus-square": u"\uf0fe", - "plus-square-o": u"\uf196", - "power-off": u"\uf011", - "print": u"\uf02f", - "product-hunt": u"\uf288", - "puzzle-piece": u"\uf12e", - "qq": u"\uf1d6", - "qrcode": u"\uf029", - "question": u"\uf128", - "question-circle": u"\uf059", - "question-circle-o": u"\uf29c", - "quote-left": u"\uf10d", - "quote-right": u"\uf10e", - "ra (alias)": u"\uf1d0", - "random": u"\uf074", - "rebel": u"\uf1d0", - "recycle": u"\uf1b8", - "reddit": u"\uf1a1", - "reddit-alien": u"\uf281", - "reddit-square": u"\uf1a2", - "refresh": u"\uf021", - "registered": u"\uf25d", - "remove (alias)": u"\uf00d", - "renren": u"\uf18b", - "reorder (alias)": u"\uf0c9", - "repeat": u"\uf01e", - "reply": u"\uf112", - "reply-all": u"\uf122", - "resistance (alias)": u"\uf1d0", - "retweet": u"\uf079", - "rmb (alias)": u"\uf157", - "road": u"\uf018", - "rocket": u"\uf135", - "rotate-left (alias)": u"\uf0e2", - "rotate-right (alias)": u"\uf01e", - "rouble (alias)": u"\uf158", - "rss": u"\uf09e", - "rss-square": u"\uf143", - "rub": u"\uf158", - "ruble (alias)": u"\uf158", - "rupee (alias)": u"\uf156", - "safari": u"\uf267", - "save (alias)": u"\uf0c7", - "scissors": u"\uf0c4", - "scribd": u"\uf28a", - "search": u"\uf002", - "search-minus": u"\uf010", - "search-plus": u"\uf00e", - "sellsy": u"\uf213", - "send (alias)": u"\uf1d8", - "send-o (alias)": u"\uf1d9", - "server": u"\uf233", - "share": u"\uf064", - "share-alt": u"\uf1e0", - "share-alt-square": u"\uf1e1", - "share-square": u"\uf14d", - "share-square-o": u"\uf045", - "shekel (alias)": u"\uf20b", - "sheqel (alias)": u"\uf20b", - "shield": u"\uf132", - "ship": u"\uf21a", - "shirtsinbulk": u"\uf214", - "shopping-bag": u"\uf290", - "shopping-basket": u"\uf291", - "shopping-cart": u"\uf07a", - "sign-in": u"\uf090", - "sign-language": u"\uf2a7", - "sign-out": u"\uf08b", - "signal": u"\uf012", - "signing (alias)": u"\uf2a7", - "simplybuilt": u"\uf215", - "sitemap": u"\uf0e8", - "skyatlas": u"\uf216", - "skype": u"\uf17e", - "slack": u"\uf198", - "sliders": u"\uf1de", - "slideshare": u"\uf1e7", - "smile-o": u"\uf118", - "snapchat": u"\uf2ab", - "snapchat-ghost": u"\uf2ac", - "snapchat-square": u"\uf2ad", - "soccer-ball-o (alias)": u"\uf1e3", - "sort": u"\uf0dc", - "sort-alpha-asc": u"\uf15d", - "sort-alpha-desc": u"\uf15e", - "sort-amount-asc": u"\uf160", - "sort-amount-desc": u"\uf161", - "sort-asc": u"\uf0de", - "sort-desc": u"\uf0dd", - "sort-down (alias)": u"\uf0dd", - "sort-numeric-asc": u"\uf162", - "sort-numeric-desc": u"\uf163", - "sort-up (alias)": u"\uf0de", - "soundcloud": u"\uf1be", - "space-shuttle": u"\uf197", - "spinner": u"\uf110", - "spoon": u"\uf1b1", - "spotify": u"\uf1bc", - "square": u"\uf0c8", - "square-o": u"\uf096", - "stack-exchange": u"\uf18d", - "stack-overflow": u"\uf16c", - "star": u"\uf005", - "star-half": u"\uf089", - "star-half-empty (alias)": u"\uf123", - "star-half-full (alias)": u"\uf123", - "star-half-o": u"\uf123", - "star-o": u"\uf006", - "steam": u"\uf1b6", - "steam-square": u"\uf1b7", - "step-backward": u"\uf048", - "step-forward": u"\uf051", - "stethoscope": u"\uf0f1", - "sticky-note": u"\uf249", - "sticky-note-o": u"\uf24a", - "stop": u"\uf04d", - "stop-circle": u"\uf28d", - "stop-circle-o": u"\uf28e", - "street-view": u"\uf21d", - "strikethrough": u"\uf0cc", - "stumbleupon": u"\uf1a4", - "stumbleupon-circle": u"\uf1a3", - "subscript": u"\uf12c", - "subway": u"\uf239", - "suitcase": u"\uf0f2", - "sun-o": u"\uf185", - "superscript": u"\uf12b", - "support (alias)": u"\uf1cd", - "table": u"\uf0ce", - "tablet": u"\uf10a", - "tachometer": u"\uf0e4", - "tag": u"\uf02b", - "tags": u"\uf02c", - "tasks": u"\uf0ae", - "taxi": u"\uf1ba", - "television": u"\uf26c", - "tencent-weibo": u"\uf1d5", - "terminal": u"\uf120", - "text-height": u"\uf034", - "text-width": u"\uf035", - "th": u"\uf00a", - "th-large": u"\uf009", - "th-list": u"\uf00b", - "themeisle": u"\uf2b2", - "thumb-tack": u"\uf08d", - "thumbs-down": u"\uf165", - "thumbs-o-down": u"\uf088", - "thumbs-o-up": u"\uf087", - "thumbs-up": u"\uf164", - "ticket": u"\uf145", - "times": u"\uf00d", - "times-circle": u"\uf057", - "times-circle-o": u"\uf05c", - "tint": u"\uf043", - "toggle-down (alias)": u"\uf150", - "toggle-left (alias)": u"\uf191", - "toggle-off": u"\uf204", - "toggle-on": u"\uf205", - "toggle-right (alias)": u"\uf152", - "toggle-up (alias)": u"\uf151", - "trademark": u"\uf25c", - "train": u"\uf238", - "transgender": u"\uf224", - "transgender-alt": u"\uf225", - "trash": u"\uf1f8", - "trash-o": u"\uf014", - "tree": u"\uf1bb", - "trello": u"\uf181", - "tripadvisor": u"\uf262", - "trophy": u"\uf091", - "truck": u"\uf0d1", - "try": u"\uf195", - "tty": u"\uf1e4", - "tumblr": u"\uf173", - "tumblr-square": u"\uf174", - "turkish-lira (alias)": u"\uf195", - "tv (alias)": u"\uf26c", - "twitch": u"\uf1e8", - "twitter": u"\uf099", - "twitter-square": u"\uf081", - "umbrella": u"\uf0e9", - "underline": u"\uf0cd", - "undo": u"\uf0e2", - "universal-access": u"\uf29a", - "university": u"\uf19c", - "unlink (alias)": u"\uf127", - "unlock": u"\uf09c", - "unlock-alt": u"\uf13e", - "unsorted (alias)": u"\uf0dc", - "upload": u"\uf093", - "usb": u"\uf287", - "usd": u"\uf155", - "user": u"\uf007", - "user-md": u"\uf0f0", - "user-plus": u"\uf234", - "user-secret": u"\uf21b", - "user-times": u"\uf235", - "users": u"\uf0c0", - "venus": u"\uf221", - "venus-double": u"\uf226", - "venus-mars": u"\uf228", - "viacoin": u"\uf237", - "viadeo": u"\uf2a9", - "viadeo-square": u"\uf2aa", - "video-camera": u"\uf03d", - "vimeo": u"\uf27d", - "vimeo-square": u"\uf194", - "vine": u"\uf1ca", - "vk": u"\uf189", - "volume-control-phone": u"\uf2a0", - "volume-down": u"\uf027", - "volume-off": u"\uf026", - "volume-up": u"\uf028", - "warning (alias)": u"\uf071", - "wechat (alias)": u"\uf1d7", - "weibo": u"\uf18a", - "weixin": u"\uf1d7", - "whatsapp": u"\uf232", - "wheelchair": u"\uf193", - "wheelchair-alt": u"\uf29b", - "wifi": u"\uf1eb", - "wikipedia-w": u"\uf266", - "windows": u"\uf17a", - "won (alias)": u"\uf159", - "wordpress": u"\uf19a", - "wpbeginner": u"\uf297", - "wpforms": u"\uf298", - "wrench": u"\uf0ad", - "xing": u"\uf168", - "xing-square": u"\uf169", - "y-combinator": u"\uf23b", - "y-combinator-square (alias)": u"\uf1d4", - "yahoo": u"\uf19e", - "yc (alias)": u"\uf23b", - "yc-square (alias)": u"\uf1d4", - "yelp": u"\uf1e9", - "yen (alias)": u"\uf157", - "yoast": u"\uf2b1", - "youtube": u"\uf167", - "youtube-play": u"\uf16a", - "youtube-square": u"\uf166" -} diff --git a/client/ayon_core/tools/pyblish_pype/constants.py b/client/ayon_core/tools/pyblish_pype/constants.py deleted file mode 100644 index 10f95ca4af..0000000000 --- a/client/ayon_core/tools/pyblish_pype/constants.py +++ /dev/null @@ -1,97 +0,0 @@ -from qtpy import QtCore - -EXPANDER_WIDTH = 20 - - -def flags(*args, **kwargs): - type_name = kwargs.pop("type_name", "Flags") - with_base = kwargs.pop("with_base", False) - enums = {} - for idx, attr_name in enumerate(args): - if with_base: - if idx == 0: - enums[attr_name] = 0 - continue - idx -= 1 - enums[attr_name] = 2**idx - - for attr_name, value in kwargs.items(): - enums[attr_name] = value - return type(type_name, (), enums) - - -def roles(*args, **kwargs): - type_name = kwargs.pop("type_name", "Roles") - enums = {} - for attr_name, value in kwargs.items(): - enums[attr_name] = value - - offset = 0 - for idx, attr_name in enumerate(args): - _idx = idx + QtCore.Qt.UserRole + offset - while _idx in enums.values(): - offset += 1 - _idx = idx + offset - - enums[attr_name] = _idx - - return type(type_name, (), enums) - - -Roles = roles( - "ObjectIdRole", - "ObjectUIdRole", - "TypeRole", - "PublishFlagsRole", - "LogRecordsRole", - - "IsOptionalRole", - "IsEnabledRole", - - "FamiliesRole", - - "DocstringRole", - "PathModuleRole", - "PluginActionsVisibleRole", - "PluginValidActionsRole", - "PluginActionProgressRole", - - "TerminalItemTypeRole", - - "IntentItemValue", - - type_name="ModelRoles" -) - -InstanceStates = flags( - "ContextType", - "InProgress", - "HasWarning", - "HasError", - "HasFinished", - type_name="InstanceState" -) - -PluginStates = flags( - "IsCompatible", - "InProgress", - "WasProcessed", - "WasSkipped", - "HasWarning", - "HasError", - type_name="PluginState" -) - -GroupStates = flags( - "HasWarning", - "HasError", - "HasFinished", - type_name="GroupStates" -) - -PluginActionStates = flags( - "InProgress", - "HasFailed", - "HasFinished", - type_name="PluginActionStates" -) diff --git a/client/ayon_core/tools/pyblish_pype/control.py b/client/ayon_core/tools/pyblish_pype/control.py deleted file mode 100644 index c5034e2736..0000000000 --- a/client/ayon_core/tools/pyblish_pype/control.py +++ /dev/null @@ -1,666 +0,0 @@ -"""The Controller in a Model/View/Controller-based application -The graphical components of Pyblish Lite use this object to perform -publishing. It communicates via the Qt Signals/Slots mechanism -and has no direct connection to any graphics. This is important, -because this is how unittests are able to run without requiring -an active window manager; such as via Travis-CI. -""" -import os -import sys -import inspect -import logging -import collections - -from qtpy import QtCore - -import pyblish.api -import pyblish.util -import pyblish.logic -import pyblish.lib -import pyblish.version - -from . import util -from .constants import InstanceStates - -from ayon_core.settings import get_current_project_settings - - -class IterationBreak(Exception): - pass - - -class MainThreadItem: - """Callback with args and kwargs.""" - def __init__(self, callback, *args, **kwargs): - self.callback = callback - self.args = args - self.kwargs = kwargs - - def process(self): - self.callback(*self.args, **self.kwargs) - - -class MainThreadProcess(QtCore.QObject): - """Qt based main thread process executor. - - Has timer which controls each 50ms if there is new item to process. - - This approach gives ability to update UI meanwhile plugin is in progress. - """ - # How many times let pass QtApplication to process events - # - use 2 as resize event can trigger repaint event but not process in - # same loop - count_timeout = 2 - - def __init__(self): - super(MainThreadProcess, self).__init__() - self._items_to_process = collections.deque() - - timer = QtCore.QTimer() - timer.setInterval(0) - - timer.timeout.connect(self._execute) - - self._timer = timer - self._switch_counter = self.count_timeout - - def process(self, func, *args, **kwargs): - item = MainThreadItem(func, *args, **kwargs) - self.add_item(item) - - def add_item(self, item): - self._items_to_process.append(item) - - def _execute(self): - if not self._items_to_process: - return - - if self._switch_counter > 0: - self._switch_counter -= 1 - return - - self._switch_counter = self.count_timeout - - item = self._items_to_process.popleft() - item.process() - - def start(self): - if not self._timer.isActive(): - self._timer.start() - - def stop(self): - if self._timer.isActive(): - self._timer.stop() - - def clear(self): - if self._timer.isActive(): - self._timer.stop() - self._items_to_process = collections.deque() - - def stop_if_empty(self): - if self._timer.isActive(): - item = MainThreadItem(self._stop_if_empty) - self.add_item(item) - - def _stop_if_empty(self): - if not self._items_to_process: - self.stop() - - -class Controller(QtCore.QObject): - log = logging.getLogger("PyblishController") - # Emitted when the GUI is about to start processing; - # e.g. resetting, validating or publishing. - about_to_process = QtCore.Signal(object, object) - - # ??? Emitted for each process - was_processed = QtCore.Signal(dict) - - # Emitted when reset - # - all data are reset (plugins, processing, pari yielder, etc.) - was_reset = QtCore.Signal() - - # Emitted when previous group changed - passed_group = QtCore.Signal(object) - - # Emitted when want to change state of instances - switch_toggleability = QtCore.Signal(bool) - - # On action finished - was_acted = QtCore.Signal(dict) - - # Emitted when processing has stopped - was_stopped = QtCore.Signal() - - # Emitted when processing has finished - was_finished = QtCore.Signal() - - # Emitted when plugin was skipped - was_skipped = QtCore.Signal(object) - - # store OrderGroups - now it is a singleton - order_groups = util.OrderGroups - - # When instance is toggled - instance_toggled = QtCore.Signal(object, object, object) - - def __init__(self, parent=None): - super(Controller, self).__init__(parent) - self.context = None - self.plugins = {} - self.optional_default = {} - self.instance_toggled.connect(self._on_instance_toggled) - self._main_thread_processor = MainThreadProcess() - - self._current_state = "" - - def reset_variables(self): - self.log.debug("Resetting pyblish context variables") - - # Data internal to the GUI itself - self.is_running = False - self.stopped = False - self.errored = False - self._current_state = "" - - # Active producer of pairs - self.pair_generator = None - # Active pair - self.current_pair = None - - # Orders which changes GUI - # - passing collectors order disables plugin/instance toggle - self.collect_state = 0 - - # - passing validators order disables validate button and gives ability - # to know when to stop on validate button press - self.validators_order = None - self.validated = False - - # Get collectors and validators order - plugin_groups_keys = list(self.order_groups.groups.keys()) - self.validators_order = self.order_groups.validation_order - next_group_order = None - if len(plugin_groups_keys) > 1: - next_group_order = plugin_groups_keys[1] - - # This is used to track whether or not to continue - # processing when, for example, validation has failed. - self.processing = { - "stop_on_validation": False, - # Used? - "last_plugin_order": None, - "current_group_order": plugin_groups_keys[0], - "next_group_order": next_group_order, - "nextOrder": None, - "ordersWithError": set() - } - self._set_state_by_order() - self.log.debug("Reset of pyblish context variables done") - - @property - def current_state(self): - return self._current_state - - @staticmethod - def _convert_filter_presets(filter_presets): - """Convert AYON settings presets to dictionary. - - Returns: - dict[str, dict[str, Any]]: Filter presets converted to dictionary. - """ - if not isinstance(filter_presets, list): - return filter_presets - - return { - filter_preset["name"]: { - item["name"]: item["value"] - for item in filter_preset["value"] - } - for filter_preset in filter_presets - } - - def presets_by_hosts(self): - # Get global filters as base - presets = get_current_project_settings() - if not presets: - return {} - - result = {} - hosts = pyblish.api.registered_hosts() - for host in hosts: - host_presets = presets.get(host, {}).get("filters") - if not host_presets: - continue - - host_presets = self._convert_filter_presets(host_presets) - - for key, value in host_presets.items(): - if value is None: - if key in result: - result.pop(key) - continue - - result[key] = value - - return result - - def reset_context(self): - self.log.debug("Resetting pyblish context object") - - comment = None - if ( - self.context is not None and - self.context.data.get("comment") and - # We only preserve the user typed comment if we are *not* - # resetting from a successful publish without errors - self._current_state != "Published" - ): - comment = self.context.data["comment"] - - self.context = pyblish.api.Context() - - self.context._publish_states = InstanceStates.ContextType - self.context.optional = False - - self.context.data["publish"] = True - self.context.data["name"] = "context" - - self.context.data["host"] = reversed(pyblish.api.registered_hosts()) - self.context.data["port"] = int( - os.environ.get("PYBLISH_CLIENT_PORT", -1) - ) - self.context.data["connectTime"] = pyblish.lib.time(), - self.context.data["pyblishVersion"] = pyblish.version, - self.context.data["pythonVersion"] = sys.version - - self.context.data["icon"] = "book" - - self.context.families = ("__context__",) - - if comment: - # Preserve comment on reset if user previously had a comment - self.context.data["comment"] = comment - - self.log.debug("Reset of pyblish context object done") - - def reset(self): - """Discover plug-ins and run collection.""" - self._main_thread_processor.clear() - self._main_thread_processor.process(self._reset) - self._main_thread_processor.start() - - def _reset(self): - self.reset_context() - self.reset_variables() - - self.possible_presets = self.presets_by_hosts() - - # Load plugins and set pair generator - self.load_plugins() - self.pair_generator = self._pair_yielder(self.plugins) - - self.was_reset.emit() - - # Process collectors load rest of plugins with collected instances - self.collect() - - def load_plugins(self): - self.test = pyblish.logic.registered_test() - self.optional_default = {} - - plugins = pyblish.api.discover() - - targets = set(pyblish.logic.registered_targets()) - targets.add("default") - targets = list(targets) - plugins_by_targets = pyblish.logic.plugins_by_targets(plugins, targets) - - _plugins = [] - for plugin in plugins_by_targets: - # Skip plugin if is not optional and not active - if ( - not getattr(plugin, "optional", False) - and not getattr(plugin, "active", True) - ): - continue - _plugins.append(plugin) - self.plugins = _plugins - - def on_published(self): - if self.is_running: - self.is_running = False - self._current_state = ( - "Published" if not self.errored else "Published, with errors" - ) - self.was_finished.emit() - self._main_thread_processor.stop() - - def stop(self): - self.log.debug("Stopping") - self.stopped = True - - def act(self, plugin, action): - self.is_running = True - item = MainThreadItem(self._process_action, plugin, action) - self._main_thread_processor.add_item(item) - self._main_thread_processor.start() - self._main_thread_processor.stop_if_empty() - - def _process_action(self, plugin, action): - result = pyblish.plugin.process( - plugin, self.context, None, action.id - ) - self.is_running = False - self.was_acted.emit(result) - - def emit_(self, signal, kwargs): - pyblish.api.emit(signal, **kwargs) - - def _process(self, plugin, instance=None): - """Produce `result` from `plugin` and `instance` - :func:`process` shares state with :func:`_iterator` such that - an instance/plugin pair can be fetched and processed in isolation. - Arguments: - plugin (pyblish.api.Plugin): Produce result using plug-in - instance (optional, pyblish.api.Instance): Process this instance, - if no instance is provided, context is processed. - """ - - self.processing["nextOrder"] = plugin.order - - try: - result = pyblish.plugin.process(plugin, self.context, instance) - # Make note of the order at which the - # potential error error occurred. - if result["error"] is not None: - self.processing["ordersWithError"].add(plugin.order) - - except Exception as exc: - raise Exception("Unknown error({}): {}".format( - plugin.__name__, str(exc) - )) - - return result - - def _pair_yielder(self, plugins): - for plugin in plugins: - if ( - self.processing["current_group_order"] is not None - and plugin.order > self.processing["current_group_order"] - ): - current_group_order = self.processing["current_group_order"] - - new_next_group_order = None - new_current_group_order = self.processing["next_group_order"] - if new_current_group_order is not None: - current_next_order_found = False - for order in self.order_groups.groups.keys(): - if current_next_order_found: - new_next_group_order = order - break - - if order == new_current_group_order: - current_next_order_found = True - - self.processing["next_group_order"] = new_next_group_order - self.processing["current_group_order"] = ( - new_current_group_order - ) - - # Force update to the current state - self._set_state_by_order() - - if self.collect_state == 0: - self.collect_state = 1 - self._current_state = ( - "Ready" if not self.errored else - "Collected, with errors" - ) - self.switch_toggleability.emit(True) - self.passed_group.emit(current_group_order) - yield IterationBreak("Collected") - - else: - self.passed_group.emit(current_group_order) - if self.errored: - self._current_state = ( - "Stopped, due to errors" if not - self.processing["stop_on_validation"] else - "Validated, with errors" - ) - yield IterationBreak("Last group errored") - - if self.collect_state == 1: - self.collect_state = 2 - self.switch_toggleability.emit(False) - - if not self.validated and plugin.order > self.validators_order: - self.validated = True - if self.processing["stop_on_validation"]: - self._current_state = ( - "Validated" if not self.errored else - "Validated, with errors" - ) - yield IterationBreak("Validated") - - # Stop if was stopped - if self.stopped: - self.stopped = False - self._current_state = "Paused" - yield IterationBreak("Stopped") - - # check test if will stop - self.processing["nextOrder"] = plugin.order - message = self.test(**self.processing) - if message: - self._current_state = "Paused" - yield IterationBreak("Stopped due to \"{}\"".format(message)) - - self.processing["last_plugin_order"] = plugin.order - if not plugin.active: - pyblish.logic.log.debug("%s was inactive, skipping.." % plugin) - self.was_skipped.emit(plugin) - continue - - in_collect_stage = self.collect_state == 0 - if plugin.__instanceEnabled__: - instances = pyblish.logic.instances_by_plugin( - self.context, plugin - ) - if not instances: - self.was_skipped.emit(plugin) - continue - - for instance in instances: - if ( - not in_collect_stage - and instance.data.get("publish") is False - ): - pyblish.logic.log.debug( - "%s was inactive, skipping.." % instance - ) - continue - # Stop if was stopped - if self.stopped: - self.stopped = False - self._current_state = "Paused" - yield IterationBreak("Stopped") - - yield (plugin, instance) - else: - families = util.collect_families_from_instances( - self.context, only_active=not in_collect_stage - ) - plugins = pyblish.logic.plugins_by_families( - [plugin], families - ) - if not plugins: - self.was_skipped.emit(plugin) - continue - yield (plugin, None) - - self.passed_group.emit(self.processing["next_group_order"]) - - def iterate_and_process(self, on_finished=None): - """ Iterating inserted plugins with current context. - Collectors do not contain instances, they are None when collecting! - This process don't stop on one - """ - self._main_thread_processor.start() - - def on_next(): - self.log.debug("Looking for next pair to process") - try: - self.current_pair = next(self.pair_generator) - if isinstance(self.current_pair, IterationBreak): - raise self.current_pair - - except IterationBreak: - self.log.debug("Iteration break was raised") - self.is_running = False - self.was_stopped.emit() - self._main_thread_processor.stop() - return - - except StopIteration: - self.log.debug("Iteration stop was raised") - self.is_running = False - # All pairs were processed successfully! - if on_finished is not None: - self._main_thread_processor.add_item( - MainThreadItem(on_finished) - ) - self._main_thread_processor.stop_if_empty() - return - - except Exception as exc: - self.log.warning( - "Unexpected exception during `on_next` happened", - exc_info=True - ) - exc_msg = str(exc) - self._main_thread_processor.add_item( - MainThreadItem(on_unexpected_error, error=exc_msg) - ) - return - - self.about_to_process.emit(*self.current_pair) - self._main_thread_processor.add_item( - MainThreadItem(on_process) - ) - - def on_process(): - try: - self.log.debug( - "Processing pair: {}".format(str(self.current_pair)) - ) - result = self._process(*self.current_pair) - if result["error"] is not None: - self.log.debug("Error happened") - self.errored = True - - self.log.debug("Pair processed") - self.was_processed.emit(result) - - except Exception as exc: - self.log.warning( - "Unexpected exception during `on_process` happened", - exc_info=True - ) - exc_msg = str(exc) - self._main_thread_processor.add_item( - MainThreadItem(on_unexpected_error, error=exc_msg) - ) - return - - self._main_thread_processor.add_item( - MainThreadItem(on_next) - ) - - def on_unexpected_error(error): - # TODO this should be handled much differently - # TODO emit crash signal to show message box with traceback? - self.is_running = False - self.was_stopped.emit() - util.u_print(u"An unexpected error occurred:\n %s" % error) - if on_finished is not None: - self._main_thread_processor.add_item( - MainThreadItem(on_finished) - ) - self._main_thread_processor.stop_if_empty() - - self.is_running = True - self._main_thread_processor.add_item( - MainThreadItem(on_next) - ) - - def _set_state_by_order(self): - order = self.processing["current_group_order"] - self._current_state = self.order_groups.groups[order]["state"] - - def collect(self): - """ Iterate and process Collect plugins - - load_plugins method is launched again when finished - """ - self._set_state_by_order() - self._main_thread_processor.process(self._start_collect) - self._main_thread_processor.start() - - def validate(self): - """ Process plugins to validations_order value.""" - self._set_state_by_order() - self._main_thread_processor.process(self._start_validate) - self._main_thread_processor.start() - - def publish(self): - """ Iterate and process all remaining plugins.""" - self._set_state_by_order() - self._main_thread_processor.process(self._start_publish) - self._main_thread_processor.start() - - def _start_collect(self): - self.iterate_and_process() - - def _start_validate(self): - self.processing["stop_on_validation"] = True - self.iterate_and_process() - - def _start_publish(self): - self.processing["stop_on_validation"] = False - self.iterate_and_process(self.on_published) - - def cleanup(self): - """Forcefully delete objects from memory - In an ideal world, this shouldn't be necessary. Garbage - collection guarantees that anything without reference - is automatically removed. - However, because this application is designed to be run - multiple times from the same interpreter process, extra - case must be taken to ensure there are no memory leaks. - Explicitly deleting objects shines a light on where objects - may still be referenced in the form of an error. No errors - means this was unnecessary, but that's ok. - """ - - for instance in self.context: - del(instance) - - for plugin in self.plugins: - del(plugin) - - def _on_instance_toggled(self, instance, old_value, new_value): - callbacks = pyblish.api.registered_callbacks().get("instanceToggled") - if not callbacks: - return - - for callback in callbacks: - try: - callback(instance, old_value, new_value) - except Exception: - self.log.warning( - "Callback for `instanceToggled` crashed. {}".format( - os.path.abspath(inspect.getfile(callback)) - ), - exc_info=True - ) diff --git a/client/ayon_core/tools/pyblish_pype/delegate.py b/client/ayon_core/tools/pyblish_pype/delegate.py deleted file mode 100644 index bb253dd1a3..0000000000 --- a/client/ayon_core/tools/pyblish_pype/delegate.py +++ /dev/null @@ -1,540 +0,0 @@ -import platform - -from qtpy import QtWidgets, QtGui, QtCore - -from . import model -from .awesome import tags as awesome -from .constants import ( - PluginStates, InstanceStates, PluginActionStates, Roles, EXPANDER_WIDTH -) - -colors = { - "error": QtGui.QColor("#ff4a4a"), - "warning": QtGui.QColor("#ff9900"), - "ok": QtGui.QColor("#77AE24"), - "active": QtGui.QColor("#99CEEE"), - "idle": QtCore.Qt.white, - "inactive": QtGui.QColor("#888"), - "hover": QtGui.QColor(255, 255, 255, 10), - "selected": QtGui.QColor(255, 255, 255, 20), - "outline": QtGui.QColor("#333"), - "group": QtGui.QColor("#333"), - "group-hover": QtGui.QColor("#3c3c3c"), - "group-selected-hover": QtGui.QColor("#555555"), - "expander-bg": QtGui.QColor("#222"), - "expander-hover": QtGui.QColor("#2d6c9f"), - "expander-selected-hover": QtGui.QColor("#3784c5") -} - -scale_factors = {"darwin": 1.5} -scale_factor = scale_factors.get(platform.system().lower(), 1.0) -fonts = { - "h3": QtGui.QFont("Open Sans", 10 * scale_factor, QtGui.QFont.Normal), - "h4": QtGui.QFont("Open Sans", 8 * scale_factor, QtGui.QFont.Normal), - "h5": QtGui.QFont("Open Sans", 8 * scale_factor, QtGui.QFont.DemiBold), - "awesome6": QtGui.QFont("FontAwesome", 6 * scale_factor), - "awesome10": QtGui.QFont("FontAwesome", 10 * scale_factor), - "smallAwesome": QtGui.QFont("FontAwesome", 8 * scale_factor), - "largeAwesome": QtGui.QFont("FontAwesome", 16 * scale_factor), -} -font_metrics = { - "awesome6": QtGui.QFontMetrics(fonts["awesome6"]), - "h4": QtGui.QFontMetrics(fonts["h4"]), - "h5": QtGui.QFontMetrics(fonts["h5"]) -} -icons = { - "action": awesome["adn"], - "angle-right": awesome["angle-right"], - "angle-left": awesome["angle-left"], - "plus-sign": awesome['plus'], - "minus-sign": awesome['minus'] -} - - -class PluginItemDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for model items""" - - def paint(self, painter, option, index): - """Paint checkbox and text. - _ - |_| My label > - """ - - body_rect = QtCore.QRectF(option.rect) - - check_rect = QtCore.QRectF(body_rect) - check_rect.setWidth(check_rect.height()) - check_offset = (check_rect.height() / 4) + 1 - check_rect.adjust( - check_offset, check_offset, -check_offset, -check_offset - ) - - check_color = colors["idle"] - - perspective_icon = icons["angle-right"] - perspective_rect = QtCore.QRectF(body_rect) - perspective_rect.setWidth(perspective_rect.height()) - perspective_rect.adjust(0, 3, 0, 0) - perspective_rect.translate( - body_rect.width() - (perspective_rect.width() / 2 + 2), - 0 - ) - - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & PluginStates.InProgress: - check_color = colors["active"] - - elif publish_states & PluginStates.HasError: - check_color = colors["error"] - - elif publish_states & PluginStates.HasWarning: - check_color = colors["warning"] - - elif publish_states & PluginStates.WasProcessed: - check_color = colors["ok"] - - elif not index.data(Roles.IsEnabledRole): - check_color = colors["inactive"] - - offset = (body_rect.height() - font_metrics["h4"].height()) / 2 - label_rect = QtCore.QRectF(body_rect.adjusted( - check_rect.width() + 12, offset - 1, 0, 0 - )) - - assert label_rect.width() > 0 - - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h4"].elidedText( - label, - QtCore.Qt.ElideRight, - label_rect.width() - 20 - ) - - font_color = colors["idle"] - if not index.data(QtCore.Qt.CheckStateRole): - font_color = colors["inactive"] - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - # Draw perspective icon - painter.setFont(fonts["awesome10"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(perspective_rect, perspective_icon) - - # Draw label - painter.setFont(fonts["h4"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(label_rect, label) - - # Draw action icon - if index.data(Roles.PluginActionsVisibleRole): - painter.save() - action_state = index.data(Roles.PluginActionProgressRole) - if action_state & PluginActionStates.HasFailed: - color = colors["error"] - elif action_state & PluginActionStates.HasFinished: - color = colors["ok"] - elif action_state & PluginActionStates.InProgress: - color = colors["active"] - else: - color = colors["idle"] - - painter.setFont(fonts["smallAwesome"]) - painter.setPen(QtGui.QPen(color)) - - icon_rect = QtCore.QRectF( - option.rect.adjusted( - label_rect.width() - perspective_rect.width() / 2, - label_rect.height() / 3, 0, 0 - ) - ) - painter.drawText(icon_rect, icons["action"]) - - painter.restore() - - # Draw checkbox - pen = QtGui.QPen(check_color, 1) - painter.setPen(pen) - - if index.data(Roles.IsOptionalRole): - painter.drawRect(check_rect) - - if index.data(QtCore.Qt.CheckStateRole): - optional_check_rect = QtCore.QRectF(check_rect) - optional_check_rect.adjust(2, 2, -1, -1) - painter.fillRect(optional_check_rect, check_color) - - else: - painter.fillRect(check_rect, check_color) - - if option.state & QtWidgets.QStyle.State_MouseOver: - painter.fillRect(body_rect, colors["hover"]) - - if option.state & QtWidgets.QStyle.State_Selected: - painter.fillRect(body_rect, colors["selected"]) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class InstanceItemDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for model items""" - - def paint(self, painter, option, index): - """Paint checkbox and text. - _ - |_| My label > - """ - - body_rect = QtCore.QRectF(option.rect) - - check_rect = QtCore.QRectF(body_rect) - check_rect.setWidth(check_rect.height()) - offset = (check_rect.height() / 4) + 1 - check_rect.adjust(offset, offset, -(offset), -(offset)) - - check_color = colors["idle"] - - perspective_icon = icons["angle-right"] - perspective_rect = QtCore.QRectF(body_rect) - perspective_rect.setWidth(perspective_rect.height()) - perspective_rect.adjust(0, 3, 0, 0) - perspective_rect.translate( - body_rect.width() - (perspective_rect.width() / 2 + 2), - 0 - ) - - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & InstanceStates.InProgress: - check_color = colors["active"] - - elif publish_states & InstanceStates.HasError: - check_color = colors["error"] - - elif publish_states & InstanceStates.HasWarning: - check_color = colors["warning"] - - elif publish_states & InstanceStates.HasFinished: - check_color = colors["ok"] - - elif not index.data(Roles.IsEnabledRole): - check_color = colors["inactive"] - - offset = (body_rect.height() - font_metrics["h4"].height()) / 2 - label_rect = QtCore.QRectF(body_rect.adjusted( - check_rect.width() + 12, offset - 1, 0, 0 - )) - - assert label_rect.width() > 0 - - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h4"].elidedText( - label, - QtCore.Qt.ElideRight, - label_rect.width() - 20 - ) - - font_color = colors["idle"] - if not index.data(QtCore.Qt.CheckStateRole): - font_color = colors["inactive"] - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - # Draw perspective icon - painter.setFont(fonts["awesome10"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(perspective_rect, perspective_icon) - - # Draw label - painter.setFont(fonts["h4"]) - painter.setPen(QtGui.QPen(font_color)) - painter.drawText(label_rect, label) - - # Draw checkbox - pen = QtGui.QPen(check_color, 1) - painter.setPen(pen) - - if index.data(Roles.IsOptionalRole): - painter.drawRect(check_rect) - - if index.data(QtCore.Qt.CheckStateRole): - optional_check_rect = QtCore.QRectF(check_rect) - optional_check_rect.adjust(2, 2, -1, -1) - painter.fillRect(optional_check_rect, check_color) - - else: - painter.fillRect(check_rect, check_color) - - if option.state & QtWidgets.QStyle.State_MouseOver: - painter.fillRect(body_rect, colors["hover"]) - - if option.state & QtWidgets.QStyle.State_Selected: - painter.fillRect(body_rect, colors["selected"]) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class InstanceDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for instance header""" - - radius = 8.0 - - def __init__(self, parent): - super(InstanceDelegate, self).__init__(parent) - self.item_delegate = InstanceItemDelegate(parent) - - def paint(self, painter, option, index): - if index.data(Roles.TypeRole) in ( - model.InstanceType, model.PluginType - ): - self.item_delegate.paint(painter, option, index) - return - - self.group_item_paint(painter, option, index) - - def group_item_paint(self, painter, option, index): - """Paint text - _ - My label - """ - body_rect = QtCore.QRectF(option.rect) - bg_rect = QtCore.QRectF( - body_rect.left(), body_rect.top() + 1, - body_rect.width() - 5, body_rect.height() - 2 - ) - - expander_rect = QtCore.QRectF(bg_rect) - expander_rect.setWidth(EXPANDER_WIDTH) - - remainder_rect = QtCore.QRectF( - expander_rect.x() + expander_rect.width(), - expander_rect.y(), - bg_rect.width() - expander_rect.width(), - expander_rect.height() - ) - - width = float(expander_rect.width()) - height = float(expander_rect.height()) - - x_pos = expander_rect.x() - y_pos = expander_rect.y() - - x_radius = min(self.radius, width / 2) - y_radius = min(self.radius, height / 2) - x_radius2 = x_radius * 2 - y_radius2 = y_radius * 2 - - expander_path = QtGui.QPainterPath() - expander_path.moveTo(x_pos, y_pos + y_radius) - expander_path.arcTo( - x_pos, y_pos, - x_radius2, y_radius2, - 180.0, -90.0 - ) - expander_path.lineTo(x_pos + width, y_pos) - expander_path.lineTo(x_pos + width, y_pos + height) - expander_path.lineTo(x_pos + x_radius, y_pos + height) - expander_path.arcTo( - x_pos, y_pos + height - y_radius2, - x_radius2, y_radius2, - 270.0, -90.0 - ) - expander_path.closeSubpath() - - width = float(remainder_rect.width()) - height = float(remainder_rect.height()) - x_pos = remainder_rect.x() - y_pos = remainder_rect.y() - - x_radius = min(self.radius, width / 2) - y_radius = min(self.radius, height / 2) - x_radius2 = x_radius * 2 - y_radius2 = y_radius * 2 - - remainder_path = QtGui.QPainterPath() - remainder_path.moveTo(x_pos + width, y_pos + height - y_radius) - remainder_path.arcTo( - x_pos + width - x_radius2, y_pos + height - y_radius2, - x_radius2, y_radius2, - 0.0, -90.0 - ) - remainder_path.lineTo(x_pos, y_pos + height) - remainder_path.lineTo(x_pos, y_pos) - remainder_path.lineTo(x_pos + width - x_radius, y_pos) - remainder_path.arcTo( - x_pos + width - x_radius2, y_pos, - x_radius2, y_radius2, - 90.0, -90.0 - ) - remainder_path.closeSubpath() - - painter.fillPath(expander_path, colors["expander-bg"]) - painter.fillPath(remainder_path, colors["group"]) - - mouse_pos = option.widget.mapFromGlobal(QtGui.QCursor.pos()) - selected = option.state & QtWidgets.QStyle.State_Selected - hovered = option.state & QtWidgets.QStyle.State_MouseOver - - if selected and hovered: - if expander_rect.contains(mouse_pos): - painter.fillPath( - expander_path, colors["expander-selected-hover"] - ) - else: - painter.fillPath( - remainder_path, colors["group-selected-hover"] - ) - - elif hovered: - if expander_rect.contains(mouse_pos): - painter.fillPath(expander_path, colors["expander-hover"]) - else: - painter.fillPath(remainder_path, colors["group-hover"]) - - text_height = font_metrics["awesome6"].height() - adjust_value = (expander_rect.height() - text_height) / 2 - expander_rect.adjust( - adjust_value + 1.5, adjust_value - 0.5, - -adjust_value + 1.5, -adjust_value - 0.5 - ) - - offset = (remainder_rect.height() - font_metrics["h5"].height()) / 2 - label_rect = QtCore.QRectF(remainder_rect.adjusted( - 5, offset - 1, 0, 0 - )) - - expander_icon = icons["plus-sign"] - - expanded = self.parent().isExpanded(index) - if expanded: - expander_icon = icons["minus-sign"] - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h5"].elidedText( - label, QtCore.Qt.ElideRight, label_rect.width() - ) - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - painter.setFont(fonts["awesome6"]) - painter.setPen(QtGui.QPen(colors["idle"])) - painter.drawText(expander_rect, QtCore.Qt.AlignCenter, expander_icon) - - # Draw label - painter.setFont(fonts["h5"]) - painter.drawText(label_rect, label) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class PluginDelegate(QtWidgets.QStyledItemDelegate): - """Generic delegate for plugin header""" - - def __init__(self, parent): - super(PluginDelegate, self).__init__(parent) - self.item_delegate = PluginItemDelegate(parent) - - def paint(self, painter, option, index): - if index.data(Roles.TypeRole) in ( - model.InstanceType, model.PluginType - ): - self.item_delegate.paint(painter, option, index) - return - - self.group_item_paint(painter, option, index) - - def group_item_paint(self, painter, option, index): - """Paint text - _ - My label - """ - body_rect = QtCore.QRectF(option.rect) - bg_rect = QtCore.QRectF( - body_rect.left(), body_rect.top() + 1, - body_rect.width() - 5, body_rect.height() - 2 - ) - radius = 8.0 - bg_path = QtGui.QPainterPath() - bg_path.addRoundedRect(bg_rect, radius, radius) - hovered = option.state & QtWidgets.QStyle.State_MouseOver - selected = option.state & QtWidgets.QStyle.State_Selected - if hovered and selected: - painter.fillPath(bg_path, colors["group-selected-hover"]) - elif hovered: - painter.fillPath(bg_path, colors["group-hover"]) - else: - painter.fillPath(bg_path, colors["group"]) - - expander_rect = QtCore.QRectF(bg_rect) - expander_rect.setWidth(expander_rect.height()) - text_height = font_metrics["awesome6"].height() - adjust_value = (expander_rect.height() - text_height) / 2 - expander_rect.adjust( - adjust_value + 1.5, adjust_value - 0.5, - -adjust_value + 1.5, -adjust_value - 0.5 - ) - - offset = (bg_rect.height() - font_metrics["h5"].height()) / 2 - label_rect = QtCore.QRectF(bg_rect.adjusted( - expander_rect.width() + 12, offset - 1, 0, 0 - )) - - assert label_rect.width() > 0 - - expander_icon = icons["plus-sign"] - - expanded = self.parent().isExpanded(index) - if expanded: - expander_icon = icons["minus-sign"] - label = index.data(QtCore.Qt.DisplayRole) - label = font_metrics["h5"].elidedText( - label, QtCore.Qt.ElideRight, label_rect.width() - ) - - # Maintain reference to state, so we can restore it once we're done - painter.save() - - painter.setFont(fonts["awesome6"]) - painter.setPen(QtGui.QPen(colors["idle"])) - painter.drawText(expander_rect, QtCore.Qt.AlignCenter, expander_icon) - - # Draw label - painter.setFont(fonts["h5"]) - painter.drawText(label_rect, label) - - # Ok, we're done, tidy up. - painter.restore() - - def sizeHint(self, option, index): - return QtCore.QSize(option.rect.width(), 20) - - -class TerminalItem(QtWidgets.QStyledItemDelegate): - """Delegate used exclusively for the Terminal""" - - def paint(self, painter, option, index): - super(TerminalItem, self).paint(painter, option, index) - item_type = index.data(Roles.TypeRole) - if item_type == model.TerminalDetailType: - return - - hover = QtGui.QPainterPath() - hover.addRect(QtCore.QRectF(option.rect).adjusted(0, 0, -1, -1)) - if option.state & QtWidgets.QStyle.State_Selected: - painter.fillPath(hover, colors["selected"]) - - if option.state & QtWidgets.QStyle.State_MouseOver: - painter.fillPath(hover, colors["hover"]) diff --git a/client/ayon_core/tools/pyblish_pype/font/fontawesome/fontawesome-webfont.ttf b/client/ayon_core/tools/pyblish_pype/font/fontawesome/fontawesome-webfont.ttf deleted file mode 100644 index 9d02852c14143d311bbc4fc1f9a7c6aaf6b6d3b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152364 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 diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt b/client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt deleted file mode 100644 index d645695673..0000000000 --- a/client/ayon_core/tools/pyblish_pype/font/opensans/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Bold.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Bold.ttf deleted file mode 100644 index fd79d43bea0293ac1b20e8aca1142627983d2c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224592 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 diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-BoldItalic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-BoldItalic.ttf deleted file mode 100644 index 9bc800958a421d937fc392e00beaef4eea76dc71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213292 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

JU{jq<7f203t|Ihq? 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

93Tg2c}Lc^2oc3nPS$ z;(1O?7>~C-N&_)HF>1THA0EJ6auWtJS9RLl+Y1MFu)W*12H2VWPu`@7nnkz1Q-ZGOn@l}H$L|RKnn?mk~fn~ z@+aKE`*SDNOr7<1ZFi>T@Yek+9&bH+kxNV*ZOTgSKQ=CnzQg^*vA<|R?T*~PbJO{Q z+p%WxW2)elyrGti`Vj?C8YOtNkxVVvP?be1u|D+MfaKg2*T681MmC=TS?7zO?zj0^j@ z?ie$Mj&%C4wT@~yix0NqhYz_E9Hk6VOJz@iy!JuKD9Y?5af($vP-1U0V|IGz zgjG%|r*66!x?4}M=3RmcS0jWLDk6u1GaI58q>%S%HM}cMIGool3|LnLXC`-G_aKTG zT^)GT4t`ggg4t7ZZ?+UoRd-*tM^+`sdeYM25Gd#|F{qZ*$*lADB8Y_4PF?F}>ErmGnr|CeQtP{H69| z+|F-*^$U;B1RR z!H8)U15PAac!a~=fet5XjHZL5qgn9PGP#i5+=mp0r>*2Jhj`0+Bp$;h+&0m%r^!?q zE^U|{G;<+TcF+(pAm%1uS^=^cK&&krpi~A7w8)iUZIwzFg+c?9(k`Ca78h`DI!*oy z)|NLcg282UI;`f6i(XGVTDMz2Z(m=pe58O+oERV;%p0i72p6uJG>;5E07U< zTw+m32o_8)UT|^l;x#KiJbi}ae*6-bd|&r*&Gf2f=Ij>k)Vn*j4CFcnLNSI4v-rrZ z@cvKx1rxplY>2`D8&aeunu%3qTi~S}b+$$1SUb!~v?X9$i?HCF!9qo0C%KjmE)GO) zDR#jj9amR<`_l47wV%-~pbtlVBRa@^-%+^^haBJVMn^i)A_VUnVJ0Wk)9l4cVO1<3 zoX9dI%C=B6mStP8W76mD4GC*7iI(|;9@B9iN7GR;92$VHnK*qe1^WdY_W{Q>+2>ft zug1@;Aak%Rg;m1%0tyKSbsZ9q;mCkJV>o6IzS4&aA)_@}?X;){TKMtahF>;#8zQ+J z@FQnLUVtA5O$1Phx1od8=(Tp55R!o65L{Z5T1P#ij9V7on#)Uz1&$#Qi}>TiKpEsGCt^Io|ursAt`JZRgJI-Tj6q4ii=;+Qwtg->j6-*_Lbx zmPkZ0md1oqyi$l5&M%F)HvkI=2UEj+v5*U0NS27BX5q`Ta3nRux)`|b1QiF~?kP~p z^Y~FUW|@>>SXxHYgb1Ke$w4q>GO0r#dJxkZD%(0M=)E)R3{;ch_?eAu`Sia0wzm9^ zoP3}!cbvk8L5VKg5&f#CJQ0m3=B29Qv1h8VLS_4?^oX2}dfW2sfF&hX-jOgIA zOi~5MKP7g9r_{*HEO(NqX(e{0WsH*Gix7a%kpK`nh&1#thMt8m6wc^WDg^J~&T>C3 zkM1q9p5|`HF-|d?xqC3js$@KwNqL${9r*Xh&sH+kMcnsRDNOmEFds|iQ}XuQNuq!x zn8ZPBJ_NyfG3Y*+0~=#hK=13^E^Z^sXakq?gOA1qj67yNRx$b7Z+2P1XMc7!8+%g_ zSqJ>>3|R+|d?%M|ixb1RcS$hVCEvl($lzdSK0*_%`-Izh!-lx_PjJZ(HtcEr za@J?Hjk9K*o;07{#ogi(e}Eb=7EAH__qiMXhqw>lTe)Trj{juYtAgHPw!_-QfOwEq zIp{jMT;ZZpfVxwFt{1W!yc2KNsUkO|gD?W?qC6;BpJiK5WKw4CZSLzrf!g_xp3rRb z)C0SJI3q(h#{Px#e}N&4?^ z7yQRp9JhX-_usDhB&Qq6&A{pZW8J%9-4f&hdPS|4%8_&+s1~`qlS(cDB-q#9)zt9_ z2nMAqtR7r#F>n{9%4t|JVoc`Z89cX-y?1~6Ml9kEoTtO5Ov}%=&a$2t9sK;%rkf_~ zN-C1@Zz%Z7h2R0{iC^#JPkVUyWLrEK8m6VYYBS!S==6`lc?b^SvwaY-2(b$UDH;zC zdLQ?V^;*+MZ%%sICu&slq?c~3zH*>?%`eKJRVJZaw9 z$)oOM7H7r{%gX%-pxFub-~!J99%dO&v`}rG34cPxX0Y1n;)kq(XV!l%QWs=zm8r_<~R&Ge=^MBjFbnRSPg5jE@K&Wb5bjuuJ>`di(&ef2oR$WROpyB1GR;?sVwgcpsCu#YFc4deL}4I=Yb z(~L$TqP^h_E26>e8ic2=P7PExf&&Wr!wtR=<)))UdYkC`JyW=2+@5e8z)D!wqgVko zk1p;~HmnFrC!?*ixa}SkLqA=y-Wmxj`@Z+1X!9LE+Ssvn#mn=)rF}uwTv$?+3eeb= z!0KiJG)~CVESETmD29=Ojt23RBhKXU3@X1tg@9yTNa=R$=eQaJ2WtT

o&XsL=oKPcbq0Wb-QCn0&=Iqe3DXp!CS@~r9fP7ZSaLwj zBs>&z8X!GS+9x?E`VF4xBn*53_{UEKLM92PvGv+P#!G{@8(R(=?;1N03VP4uD^6b6 zjZmGz4oonUyH9~$GGz9G#=at7QuA~#?5~+FSH2q3v?8PdE%f+&u*~M=g$mUCeDcDb z+*+XMu8_&-rd(1_;UZ>Qk~_DXCVemzs{$X6D1)LvMru*27=s~zAuxRnMvA>UHEOJT zMt;n=Z^_zuwFit9>k{Mp8at~;4OwTbI8Z<9l4ZsLb(ir6dCY^xr2JX8ZZ-ypX?rhK z+i#PX$O|7Z4v51$Z}W^7wN`D5dh{Gsu}UUm5`#zT46Z!qo_wO2cg zVdAN6D5N@1pd!e)TTUNO$`yG;7Qwcj4P+nRBr>nvu*uy3x6wfW^WA!iR%lSFTbD;z>oH>RV$>u_KKRG8P7#X&g(*)iI~`8zjHr`dHMXB1IESOh#3qUM@agRC*^HrwXyD5+ab`jp!JT8 z-=^t+Y1Tw@$%9Bo{RhGZ)}YRO&^90V*xJ}sm{(9F0=arF=v!3e@Otw=U)_-dVuVVH zvD_*tk_$y%UVdSI;kZ^Dwrb;Ba}b9u=g2vsd{34~cRNB%Zqo-U#!|yx!Tw^4NP#jnVCQfon~2m{Z07;3D&1gm;Gd~Ea z%E(v0a>zKv-O<=wt;c$$_v_~xF>F|4V-r?4O=(R-ltHdRpC{LZS;>A0q1vtBwtO=Mx{Jwa%*{RA6|sBS72@J3*_n9a7C1*nlWLb!*o^x ziqut#r;PvIM8p{Tu=Ci$KQVXZR6~N+xG8IWxH$r`HCB+ z3j5-@%eOgX@2}D)j_v)xDl7Bd(GP8EX;o>ymgP4-EK$|N_q=>ql}Ak+)0|3#*D&1k z^NG0=R^JRi5VI;s)V4|*E8W^Sp{_Ece}C7gkt2r>AA_aJn6xp<x#PtFp%D8{e2! zSvhXVp!zX`#BmvTMmSp=_MeP%@%?bl%EK zDVxY?zC{Xlyfbk5tX9Ts-`%wbPbe zZ(HR*n9;9y*_I2eY$Ffem1ZxG^eNCPjSuu3o9)pD??2>J`o)lZq*tHvFjABF^Icbt z%57VC;~cU@tt2xX`K^o9Y7rkWR?(0Bwo zqL=BYdEr%1`63w%{q0_Y>|J|_f=p(;dxdglWoCA_8T|xhfZJc$mnAl2%$hNMR)(?3 zb6$6Y(e=jPHy>ZPw0-A-^Uq&k?on;;;!H7%P84163{TI@bRvQ$C*AGNc4JoeNywRn z{hg@@Oow|*$VtXo$XSlD$fh+FmAnBjVG37`hE0+1;JSt&w0qRZ5Kp|}ty1h!eRi+8 zH`$It_FfAN8+8_$Kij4LZ#MM*&0w+p$9u(G4r@rE3y0-Eo+8V+ zdT3Ou?xotOq@>ApKDDH_W?=RTrR;;j+Bo&Z(lqFyQi}kDN#eF=5Zb1u`BwIwxn^F) zg3?~SE2{n2{m!>#6_C!rmO_nsN?lwtXJ+II@PMoxpfGmp0gcwu)6f zX0|0A8||a*wOF-8;OAVO5e_@Evx{&@6(}iD@{1%X-vwvyvXXEHtR?{+aeMi>+0Jb8 zjB}C+f$MoSgm5DirTl{4s)etEoBWpRGR-ncOa-D^61#;GTBlsAG zmC^8A|3mSrfz3K{0K6jNqC#&T*l&yYU<%=qwg@in6TvN6> zF}O@zA;yR%u_ipIwsuh1*o^%5zZy4hUa#Z3#mMoC7f--QTROI(O3Hg!O;<`Y8Y|0! zN>Oi5T24_8JfGbq?2r~ILv1;fIPx`mJVp6A3j8Bce-Auh4X)cT*_=u2z{?a`JwuA% zOoJUSh8afIehEsDhM9>ZDi|VNLKk9;^Z;_f80~*aYwlOy?E2U+g!D<;xPI|*X&(vO z**oW^jcvg7ym`*C{p!ZL=E@CU8-FaSA3ChAOjLgSS5aP6-#9E@X1sCg#K`)g!{eiW zePlE~t!*~7m-8N{0nOEn6Y%YgBWtr8MmJPV89UZ3TrCq?TEfGI4H;53dURn`;p{mR zCrs$u*jUjwv%YUV3;=!AaK*@h6$2-ocvGzLe zjE#%N4hdJs-nwD?@GI6|GvU85zvYe}2K$@w!jnH9kQg?6!Ho0fIyZfx=au9�LN9 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%0DpzNyo5m`c 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 gdXGe&xpSVmb3UZsF@w&B^gHI!dG<);L$9dk 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 DzdWN1 diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Italic.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Italic.ttf deleted file mode 100644 index c90da48ff3b8ad6167236d70c48df4d7b5de3bbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212896 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 I-Ko?4FC8~NI{*Lx diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Light.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Light.ttf deleted file mode 100644 index 0d381897da20345fa63112f19042561f44ee3aa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222412 zcmb^a30zcF{|AnrbMKuyE3+}fun5dB0wN;F2#6b_0wS`BU<__7F1X^7n){v#nOW`& zqM4xZ2=bMHO(p3nL0pU?T+ ziBLjFAU*_=lsRzVr&LahdormlJ$Qa^Tu-gRiGUUAjyS#-#~o{?+<{LJp^1f+`iS$q0zXkW(r&dlGYyW21UcyJbO^BE1Z z6TV|JAu;jOW{jQo?+5pcB&5R%+|!dV+|iJ2AAK_Jm(hmazZ10zCkT0?zvWZ8|8DSa zw=T7Otd3Mw;aE*Lb{2oQPqnt?37m?&bxC?&9VtJ_drki2A7y9w1Eh`wkusvdoko&K zM&Q;I@)hB6l^0ggF1VsnY*V;!KGNIs?~!rznu_DJyvQpAjuWmB?k6{~566QUR!O&h zLkFOwz>gzjS3gbc+ob`4KIPtM3^EWjojy+-zO7s*ORJV_JQlfB|B zGF@?$%%^L}N-lvEqxBJLNDZ#lg8G@Wc) zd-!nNe}a^7hk%pg0Y8fr<614lJv%3m)|>nkQZJutA}L%GiIdlIjvQ0;A=71i?y~U74SvS zhjF#7-(sB`f$NUw-*4BK0}rt{X8ke9W;x#g9)*lxTmzF6#;dF`dBK>~zP{4ed(VF^ zUk}H16{Dj(_uaifsQ;QQZ@}r@y)uz3Rc^xi0Y8%iUq8jyKg#5i$;LnSkXc!7?&@VZ z2L1m3?U}qYnPWUA)7dBYO!k=kx9_=`fO9?c#QE0W7!NR>XME>hTN|tmG#+vlFU+FR z=+o$z*j_0c=>Ktj`!tWj%*+emy5-kT_YEj^Q3AJ2lWFz!h7k7y>?rj>Gl&q6<0( zaw*H$apBE-~ zkMjKtS4@vFT;Z91TrpfLMv!AF9l4;g;d>^ot;AT&CHz9NRq5!DfRC7N*h1zDtAMjn zz{e=)Sjf4en238rdWmVy}WZVJ;3JXui{?F5M-6vq92%?i*txYWHQdi00uUP zJTGv??(xgR-F=&U5K;`UMsCa&(l`<>x3eUZ^~?sc#uJhWxaP}^Bi};r?NNup#xVZ? zegmfi-|0vWzlrQ6H>9JSB>Mu)Uno#~4*k2H5E{6#o3zHh?`@S*68ko1<#z%yT~DN2ML!O^I5>@OhizTerX) zYndp^3)92QrhvbcXJK1);GZh+;6fsBj+ z9FP(4sgvmjCZBD(?F8;)F#3At%YB=?13nWS!%qkMM*SPt2CcI_fNB3_cF@%XIO3^w&wTs3Ao{BjEaw3{x_F zyPQ~*jOR7L#bxjg#wjL(Za;$#tH?}dU;H@p^K$h3T{;?SEJoJ~5 zj9|7@_SG;SlyT?&|g8XKz{`7C$#H0?uYg=bk+r~9R3i-Bg*x*3uGlZ zEo)iCzlebe(Lvkm(KnGLL?lThhYTW%2=oDsrOC9CE~XFD-L#4ROk24yE{!|My~e%4 zea`*LQ=aG5yn%P}b^HVTZvFuO4*xIy10g^t6h;g83+shb!UaWO1dYguSQ+tJ#G4WS zjrc1v->$V=?Gg4UdyGBF?y~o^_qJ!+r`hM)7u)yRAGaTLC>()~Acx%%<%n@~b&QJQ zqQodelsPIiDk3UADkrKUYHakIw*;xRwWYNcklO)qE@>ohlXqz+>Y`PExRE{$h;MSi zGQ_U};*S6^X@htLAbya4lz)1c~Syu_U4~;`NBPB7OnH z#2#q3+3hmK$@X*~#PFNh_rl((Kln*6P-(){$g>-3|IKe-(G(A^7+puKtmOBLuDR0#O7vJl(287(fofu z`@h`PU%~NzsL^|tB9}yy(DKlbf${&*X*izYKl-PhR{YbIJI~7*Eu7*v@tgTA{8oM& zzn%QR?;t<&JIPP5(EsIkkze^nejWc1jRALeqH(k{ji+5`0_{o@X%g)Qj(5@SG=-)j z2I)c5X;0dV_NIMkUz$PPv>zQrb7@B!O-pDg9Y*h=Wz@s(rX%P`I*N{_6_A%IT205$ zv2+~2ht|*ubRwNZC(|i(Dy^l{=yW;*QO&({7M;yM%GJ;vbSG_~yJ#c7mw${tO83&o z=sx;5H-Vc-|3iPE*XWP*I{gW}{9pPD{gwVkf9EFA8}twE5pDywk=w*==C*KKxoz}M z`WL^Cdy{*Mdz(AYy~ADL-sRro{>5G7-sdiHA8;S?kMmFP+5CQvaFnm*r}5MI8T?HC zUVawWfnUpY=Tf+l+$cia1BaJ;%1TRy77r;ZEXdCroI5CIV0KpKfd2j58GZZo?$tBB zM_Ou1x1_|b30*qH#zc2?MmZvbEheKuAE4E!RZ3AI@Ej#w>`k;HtBH@co3bmNSYE?Lg(iY9xdy$PKVZ#G3YAWovvtdW8}EIM!LNt-EAYO z(cYW%W>YxrP3JS6_T#jCNEyDb&2)O~O+U)t^X2bCjC>G)1BU~5$WSq8?O9FPvuoOB zW|+g_N$4`DN$guwc_UyWfy2|6NORAi9qqFX?PFFWA z3+N_AWla?4Pt=DsWk29)GFH^ko&Z%|dUo;ProbV?%bK|8Y5kb(a)oQ@u04pZA0 zJmBvy6QBji0frq8hKBm%Zc>GVrX@qlyvKG@6?TNU6XQKiTm`#w+JD7b%C0Q&UunC& z!U@_A9$Hq{Bt#Dynod>P*T?9 z&IHEXl|ClPI-HbpD6rl0}fvY4#8vN7YDA}hki zK<>ub%fcK@ZVxEzah8qsFt!7R;;vvi{9#$(m6Ec-L!E<%l$U|34v&0=U0{zBqO

    !k&#U&i=je*`$nCp&5a2`7C4H{=MyGG>rI11B|9P zd)C-Y-#B)B$3t;j-N~F>Y5eq~@?|>=M)zE~~(w z1K2q)E`u4<+sm9|ogQb6y~$lv#!$r2BV*Zz3>oKMsthhEYk$-ZSwItU;IfQ4A2OS= zC)M zaI4&Ew~h+H|YXRz}b-t!_muXOfrqCph5FM-?@HBo$)3@>v)B<-PZgw+{;WR&tisni+2 zPH>-Amt;F4hZeU+7H5}57G*DwbmZ7e?UXN#%;8HT_}0k6{IbY`{Nl*`>>-h{u9(so zLvm!at7B=1E4WnY;!8!0$;&N_9GrV!WNvoP$Uv95RDt^iT#MwXfzRNd=6P`t9-ouF zEOKD>hDdF8ST>jTLN;}}qDnis!b*c&)>0ESlp0-zQiGw;z(pDs8+I8k7zBf1ouSph zi*O4{sf(1>lEvg{@)Hq^gf0o93VNJAbhu<_eEi_!%2rqsb3VeZ}T!o+rPH|;fQr?c0A#D-tng6Gslln$x#haPe+}Kx)60a>Srf) zZgK8)9(BIreBb%C^F~Ky$KZ~gJ8tawSjT5Op6ht2#|?-piK~d47PmC+;kX|<59nOdd2Hv|omX|<(s_U9 zqn%H6ez)_toqz4z8gGnu#3#h}h@Tn1B7S3hWBkGR7vo=#e?R_8m*6f%T`Idw@3OGV z>MmQm?C)~4%dLcg31ta233C(HBy3MO*wxx~Th}MMzR>lxt{1z0)%Ev8F)=7HHZd)6 zKw@EHMdGx?`H8C%wUN>qm2Uq{rpcX?=ed-w!>*TI7hPYuesi^U*K|+sKEC_X?)$sH+Px*E zOUlHQohheMT2qawA*r2Hd!^>5j!K=Dx*&C3>gLp4sV}9zn)*)a2dV!~%SaoTR+zRh zZDrcpv^Ub;OS_RK_2|cO8FHL_W{n7Mi($A*9pZ-nHK|PQ6{I%DJ zUeEV7_b%$axA%qKANEP=)1yyKpHKUI**B{1%)T#YXfyg{EXcU*&TwyVH}})`i|m)! zFQZ>kzp8$R`@P)X(!WFhj{Q^n_wB#C|DOYT3>Y+^d_diRhX;H<;OhZDW~OCkWImAj zV^&m_D{Fk#qghX8J(Kl9*4eDLvMy$Q3S%0d?aCgLy({~|K=r_qfqMpiozo+yA?K%@ zKL(WzdSTF~xz619+_||Q57rD09b7p0{=x6$De?mHlJk1z_0Oxxo0j)p-pBcx{G9x` z`9}+AL2<#Rf^Q153fC1j7O9GYizXCJFPd9aU-U@P??VcQ+%shSkU2vhAM(zS-;2fK zpyJr#9>uxEBZ{XKFDl+xe6aXv@jJyo4K)mP4J{d3H}urduS%jyR+Q{7dA_u?bY$t+ z($%F0OOKac8m1d|&#+hS>3+|1Wu3|%@F-!hDm~A5-t=7c{8?@&pILsO{N3UD;fceC z4}WF&jS&+@yfWg05#NoB92q|{ZDi%h)gup#e0${eQ5{E(A9Y~VucL!T&l~;LKmV&# zRn}C_tz25As2W&xtm?I@kE*_^`nj5`?omCv`u^%o)i=kKj@dKjud&=%?O4m$^sz-_ zE62_lyKL;kxI4xn?SJPL_;ARY*v0}!>nZnHUnbkAb-uvjRzOxR@HqD+i z`}~|Pa~kG+a$m@OQ}26Wu66FTxeMm5p8NJZ{k);`w$J-+evkRH=RY(5#)3Wz7A$ya zp>1K!!rvDyShRZ4hDGNV{jsQZaredZ7N1%oF3Dd~x8(Jurllj6o?I5RtZ>Xy|#QukQhvAUP)UakA6?)$o1 z^}71-`h@z7`l9-(`kD1B>o?WETz~xm@__k)2@kxyHfZgnwZ|V+Jvi{e!Uqqp3tLyW z?ze|ZA9`xNYJK_o$JhVyaNfh4AO7GG>m#!sS@_7s4IMWmZRourf5WH^(>5&Iuwlc# z4aYaUx#6=7*EjMTZ5!h@_S%@garDON8<%g~xbg9gCpW&k@w1KJZTxGKW>eUv_)UE_ z6>h5BG;`C+O`A9E-}J(!cQ<{t>G#dzX7lEV&7C)=ZXUR~Z1cp;3pPKvxpDK6&97|! zVDopITeg_CL~TjlGGNQlEn~LK+H(JvOB*G-FkKFkK01F9BULZb^X-lOql73_qy(xf*V6%PPgOo4GZ@H|#lkTd&b<`8iq= z=MGfycn%k!qg2E38gYn66G$@RNpOZaJ&`@qyVH3nlE*7_Ts6-FY-qwdkF$PQM)qBiMtYO_JF6*Pf?mZ0zmr6Ae_ip{QIg+jE5 z8V4V0$`*B5xA19(vk$oGiHTTed|6q3Vww=KY!(~^28i)iYrGgB*s$%Dn21#+ z;?E>69_}bm;r-55O67&)1V&-a7Ex(+ro_a=rUV7KOkKH@?x|@hE^ClY853&?3Q~4Q zmzqi~K{k^p@|RzIddP^6-Gkrrtbf4$V&jl=+Z<~TUuvAXXwAH}EyrgpnlqOkU-?AP zwePsNi18hhX>*sd`-UypZ#n&vI-|U=Mhfk}V_s=(Cn+vq0e5QH)EIiscpr8Rrni1C z3fLXgkRafs3-$*FyJM3Qx+ZpT*kTo0V$zx%u?dM{a<_=ios)RIJ~2s53|3i`p&diO zmH5XP-DP6!_5zt0$%14FawVI($2g-zg++8m#bAxD z`KGliH$U`1{T4b0r_Qi58`iGg^ho`hEmCvyr3yY?;7+_iXB~h3`Qu;w@ZZla|L4EW zpFRIPvai#hJMqO2zkYJ{yPx^ko4F7IP<=isD^_8@+)hxNM~1i)LV_*U9IFw9) z0~F~!Y@w!(f+aeUHwqR_h*^`8=g|lejyz9?1h#_BmNTalcaM)jQ462ya?si`S4O6EWE#^C<$Q@gi6`@nPbl|IAl6&Vjx&8jma z9(r=hdq2_FmRuSyg|D8QcA`_!v|S4pOq{Wt&a8WRa`C{`x$BgyO>cCzWm(J!%%V11QriRFHLx1*NF0=#lA^^Gu^K zGBI@`s}ipkVRsZuC+W|)LkC@?S7I|1x=2m*0UCCrtk}{`kZPtZjb<#BGa0EfhAZCw z@bnE0G^zHHZCp31rtcg&Cb?RP6fR9ZKwrgk`{22s`k%|I@mvvw1Ns0#efzmCJQ+Y4 zIaFMjj9jcM$V@-o{%|c#YS=KHJy`ln>iGoSK))8`r{)S_$BTdfxLejR}5~qx`9j} zfDHA(w8i`G3`}uYN6=3>Qe2GDdtk*fu_ve`!EP-kV1{OhM`XP21-DyLn$KMAVV0_Z z87bU{=$YFib5aMl4jYUdhnaYuGn0%Ac_OdiQ^+#6r{!`DXJ@7pH^3^*MEo2HDbyl{ z?&P+ZiB1$rNU++P?@=pxL%xTHN?~H=1=;r`BLoQCWp*W-sr(6+z!@R(hwEQo|MuFq zzcimd^6ZOGJ@w*?2e`}9V(CG;kj|!y=rn1$v`#uGy+DhwtCLBKq$a@s8UQ%}a5Tj3 z)`LWp>ay0_I>LG=#HJgC^f&P*Pig2m53Gb&=c312Z^MXkX}k=tpHN zQ;b#%r=(VgAT&>*EBCJGdvMKjkEcF&`P8S6oR!w_`ZBudz|p!ts` zCM^c-C>aIJ5aIBoN;YKo}~&oeBbIf+I>cRmrmYa;CVvU!p|7&Vc7HKKW47ap`;M=H-1Q z?~VBU-(P;#(0J_9Qx8lzI;~sj&-35>Oc=HLM5q{abj$gxL#w+aKlAAN9TiV)nDwCnlk z%`K4(qw9cCBlx5P>FJI%@?k-|Iw(Yl2q%I09;B3Pw$K8P&FdQ6iABb{UONwIPZoeS z=>K+j1Em9*vy;k&{D%fe-`$w^z72HU8mrPRdBMxA_yar2z0Sj8borb?Ls$kIg zKcpW`0eP=XIsfgi@BCY8rI+^}JGy<`(>2xmxmp@dSDv($g-JW4gH>bx^Uf8ig$}*- z&Z#F(mTxSmJ%1lCJO#9l1cpTt@3x94k0qRfS3w^sV4S!D4-e|f{20s>iyx&93f*a? zw16vbKF1$v*{c{H=Bl(glc%1~rws4*4G4KOpE%uZEEnk{~LXJox;XG}-Q_!mxVb-$F6-cW6lGQKDb|T;iWy(4h||j zdH=H?y?JK&tJ!(`XHVEQ?AF%0ExYR$Y%d*nkgub>X5YsdhIKgBRY__m`k4-6Pzz_ehP(O-gf56CrYrRZM z0-d6FrO?1I#PZUxQpOT7f^M^P`Fu@T{9OAVG}x#c^ybvJzWnR8hd!DmIU7zM+B5d) z@gpDSbEPZNf)k;`^agC3533k|<(o3Cu_AuG#k96;V*O^zB%3b=x@^FTiW8kt zU6~o zFZwKTsyWRY)vo}Xa+1*5>Jd4a;0^B7S5P>YJ0Q}i;0HkFrRd=*-NX?(~Qgr7+jS}mr+bo{L1 zVyQ;<6z+hSDmaA*h{eB!IM54nEuRK(@f_4fRMU;4rIgRT- ztR3ypwFawQi0dSThN4U~#%$IWdCWpg)DTaM-|uENDL4_9xxFj`JuF^I^ZU|18)UWU zIVBuzYg?=xJ$UT!oW75*F03nxp1QH{xqo%+H=>6-D{`hc{*Io%!&BJjzJkS@oUuho z_Hzd_$K5j|G1XI5sh2LvJ`aZ&sh7A%U6L^7OOq!wVHMzWBYMM<)U;nikxYCx7ZreYP@yv2e!5ChpQ#(zVGW z$BaF5=uJxFq@;+N3E#i)GVQqYHEF{&=_P4*$L`fdbUmb((5+t_k)D-yt$T=;Q)d{; z2syTCA)Hs71Q+T_Z?|1g=#(OKxQ?pSL{u6LI$luGJdcW{N|Zc2#Ws_D+Zh5bKvKm2 zsLyetkBJqP{JNGyZ%^m)`f=`=Ehg zhh0|sR@n=3A|K&3hz@93yUFOd0Mr%U#iywzf1d$lGh1XkDdiZN*k_REsin$)` zZ6T0NAaKMO0LAMB57RMbk1(mT>? z(!RuU7k!Y9y-Z7mk{f@$ z`6?gTGLUxdnq`XG!oijsZ?*iVQl?5|i*Td%& z-*!I`yLl(0DdTuYOBT1GWip#*!)P`OsHupPZ8E&ZKxHtZfl#Fo)`5f$hSw-)wZ=S; zRuF=K*xQ6H7RjCvjk*5k-LS?1J!15+Vy}=klA{M^7@aYnM8Q*+h+&F&Nc^26$DH}qP!f-AeWU2&m0+~n*g=Z zF``$&z+c8m>%z(`RHZn5Ggn&Y;|^gTo~9!y?hce_vJ?-fG;XCUe8MqWy-LgH4d*9QcsMc!gH!`tITt&-Oh zWH>Wi-mEOMGVP(2k>a4vAS;6y@Vd}}qzX;~{zd(3%tXX>Wx&4na zZM{_5t6Y-!w0o_wHCCPG)%=$20B!{ebClF3vomd z2&~ELmYNit^ce(3`f~h#K6vjNaA`ra^c4+nRnN^LQZ=^WaZtz&sk#8Qu+t zbQ{u=2(ffpC~Kq*nT*`!5d)XZ19Va3$ zk%n)tI{k5i&&B{WAO`;l1+%0gDQ7ypCiR;Mw`=_rsTX&kWf*spJK6FeH=`xnKVv#( zRFN)jE7JaOa8ybTezitT6jaGGLw^_g<%Az5fKVap8A%ULl|m*+wprXJ-rl^p4Rs%p=lKL3v^mrCeC z=@`wGPKq~2{dE>kxg%qZ@-!YE&fjFL1A*MdoO683zr9?>_zQa#m;Liok%#R;#+V74 z9M!d(--h{-(s#Hp@i72U^0nA!)L%$ykut8Ekgj0yeyrQfv6{er+lV~;K6663vxA1}-}yzUo4C;o%`sDpg1z9}~?Ht1e=Y$DoVU z!Hd@Eti)yc^Js(eRko&3HR(>zpF_h=4@{$8CG)QTuVasYlkPP zv~!n?LyDdw#}H@bVRy(%9PphY?Yt($V9@D!XOzY)!ZrcypllB@is3mPu^rIJJ<5vS zs|mdbPBzP15D3_n%onyOmB^Obpyz!tyOCS^)wjQXcJGN)b%(yyhrLH)aBee&Bqv)GbL7#he{E3iKKq7Yh!#n(vX^GGAZ2G;D3n>+iht{D#Ff7Q7at zq_r0Wdu|x$7U3Cz)c{cXW&K~E{bYo5Y;fNIz6OMV*#|R4v8rE)YQc9`uV1@*!vR32{3!iF$;YSv-TYtf z{Hc*^Cy&@Mf9Fd^u5qsgF9=~Pfu>8peN%MhT4;QEr*Oe|;p~f-K)rAv^pY3}>h*C) z23Rag4nhujw*~Q5EyP00G_S=%b37J<*Ib)e2n1~U=qcApFe!!#cBViaQrjpCJD%H0 zs^5I`k;lu+ZAFFmRzjP7-)!T*?-^b4`U_#hLe^{>3%!saSrBTKkT_V#46?*MP#4)# z=-#nwVCN7j1cpR*6#Kb_SY?M~gF~%UrVT<0yh8bqk`LgO%Ahzy@<5Nl5EK{NXOJh> z7{?9r#2JFR4Dtkp-4xa!jRtMzlb^{O8|@j}z2 zUM<&XK0SEL6=?8Y%Q_kI4x4 zOKoN8t2S%aPAxdRbq;b=Wnvn^WjC4lgLUiAK3iR_Q1&@dM`v(n_HI1)dCL>sStkQiHxiM|GmW87QXA$T&Ze*&Eqck^MTkn#hY+8d6<7 zJtNZTjLZ-+XxED5t_j`2hOG^fMSiXhIs=i4Srfnl3WJ^(QQ)Me8bS>UA4=uAAiqcL;~bbm*g|h;%~Kl}e;@4odvm1f zBh}SP`Z-r6CAF;QqW4G(MN9K<5?;WmF_)&nwg`m94bX4E0OiX35pG7u&qBvb=)sWI}#Y!B}@2@Jt< ztPhzt5@fWw6lRyaiE7^#qL7Z5VcjS9Qts-N*5-ZZjvhVt+R>wDIfryZx>QHG{n+G) zlP*d(uiUzI&H~9f106bP=0_$XScr7@$>efv7c-6qkv>Ts&`|JCUKPT;b z>By6olO?%jQk59Ly$LC|YtnDh$7O$BrkCHlW^et3;R#UcWXi}^G)Nr~pbO`9yu+?G z5m93Z2ovED!7>HjhLP!gKNqzD$w2fLJlI@mzT}Ju2uu9>tB~>!@NZ=m3@NxhTlK=^XIR>_wo*R0}vWnJP@E( zYc!nM6rdqm11vmK1YmAIL~U3DEm?lKJtK2fS%ra_gX)h|m*ACyEkcQYwCUPW#;dQ~ zSBZH_;K7t(9w&>XwIm!>b!M9o-O(FMXTvq*Rd=w>-LW(rhqhQcRxXTdvw7@L55B4wb zUsg~$E+eIMGN21-y~fQ|WWz%Daz_|+fq^PcrNI_ykY2Adpgz(JZ^ED;Ik$aRxromd z%t~;WsLQ*I(agsJi>ELn4Jxs$uqB9I8kLY+8ecPG1OhIFPZ(^wR|?J@49o(VUk zzlOCGEo3|22jFjUc<;nalH4|(QmLUPlg1JlpjDX-TB|I_K9TZ8XMWZIE&TDf*TG^U zOm%hZUw*Et#4JvPW4TtyXmRh|_{!&8*{w614$!q6tGdD;e-JZjA;E!gLDU)!icy24 zyBZ1@sK`i0CB<#!%barPUifmTTZ0bXD~%XOLwa-&KWYCTW6S7 z!Zx1b0G=O4^5C0=YSd;Eej&@MQX-$Dw}v7T=TVFhWYzNl0jdy_+CWsYl5(+d9A#uo zYj|ge-SPe>D=cgSIh9JbDHJHf66oz&WG;kVw{;Y)3BOcHZ%jPYCs6DacBTAMj!{(Q z>>DF}%zc=hH=vXGG!IA{5csTE_WzOtP~v8Xpx=} zm0A&4;k5!asI*=ny%E4|Ee|pel3MD91+PP(|}b9g{bgyS}-bS6>1ZTg@j-gG3W!K#eAmNTaL_T1Tq*VOs+sP zh`|=l3JPWE=*y*z`({E1vGeF}#z`T)iYDcyXZIZ7D##eB!>SvkZ>yG4!i~6P+BP}W zI?R|f_EQIg1F<__j+Osy><+Fs$gz8lhuKW8pV#K?cu@e}Xp7zX^5#Fel`RLj50U<_ z&)}aZ%V;iRv!Kq8t4Hh{PSV_=p&F#~P=%-Dl^Q-WB1EInYE8UgHhBYSRzTY3H_HAm zTe1S8Ze;@2JvIfqye4C`75<}%Rn1}_j;@@u?(AdV%$rPW>K2q$ReHk4jpc`L+Chh$ z7mB#5Y9w&u#d5{O8tKBaC03PX@q9o?JHWkS>W)pylHs&@ z#0W9uj~}s$9IRp!RuREg!CSOp2316a&0-7V?U91T5*Vlp3)P|w&bP_?H>;qms23F? zFuk!U%=EKW$;)uERnY=ZWu<3+{h_o*|l?N?EJ+Rm1W6t+HL$KMfvtk(w;Gu zT#;~IdV2fj;oR8qfU1_O!M=YVh;0{lF!l?Bc(Yz>UP5Zc#o{iJ7v1WB91#hDk*?75 z$*&k$>7!hGh{z@d1~16Qd~2vLreZ40>X`}7($ zHLOd|KJJQc?i_d?9N8zWp*hg%%u8i;??^oGya7~YA(IxhgFfp4YI?CLGcF|7dAma5 zLlnMSyu}gRYqwrjOlYf%xConZ9{JaBGTPlsrR>lFrqdQKL`JAl7l^z|llrv!UA16F z6>7MWgu#>d7{lygNnw0N*tD=EVSHFvxKbI8;ucm8HgdXmQ^{M^28XS^=7w9+-OMVj z+Zh!(WOh2OPOkjK`ST}=^~)cMynp-)Z@qDJ`Rb`Vb;U15(5N52qw$VI`_f8WzhArh zSI-eW-UclS5HobcF91c5LU(6D)xm%tTv4cW0oDl79!Uc90fn9pdc86*FgV{6s8k6` zrBL8evU)%{gz|NX{yI^geUVv_u{z9(%>1afGqsBZVQ%P->;gWnIehB;ix*#c?&Y(=Cangh+KgW?T=k%1_NfxKEZdV{{uqg;&lRd*S9 zqBNH!N{eWibchy9hbBl*W3T9G z8YC6Z;UYG#S@U4=G`h%{+&+DN1*EUMZl8hbU#01Ud4HlI(KG z8+AVY#57ck(DHQ)GRW8^Yvxx$t0j@Xq{y9+64p7QM@VnpETS%&7quZ_5z#7lMsMEA zr-UW&4z=1a$fG8X*g+l#)A8^@88h0_!F;z}0A_a>MOkUPud*F(ppSe|AqWNAs^Hq3 zwHR!Cq_N!&s5X>`A>dy>wrp6zTZf+B?Hs-;|NeEZ`<9%3=)|yLU%Wb^Vc4-vdk!Qo zUi|t)V>3216_>fUHV&!g6Nc8uBu*VNYo)s=D%3MLHLGvDd(F6~#&(`uylQ z;c5N)cOA55dcT64jJO%@zD2powrLEjMG70Ar#Opzw~4*sqt+|1wG?R9@kS+HQMvO4 z8YWEaU7TqD7g`i)ce^v_Lb+2+>Dp=7uwm>2I3#0QuZfw^PF504Qc!J|$m_dC#007I zfk6?9v{cMzGMcWKel}q%&Y+Fd{-kZy3V3xw8z}4X2F%*(VWuVTM@)BeQ@{2hf8LRoA|<8|2{Eia&%Sm zs@LhLH(7gG+Vk!OY0ue_hiTH0BQ){IlhXNTo1_azgv=KYoH!C+mhjC-@BY<&Xo9p^ zzD3&e<~eEKyBBErt4E~wj@aqF3vCQ{Jz5*+F$IBqhtTL4#=X=`acde#qZhYn}=V)r*F(cdx{MsAJ*~vma zfqw!S_5hOL4&p^k04HeGY9T-r^ad#7(RfRMX{=XnD=HsrQyU+s#E<>RT`^7RD@lE& zxA3bzseL4=kMt%sK-R=d8>LP0bXC~xugt%xrFucnHKTmO<+gLk28v21#3;2>Bq}l# zqZNx4>l9BbPAM)c)ObOP)eia$u~)YOa@b=iNof**=XiWcx(F~*iR- zy9}9}c5^!Mx{eZ-o9$D>WB9+AkvlhSc+I(~bbEfzk8}U^#Mq1L8k&XwlN-5DM|uuE zUN|>OnnP=(E&Q|v(kuTLKhoBlBC?y}I7lV-KafrRyZsMAy!9JPX)~KwkKaGK3fW~f z1WvcnjGusHZygvi)KqQ74qS%om67e=t%JhzC%Tv}YfD4cKecGlQ%@~gw!eJdJ;UbT z=P92fG_H93@f9l`-?wu3q6H&IEL;K_)}peAy(1FK!7{bngQ?&lfnQR%Ty7Siu(1pu zgoopFCYyklAM}x25VZpeXoxno0Es~jjI2a&zZ#(DsDqo;a{8BrqekXWp3-g3<^>Og zrVf>Ur>D5wf*w2LZYgGUqCPa*s0bn2NDHcD#ORK^ z#ZqV;ZLPKPT5FgU1(T`{j>rI&!>6h6Cf{vm)@#^N^eoprw<&w)?NLLAh8C3ElRsccLG~!b z!*}jbp@bD1iNX@~ojX+QeJXgYN+(@2XkF;qj!)HO3*Q$$pJeKO@E*3(W5~Yf5!Jww zyCdb88dk`r2@MGh)&(H44n|~c3o{}|X7tC_a&Yb43-$-tUfJ{#9Wuf8Fqq2stm-u`MfDxAcK*n;^brLtLB!yhE;S3qq6HN)38;`E0zy44kF9o;XXq7r z)LZG{prf9cdCPauok^V2B&$nfs>>l1kNIkhyZEEGKKYJ|l$ym8e`WJ7^UYh7{X8Do zgG&2M-i3sm2?lUE=8Z?r7iCyD#(txg7z2nHfQse-)IF;BFat@H{hT{yWQFX0qQ<7V z;Ha%xH8(RfqHjU*nUNL!pO8KuGge!s>Ct^u*r8d`m?IDV&I$NjYFOC-j^H-}wHg&~ z7vTgWkKj+Z`JyTI4vjLFjv7&=3%!tt?ePtqv-*D9ynGf@WW%;Jz_w_ahI5;Zcq0h! zRv=WAElMDMQT1-=#9NbIO9B~H7*W7T7j1kn4w6#Z7Cp$G&T^m z*9vdPJEgannV>1sX6Y?vCTI#LN^|1b@4&@NbHi?b1$}xUFES07iXroVBG3soE(6S= zU$S;jYrcK645eZ0Q(y{0Zlk zGr1V-IWs&AXnma)6V)@EQ4vGpQ~qYeY*=M^MDRb1po8(t%<_m1ca8}2&w{IhcSOjY zBdq=rWFm>g2+dKRiOe6t7p0R_-@YD}6;EQvL4Ntk z8?kr;UDve*Y~|c7r@8UBmeM0*~S81eIAuHQOy2?oJILW>ruSet#5BOK6By zRHCNaY*1z>M=NWU>y*2cr<6)9b~6OE+LY(P>l&;y7-c!_q!4>@cf_I0RzWMFyb)wX zTEJoV6=E~m-Y&*&Y`mi!8GKP}9SSC~C5nLOs?jA84&b2IWj(>Ot=zYdJBKpn|3=Vk~Ty6A@h`MtG zqk|WYiSjd;M0S#gq`*ea!T(PnOxO#p8sRpf$_hnR$m@<)FIHbx^J)^3$Tm8?A-#=a zwfMPEn#SeOtv#h0x~Zo$L7JFOH%b%I@fyPxc36{6x5yuKb5Ho5@=Ebb>w(CnI7z9y z8}=oT66|0h9W+Ft(iqHevT!O$aD*7lD%AZL14KT+G1wCz@FDFBSJ^KF$nTW555K*? z5C9r`6De}Vznnzjjj=SyWk=DbjmDsa(_7SfZP&2e@{_Er^}EK&Ikl&1>svxso@a%v zx6amcU#@)Pc5Ul@_w`!*I)0`U<*jqK^xk>_g^|6Uy!GouR^uwY{-mH8CmjSg$Q6Pz zt$X-roi0;>2KuRR_lVi#XYYuxe;5H;ulJ7dD$cf97$v>4Oq543iSiQLuUpe)NP5eV z%qDT(rLqff+K$q9N|AllAuQ&YZE764)ik+QIRDX2GK4p}`9z#L{ z67Z6cy{ko$ni3jgOzvt;j?3{R6QeDs%@Jj?{y#g(kZ_CF)UpjP@5_d8(tP0v(k!fa z!WSUOTg-CQ!k?)h(knc--^AewO%0=WCH8wfYsTDysf#Q6<>vN#V8!}9)fZkoc~&9R z*SZJAc1h}=Ft~csfE`apM@))Inlf?dyutU}H>~%Vp6Qi6M~TO4EZ+2T7#h-5YjVQ{_cKU}AS-<7W?-*H{>hHnOu7C$9lPrUQ` zS^sr1@l7Jz;1qcP;}b6g%l#v0(Z9SSI(bJxW-&r!`^ZegrIBUsK`G&!&K4nb?;fA1 zQU!)P(a0;s~zFj?=!rDCoTY}vqyQ9FUdv}F3*~seKLd;g9JysEmZBtg{CaY*v zD*IG*n>lW)k3qrA?erRxcPMd%9dDidPh!N*GN!C1i4{$;G<~}&IBNfYIC~GksLHH; z{J!_zxzl?~pCpq;LI@#*4jGUVNC*&PB$N;m4G=(#C`DueK>-mFQB*)h7STmTL>3jx zilSgabXg1QvWmT}1zp4>bNM~zy)%;u>i2#BKf5u>%;ddqIq&J`ITDhS6)Ru51h zmOOClmVJguj@*fN)So^Zul7zToYXC4QY=|cI-p+h?7P@c#Iq?64f}~?Qj+>?443+p zpY|Q6g>uk>B3$$UKdIE{NnQB<0w}?tgzP_fpH(2nkN3NEd0$5-t&QK(uAO6d?Hqxx z{ck*XDxbkm8a1kyaB5__=oqu{`qy!vSfGBaeZm^~#Q9es%PpOsfVAJLeZm?2#5Wk@ z3qB9+DJ}dH#G5?YOu%`r!F>goKD#cBo#~kp&Y{pMkcwDB01{-9T3s$8~N=)zq&gd4kR4H2qLAs1>h}6p>QcKmFC0hM2v@yh%;=)P1a5h$I+*x#tLfY^ zdv;e>v$ai4y?gWsUmb~iNyxoCCHF@sqON*GiYG|UHRp+I8^+AMvSU}3 z7-+m5**Ry*!>8+S-T$(drqkFuVA0*bQliex>-w(U^z+ec#Y2;R`S|&T;bGP7pdX&! z!0CfR!Jv-^>HH$s=6Bv-(9GXYiJtETf6pr5Lc`yucX^+mzczjg-lu!15eU~ADAvwr zFon-x!AE=qjRJ9R$y-#3d+QO>DdROkm*%qD9S&V)hDk7pPQj5ZB3DOr7?4Y9h^9CF zCRaz(7l>K&eBfAGkyB5hu4q0HOT0=&8qwditRkeX%|}Af&O?trENy;1nnc0zq*iAguUWf1AML8N?LBz)*7L8`UKg@%4+aSZYQ&`=t{wLZcRVJC%9zP zmEsd5y%jcAyHA>6jE8_1X#VuUiXnRzX(?&QR8=(p;IG(2q>M;4V?pbXFLC+>g zD={tPjjO|?7mzpJ4H{8PXpmsCC-vx&+sh}Ld>Oe=i9|sk(vR0mFoA6L++b1<$lats z5(I5hQZUyWgy0Q2-1#Hx+;N8hrWws6*Tih6H!G4Jsq=J3Q~Cm#N%P{B_P#K^yo+d+ zYSfCXY|wikO79np06iLEM*2k7AB+%@sgVm2n?nHS z!{@-piOJdsF-@G$L3a$FpgHK-IEK4L&q({DSyT$UwJ~D$;RWkpm0Ys3#OMgn({@UdVUM6)(QW45 z4%}wVV}&%W&OJi?G~H&dAF;6r7U3?&sD+QfZ?iT+m0;4&$##P!$~^X3_EybhLBY2vtw=5kg%@pnIl`FitGf<`9hTy*2{v9F0Ib z2SbTwQ63vZ9Y{6g+8DS4{aJwT+y%222bA%@8y@{ToSJ-wub%=C`c7**=AGesoqjh) zu84fXhFAg4H4`rtl!9O5pY9O6S8ixc4WZsL&}7UOLGnv>hsA7n7|jkBVpb*}(kjNF z#?|e1Fh5+9MSkY@I{+pQCuS^w#1-3wqD~x0vUw=hL4FPnIf7r{v2ybJgr9J$N*TT% zr<(HP$EmvfxV)}oxBL*Zsb}b`zWo5zn#*JA)f-r<`U(6z$)u}t4ZJSY%j$4T$cRoc z7>#CE3Tk3~Uav9HZMK+4*EuYfMCb`dZz5tqLZTxvJyA+T2?oFW9FQV<`Du<(J)BQ8 zE}uO@PGM*hw`vn&-a*DVYBbACb?|Z9k)>`cH&3IR6F2;{Dvq z#(juL7kNXY5MP8s&fkGgjN{rbw>$8OYdM85TlAzMrNwcaM`&Y=1^7~zyK?>?IN$S} zM`&Y=RUgyF(8rH4mU9Vhj4Jg%+89wT!N&-5TGz&?BG4J47CAb`By|{7`h$2dhM=$s z%_Q$~)nUL#CHk$@Uj33@CI72OPd7t*-nghISmP-;Nl+(CL7CW^qhxOk9ZEX6x!4E!dP@8qkRWnfSAQ^N1;ah>J!>} zqql>qB{Yjr>>K$+#3GE@HOJL~gc5)?^q^;o{e>0$nFrK?ROx`%i3=)Xe*|dwdZZ7r z9xJjYQvaa-ftjM^50QHjr8`Zv)KX0UFXay?Ab(YT2kXabBR=^eT7|ISd|g-C5Z2%% z^!i6I+i}__Kt1c|T&;MC$5pV-2=B%iw4PzKv(wdqERU}ee@ClV-{q@6PEXW5i9g1t z5!>p=4CnuhcopE98-2CU{3=3z!9SC)qM2p|Z8DUl2^ z3+Nd+@gIW&ADV{_wi;5YwA8~3p2=Wtfw%Dov&6d#4mYhR+|wi#<HfTr94yEuSxC zM)m{#cZzp$|GTh2INg;(HM^Y4FK(9$3mW)+?3CM}PoOerBXIhBJxZU5jg!p9-_d!F z|DETdch@HEc`*+!P6E7@HsSK6k8@7S2xfs5dRny zN;5&pBGXFHIi#dSi^t(d&aVmCDSE)WM36e7I2AFb%ZVS}FD)wVEWHpiLft)eU{(2# zaljD~Ko8Re01|Wyd0s#bs0Vf)0xiiN%qH0pNJCzu)9QCR86^dJ-B>f6X4OKeAz zSlVUOW8XtjkaBYMt;U#{4 zVl4WpjX>IpHbRw!j}X(xwGoKov=J_59*n>}DO?zlCC$g+G@y-fagF9{$>&5sI>UTkkY^l7A16NFCy{cx3--KJm znc=&{4INYF4jpm@Fg`wKi4Jxv96XcRdZz{tWOK=99O~ohQT1hY510zASs##VU@LS- z1?7xTUx$?2)9=qM_GOq%zSLZ~w6_BVY(3$_xAzS6gtgkUr(!M4x57|GrbSYgk_B?2 zD&nYDI6fPhFR#F0S9{S-o=kP0>25o7VyP z1VyZ$f6j|o#nO-3|NHqzzy9*;TQ8!RRpZg}CeD*eiAuqf8Z{b=*{;N`&vN>J50=62 z{~G5JU1TQEKR{y`Ax>rzU;%28@H+mEOA~l#fBQsmC7brdn3YLTD4kE#v&!>}Far3J z=9a}69Ls~xx=&=*5iPt&R14=7N_^I}+N|H@BNRqC+XW-Ait`cesMjBiz$)(MBNWF* zfCmbAB3K{hz`f_Nt1-74ypGx^ypAW+>%zDE^$VX^c|H<-j#)1k%$Zf*$e)-SeWD56 zAJ`xm8Tt!xc>_H!5&Mj4C%k$oJx}a^{yP3V6L@zn-y==isXQOiL(S0@Qk~Q;F16_| zA+?8NtAOO8A&@|G4rOquDC+cPn-0)F073@*lEGlnp^V7_K}>}aTCHTKY0dAmmd;E! zm?u$FA~j)@utxRX8R{0+tYMW%iPOR(#e>tt!5p(BTDO%76q!QgQCZM)C`B^ayeX+l zAkCgCpmHA!E&+HNA<=9=QMkdLfLgnRsOtz`(xe2(F`k~PT}W**+!jHySFDbUCPq16 zu?nwl8>0nXNc(wxV!c-6wNc#5`fG(<>XBOY!=;OOu@@_%eb=I+6vggGGzc1Q<#ZfL zTEHi`my_%H+>6Pof8^&G^Rn|1xP;I~7%S}3Mu_E3Xd{p(KpSCfkdF|P&)OVFerqFC zMJ0U9GpLO~5<(lHiu)3nJ4#d92=By37|%x_U#m6=k`S7=mf~*O7~?SpeL|ZCeL@?B zy^gp8x8k}*Zy*G|Za3#ru%5U@za1+Er;EkP@T(jb8wa0yOB*9L^Hw1vWY^;{D6$73n^5!ygm^6sPo^e?W?^hnL>-Cvr0v1%yyJlg z=7PNK^`|)00!Dlu-29C0b!68K4`ta!w>woZI1^KK=>Y^3fK)94NI~%dd)Jq&m{G?B zoYpuEC*p93V~H2yJrE;OBH5XswxZ*A@2tK&v2N6SRAZ`N+_L79zE#8aO0@&CZ+rD% zpf+{&wjC4OJGTDWJ;`+vJ)hFkIS2uH!n}h(;3Or zb9k!O?*dK~+#u|nza#+_K^%#OD>mfw!G3CcN*yoI2ZeIcA{8Y*0LT!~XFS{ku9ueS zD-Ew6E_R!LG<@gVSBRyDmAx0a36t(-8^ z(>{hh4`y({IkN_R4?6m@S9&*(oTP@NnZqh7hJ`9B-=BBCWAJt)i|MqO2zG`LZF&d; z;PqZbdo#3XU}-11O=(e&e0}+-9zFWmv1~DKxV;~OGZ1hE!Ay z98~o`BIzhCy>9DcE8JYFo}Pa7)zj;*Suj=G+6u@#EyI!X2{0KR={Uo0xtJQ|XgmXh z^NEKdpIFLoximC-%R6n7Es;-X8DvsnbPOIbJx*NO~othW)pbV17 zHF&jC@4~C_{W4YnY@}9TBUz~m3su093CZFnJ<1wQ@^UL0I8X&;%p0hQ1=@Vna0ZER zgREf4x^9ib_l&%@X-+FL(l6R~(V`a|m$zIsHEwhZjcyk7fW<<=0s!u;Mzel7z@9O9 z)DVbX5sb;9nEZH()VjO8v2xeQt7cB%4umgRQvce`Q+J4D=IEMi}8egIt#GTjv)`+eE=NPY!b0!v8E!O zcp>{jfy~T6NH;f%Ov2{NA4u8CO3cJhbBT6>@ETvRV+AD_%;k@WJUd3c_f~M~9a5p& z+m$ZimtaB}Jd*XGGlu{~B`W|MWs^+;SY|q4Z>~g5GE(so;g212aZi7ow5J_Vc0}a+ z`0`2RqoU!zoBEZtVpo%~mUmDAnuyUIKEKiAHzg7X>ELl}U(g&v(4swk)wbb++pPv~@?J>kz>1dHZ$SzIF8*%BpWxX;}x{tp2 z>T4m)kmGn#x(oD66>q3*u~<=NZ?HOL6ursIofpzC0$Cd8&GPV1&I953t~du?{}JT5 z7F4*W74V6+io9#QGkOg;87eCavHkR4-O`<#T)hvDLIofFqx+A4h?1M2fxvqX{h~%4 zaQ;rps&_&3^Q`&{YAL|a-pAjR1Nj}XxJmqP!mseZLGG>gH}nZzCI3W0*H75vJ|Vt> zPvpXvuupsPZ%2VI=pjBzFH8J$LtcIS0 zRlEs1ZoINz%waGXvGPk)n5u&1uaX$i0!vB9_EFU*(9f?2q>n3H7ZOK<^f?@!qSl-Lus~pcSRk* zYT`AnE^Qhu>*vm9!1IoLkILfN6KVi?~KRQVju~`NT8&a5KbnPDM+H$Rc!9M z+m-ry>;aec(D@beKWa24e}Q=rx~Y?DrgXqt1RS*ja8sZ!@`Kcbo5o0?qjL{}{6%xe z{$_Nst8Js!K9Sum<12)fg^@v39Iw&>%<(J{Ob2cZNih<-vxo!%IP_%$67PXGj%C7$ zL92_a&&R%ZRyl$^am?} z^5}Q?(-Do8{P3|R<=gTBCqCZBfB-GXR*-rb)XHeJKqNON&eK7=#siuRNet-%l8R-5 zJSatZF{Kk{m6W(qVT{7<0*OkJ<&qNdWOD%l3awMWKWuJx9a4X~>1L)pk=<KQdtn zxaN8sR5G%)(@~)&FpuAe^gBPm{E{VyMY0g|FZ0^;645=9YT-ZP>xe%E?nD9!kn0yK zgZ9gHC}dvYH(cbP)VDcV{Zc*E{Pp+#X8-qJHv_Tut7P^vO1X^e6E)-GccnW!X1)6{ z`w;EPeo@;oGw>nZX=pSV0_?ej6q6G0N;U-$Hi;=P!%`F@P-F?3Kf!1KSbZ|bl+`?3 zzvI;fe*rS1$K}T@$RHFWK=os;Xz>KfDedC|IF^*yuD<`*e}`XXciynEayu(H!uql) z=-99+{Q7$b)qm+)f#2T#vHF*1%j*}s)4uU2>#+l@=9RbA9lPtxA7k~OBaNXa(bNF& zm;bRgB}#X^e&vsCee(BjQ{by%f5N#atOo2KQHPSKeqN7Ur5gVJ_1gQ7a~nIB=gs3S z{QXth`vLxbEQ@Ftmqs+cCXH&iG=i0$5PbqZArI#A>NY;YC6KmUT4@?Sim5cj%5Ct_ zqXQ?tVzi)FT>Hl78`tmM4M_4Q zpKChv;en^-?w0X+`v zWOF9|`JQ##aFt)VBoxAe0BC_| zbC=M6(Nq5Qra=Yjz!}W4RHd7l)*|fiz<7 z3b=!~KL{W`RFr`PZLrHe4A?%lNW&I!@V(bPbI0_*E-Ae7zKiEBYk7Lt=278~PO2Rr zhzr+0c>5hwieG=^@fW2>>h8bn+O6FN)%MT7yryRUoN4QKUG^k~3_L1K>(0Z0IKGfYUWcqYY!Q%9x_$}KbW@U+>jjTWxf>2pmw#38$Dia(w zz^lc8TH~W>L>iHZ8b^Bel5>mu4I;ko-T*7YiYVn=^L%+S#O*i#W#^uIDwkie`kB{0 zKKI_6Pc+KEubj0grT5gaJ2rka$XK@bhHZiDno!I1BbfO@b)K{m z5%esIy}I0Puh*U_0oz2fr^q?kiHP6>VE~^4))bUjjgMX+YKeBCPe9fJ=may36vM!B z;`k!uMj9gZRWc5U$IxZ+)QSPCR^7d9K(Dfft9mbJdHbo7DK|X!<}5X=d%m{fTlFvM zr(0W>jZ4iKPP22mJMf|{WK(eW=>Rc(EtzmbzC5rpQmAVb;lV~ne&IV(T zI0whQwh8E8f!+^_!)~*i)A@XH%yfDih;6iIr3MnxdiK63>5+zHIlWFjDGz^O_Qm;@3TKbmtJ>EW6qv_2LZjJNEMe}n zcf%aLL!%v9!9uMk@Zy)i* zMDbq7R<-SBcHL&la_GLP3oC*-&C?g&c~4WX0r{SsSMI;{c)ls?mU%1xR$#0hSO4c` zLO9_02Z)y{q?=*Yhyu!lWay==DuV~$YZjZ!qx1VLUbo5Su%WE(H{gxDOgh5$NX|#& zdRoe1U(DUq4-UJQI8 zphu>9O)2OV19w`$mk6gJ;Ne_^!72eKI|hoXiDoX2qv<5rRI2R(wxQn-AJM)hQ0$A+yPXxV0DD2*)3N8pCmuii z*Kucm5S3rm4_WH302L3`9#v1j{mqxJv2I7dBYN^cpB;}GxrFW^zl3Uh8~Q)HMTZL| zeh6bLKnpx_=77;H|&zCoP{G!6J=y$(^F^ufu-2mbop z!K94FK-y29HmFG#hj+aC4^$U8mi=qZ)nDAIzK9Gx4VRWsUlX7TjS1xd!NHVn1yFh- z8rBH1TguK-;8bxSKi=U;w=xJC@W@!|3Tev_bCKJ_^)q4)*a2r2Qf>$Y0%F;Z)=EfL z@ra7v32lg2hI5E0E`>~PJ3793%$skq`YO zZu-}fQ)gY`ZbbgS`0sx8*W1l%p4{_|uh%UL*O*tJ)`Xq^7B|l%S8<%?Zs=U)^Cv6z zbS%c6E@dSo2K`B>u})O%iQt0(G#h0*}AU8+Y{hMqu4WUuHA22&!l z+2Mz_``jkGTS`ezLhDYuJsIgqyt=FN1Ry8k_MaCDM-BicB&P>hyE^a@oCl7E4ny*M z^o2jGp-lZm1w86`^}mh(dQ)+=^>Z@a*MB;-B%t0EJLuA*I93s0I$p^5DV>OMM z)k=1)X0>YXch>OmUFCN1X8b+8v%(I{0(NiAvgY})+&YI(6w$Na;9THrOyHm2!gG=e z|3tPS8IZ+*Fm!s7b%8V=kVAYvM4i0={;H^R0p1VTEunO6OIk1Bu}Nklh_S>%Q_<`C9sfl}^ z-2UXt*dtm2&uC!N`3e>Yle8$mR>Oh$!{f)h9VU{OQ38mZYz7Kea*_mC47&siSCVXA zFI+5M*#-xTEjE4LZs0=iZMq=Alj8clW{6-sM?8|D<7b6+XZGJ|iTI}aLp@Ta_g&Bv z5v$;tSjgN`D02(HU+B60KX_WA-RUp}vdzJRh4Wu2r*Upc;K8U94ha6_WT;Dm?3J=@ zwk-1P`Teq2PA2pZuToJ5ST09g_eU@=%^wME4HQGXOw-Yb5hFZ?GehUMNTNU=m&=X1 z3ZS-;RgaSmDyKU>LvrI4qpHU)XdKdaaL?gY!>0@pt6pmRH9Y3|*Isz`Uwhd@$3D>2 zs=DxbRh^eO#n;TtVhI3@mpmK(?EXjZzlBzT^VJ8j3Ku+gwV@)vEC4*)4!}*>U?JC? z$xgo07wm7@k@vt;J`GN*WN{hXX#N>-7<2e39Awl>851lKsY3a*+JeB*BK0AWRmgK+ z{IwkmIUFu(ZWiC3@%*bVJo~|3srcDrZ~RL<5iUfNy(QueZ9DIK4Aa#z)#$Fcp{WHF zv^lg`bygJ6dXdtNX`2pC;YKAZ})TIbE0iyt9B=dZYYAnb0^vU$ZgPYOggKm=r-@y4+qb9jy@sw5i+#< zqGj-64U|hw9&s~#CRZSt}YG?bdZh#iCIk?S$oOJkJ9Cx+0YCMAt?u2W=+(y0Y#C%Uc!^h^f@( z2QmyPeg;X8mSNQHj|{==3HFP?eZ2)8weAGfAXz;dV(hUuc%#i8Sr_jf;?>7Fj6KZW4YGMZMh7*#2JA ztlK}bidSq_)#`*dR?=qT>uoe*^RppsyXCX$%i+9FBB$RO;lfGcpE4M8bOIqg4_N@R z)oMfEEM_)2U>KWhPMzCjv`7k!7r}|H_ma(w&E%+nAWFhW?_}IGNAg!LRr) z^cEaA3yQ7bz01VPX0dWvc#nA1>hQMa@YdDhSK_hoAn`E&8W47fpM{g@3p1+*6)K26 zWK$P$(VHYVv2P}ChAuZpB&6+%I=c@i?(-Rf!PGHzLD;ad{(@1i)aiJP_bxefUKy4Z z&*SGFM&d=K5vGI$=G;EGT+&y7C8$(CQ`IvMOnblK=ohCyI(hmZuRVRoyn}NFyvB6L z{v$W29}iPTsh_-hx^`xdUXO0OYg@ziO-nbIjUB(`FfI4Brcf?K2n>rg{29Gxy6sciGlhx31PcxwEhByD9bG40;4(xcg z?c3;`KFe?HzQ~P@)J$lYWRba0w?mWA2e|UU4nQ^uKqi4QU@?n&Bv2W2aXsGR-Hn`~ zQ{ow&BR^7k)4FgWSl!=NhX;#ag%P6Dj*!+2i6T?|lUpbXB`^ULZFa#0$>1F#;cvtl z8Kcq@Bp#z4p4mpiqElM9Gvu8!!ryh9QZ}No;-X%IdtW+zet+>eq`{-%?W($TAjHA@ z;v4L>$PWA_mKclF;%;_{=|%1?I2i8g6LAWiR3ywLe=I~wyaIlb5*(Nd1!qK%2>e>@u=G%SN7I9ghSU5au{x}0KA$h{ zb(h!{IJRyj=4%#!@-H*av002`gMWi(6U)}Ton2TDTocGb?&R6q8r$~}%WhDMVmuo= z0#BJt(K9Fd>LiQQs>a|nxLu-bw-{s#+Ca+8fflTI2A8#5Z@@-$RAARHf8Eizu3lB7lOJ@A_pjZ-qRXrOVC{mpz$$sKrX5?-p(5m3iimVHf zVl;J(Hv1z*vx^fQkBf*QTAF_FueUXe_tO70F~mg?mvs7*7~igS|lw{SX) zs4TT*qOWbNORmPdyFQ05G~J=-!)PzjJC{FdTVvuSNju$JIzS+R_rV1#Nh0J1@I=rwoQ|(kBO_&|tIhyztW?vYp~*x$l4h_;>R$FF zOX4!`I7{d9?q_Jw;Uy&UCXuj%0!=%uJAhe9@UZ$x3cC%6g#)NufR>e*Ap0>h`7QK? zk})$`wun|7w#8mqXNk-!reejm(9j$^=SJ){Xh$IZ~j9%95{ZITV>} zU927w7@%`4QAHvZ561`#D4lXwN_tTdR4`M}Kc((h|EYeU?iba@=4Q6t%_^A(SbP=j zH!)LN8#AfD{CZA36X|xm0l7lFfj8?19A@fyd_;YSU4o9s15tfUJ&zH@l|G5w6!h>F z(96zjVxm(K0k@6TITB)+=;7-Lc*;EEJP>vskHIXu4Co+_Mz5NZ6*HdiO)3jZJAjbLz<9=a`q;_lg_SUb~y^Y#Spk6;Iv0d;1RX zWsXoHSHt2O4wftmOF$z*B^h3?Z0Z@6;ASBV-?;n<_009S{lw?}@Wa>zANGGzs95&+1&2Px<3TE^S;|P4+$kyUQFV?KW={#E zATpGa0*7z%s5*={)@&1<@nJpmWRMF45{xrZNV*`(-cW*0QF-Z%#g&rKfi2HH< zpx#`sNh_%otKfNKaA*s91sQ*6e+Zu^pI$^T5j!B^2V3CNCUF_HHN;we}?IpRevEzlt@-?^1 zqIu)b>Yjw}BAWg2zp-;Hzat>s5dK8Hi{wB2I2^x1A><3JPoWGz%#Z8!NGvfTy;K63 zIP4ay3@Ca#^m?7aDVlXi(n0!~-K4it><5J>q^tZ!uc!M>7rcv3Pg=gX@G^E{vD%Gw zEK_ZlskUXTO)Xf=PN2}_2hl-|hQ!?PN&ZzJdcr?oZo@IxzVQ6!P~_N|E!r|XMw=%o zTbGyX$j-^hMo2LykORWW$?==bI=H$W{xp=s#vI;pUY9t!;WU#TA^Tj4s-YD`gn$s# zDAlqFxUSDuvfMR)8h_Kc>^(d8J@w+^D^4A5d9448&6nK%z`gh0DYm>my*WErU3L4) z=Xxt?>*n0Kr(0%Iad7F?t#fKM+Tl0A4Bxy5C$2NXZ)eh>fw)1kDOx z{3R0peHrW_k0Rc%YMLBgg0Mr!cgLg;+Yd?BzjtKd$Z(qcj_J<80{=S>QxW?la-W=q z=b}WY7oL1gn-`cY8KF4qnWTp{uQS0Y6byD*PxBz5`#aiv%rGlKzoR8;qG-AytX_UO z4(&|%I2{%pAJLKL{8?!ip5%z=^LAh*AxD{c?Gi#tc0HO8(sTHQ{;fVQ#+*MOYH3%a zx-9L|wR3Y*-NDv%S>hn+6SNVY{*qymf}V+v$j#dGr=c6Ph>|^>j52K*#UEb5 zsUVUlI_)N%BeKV|xZn77L8sZ!rLolrq7nzEloE6e6ZZ~b{nys}iq*eQR8P1wJd;+k z0og5&Njcd3BW0^PI>e41%>z5Y7dk$I39sq2bjzvMJ`-C^7m1%VGnCh9fNb-SbIMGT z*{<+&=Sof&g+e=e(*8(?#?dE=`x+a=Rlti-gX~oMA<+{4nPW##6bc;;AuFo+9VRLu z{AHcIeLpaR|nHPZGm}jwjQ)O8O>OWeFqU<0e-74sk5p+Z6M!Ut4 ziZ)aTv|U)Hd_lN}L*#k{LNGuHZ*PIn6MO>o=E9-t5Pop>=_7xp{`lXE3r=IrS&j>_xsXf!XZt(F z&NkdeWJgj@Hmqo}b}r!kn>kg$mJ-c+a(bD}@O~L&J4*bzuzO6qtH{RXk68lp=BAF7 zP2$?d+la4&>H^Z~jsWOxC!Y8np5P{1N>4hC%Vh#LlPz{P@55&|8{Fs|@ITm6k&b+{ zFLV#MS;dqa*EQ~IXllCb(3KB94EnqMrfWxz2`>jpIpQT+YZd~!_ZES3t6M14sKrN1LDrsJn zO}wA4%LBc)2k;)a579`B1*+Bht?H~&aEWo;RFo=v4%~;?2fAFNmsG{E=+BI zW1}`b@P5p0!Gbx{!(U-q*t@9CM>8BA%0)2@W~ZlF{@&aS_V|hYfsGZ}u((Mj0^E`& zG`NXfdCQ&Az0xML=;}GsL7SvGEQIEegjwWRjK&1X>`H)zWk$vk^*6%FA?*SAT-3}{ zP!I$BJ)I$zazW4ru&dx{fL+CdtGQJ9Le>gvo^o&AvU?7HErxFwXF88P(Xu_Y-$NsQ zhKcf-C(S#h^w$#;Uw(a1oj*tYNc~m)2`!0epAe(`7V{(93fSAfv8^ymw4*PxjI*F3 z0LfdFyQM|AB{08OasdSCA6rS(2xc@N?B$MT|k+@NV-!Q$_&$i=3Kr2LU2&4k(v_9-_&?>sXdC;tiF;Qod z6a1ppiOb-08q5rx3rC@jJtBi}#B@#LOcQV&a(hGzD!B?xI1#uZnaZxEtbFqgM;>A? zG{PSIV&iRWn7aMCwd|dgyB6O5aQh+lDjRs^Tz0TEs@HOyN$9nxs=EMn7c`L!`9err z+32*^Ptxmy9)A*0Jsb{KVo=V_NpYkn0Tyg@o!{+F_X-YgI*?U_bVqu6IuQ5LyL6f+ zlQy#VG0-i|y&Z{cLGt|uZsoVoTQS&`wYU8Sfh!8WvXVF4?gGTsaRWzS!CjL|sg95x z<11e9BiH@xQQePZ_ZHAtGTi}(+l~BDS0d_79KaN?I|2^e0S9PbvGS(AAgllF4iLFR zS&rn8QWAJ16-^Q6|403iCH^?%{sE~2H}!n_A5M7j*NOjp>YimiHhYlJA|hkGSCW6C zH|g)sPdxck=@fqo&Ws~MaP6X&Ya0(?9fTYQ*9ZN6{u})MKOz|8Gm#$Q_?xkixgzPI z3Ouw}OFCkFJyT=>p?;%R);qj%W`^KLZ}=GJhUW$M->y#}?r25o6InJ<4VBxP2^V@V zxROSGAF}lkRf&3`$rg+7^PHem&r**)1$Mq&J(7Cq&GWL8J?U57l*4{z_AMLxU2}st zcf%I-Stzv&)cQlD)V?VO!pGI|^B0b}Bv<323Tas`;4;^Q3KMiN13X3mz*!;tq_kAG z*#U;mVuxsS+3Z$e#YY4oQ6@P8K&9~&+H}>T`M{WJ)Lw?vC$Z@6oc$4&d-%ZHN7XyR zX#6f#G_izRHubyaTJVA%w`{%t&!R1ShJI#$r#a7EQgLYx&N3NN6SoWJ@D)ExqRt(h zI{%?jXDX-87{-Z)3k%DIv_G<3*6{Z=Y#-7tpgWOH%Oi(6fZ~zA)DXnh`kAU-+6{^>?J*2}zxh^D?>egzb=>+l<7!3;0f#N*{T}R`r z4%!d*7X%tKhXEkUv6ra%iAPotE)qJlxN58p8UCy|WCb7;qec3#8P0~H zm3eU(59ovQCv7#RXhk%q1zr(Nw4lc);DDsLXed0`-)b~E+9Os&2SU#@1WZ5-7bEGK z$GWLhm=dh3CG?-GuGT)S|6f0?=by$JFgAS}wSCa$z)?EXIa1a66GE;q46|5=`QSVH z1tdH!Fmx4tB0Hy?fA=MqGr?H^+(}@&Nr#vPq+k&IAtN&wbg)U`|1aak#4tTE!`wbR zWk}!6lHOD>G;M@gI^?;M`6gek9-SCBf}W2+4OP%<6(d9`5E8|Yq*@HZ1WQHJ(A1Dq zcfh0m@_qvgUWHW*SAS(C!gq+mgQDB}2_=uii;CH%rk3;XZnyT`X4Ae~EZTRYu^i2} z3g{m}Bz0Yh9vf~lqKdD}N_;MVFh|Vod#`y!+R(hFAQpb9dbGKQIj{48dtom`)I#!_ z-pr80gz&J=V56E|0X+>dl-9{oO98>Mq8pPD{ed-GG8^-Ur46&IRdJiOh}kALswbK| zcdheTdiWjiETeh+?GCzir8=oZ%9iG2iC_ z3X&--n3a|3t$M2t8j&`y{-{3JW~RCNe=|jIbdu4T;sw#dX2hoJbW-D4K56xCCp(sK zWr8}Nt;aVuf@RaUd(k3$d@5EN#dBrG*@DU&1D|!?CbT`d-47E1DWtz_0oC z$fo^b!syWxs>{o(#c%5-*VkUsK=qqDpoRN|z3?w0#S!=2$soJH!=2Iz{=N15R|cwU zG;wXBJE~6!XJdM(q|du+@6I!igacHjZ!mm3yTI0HI$F)mzFj>KfkABvlaDJ ziV_SSeea~CAzA3_OrBBr*+^lBD+;)>kSASpB1l4WUO^QtS{(-3SdvA~D_S^yn$rPi zeV13^x%&NgPE2Yz)KuRviJjWK{)Rhmy?NbTY$0AcM6cYwe$D1h>(<_-c67YcAQgy( zoo}qZYtzkZ?~=uX&#-Hrdiv?7KK|y%e|`A%j~)Md`e}9*Ykl(I$KU+)&(mN2Amz5< zns*4DFaP*2yo`OlLE6P+UN4CVrS`k1)lVWh1Sg1&r|f0k$1Wvgl@v5Lk_>uXq|GUH z`zGE1mU%&6)A(+t7zN$%*umH+(EVhL!XiEJI!B4Lzheh~uiG8XZYT09sWLnV9ZTu- zWwx7UsUqu1q=>O3B!yt;>llho5Je@LryMr?kLRxhnK-!PoqDMN5k6vjm|3e-DBcA` zL6^`Y=&*58tS4~vTM|J&42x? z_)B=6I6Hg?TUxEY!wRb9{`KK|0ael2S3F0U8yaFjXCr?We0dJ|zZ_0)YK|c)Z% z4AG0(A{3<6sG=g0%r5$(D-@{-!w1+lwpG2nvjcgT9^_Ksz3S>{6Y`qs_p0|qnvm}! z4FpJEI?9moAd59eXbJU0?|PR$)0>ud;mGqg3`ZS%{m7JEIr|({%q-Ug$tCKS5)H0Gn zfC@<-S7$9tT9))!lC&&oL(=i2?~~3a>4YRl5;75zl6=|OSZ%i6=KzXSbj_Xqu~trQ zzd23fj$i>4+{St8y&l5LjGCK>5KB1HoYKDduPb7cO2Iv!c4KE%5&0vB)DdalJazlJ z3F}6Es(zKf{}EY!cwb;dgh-SZ8Sr!pMr%E~!wn~bq48I6s-&q3YMMh;rCDB+JCsm-%a9^^5LWI5? ziwqX0s7HRkn+Ke|aJ-9?9!{##ZC$qE#+mgkQ% zlcg4TYf^Gp+pTxree<*GC$;K{8?Te6%xYbO+oZQnx$ThKwVtvQE;o{e z!Xskb{HM;>5zQvHQcYBA;yNSP72uZ=nxis1?t~@SDDqP?J5Cuajm=U}F*J(b-FMUK zZTGIb>2B3`+4TC$n;Kp&U?+8sFR$*Xgfuz(-2cs2?hjhEmna^OaYR-l7yNr$>Ts7C>n_SoHn4;#GeWdRWh7y*M50Qm42mK5~@WpN^lnU>W-I-cU7-^azc6I$xXffJn_+MgPWM{o`=uhe2W@>K;2e4 za@LA&Tdxz>F>mRxYt*ku8}Na z`1I?zL-@2*75VfH{L|OKQ+na2OYv#yJDej9hOdrBUX0J`@wbEVx90t_BqGfwI`$-d zn&_Iw?iLvvHadJIW$>kR85?^`F+m%+Hc*_19#V~FvoA}M(B(caH#I5UDCqTQ;icE3 zXOL}VokuUAKakMX>CL4qZgbpjg9XF)4de$Bp3zJZ6r!f1VUV2L%jKpm=XEgb_ybR@ z{dnWC2E%Pz2d^q#zMS2#MLoaczVEx|-aKu=)AN>ljvaW6^||-siV@#_mXX+068=Cv ztCoB=1(`j{k`?RKv$vw^9{xLAX&~(&-@-%{fFdKfa#&2jmVy#yKx9>lr69u5h1qtw zlbA4NM?ytWScc#cjpIc}`<-IXQuR7%vATGmZb{q9{<`4Z?eEHIkafpZQD?)`c;r;j zBk=)tw;-qJkSqr5i5tOAePuwjB#SUmE-9jmvf26wZ4_blYn6VzsXM!eQBHDUk zgzWQY(2-^ZtajuoAT2}(lN2s7y#d)EvO!_M;sVzv(UCavJ7grCIs$ItKEcmDW^GiQazUE0!e>6rP02VE_jW?#E#?&CME z-aB{Ek{0^jFlYAcISU)6FIX^r`hv!5u5Q9k$>_6i5cWG-@`NmAug>X3zNOoyNAFw+ z#>nLXA4O)W-%Hnn@YN%wEly;lW%tbQ(S7>#4Yv*J+k5muc_UkCnPgeBtzCTOdEj2M zbmElr*g^Dv6A;(U6J~{Mk_~9E`Q7~4*@5c1YgT29_g~u3THasXFnD1770KNP3=Yw+U}V6%+J{x1!zyQlhDjhgr`}<*noYPbvK<}* z93nhyGQ!H$b>G9~Cp%B*n5nVR%AdM}O0f zL^EZiTuP1PUw$gS!j~wnt6ZYK%Ifb{jbhrZqnEM)>h>+Hjh#~8$!1D58(GbYbJere z>Oo#7KMxL?pnM2^QJ8b!-az;YUy5GY@CQM4(BwkrXEJ{OGsLmM=?Zl(fzuhm=~6_d z*U3h5_kdLZZp4J#99%#})QPfCc0qSVO*^LIPZl&v3)zds>?L(@F}qb=tFC1W#Sg_# z!ztkme4iWM6y5^429Y6c0Io=mFdE`aFI0-n+%~7zm&Lt4j$Vht-~ck;dfgf176aeV zWR?LzY_po=<@(>lTI5B&6do-R#i~2DStr_C=3Oz{GSMrayy5mMCfCnazrdJ0Rzp60 z3n4F<5Q4K=A?V{;7Z7LSdcQjwJwt(ml0Clfh(UKYEz{Z&=+fK1TYVd5xJ^DLHsT(l z8FI*k%qGcfGA_rRDNE|~x<>E+4(Q_5=T~A3 zAOpg>2L>dK0bom-3im?94Y4k!ZVsZ(dROlIG1ynV|C*6og4W%>Ch5B%+!0|%b_{FBpP zvR6N%XC;bb#Em*aYI6#R-s;UpuzDwwQB0ECWs~#@s%&*eyEEVvolZ%gCZ$Kbk7SdP zcF?IHKtCfRjWD<}O35b@EbZM_QJA=K#U!1A`J!zmx|4_~RgPMEKaLRhLgNZN`#m zA$-Ku4=%su!8SGc;#)y|-AxPcP)|;rd)eeEORkn$R_=NFz7s5E-1z>FuWr3leel|8 zlbbKEn7b6zBn-ys(7q_30~G%-1e0{|YYFM1L_VYVLKrs#EAuiy7O90|NWDWGBY7rX z)bSpqZVBJr*FjArb^C-;Qw@Gwp_Ge~Qky3uACfme!>NPTv5ozI#vLiGyxbqy+R3QbV)gdA-H*+ypFHQXk zAUj?pj6y!$WMQ^&wQ!xVR`_>l@|6wO&X}`c!R(3UHC0AaR(4*u;Uf%QFEZ1577rR6 z8fu_yTe9MOiAkwx>A}Jtz5DhXSYFjIWA?IDiR*5Cs3Gn6*C72o*(G)jqnHu55W3yLM& zgh6jMN=T?58uc5@psr(Dmjt3%`l6^azM?lQ|0x*DyCBI?!-`(ey~%pc{Z*Lvg3k8R-0*1TY(K5FW0Rkpi+}AeGDkMKG(~r!(vPbuUJ?GXRUoxymR({dI zF~!-FSbgu5g2dXTv*hPl&v}Oq%~Rhx^sC5t2wT=CNg_*=%W< z5-HS0++!1Wc#+dGN;+uIefbTBva0tJCnENT{u`cD&AZ|Rr6Z~TCw(uzLA zuN*T_{ZCo>=pmuelf*P``+$CNVoRYflk!6=o z?KtzqL5ICCnAwmf{>Ge76uaxVREYvwFuMN<9HTxe}`AA4-b zqJ`Ogi+vS=X;XW*%pRwTd;0a3{{GU2o$7PyTazYru+%Fqzvn^72>g?Q2mxEUPbe9= zb*P~;INiukM4_mFADk{73fYke%!PTQDlBmPSOQ#gB%dZ zyaUith7WZsiW@i^86pSgf`h+=b8<&-k6|*n@?894_^w5Bm+6`*Qu2WyI!&_O>D%2W%`}E^?tvYq;#?7ES z_!N~y{WZ|>fjEl{fCX#?5HBdhBAT;+K#$VQa~8%&KJhG$1)tE`ykiUj$?-tM#TZ$5 zM=wrBNh5if8p9{0O{nzY-HhpV0g)zv&xf(2AHjozZdSv|u9%{Z!`~U7ki;Z-B4Afw z*O#C|qVW6*?v+jppr!CjJQH;M+M)uT&J@*6CM7hvxuz43}88foM578))Gxy}YhL3*}_Kn6*9 zU{76|H9yc&+*>_6qj_0sR%%{)k3RCUCsw&G+B=7BoZ$&RS3+VSdnD}njS2d{+qk~IoMfF9$i5x;^D0In&W65uk@N!ckOoe!~>*o%U0TNj|^F}o*>n`t%8 zpFO+1hJapxYUBs>Rh!zbeio@(yMxuUschrt-dk>RZQ30?p#G@7qW*=|u)!!?n}D)4 ztb@FIPsv}v*FcD_86JzEw>zCFK*Z96D|sxE-4rsR63hh5uK2vr&ShwxEBI3An(=ku zI3j>pWRUQM#M1|Fq^|Yj`d~%*DCshbxw)abW7o_3_rH8(KYFQWz;E|Xv;)_sgf%z1 zZrzcy_QXf+?H`>!2d^i(ieUGH*!?6S6bb+Z4-R*?!(mEEmJ#MNR@O0QO>#b^I++fI*=ktuXy?KpK}-NmykmQB|K7PlSE-)DUN?%SX14jgyi ze2eSB#}dP>9qOkn^}MvFqw2&rUmTO3#mp#n7nILHM;jZ;F?j(u=hy4KEG^BOk)GhQ z1wtmz=sNUBGX(5DZ!-GqBuC^0PpH#o$i;-Vtq1`V!@@Kp%m~`f5E0a2Bc7TYQ903N z2k?n|8i$R|xZGk4Eu3ECQhGnR;MEtAvK-oZ$18BDUuzzA)fMg~H%@VOY}i~q@$jqE zVW;C}`J`%w9fXXicAlFQ9)wq(;jb)ce}Zm8#{~I<^JZws=+M;O2ei+^9{DNtGGzm7 z8q+?r1IFwd6sPte;T1{Cn!UswI_hN)nZ}#yCR|k2v#@vHIg53tZ_Zl0&)%gL$P_B- zk~2|Y%fpbU{MI7MB#`2yCLJu8p& z=tZx{9S_WCzT%0)|Gaq1jIwD(1;gi$`o~x5nP2AJQzch!o!6$GId^jO)k6!)rxeY; zzxK1r@u-Y5RI^!Z=pmN6IWw_7D@9$YKBR6`n+i%x3)oiH3NEoCHCUhJzg_+0CG}Z# zi>4Lmo`OV920~Z1O-N31>CmR$@38C1qP4sHN@|Kt5R!d%#qKicq-3N|`Vr;A%7{C} z0oh=+8dXCFM6a6?x6h55QMW{~pgyh$y5pI&Y}aEm)WtL3&YiAgPS~`o`R&`4eS7O) zdGB)3HnciNMcqjFZ!PNI$3DQ4*&E@%sgw2rZ?Y20FrFmd2Je}C23&&BcqWH>`7Hz& zkB-a=nlQr;0B4#DXhJw&(EwPX5FjxOD2+B49HQVrwYDP?rvvAJuTLutMYrbPW&{wH zke0a63x(b?6` zdFizyr{tCG?>w~g-c6C2H|mft1P<2_LRl@!+c9(CW0r*C<}&{*f%%p^_uz zLYB=o$!Y_rn_#n>QS}s?Paz%lgVV8Y?EXXLNYK}hOi|9Lf8>#B*n>WCs z9XsXM9=~~2<;HXo61I+S5i*K=A*?wW$b^4m@02$=6CNCe|8RtE59~T0V)2!zlw|j& zX$W2`Q~}84wWs^NHnb9OT1K_Zbgpr3K-o}KFoIK-e9kzG3n_rRn~qj=wOve-P<{x) zG;kJ&1x~NPYojcGuJsw?{qyA|edX3A9T9-DeB5&O>PI!E8IOz}YW)t}ropw3qL$>)Rz?C1;H@mS` z#}QAz1*e?UOdB#3d{&!V!y!8x4*Xbhb28JMIEzWO*wWqZG?eD2qf%+W88qJk%EC)- zLAohG(uWYi`uZ4sI+OTI@PUa(>idO5kB_zPFbZW>C%1~Sufb5IyX zcQ&=8Pta%SH|SgRy}Cu$-F}ZphOg2c$n-PHu_n+z(LkZQN@oG>hI^<*W@|V&nFh)> z@$6Y2T-Lhg!?q8Mr@+i@`_DvbaVS9v9LfHg|KO-LaWOTE7g@6=T!wWZ&}Ug<*#$pD zA+o&&FP(fZ^ z!KjuzinwwFhf1)Rian_xyU@qn&zigq+fDYN3OC}=Or|2(6y!|EQG-fOPRL`=2#DPLPzd$SLv$Rf=Q4Y09};9zsiqN(RR+7QvfCmD zprQ1tmb$g`*7aVsv}AVC@>LaA%v;-M>Y7=`eMlV`E6z|GCK=b*`kk}R*e#|knT&0G z{^Ui*HqmtP;sI8%bW*dCkSrUDt6{$ykybaJn&SzU2v|9CBw{WyBUl&;-8lzVKdQR_a=WXo0;n$$=(v{;vpl|MwT0?4UQ%Z~>%B$_a z7Y`DV539qjz1fJ1Pi|=Ih{*3$&YMLvp0U1nM+DNjRxSsjKTF7^bt)(w#$J4JV!X>v|bi2P2~l3p^haK!zZk-C8+>Kht{jcI9UM9;Q{5sh-BtSmCRC0f={CVK$xD#NF# ztSo25i0GJ>5o!*qdF3R!r8DRAxhm6U<6yKIg(fdmcUzPZ18zViHjYCeFP=M@!r_M4 zI$7X}$OLvao$VhIhIu^Eh|YYNZf0<`wtMbxH;&wS&4Gyv7K~jps`t4~6COL`*1Pt< z_4Q-M@Xpe0qh6YQ$^)a%z2uBl1IxqNq3U_VAAQNJf5a;@7aDw1{G`k&?){eV7p^`d6iJ zl8S*2w=Xllq{@48efT)*VP%rArDQLO;m&#(i1I{g(IJqM zEX)K38|>ypfei8rQ{W^X=bzcS;J-(~j2qWv4ZL#9k`>c09uO`M z(~8q)HNHN1_~@atPkSqN<@MY5|L)2z#WYSy3pHM;PhNh(B%p5i)ZU+=v_|ggxi|hY z!#iMbypK!HZ;QyT*|W}>pYESO@7!5jO91B@s3Q+D&6cu%C{e8#|GovLRcHS#vahnR z_pb<|^csrjzyPGL}=j9jIDzW_8^r zkaOb%PUfoqNb|$@zXWwesgV%y;Z6lq>*8^X)A$3tmR}pMq41U$pX{n*pZ@*xFaGiE zXJ7n7y{zs2htsXv>B2R~$gG`s?zsBl{fAbM9b6sAs-wDkVPR>Wl&(oysnV-FD<^0RA|?X!zqqi}0}H#9TI?lsCM+$o zI1_t;PTk1GLr|O?S}sD>;-wPOi72q*^--8C2xo{_#y$W)MxM?)#!JRmfzcxaXxjCX zs2732$k72*Q>!}`{NXS9`0@Hz|NQMdgPCYfx zxWL$pn&%INCX5RKANlA!FecQibVA5@l-9ZY_=9SzdI#1SG1nQ)DJ(&6CzrWSmr`EF z>qMAKv1Nn{OK6=XyyZ2&4gYbSNY18(z&VU)3#~502fwhnPV$ySc$rqU^_^$^qlTqF z{lVM&++)W2p8He#sx3GAM~w2{eAkv+0>g&}ZrLmseq($XY8oCAWncYGghOM-gp7B; zdj7jmQ&Zme`}Td8H)KdIGGJLR+lJUHr5Ix5Ez-q}BODp^)#E4h35O??#|JC9)!vqj zv@@o*3>$WeG~PSDV7%fOKfZ4C)yQslW6z@L{@(8Zre zO+bjQW=dELA(Hwi&P8`->XC^}%#Ze2@eF*~h@=Svg8xC$zms_OsJ|8xy^$MNTSL9k zfUt}bpwH}#8bC%2-$EvxeD{poMos?p%;(?UwdudO|F-V=M82YJPBK6uLe7P2hE5(@ z(yw9aytde^OP4MgGjLEtf6<1Den`Um&#pm)8s9daFg96lnKUQVkK-%B?EK>4!}?|w zc>0gF+vhIHu89&A@Lj=a;vbQ-Fi4sxt#2Gx-MeXIPK4^{rez?U+Na;hiD{E3O&mOU zKvUD$K2j8!0^aDiQN~gdN_@bQ#~DUNSr_ELok- zRlOdmh!wCzVh7;4BZ{1i9$+`Bvrn98D+NVNy8{$r*Ikhri1k6Ahl~77h#r(+^RO!+ z6ld#-KYe=oz2~i*SU7R?x@&iCTQL8;^R{ij^rCSS^CzyH`>RQxzj29YPFwx(M1V?! zT#HuUdTO{6{U!DA<9K%p>59tcihqCg^_N43e);v6KUOqXt~ZQFjPH#jmc10sLcV-v zhR)%vVSVd?ZHFU)ZH`UZhS*Tc20Xv0ad2koDbMRb2tI^?ULKW+@y2ji=b%RE?gzMCRyN`QxOL?Czj804r+wa3z>|li%?&;>jCJoOz zK7cqtIPs^}|9^65TUJhR*1gov8GpIfIbrJm%DHV*a~@oZ{~pZwsp&kVPhsw(e{JyN>5-T#+LMZ6zO3w-vl-MeI;fLv6(O4Wx_Z0ZEl=7ti zy9>%vUq#fl!~gjI{H^5JSvS<7R>03X$JI>}T(vL92|=z2;_9FGlv}nY(a}Etzx&Ju z*#O*sAV^i#sEVB36F^N>EyoW}tzUJbHn=lcEfnARl${TA%8Xuu-td#)nwmM1U?~m8 z-_RPXzsNnc|C~bOVdK{O#h}*{F4!$_!ai^OR_l0LybT}hf+J$`;|cF8zQt4-3^r9o zV+NANaqch^oe-SpedENb+z~BQmy2u-`jeQ489>4NDM$iuYD%u^xt0``JSw~C6ZM=3kOO4h=}%*TwYyu!dYh1 zR8;kG3LMh3qoWeEg@JD>V zf8u`QaJh*7%{W*g`aU;dzo@M=4tXsvlmQpB?mNWDYCs7n*}Hzs8umlVe&D0zyML-Mtss={jbQ539Vpt!;HeDO+}R z|C}K|p&Xe|`z$wNUz80CunrYeC!(A30_oGnelU$~@nM4oPny{>HK(kw$d&C+FRzRC znYEy8&Z!eekFhz^vy005)D0dsX5!SDa~62dKd-zfN9jXJbROv4KGk`>dzVx%m^w*m zOWtdn-#mNvwE3qFjw@s6UI@>P8Q;ID>6G!+^T&^$U#&R2Cx+VpJN5%Jy;Qhz6NU`DL;9|_c}M}q>E1eKhT%IbEswjJ_>T@A1nUB|4RLc z|An0#5&wVlGa9n{uc_zT##9eF#Wp%^|MW$+@ri#}&YClAiB)`W9aBB{6zdqv+4Rq2 z(Qf}#`*!Y@7TJ_DaUd-_k`(M8L=wrL`xzNYU$ zSVhy5e=F}LuabWoS^R5rr&ibZ88~%r8;Xk%udK1HviuSGwJtir0Uuv4obIfb&T8y0 ztag+_CtEw>;X=~WSp#k~Xi7Y6In+`{^{D+&S?Ke9r@^RT!h`5cH*(y-FH9y`Gtm43pVA_VD{c)#>BnGLiMt} zbuXNy`04Kt$5!~pkEmNawEY%uuSq$$z|MoJ!Y{QABdsRQYwVwwpBdB~jx@UkQ7F_- z@Y(FXU_PARd0T?pgEF}xs0w*bdZSlyTKs7Fs1;Gc=v0Mo-hpO+7<+|+o}CpsOde0L zI#@f=-JlvCeRMF1BZ#G!z#2yfF<5~}rWbv=-@%&eFSyL?c_904+2ow!p0{w}eCHG# zk4$txxQ{CQnOzVpyOyrIdBL<<^Kc}AI@oSdZ?$}fv!l5bc*$sP?cbxrfBiVrb$IAr*HZ93}B^D`%psdFGYz@h2H z=PRC!sUO<1rA^#tOc51Fk0uJCA9!ltpNzk#mrXu$1krRVhmLCTTQ|_2mg+_YtyJ|h zTRNw4aL-tJB4#eRg%f29Z~zFA^^a7MuL_Uap#F}Ir69EKmAZ!1&Dk$XP9H4B`;UYi za?UcS&?Br4>Le;)g;DWvma!#SWbjwN;xdC*|Hk5+*zqKV)zv2^3VXYfjH`1*l6pd* z-hD3c9z!uHgzc=VKU4A`6p2rqU(JKj>9nLNR;MH2%k_AW1%>qMG@mmUb?+%i0_MTI z&9D%>;N1T>WsQs2fzGw2NDMqWc;#b>^Bk)7*yXn5#2m4MbdaQcaO61%f zIsA3O@ps88qirtL_;A5yEJ0}|vqFaI!EqNm^pfPUH`w84M_W6KPeUUzY$}o@>B&C4 z6CmaR3CxNZsQLexF|uvT7NxM`R7IW9F;(6+L*7B>GLiAg)x%w=A5fk~wU;Fohr2v( zi-W3$Lj3Zz@!CvHbwdc9wty3b#hny0GTGte@8)Ed#M`NSm^jb4Y4+BwRCL^!(`;D9 z(Pr^?TJ<4AQa54MxyT5Lq*F159O3ZffEg`l!jR|;k(=Yol}EMYDoz~=nhvGn-JS0T zN2dH?kQ%HT^g@p>%8KTb6_or`h-24bT~#~!E;;vQQF6QSuYcdL__8(oUPoH(kLS)r zv9uW-JA<2zclX1;wXN^j{X`Y~g*H(f?3jT0QMETsc@q&g_$eEUd^R~VGfT3$s9K?l z8o#QVofXIiYO*~VDt4l2Y1c{im?2LP`-_yLl8s)PU!gL**VLzJl-eKWo^JNB;5eFmTs^nMtTYi^KS^6{a=DY<7?4PIDmk zu1Y>xqzBOXC=LD2f*B64UqM+9$wRG=d;y=Uh}rO|xn?i*WfQmm6s$x{8mQ?#G-h zD6ove**=`B_IuoVz)_Tj`t!-eq(llw_X_Xb=X}C^X%(qD@7vC6tvzX_cO%O4?N;p_ z-rb4D5bEQE+`iw#t7}u%Amf^HeTN~2iz@KAf7rY)eT%<|yEmvuKp8L2TbH`gcM3;X zP-zEggAR|^3bwR5(LxL73>`kS8k6AsO;Q8aN7t=jf&|>WCM<|ks!@BrTBx(uv`+3g z^jurp-SVJIJMN#sR)qRs2T}$)?03t}9kTT<;hI>M?he1Ckh&F*z5f|}V;eTx&YfH}ZSEoAmf zAj0HIb=xXKZN3|*Xcm6~F62GNO0j)I0u^Gm@h@Qj9cCp&syI)b%t}$i&H~3(n&kAM z(5l7m^dgdoFS$gnF2BR8>K@d~ImujT9(XZrUy)J^THwHtK%3(T6Gv=n7QU6J5Vn1t z@n-Ydv(J{F5Ce~hGGp?yXpJ-EZBcVoqAgET~Zvr(RWGT(#@iW56T1c2RDJpiK8hxV_GzUw~EeDp&T5NwTa>a$l zWN~=8F?+}CUs=Zd;SbG*&v;XAHTrbiD3{!AtWtl#{E+u$L1F~>dT?Wr(}f%OY~nPc z&E=++=`sofSey#__t|aKN6+MHWTK^}Lj)v5ft^_W))q3VED^?w@q~ErZh7$9jt3Ye z7W5J7l&NyEWWhyF3B6BHwgk-_@$E$40*C5$S)6EGprHx5Ent@3PQIE9kDdYr4q^KEAU8yDp#y+tX@{{OeK&7^+=LGF^=3>%^iQE&o^AkfPE&hxH2OiE)k?PS>zbQ878pVq#+yXTY%^L8p4`N8Ff& zKqi<6j229~!jPqi8_ zHyCESfrNIk#(@(T?1)nIIF&UrWMp&mE=0V0qmqVApkpUW72@rJ%6WIigi~>O>?kOM zw%1C+j52fLU`chg6HLv5yjsXeClbWn#@^0`3(f7p1Pa)avY|^HGTcTtmS?%Cp%sE7 z?sT;vUxNjozFPKSK0=qN^?=s$f-gw6N4H;FH@ReFbY0%^#%}O=8bLorV z#j&?QR<6#j>nD!0eg?hb|Q;sDq!?=v#DUAvrH5Aln`ZJ_Z!zFSsMoUT&h;(ljEeU<^UNCuoOS#2##@`ja8Z8g_?*$_8`zQxOXk>^x!Ggdsbhwq5a7*71v>>t%+*i8UCNXy5^0=8F#erIIwr; zPN!P&VRAVwMS{0=Xa*s7He5L24N!gwv5yD5bC#jWNe<;Xiqvil1@ib@rg_pobt4`QvOO- zzg%m1g~)1CjZFCD8#+SQ&%bi9{4rLScr)zkQH^Di;>NBRo+B?(jU8zo3j&pHRFX&M z1}C}@tLRN2Jg~6o)hDq?=sN?003wGOu)7HQf@?*fnCFA%8Chb0v45WN9L`zLVN<1` zea+CB$PL*za{!K+((a3U#K4+t#J?sS(`~5 zU7D$+Y@))1uCFcZf($z{w_k2GUKX|8kkV|-7q>Qd#|pS=Dcc3Z8zZ#WaJf;U1*O$6 z6Q|n-+ra8UrDT_;*#B4C4H%AOpkTM*J$rV&F~MkBB)&FISt70xH!d&+n6|+K9nJDl zc~8gn^5PCKyO84GKRdr})Uw3JZ;71K$>Y^ZIlD%-#n0osb9>U|fxpofLV zAH&o<68q0~7# zrlA2eOQK~W;{k7$)2N0!EYUQV1>0PYqX&mX(O--ig(c%;{+6o?}eSeT<1fD>% zHj$yFWgtRqQ@uFrg9D9zKQit;J$T~hXVj%Tkg2mcN?EHXI|MCjUEZTdP{MfZ)+dPj zy=LKC)_`Vm7AsO8C7{ZhrsQPLvM#ho<<>q`&%6cm|D;!$K` zuaQ(Ui4PdEs&Q_dEvxCNZ8WPK`pOrgoe|k6DP)q(iyUfh7W?py<{)>A^)!#X{^(@55`q%#|9(ICL**W7NG$aH1;F@L|`?a>1`R&1q} zsOW%gM`NcfO8{k!0s(hg7A*6Ga-><)lwk zM_XslX4Vo5uD)p41Ox8vj`p!bAKq$QK{nDLyav2&1$SG_J+}KCIk=O(}Gf%0@k#-^lp``}#I?6w) zL)HY6Cj!XvB_k2l&@vW}B@R4%ekjhy7kcPve;UVLC^sbrO*e9G`6bQH!Xghf6)kvwmNC_0dL*Vih1G;#NtZG*uu|-oSS6ixqY6K~Rv5gnegkIFU0SJCLGZY+rc}xQtMPAn^)jsW z#*Cx;_kP&^5BcCT)2};s+NPCT_PFJ{wx){$xH6WVc}iuh-x}-3!fJf~`Gk+Y&aRkR z7E--$KK0a_L_cUxqF*5*_UASZD)nZENNm$ZQGPC6Tk_{yw zVM$50Gq=cDL_`ds{9S^Krdo&RI-w6vPRaj5OK?o_|A?BiI}YdmPl##UBhIXmuRVdD z(9=XuT00^xCBf1RoS*SnTz1)F&j?}dT8SlC62#`#Q<7f-l9rSd`b3ehsHw&0#mOqv z?kptB{(rt|#2L}D;XhC}oFs2(_EWE)oV>GgO!}e+-Ps|L@#TLeF>F}$1d-F(hDARQ z)26i!vQ2;|cRP$2n_MRy7Bi@3hA(;k8wnwnG2GAcH9Uc?Fc^Y<&PmXu*Wi^uh;ME# z_TdqHNZ{ecbHeKMVPBh@jo6AK8%fU&VUh8*@W7$xNo;J{JSBt?vv+g3@|S{L*{Ewu ziAmi98=@8```%pJF{-W#3Ca_VZ;xNGc>R)>{)EuR=Nry|GAx(h>KNlhI`7M<({exU z!+VKzM4y~+Vv&`gDecgUx%5rOjJ3p!HT3bJ3EOa#!)C*LJtE*nzNEY7e1Cqrm~1-d z9(Q&uEwZ7f<80Z!(#R79qNsJ(MXS$<&KZ9F?SK7b+5{+29R4x#jlUY-{{Gly{m;rR z`^V=Wf0d0{BFbT*TK7Tt+lm!gMcFZwX%U_b*^-rMZkLpz4EXsnlx`iVDcnc;92O-j z5z~a@aD5@#0Ad4aR^LK}@>?(QT9`)T8BSg7?S=lmX9nNxBp1=J08wx-|0M{5ap_@x%1P@%4bN zczp^EoGM)@+0FT5epE^z$%!&{KE$5+0|*OL{q6^&eay0^^%uQ%FySEFXgq4{4&4f=5@dCEjh^w$xEJZ&J6E56=DC+&X!;3D#Cyj+HNG>B*R>9M=RNUPY6`_z+F-c` zSn^1xHTHH{OkOfQsImin8~?8^)UKS>i7IBPpK+tIg|5DUS$U+>8)J4Gc~Ko!(~oL% zV=n*y9H}%T6I*632T_pmbjQt0FHbmA3q>MZL)JQeVEjSlPO~>#hN6`;Z)(lQeO|15E2Sv6JLI1ML`Guv|1} zk3B;g?-|nEUBiPvJD)Cl26JXT_6*Bs_uMm3Q!a5xY)kbPg%{qs16T?oYST-1dp$wP zfi4R!J9ZCNhYQg@B)w+HiVwXnC9BU{mAHjPJ71(mf;s)dTcj}u^+Wqk8&E3I{AW&k5Vle+W(CLO*3&8r^05nTVw}C zC!`FBAl$>&bW3)Y6wrJ=haB*@(S)z_oSze@nC z#Bk;hRbJLS^~oiV&k-X<%dN(z`;b-rsajiP6nr9dcYFW8SmuvBt0H8qFgAWVu(>1|tE-=8#?hd){lyfXR?_ z>?T~gUB1DXC4BYh#E0_}wPw3gI;K+R$A+a44MTqXn{X_9``TwdRNV5*ds{ZnpL*Nc zJD>J;%(@4qF5fqPdeHdkYLWK&gd^YN_L>^WKlbfw`<{3U{KHrzYQswiegU;_Pf)ue zMQxK3ptk&HqJ%X6bCcSYNotdK5jG%3F>OGMa+pWydK%8O0x^nUD^QvbE0E92rAC>E z9TAE^;tAvm&|Hpxo*1PrH5cw4Prd0VZ-Ad!jF;0madRBVFpWd?-!tLsjHG8GN!zd3 z{eet~F4~nzK7<@_vX0Iklj&nk1Cw-R4Qt+|NQ-Dx68F1er2%|{nK-Y-kY>w)&sw~?bpq6oW0EX@O{pQe#xf3 zK4@#6Em4WwU^c2+;CO=sTah|hHb_W;gIhd1n3;(ULnitVqs)KuydTYtCTV`?d(KnE zJbM_BsMtxqH^dgh!hZ3`*B{<6^}eZ(zW%`%7o&sBO2s91D(}8^^T*4FwN(_q{rH0q zPml*q8A-N1c^rPQUIA?HAZ&}V5}k5{?e0%XDc5eY)Z&TPi8sX9O1ACIex=C~n5H}e zIAddirUfaQnqv@6c?@YjN>30)O;M+ll&An@*+iq@Oqt20`*;*ZE=!F9E`@I$qaaV5 zxfG#p(n;kh{^C)nbA`Oxj5W}2Al6`&*YLknZ=s*FESPUmm0l;lg?@)*f#0Gk6{)uj z$Eb^V#V?qn9RDe?;!cU;PR7i|e=$c{Z?3p=6sn53lQI7kjRMS9nwZZ?V&3ev3Cx?l zHpSRG8S^I6fHNL}=fNWY^YkSkymsQu9D{I%G-_H+Vji71Ge^NWS7FX$lC&T(N_h$= zJPMt2P0VBx$Vl($mBI;8_&T1;J`*R>AfkIFRiJ*Z8Owidm3hMSoJ-6RsX|JhQ9w5T*0Yo=wSX$E@V`HNW|s z&v%k>kpzhVK<5YkXUE+ouj%--wH5tHo7*|yWAeUb7q5DMV%6XBs#T?v0>F&L$E*G< z@yxBP&r~InT6Z+D?rh5cwV=^GN|DM=-Itza&COBL1K1Eto7-b}wUzYdi7=4{f%(XG1QZHnqN2Wn}is&aBAY-_KnV znC83q*3Rrm%(j*_+|0(f%@5NHhLhW$o`K0)GJ^QbaVq)v;LIQrB!j{9tSlq}W_i+) zrj{N^^THbauM-9>dA=O#N2$={mNYfvWCrE3X;TM8DkH7SJ2NTIO3pZyQHg!BdKG^! z#$k6O+)_r6QtGf%*8PE?-Htp(Po__GXHXGVH*(J0W+~P#)5ON>B=MJ$?o*?jokUp3 z2UPOJ#+?`(O3qL29w6$B4`oW2XSzLrL^xRlGtoN*DU2DJn5U9~HpysD>&XlRY$M>R zcIqQqod0#6iCKvgr@OCPtZ3R9Jm-4j0mPBIhK!O^rce^tnCt;{95AX&BOA+|X%;k} zbaSZDrFwLMeh^N-&!y=S>IR`)pX7mK$4P0GiR3Ky^L09OOltcJP27P=36Eh>h|(O! z`H7fXn}`~Ziuab9===b|od$V1ht{r|+L6PkBmqP1Zb{h>?$ogdsY=aEMHJNK(d~&@ zjl`^?_z2IG&WHb-rsqLcWpa8rXKcEhx_d}YoZm_jHttJ=+8!3=2uQA2!l)BVjKlIQ zafBjnM&ZPc?>Gd9ss`v0g86+9%xSP|`#0ukejHH1<|UBsa3Ukc0dIr@t#TCwRrHjE zt}|mLWYUrtCqx6K&CG69K$m&Swotiljxp`aAKQwEA@LG0TRm6B3SK(EEz?pE?0?igPLY5%Ejx8l;UtuvU|N@yEPj#vtnkj zI_!!wUqTNqRPRFd1=E$8nje+gfG0Plml2>MF}Bb|iwkMy=y*`a#Xw1*t^P0yCNACT4#tNL}8}r*=Uw>En>(9OZp}g)Gj`rQU5uE|@cfY)A zm;9x%W7EBx*P9kN`lY19s;YqZqTJ&v%S5_SIArsMl*-<4rw23D%xn}MkkCp=@;Xbh zQQ{|%4O857Hz&R&jN2~JoutmhVJh^j!`#i8(gjUEOolp6lLsl!gjNNMQ69E%OWbOm zebJQ*E@|C<-%mrV3;y}>H(!6e;}L;447$3{n6-L^xZ>E=zrTOuW--EenJ^GCi`|un6yT5>oVc}Y%sQFhxCld;b%Cm#A)i1mJ*+rJh-i3vw-VCG>AZ5!V zH^}%Z$THRm=apA24UKLo?P~0HwIZ23W;!G@621sXhR58^1BP4`lZ%MMbPn=qog}SF z;e-}Do_+R(gAZIeXUD?&g>S5W_Weya{`S^1=d$%HmbY!+_RQ}cwwL4=X5Dr3>RSel zpD?lM`QKc(Gv8YByL*0jK7oL0l!8=GsC&1d9g*{UR?ZNYl%Cj zELvSbL5U|otHXCh?u|1SaEN(JLQO3tCv2X$`gf`bH8C51H9FU17WzyrDG$|1^aKI1 zGslwFZCH5S&6i%d^3IY8znXII?%A^z&GWkAQ>TC3`oPu;t{I!|xm-?fo;!Q`{HBTF zikq)EV`b2qxomOk!aky}Nxvc2U330STGK>)V`hP3MN&&+Uu;O64woG%U5KaUp{^m$ z>G-iN@%S73AeZ0oDo86Tz-PQbb=gO?Aj!LnQIpI$>t^>utUxW~?$E9VOCT2wogV2% zKC}~2-n-+L-`<4C^T(gQ`tchtJ$}o=C(o&WNmvg4O`Ub!BL&v*wqK$82h1X|@mIg( zx<`#;Cfsp=R?v*^`rV*P0eNq{?t(0&n#m{&fWlcgBj(L7C_r?opddRfvnb7iovk-J zFrp@8J7H8Gd1lU2=JH?5+$t-GR0oIXQ3@RBO4>g8FdGcdTn$26c|cWbtypK0u&1O1?iAr8jSO_ zgvufRG)`2W%tKA;vlEwHJm)X2xZ-HrWB2d=UHc!ASX((qEdAw8qCk93ivGnvh>6`Y zY@rut0ehL?(Mru_@e61wmp_P^3B*##bDqerO%rKoM4agf2E0f!@+Nf0f0&=?K__jP z)DUpPK+518-q>m^ySVjt_io?bu>czYBP>4Xs51`Tb`xRnsRIXI-q1m%Hi0?XVa|mX z2;tkFpDQwuX=Fz&**pnq85;qo6UX(OswHboi>14g?sQnOnT~8mxT*NGzgd%*LtKS1 zAM!R^Wh)#$?-(c~_o$foVcW;Qh)j5KmhmBK;Fbx;Kff^Asj(Dc{ z7$#@R>4tDlWKv{m>yI!v2s@ zt{OIF$iF}R^k3o%nicIvQS}e8BXpW1`bvt+uVw_&(!9tC6ma(m#S4w;O<|9Vr>4FE zB*}ixLh8Y8np$WSuDb5qww-rBxr-74+sl7_GvfR(C!YMn{#TTD=NN*Or!UUi|vyLVvpY z(ZAgBY{&cCp8xWv_h0*F`)$TgsDJ;}i#xYHs7^ljp&92-J?D;f8*ZO<|6R9jZa#bb z;pcC9p}-n?7`EjMQ~{q%-WG8wY)j~8c-nJ7ORD8xn(s%eK(E8=^Jn8@Qh<`h1wJp- zvR5%xbPy#egI)G92{oMxQ4+892u#z$H+S$z#zV4(VCg$C^_`^?TD5(J8*> zx1^gM?lDniSdYkMa+b@wM=#s+Q!@m1C1X#wz7%uRC)lUeYV7IOmtu|zW}@fW%l|Qa zHfEflP5l_Y3B&=HBPj1g~UbnDXICaN{v(4eH@!LHJ??faS|T;)VeyyS)CfE z$GW!QD~hP9Z0kYyg}A?$>Hg2e6Y#lX3}H`= zVeH||DjtJ)N`d5pW9~Kfh;qa&&;uI3LH7sn{k6s(%O<{$s0iJ+v?h6z;~I&Iir`ta zoxwO}e1qnQ=V^|X`Q~bKQ>z6H>UoY6%_r2I<~mcoI?OSMBX|sDxy6hJprUp+&Z=jQ z;8DO?z%FxEQHlaR$GPx-FwTWPHx6@_IiD4&acX+ZhjhLtKwYa!OsbhdDH;h>+f5_MvuFfq*?i_rGnjOe5r%RM2i$80UrsW@ zQ0(~ebEFrVIt$do9Kbo|96C8C^#wEM0BeEgKw2%qIVnwQjsdM^j)667G1p;=ZgLi+ z)y#E>iGSl+m`p?KF!oUYCv$`z{EmJ~=I_z{1iwEG`H)$6_XCDWKB`#`9suTLwE_Gv z#k{X><@@MxWRY2h@jS~%oM+gncOL+zWtI>8COx4(jS(Qj!6c@*OA=G-Op{s?#eDEY zaZ<)Gh9%6@BF+G{+IWPNCh0lCP}GO!5mHjxbA+M4;SmbE&w;4La{#px>o5~IfJt)% zq84*!=Q>gpH)lcA;xRCTMdmD06z4IRTD%T1@gF>k)K?7Hx1wK^IYM{rQ@1Ab-ApYL z`((k=*{D*9&PEXx7co^5r-g8z^d(Vo^6|f84fs#3XKLYjro}pQhN;d(W?YgiSo|hG z(HFl2TBNj@IR<@cc?^7M6WxtcRgdkhqz z3gZ|gbtsNoa6ker+-%{Fch0_K=p`*JmkhgZx{!KBt5!{&Gt~0U6|23|Crz5}y>iu% zx~5-vSJ21o*qfOD(_P==cDkLu|M(so-iRXO8NsgaxpnB0ajT+e+}Pzc6VE!|+dQ;? zVl_*xKVnavFBLYT%L_W|nkT4TJkf z^UI2ACeJX=>RhgM(K(^hE=wzllw@0*>`l*ht{JmfinV@V^8%wq(xAq)+#;_XRZ=}g zLZXA10nOV~B%$Czk&^F^!bhw4_vUBw)TVQRunJUP_daiWmy4=oobu&*Y~)Wy+C2_b zcA2749vRWtKYK#fNcXgHky9J`heij-q|Lme%S;EAMa$Nnxi}JytU3F<@m(g0nO=pN z?y&BY(lFO>qtD@yg&dFrR$vS*;`dt3M&1=TiV{W35NwN!QiZjwolUPhHg#l7bWfi$ zWx8iVp7Byqb!=71!ir()J3pL0di3c(h(NC~)AiaFCB$#ZH#*EW`aMnrB%H!)r8ipl zdNq?qoo`IBw9@6>9fybYT3o!Uy0ogc%d0IPl&q}QXVecg{%XEC`95^F(y`l)Hs;Jy z7pihjx+h(z>1ukWmhXvr8c+*+FDhgcbD+=|uT`&}j@73412m|mb0=NrU{tKJ4%Y@Z zv_>LD{U*%V&}AalRp(?@Oic@yHh?)s8KKl{OwOk{I7uE1 zI%UP0Ul1*5C{PNL=%1df1PH#U#-C_+Iw(DO1j1>xb|c9-J$a4{wzM%FwH0uT#EGf`?gGaw$3h2kBT&t5dS^(rZ=WD)KUZ^&N;9SD z#}OmXxUH?ZUqw-$bAD`E)UfRO(`S1pJ8A5`$m1L(%|OkFoW?Y#Y{#)t4X0vJF%t3G zMA)%!A844a{+2%`-4^LHQhtj6$73~sJ;U%IX%f1tJK#$d&NQ1tP-=*IDv9O>3P22y zO-nt@EfJiR4YxWy1;%^*#r4f<`Hh=Axu1;?$m%!yGUi|(pC)BUWsT`N_u-^|?PFUU z5ydmMMafEe(-N^(5SELnb9*?HjwatGN6XZlACJ8KuOHug?}zs1AKM}3@80?QU2CddiMr#Sqc12G6xK`XWQ9gY6 z{Ut*;HHlnvG-U@?k|_;rMAbgDlagIN)LT+fQZ&P(jcxHn;x@Fnp*BF15iGz9>w zg!3{pQ!6daAKOwI(Y#|@w2~CMJG(S=3ww2qdL@$cI9_~0=!;dsUBA6y`)=yjd1Tw< z!_z+a@RRp9Z`t+cL)R|Yxv=jqzFqm!`-<(#C$g;>J8wVu@ub2MR;^kjX14>3*vZg(56q6`xP>*h-b+uBWHFM!kt2 zODI4G?BbgNhj2U$w5qe(XLRWDn-{hl+uZixSsm>q#${sFQ00gAkhoFwdcF4z9Z}%& zQsWj?QD-7T1kb!z$;r?%G8M#!B>z}==B-Ho8P^glwYtW(Si9q|(+LT#HM{2VNrfcF zKx4|2QtD*qSHfj{@xy}Whn#!g%)MXy`PoaaAJ|{s+p)QFlbreI10Nr)D4P?nc=@4w zo`^)`J7;yYhhUFg2HQAH*>%)83iFH__bVS`o>r12^cDm9sOLRWJ>+pM3VCSglnHsr zJL3grnj%DMYWsd=N5@v+4 z`NL+Cdkyt|#8EA?S~-_eC{?|;8AlGzol>!);NK$C>m2#gc`twR(+fAez0?SA-t+L? zXa8=_^xueoqEN=gPh>Z{J>qQ9JZsKJuf8wvi|>osZ$AF;&Z)PIUHZxrtSy9)%79eo zTU(+tt}IQHKd?--?8BL|7$m1xMO9U0tf*HxY(@>8sH~{66zNrItEtZPmsRyDilAnI zQ2JC9m#NSh)zxoIM`iq2%*!2wI}h_fBSOk)t)2QB9|Jl@1~Ey+0fKq5P{Kj*DRqd% zU-2*!yl+`~&EVN{cMe#z@E7anUUYi?gc+wc4>)he=pwP@1FJf5)dxpbHLKQ7ln16Q z${$y_VnF}0NXz(fXBUnstc%9_l^53xHw<~!AB?&3;f}lI868n^-Jy=HL|+Y6uujpv<+Q01axI4amg>;^m0lI; z=$ji%&p{_O7dn#^;}ntCf)oK1OzDhsF1e@Iq+s;W$Nef4DruxC>=BbxWNdq>d*pg3tgC~ME!aT1=j58iHpHP#lIRzHgEaFFcLb8g}Wa7qW zvG$?1{M7G_mrReLNS#}tELH4dCG+=eQl+dCA0K}kzj=*Sqx(otu!+eX2aew+*>(uK zght>rc7hhNmEMSd{||8S|KxXizIPpF`X=qHP29kjnb`Z=lepOnkK~XPZm=g;nt>zw zPe{_Pm?TD8FF~cW#<#rF8jo~-^uJ8EC zql|%eh6X{{Gr)#?Iz1y#p=ab4nu9!xephbg`}6sFG5>T4!&S$>#!uJq{dEklV0ab7 z>lp4}$XqJF%GZbZci&<7F0Dm=kFVe7>ks()L%#lqpZ_yof6DM%zVjW!?+GdndS21^ zI*qTLe9io$`1no+-^pf}!!VcchZyG3N|k)RF5v4zzAobHFkhE2Eaf{9zAoeIa=z}x z*A;x-o3AVRx{9x(eBFny`|@>+udDgGhOg^*R{a?EXE=~yBf}vKhcO(*a16t73|TKJ ztdW#vzB7g4REEqiPaqR81m{s!60#HHkE; zCXq(f;DaVe8dZ}>qiPaqR81mY)g+QN4c(v#l1A0wi6clFRg*}gY7%KwO(Kn|Nu*IV zi8QJvkw(=d(x{q58dZ}>qiPaqR81m{s!60#HHkE;hI}@Hq)|1AG^!?%M%5(JsG39? zRg*}rHHkE;CXq(fB+{swL>g6-NTX^JX;h6hswPOIYJxPXCP<@dtWh;V8dVddQ8hst zRTHF9H9;Cx6Qof!K^j$Kjj9RKsG1;+stMAlnjnpe+G-3*qiTXQswPOIYOGN;K^j$K zjjFLm)mWoytWh=AsG1;+stMAlnjnp;3DT&VAdRZAM%4sqR85dZ)dXo&O^`;_1Zh-F zkVe%6X;e*+M%4sqR85dZ)dXo&O^`;_1Zh-FkVe%6X;e*+M%4sUnKh~=NTX_kX~7y* z6Qof!!I)=_s)?`o4rx?PkVe%6X;e*+M%4sqR85dZ)mWoytWh=As2XcjjWw#q8da0$ z(>O9|R81z0s>!5LHP)z_Od3^_Nuz2qX;e)njjG9{Q8k&llr^d*lSb8K(x{qD8dYPB zs>!5LHJLQ3CX+_hWYVabOd3^_Nuz2qX;e)njjGAt6J(95u}0Mt(x{q38WkH^hNMw7 zg*2+BkVe%M(x{q38dXzBqoM`{VVgCorjSO}6w;`gLK;<5NTX^BX;e)ijjAc6Q8k4$ zs-}=e)fCdGnnD^?Q%Iv~3TaeLA&sgjq)|16G^(bMM%5J3sG33=RZ~c#Y6@voO(Bh{ zDWp*~g*2+BkVe%M(x{q38dXzBqiPCiR81j`swu3OSfgsJQ8k4$s-}=e)fCdGnnD^? zQ%Iv~tWh=As2XcjO(Bh{DWp*~g*2+BkVe%M(x@71R81j`swt#V)1*piH+fEv-vQW* zVT@r7!#LLEk@|sN9;qI0^hmTKk3=i-NLTSQH#5A2;jIjBV|Y8mI~e|oN7%&hPKKKq z-o@kmfuDJa;VTSZW%wb(j~M=$;im+Jo1vGXk6}8)Vw$ZeW!Q_x7rhx)G3>)I#;}GV zQOYBTQXawU5+it=Hqg-{7BF1Iqb=rVmhexP^7Vxb*YP-a@-uhwPq*>)ZoYn!;U2#8 zG>^9L_%w`6`q?Ai=bwJeFobRk>cqPND8D7iqetNt70AK%};Uf$m<@>w%n!Y<8`EiC%@%^Xy{xkf>y?me7 zB0tO5`x(B#@C|;3z9Sy_@BCZ;VE9jl9sE;Xo#LWr6sD#^UkZ=HG*trhq!Og}Dw%ve zlHq8EV;PQTcpAe=41dA!bcQVq&tN#6A!AQr>?yMehsxOu=P;bd@EnE<7@p7bS;TM& z!wr~^SBis=V9)`~|Bu(fQr18A+{d5PIr|U=gdKX_m#n(^M zHCByhuxh%duZhmGsk3bA63M16k! zl1*JA+0-SHOl1*JA+0-SHOGloP*U65?*f@D(%#xPr!OMWbOAlcLf$)+wyHg!R=sSA=#on=#J+0$@nmQ7udZ0evLJxQ{u3zAJ;5cldVn>x#;&a$bqZ0anVx=gaEvux@z$)+xoZ0a(} zrY@6g>N3fuE|YBPGRdY64#9j_Hg%a~QN3fuE|YBPGRdYc^XgbOb(T$?WfN6^=oywx zon=#J+0+%1Og=AA#NH%qaWK&m2Hg$z$Q&&hfbx6u0c-1WoHHM{lvKUfBuu@>>0NJ5y z;;v$ronlDM!?^CxkiKZekQ(~w%?xj0cq_x(7~anC4u&Kh#gGnq{w0R5FnpCE**wLN z4uWL!6hk@yAsqz041EmK8Ip7qvvd@L^D!&t{9@+(VsJkF)=|FS&d?yp99S&d=yzqZ zmx{rMcoGyLIGW*DhT|EY#&8nDUobqKVGF}E7*1zsjzf4ZW;_=&o{JgJC6WhkFOhVH z#dy91_2!ySs2y;b*xgvsHHvKMfMTEH`!dwwyu83f7OZSN@ zBFq&L=86b&MTEH`!dwwyu80VJ3v)#Tdqesy;))2Qi6C)B1o|A1xgx?`5n--~$ix*9 z=86b&MFhJExE5$qQT5?4g9M<7UC5rNjiTbL^%%oP#liZWQ> zD`AO~{ZkI?5iG)=a>ijf>wUqN(%6Tp2yq0oaOF6HloYzv$Ybocol=E7un3JoRldG7Mt5~mAF(+3s zCs#2iS1~77F(+3sCs#2iS1~77F(+3sCs#2iS1~77F(+3sCs#2iS1~7}$r4sk#hhHl zoLt46T*aJR#hhHloLt46T*aJR#hhHloLt46T*aJR#hhHloLt46T*aIm#dl;HI&5hS zNoJ$G(kQPq$}5e6x9L7VQ z@J@!C8Q#U?ki173OHsyBl(7_LEJYbhQN~h~u@q%2MHx#`#!{5A6osT<97qbmUNkVW_UKkISl79L>@kVx`5&NG#^MA z!6ghgU_LR{PADSyG8khSjIj*HSUbg7JH=Q##aKJVSUbg7JH=Q##aKJVSY~6aonow=VyvBFtes-4 zonow=VyvBFEYUHR=om|Mj3qk85*=fSjlF^4hC; z?bW>YYOEdiWjnn^b~2nocd$Bw`3wsgh8dPHj4&)`Si!K8VU%HChSh*IO#K?BehpK< zhN)k})URRc*YLV(nEExmx*Dc_4O72{sb9m?uVL!fF!gJg`ZY}b8m4{?Q@@6(U&GX| zVd~c~^=p{=HH?88#y|~YpoXbm!_==~>en#!Ynb{qO#K?BehpKK znEEwL{Tilz4O72{sb9m?uVL!fF!gJg`ZY}b8m4{?Q@@6(9|zvn!Q1YI)Wm^3x~6Ya z95`GANctqsx-rhWG0wU%j(haXFld7~yz%tp2!>4zM=_iLy2K^gBgEm2r)OxV5QjIO zu4%szhc}+yNSqLt$m)noQyG%&5tpVhoX&G2Uws^uB6vAN^3}&dDT3sykF!pWvrdk) zPL8uqjhd-a-Zy7$w@F9jf8PeV@ z4u3wu#~AKr_yj}RhsNR0C-@XY+ReqKXLxnw)sMrgPndk3ulMuy0lt2Lujv~Rhi{*r zq@73{9)7we`!EhaKV6fR7>Bo?t`9T(BSVVc#o_HINM77Hy!`|}U`SSF96o=#L$+lc zo`1Tg9Zekmf4csZuRr7Kqcn42WoT#UVo1B5xbV<3{B4R0ov+CvjSD|tlU*7Y0lp6M zZ)GtI5f30bK#-yXagGke5gnj+k-WzR$$MOoyrak@!&-*)jgE_f#35oZ!$yV_BZwnL z0IVWLK#;y}aYPE}K6z#1;uzoQASlc9Te8AXWk{A)oGq(3JneWd9|tH*X4ZOi?7K$9annu zHF>Aw@J`bRQNHfO*M0dq#@FPXj>9`m&(!gCKZgAo4rJKKa0tU;3~3h`S6Ev43y$vK ze4q9Rab+A|(@r7IU-P)aQq14cS*2`=Q{BF}a|=&rW_b%w>T4f;W!6GXNi z;N=Y0F(7hN96el=^?trEQ^BER03^OcY7-5KW^LVC$VI{*T z!@dlw0qZf(I{Tq4{mSl1R#&KU(*kk!o zFVqm1w4#ZbWI-v3^YHi~Y)w`UVbz6FDrq!`O@R%1i(+ZApcf;1bU9wxxbjGHFT2J!=T&kuCl%0NBFBwkWtEm;6tSE!~Pg{68mA$wb&(2oL4j00;a%LFb%fx zWINaac7k1CH`oJyi(I}9_Jaf9AUFgb0lxzdgGa$*U=|z&Pl8?}-6iETo(9LjGvt^9 z$3gw)j-HvK%;$K`_HSdq2m5Z&vBWM#EPgV? zelNCH@OG&OIei=U2e7we-;3?l<6Vkp{EXLCb}6c{{rlLS%k0wkIHTuiyYxNI_y^#J zKEP;9?P>+OSGe2YXNT41G z)FXjkA$K!r@M9uMP;^KyM&@LqtzpUdL&SfgcNl|DOQh! z;x41rBcZsXFdC3!~K|fqEoRk3?YgNN63vX!S^-9tqSVfqEoRk3?Yg zNT42x!0M3*tR9KL>XAS_5~xQ4^+=!|3DhHjdL&Sf1nQAMJrbx#0`*9s9tqSVfqEoR zj|A$G&=@LiQI7=bkw85XikFl->XAS_5~xQ4^+;%rqhBdrQoR%}*|vHl6fZf&>XAS_ z5}Nhcwt6H`j|A$G&fe+-K^*_wq5ILYF$mOtEqLhw6RC&9|pY>pjz5!^oUT+{;_Il<2l(6 zk?#Ft)zU^k?_m5SVxg^z6NaxrLwt(JUQ!RxwdXG&tY*)i}HEdVIb~S8Q z!*(@nS4$zC*SEobZ~z}r0#!*J%jyy>{;wru;;K}#eNO^0eAsi1TTV@ z!Kq}5VL;~U@)!5@Kdf;Ye)gWj1|Erm3CXIixs(s+meDuq-{X^quVNZW3c)lx{? zZkg3mNZa1ARxO1zI<~2nLK?kGty&6c^e(k(DWuW6)T*VBM#me~nt>V}byQ0;#i2CQ zZ>GJ`{;Q>#PWg!6EX~w2(oEwcevdTMw!fcN(^IOYnYO*JvRay{ze+QeBF(h@TiBjE zS4%UU{vPbR!8j@RV29Z6#eN_5`?0rSe*k+s_Py9Uuzv^JdlIUpna<_+v8%wHU;?ZL z{{Z|DNZsk_)zVDA;Q=!v&9wa|*mc++#eNW^-=P52QTiP^P%X{$udD{u(oEZqORJ@s zwyg}+(oEa*N@=ET&oQf|nYQ2N(0fy>rI|+Wq^g!?8oiUMTAF#TY>(7qS?I`WkJQ8X z`=H~TJ;XVCh;#N3=jhdsm$dx#eHF#7Ld+~329zlX7Y52N`W z#_>IBpUOoo)95ID52NQEMxQ;5J9`*$_Au7$VXWE1D6@z0We+3E9>$bCj3#>+NA@s+ z{NL;c`$K&JS=QNCzif>BBKQvBeWc$<`hBd(-N%aDePw3&K33%JlkWLRuV394d=RX5 zirT;N55P^*OGe&*UxfEFfB&%l`{BK1AJ(o?q3)tJzC*Z|-1n0EUUJ_{?t967FS+j} z_r2u4m)!TN_br$0CHKALzL&L~y@7MzOYVEgeJ{EHk#he+*&iu)p>j9AL-+u>KS1se z@T&*-RUNT(U75$PI*k`bN6vM`%yq=Hb;PT6#H)40t98Vyb;PT6#H)40t98Vyb;PT6 z#H)40t93-1bz0f;n;l`+5nI*~ThbFEVb;LGxQEIJT>2%M4>WE(Ih+FE2 zSn7yX>WEVQ6h8kHKK~Rx{}etSB>h3sA0+)j(jOvSCppQR58?fX@cu)1{~^5p5Z-?X z?>~h1AHw?&;r)m3{zG{GA-w+(?|vB<(FpdrQ*ZlC-xZ?JY@rOVZwww6`SfElGPzG6PC7 z_enDANzww7w7?`SFi8tc(gKsTz$7g&NefKU0+Y1BBrPz>ye7$vCdphT$t)&G%S_TT zleEkvEi=iSBFUU0Nh?jNKZ-H+N23)!NxMxlQ%EvLNHRM}G9ySvLqEeTAW8h6B=S!Z z^CyY+lNz)9jN|+yEk8-iPtx*}wEQG3KS|3^((;qE{3IW-rxn%Hit1@a z^|YdTT2Vc%sGe3-Pb;dY71h&<>S;yww3&L^Og(L;o;FiYo2jSG)YE3_X*2b-nR?nx zJ#D6*woy;psHbhz(>Cg95A}Gz9^cjDyLxtH*cs_^uw`)#JNIVf#_o*4|t3uN`xsee@`7KMLEA!uF%E{YggECTUAUS(CKI z=s8G}q6nk+r!|%RF1A}^QTCgg69-Gr{FmS&nb9L!E*|pQ}CRE=M+4r;5h})DR@r7a|)hQ z@SK9@6g;QkIR(!tcuv7{3Z7H&oPy_8cy5K~R@!qbJh#GgD?GQtb1OWz(wz=QKR0;W-V@X?RYp8+C7^?rqe)jk>o{_crR@M%~+}dmD9cqwZg& zj{gmO7W^CVxiZIQ&y_hgdrqsqi$d>4eok#^S(pHKlkW9_=gRhgO{6q~Eno_41=C=L zC#SIUV8N)iq4%q87_0n@+J=$5*zxroJHDO^YDll;SKf{MoZ5`>U%;Om)n<&2_Man$ zd@kZIiO*@3&-k05cOyTiH9h0q;631b!S{n70Ph8V2mC$oDZgKB#`rYoJ?ic7)(&s& z@YW7*?eNwPZ|!B~tzA8FS!mwcwaV=j^VZHzxpsESwQH5zDdw$RJ<(5^w{~{QwX;*M z9p2jEtsUOl;jJCs+TpDo-rC`)&p-n@YVxwJ@D28 zZ$0qV18+U>)&p-n@YVxwJ@D28Z$0qV18+U>)&p-n@YVxwJ@D28Z$0qV18+U>)&p-n z@YVxwJ@D28Z$0qV18+U>)&p-n@YVxwJ@EE|cv~!cLA)975+^T+6Js;j0;a%LFbz(D zd9Ywq?Sv}7-lOsxy#wb3mEZWApm%(|pz<5<2JZpi3%(!pPOukLexrASy`b_hmmQ{@ zhbiY_%6XV_9;Td!+c_XCLM4qnv$|vyXE2QO-Wf*+)71C}$t#?4z80l(Ua=_EFA0 z%GpOb`zU805BBQS)M(>fx=qriQdn7Vi-!^)W zM20;Q8Le;I_8y6h*0+t`BavZ`M20;Q8TLqI*dvi)k3@z&5*hYLWaLMe*?S~1@}_O? zk;urWw!KFp!>lT!6>{6&BazWMxoz)}$Y{0Pw)aS6v}SJGdn7ViGdFsVL`G}oM(>fx zXwBT{JrbER?~%x`MCh#7K z470gR;5`x<=5(3Bdn7W<>@tD(NMr)i41!rGVGDa1l}W&3A{%l z6L^nAhCLD)_DE#(1x@AA7c`^yNMzU}kztQShWTTLJrWuANMzJ6^o;t2(R(B^>K(Sd zM42yFZNen!1Ue`SwEMt#S&_ef;agKT?`L`MC{w)aS6^cBe|-XoETdXGd# zeag1?NMzKrYk3>e_ER5bGkOB$}=FS=B&Y39vju~_&O21+HOE&i~QHvi}JowE9;(+YzVtlip8yKk5CX50E}U`T*$zqz{rlNcte@gQO3U zK1BKu=|iLslRixPFyE$!`8GYwx9MTNO%L;JdYEt1!+e__=G*iz-=>H8Ha*O@>0!Q2 z5A*)vx8KjV>7(Rwlw6LI%TaPUN-jsqk`y93_{d z*OZF2~5_7`YrHmt*8|j9jwhk|mcc zxn#*DODE+@$41i73b zmlNc2f?Q6J%L#HhK`tlA-dlw3y1Wt3b-$z_yW zM#*KATt>-dlw3y1Wt3b-$>k)uoFtc%aydyZC&}d`xtt`IljL%eTuze9 zNpd+!E+@(5B)Oa-7oA?OUUQ0EPLazgaydmVr^w|Lxtt=GQ{-}rTuzb8DRMbQE~m)l z6uF!tm($8+QKwt|vQd9EzC$=h?qlRWM($(eK1S|iFH^RcDcj4G?PbdL zGG%+2vb{{%UZ!kQDqDk20rq#ODdNK^;=?J{?Wc$mr-%}#G&l4!{th*zxuMbDp{9u9 zrdV^HV$FGqHRmbToTpfGo?^{;O0z;g@9)=BL`PG^MN>pXQ$#pZ#5PkzHB+oSPZ6<9 z5vxoQrA!f@OldBu@@Ot;^!Mwjz~8T@G?z5q?_4xzbh^J^Pif95)SOX&6?^)X*fTn! zm|{epVl19w6rN)Ionri*V&t7-%$;JionoAwVuYPyY@K3Mor?PV^;Fc~uctI;H2V9s zPRCTg(3x#Ue0L7t<>5aM|9SY&!+#$B^YEXC|2+KX;Xe=mdHB!6e;)qx@SlhOJpAY3 zKM((T_|L1^6$(e*yjr@Lz!c0{j=?zX1OQ_%FbJ0saf{Ux5Dt z{1@QA0RIK}FTj5R{tNJ5fd2yg7vR4D{{{Fjz<&Y$3-Din{{s9M;J*O>1^6$(|1|th z!~Zn=Ps4u^&WmtfgzX}17h$yst3_BX!fFv#i?CXR)gpWr;j;*zMffbjXAwS&@L7b< zB77F%vk0F>_$6k%hA+RaeA8EQ8} z?PjRm47Hn~b~Ds&hT6?gyBTUXL+xg$-3+yxp>{LWZid>;P`epwH$&}asND>;o1u0y z)NY2_%}~1;YBxjeW~ki^wVR=KGt_Q|+RaeA8EQ8}?PjRmEVY}ZcC*xOmfFoyyIE>C zOYLT<-7K}6rFOH_ZkF23QoC7dH%skisogBKo27QM)NYpA%~HErYBx*mW~tpQwVS1O zv(#>u+RakCS!y>+?PjUnEVY}ZcC*xOmfFoyyE$q%NA2dQ-5j->qjq!DZjRc`QM);6 zH%IN}sNEd3o1=Df)NYR2%~88KYBxvi=BV8qwVR`MbJT8*+RahBIchgY?dGW69JQOH zc5~Ejj@r#pyE$q%NA2dQ-5j->qjvK|2=hb;^P0^qmd%%W?RUP+Yrpf-H>1A^&P(NN zd+m2#vplExo8Y`wb&UR&I&_mA|FVv-UgB+V6beZ-VpEF{5Lxc_Nwlh`*)IYo2KIx72yf6OHcy z{VjDq>TiPcn(rI^O>kawe51dm&TC$8^f$qI>6qRx9W(k{>O5<|uMuUvMwIoM?yUQI z*=vfg`h|aheV_hY;I*;`z&h}w;N$%DCidTJ=lN^IW3Lg3E#QjqU#Gp*B6McFA!Z{AiBOlbbW#7 z`U27Q1)}QUtol~zzB7L==y(=mrj>gOVByTpjTQJ$!n3k7RhUoycWr8 zk-QekYmvMb$!n3k7RhUoycWr8k-T0fuZ!e$k-RQauZ!e$k-RRF*G2NWNM0Ao>mqqw zB(IC)b&neF&C9kXGb(OrXlGj!8x=LPG$?Gb4 zT_vxp+AquX9xH?JHXf30lv--@O5^8ud@SuogLuo>;PY92lzTWz}MLUzRnKt zb#{QSmtCeLSB-(cpsfe~g0>#~D(L@PS!W0MIy=DE*#W-J4)FD8!henWf9}@V0lv-- z@O7=GI>rCDvd#|h^=KV7wT^xa+yA$+&JOVPsQ=$~J?j6{UuOsSIy=BO;B5omHsEc8 zo%0*;wgGP&@U{VO8}POPZyWHo0dE`dwgGP&@U{VO8}POPZyWHo0dE`dwgGP&@U{VO z8}POPZyWHoQD)vY;B5omHsEa|FmD_1wgGP&@U{VO8}POPZyWHo0dE`dwgGP&@U{VO z8&UJN0dE`ZoZo=A4S3stw+(pPfVWNcvPa7{)ys@={dSX{wMoy~q-SkvEOWZY`b~{! zw(ZSLjb-PA{sy#3zuTnWZPM>H>35s-yG{DtCa3Cca;n~@&NTMh`aqwmw@I(uq*rdz zD>vztoAk;}dgUg)a+6-UNw3_bS8mcPH|dp|8W~j{jf}?k`Td-#w;625_NjWCI@8$b z{}9~N=;ePfPf;q{v5Q!>1*DTEeF#d|JY%C45@KrzLz^!lxyC zTEeF#d|JY%C45@KrzLz^!lxyCTEeF#d|J{e$||!?Q8wDArKo*cQluo_@M#I3mJ}&1 zmg(*-wKv^K2fA%<;nOXAx`j`-@aYyl-NL6^_;d@OZsF4{e7c2CxA5r}KHb8nTljPf zpKjsPEquC#Pq*;t7Czm=r(5`R3!iS`(=B|ug-^He=@vfS!lzsKbPJzu;nOXAx`j`- z@aYyl-NL6^_;d@OZsF4{e7c2CxA5r}KHb8nTljPfpKjsPEquC#Pq*;t7Czm=r(5`R z3!iS`(=B|ug-^He=@vfS!lzsKbPJzu;nOXAx`j`-@aYyl-NL6^_;d@OZsF4{e7a?y z>S8kekC;&ZZz$AEMEKjFW+JjR6A@}ABGgPosF{dRGZCR?BErA7?U{&B|9=+*ZYM&u z6QSCPQ2)&&{9RE0Un5(3E|i`NrRPHFxlnp8l%5OqO<$;Q`a*rv7wVh7P~Y^0J>Uzx z;V>vYSGx3EsJ<^$-xul|x=>%wh5BwT)OT>9P6-g|+qdwB(d|U2vt)%8;JZNSxl*W$ z+llOLp!8g}`o2(lE|i`NrRPHFxlnp8l%5Nv=R)bZP<=lL+)jkjbD{KHC_NWSfrG&9 zMEC%x|9qGIQSd=6WRX{C_R_0zAseY7pm_I)%S(! z`$FltPT87_%w=7qxdw6 zPowxW>b|e{*r!qVeW86C#ivnx8pWqkd>X~4QG6Q3r%`+w#ivnx8pWqkd>VD%52Eh- zLi;p|PowxW>b|cO`!tGAqxdw6PowxWich2XG>T87_%w=7qxdw6PowxWich2XG>T87 z_%!OiA4KtK)O}yJeHwM&7uu&$d>X~4QG6Q3r%`+w#ivpCeLZQPM)7GBpGNU%6rV=% zX%wGE@u_Ze*Ym$5p9)o4qrO(_3Tu6n66%|j&@9~{&z=)%RY$0m7NJ&kgj&@RYE?(5 zRUM&Lb%ZUTR&`{zf@x4Yy_KR>9bq0U808DSU%oJERYz!LxI;V}cZ1{-XjMlkTGbJ1 zRY$l1{tKv8o!}1fZ`7)eP~XmkTGa{e@JfPEt2#oh>Ik)}Bh*TQ@E%aBIIGtAMu(c&mW73V5r4w+eWx zfVT>GtAMu(c&mW73V5r4w+eWxfVT>GtAMu(c&mW73V5r4w+eWxfVT>GtAMu(c&mW7 z3V5r4w+eWxfVT>GtAMu(c&mW73V5r4w+eWxfVT>GtAMu(czc&uLV`OP!S7@QztblM zDCNVTN1Qu74;6kyyxr;9sPJRpx4>_M{onvN2o8Zq!0&*=;8E}xm<30{li(@vyWnYX z3_J&Z51a=74*Wj&3ivAc8u$b70=NiX1U2_p`89_XUZ+Ms1m6UIYz+R5{~G*T@K?cK z17XG|2I#MFLd0?>5zC!EF(A0pGvDA&pBNzgE%3L&I2eMCWbTY^1Gj@ZB}^%fZSK?# zE~7{PJ3V(6I;y$Tb7!G;uL(Z{Ql98Xu>Tm;nuGp|kD~a`#I& zZ6)5X#QT+azY_0Pmf8E2c)t?wSK|FjykF_H2>sRGuk?8lLVLf`=S2wZ{Ysw~A++}^ zyS4?kdw6>_VM!E&MP~-Vf@GYo+K^ zXQ9qu7d{B;^k&)nuyuN~>?F3%V3)0Zl|t+1U1ip&ySxG~bX&bkEATFx&R`en40d4) zm;zhDG}y-T?O+Gk33h?qU=OG>*!3Qr!7kJp?7{(1XRyoG8SFxx!7kJp?80I2D5x{o zm7+7)g*t;>s597wI)h#KT~KGR%hnm}LY=`b%z-+CUAE3(7fwEVj;ISNa_GtJpe&T`4+)U8pnIg^QrhV3(~k*o8WSUFbRN zUFr+A-@w)x?6QA^tuxqV-@yJc_J&Iy{44O6z+VCH@H6T=dY)O*UFt!$D@fNF?6Tj5 ztuxqV-^s5kLG7AWiuOngbq2docU}v%dse93vqJ5j6>9gaP`hV^{{j3U@6;LWO3@kY zLY=`b)EVqT+Nb)gIAor6mwK+#bq2d|li!w%5uL#ut=%?oxku$`k&p;xyUn`}(V*HQPFaU8pnIg*t;>s597wcY`{EUG_cLI)h!d&R`en z40hrBv2_N!Y@NX_)EVr;d$Dx}yKJ4oF4P(9LY=`b)EVqToxv{D8SFxx!7kJp>_VNv zF4P(9!aoH6$aR;7IOPFsoxv{qPq1|cyX=o*KM2x`nO)u`h4EjLr0WcJ*^gku67$Ww zq&ZHfFH3oB{~6Ed40hRGRk%wkWczInKLwKqz)ypp0skEQEcl=KmCj%f?$*d<)Jbl^ z-BM$tPI6Q0v8Kh*v^bg;N7LeH8uw)BuO5ZtXj&Xii=%0AG%b#%#Weyp=oz<}IGPqm z)8c4aT%)1Wt!Z(MiMFk2@iJ>#98HU(X>l|yj;6)Yv^bg;N7LeHS{zM_Yxd#ySkvN~ zeHg82+}v^bg;*NE&CYg!yli=%0AG%cHosL)|1w>IGPqm)8c4a98HU( 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%0AcdZx)e(&Qd#a*s5*M_SL=`WWXP>A<;1 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

    tNNc zgH^i@R_!`iwd+Xy19j~N_2jAkYA>i?0ad&od=k_zcBgx)+N^iG?Ogr6GgNaz_=p`K9{`l+#wgq~3q>WOBd-V-7GHRzo=9f|)1dVfww z!cPQruu9pHm>@m_o(13ZH7BnH{k&C2vKaJEs*a?e4DDb&vm@yzLpxa2>`0apdv8xi z@;YMgl

    o8+@-@Iav9JaRqTDcmpL>#2+NC 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 !}#gv~NV!5|5YF|kE)J0y^zWbk4>=%|6F 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 diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Semibold.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-Semibold.ttf deleted file mode 100644 index 1a7679e3949fb045f152f456bc4adad31e8b9f55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221328 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%0DJH?LFC28_mTUAZOar9j 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

    MAzfu-#(7W5VH7%hxxNU1% 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 b^2ROSp1r5Xdx>eYXCxdsa=Oc~d6M+Mf|cc} diff --git a/client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro b/client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro deleted file mode 100644 index c8e2a5b56f..0000000000 --- a/client/ayon_core/tools/pyblish_pype/i18n/pyblish_lite.pro +++ /dev/null @@ -1,2 +0,0 @@ -SOURCES = ../window.py -TRANSLATIONS = zh_CN.ts \ No newline at end of file diff --git a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.qm b/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.qm deleted file mode 100644 index fed08d8a5197e340a8760b1df278ac7d2ec1b725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1187 zcmZ`&O=uHA6rLt)e%2)fjJ1l&6rt9in>KA}EXHcGJ8jlZcGjJZr5>WE zf~0~TdJ&;|vEo6mUaIIpi%^S44~in<57eL_lACYRy3Ov!!}8cS-}lX%_rBNrSnqgM zyFIs@-ud+W-NkoD5JET5B1u5}8TvhAA{1H)*S~c`l*9GaCkTb(QtvLvx4oAh?B5Nz z;mAm7E1X}A+>s7L{$b=^&qv5Vk4}C9TzDz^=+GGCFUF#$Rmh|G%9m}BKPtaYpM+Ra zlo>cjRV6VG|Dj__@$E~93kmDo58xd%08|eHB860>j!I5si02|_W(!I29}|)h`1Yu3 zo@36Ki6L4x(+KB=Ie`t1#|_iqMWU-}RG_nlsk5mzp{WS1o6A>Bk5;m;z{(DG7`6!~ zP7B)L{ZqNKzjED`?*~F|3CTXqG2nj7CRW~t>-@LL<|b#Y^0Z5m`zXa4C3!RLhbIJF zTNvyaDSFE5=ofJDwz7paW;!;bl-R*ClR|)4nt36et$HL<%@QT16Uu{hMbIltmunts zdo$G`cF8dFv;}!m%XVh?+^UBh@25;mC5VrWM(I}{mX%7xY|(zXkQ|(EIAEiX?Z?-L zjColTvmY;0Ivwb#B4~kur`E{pE&b2XrOfqf&)m7iPB5n9!Zh(uP0G9&jk>V-K*J26 z?8hkrH}qyK?0NJS7muxSfr4#=t!glf>07fNW@>|ZDNhYO?~ou>68qy8Cl9!5;;(MU z$cMS@)mmj~vVgh}uKt<$JhfSPp~>%iha6^EM8|rWh-D$R&FoITuM2ZR@bEut_@x!$ QjDZ{oH}9|95DQr2FRT(3NB{r; diff --git a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts b/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts deleted file mode 100644 index 18ba81f69f..0000000000 --- a/client/ayon_core/tools/pyblish_pype/i18n/zh_CN.ts +++ /dev/null @@ -1,96 +0,0 @@ - - - - Window - - - Finishing up reset.. - 完成重置.. - - - - Comment.. - 备注.. - - - - Processing - 处理 - - - - Stopped due to error(s), see Terminal. - 因错误终止, 请查看终端。 - - - - Finished successfully! - 成功完成! - - - - About to reset.. - 即将重置.. - - - - Preparing validate.. - 准备校验.. - - - - Preparing publish.. - 准备发布.. - - - - Preparing - 准备 - - - - Action prepared. - 动作已就绪。 - - - - Cleaning up models.. - 清理数据模型.. - - - - Cleaning up terminal.. - 清理终端.. - - - - Cleaning up controller.. - 清理控制器.. - - - - All clean! - 清理完成! - - - - Good bye - 再见 - - - - ..as soon as processing is finished.. - ..处理即将完成.. - - - - Stopping.. - 正在停止.. - - - - Closing.. - 正在关闭.. - - - diff --git a/client/ayon_core/tools/pyblish_pype/img/down_arrow.png b/client/ayon_core/tools/pyblish_pype/img/down_arrow.png deleted file mode 100644 index e271f7f90b4132c9d740058d8b6c915dbdd626d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRR!2%?ApR4f$QjEnx?oJHr&dIz4a+s35-CY>w z1e^Sc1@brxJR*x37`TN&n2}-D90{Nxdx@v7EBhS|ac(A-+!@lDKp{;}7sn8e>&XcR z7pulY;Wn^IBG(*7A%~?b^VC!N=hHC=sm-IdEdjT~uc)I$ztaD0e F0ssj2CNKa1 diff --git a/client/ayon_core/tools/pyblish_pype/img/logo-extrasmall.png b/client/ayon_core/tools/pyblish_pype/img/logo-extrasmall.png deleted file mode 100644 index ebe45c4c6e5ab7a7d9f4f5f3aa773c640a537700..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2377 zcmbVOdpK148XmP%p;Lq;YBi;hnKf5tCYP8QnSBmvD6-EkYi8D%!CcG?gQ(+HOe&QU zT{c3YNK_mmblI|Nk4utEPSMcWLy&r<1}KYI2bXFbnazuWh|@AvyI&$E&|J+?2? zG1Wn#P|H|u3^pLv0 zaqdz&ED*Xy%3!ZZ4{vT{5SPkBJ2?XO;WUIm1S=pQTof#p)50ClpLl7=d~O>=1D{M3 zK@RB8PWiff0(6NC2FO@4hKs|K06QucPsWodcpHF#!xKRq5yTTQcmfSarQylImj{ig z$#{Gko8kIJ3t2g!1qy|f27*eZ600O)C9(hzPo+{p904Q{Fo*?49wAmh;TW-e!#o26 zmUCr7sX{0b19OZJM-r-VKqH=hzJf^V?*5fnEdLTHBxGPXBn9zU94Hda-RqOJT)~F_ z>&7>&<=zoe7-Yk8NvMpAR^IT;T5%4L*G~qYo`CKmFjsQa#3IRrniAcg> zs5}CM)D9J*5J`3rhs;NR!Snv#(1D0k;9Q;jPu*4Ue6+V?uP z8#U0EdFvOj*WJHeFqLh%b6pjHRa|e!IF0Q(dxA@P1D5qtK~?V{}x-3E_dO-A3xZ%Vl9iO`v3^ z%Dl27VoWE!Yx;aft_v9`ysPos^()dI)<8wi@kyeN*o?ee1N)*HqGC-@ zRBy*eme$<6GqWwYV>KVlKAL=Nk3Z(xoe|b*do#N|%&0(Bn6le2+0Mrl*L#-ezsch$ zr|)nvE>x4#$$9vlcC_o5iqD-GTT`~L2KeO>9QO(73s>IFTKcGf|r z*{d5`208Zahh4gMBp-b>3HCfJNjo%hzgA1HO4;VLykj~yMvFsBUOJP9FZ#D%+pEFi zp5B41*{Mc#Pn&%6Zj;EMb45SL6IY0eo&{^keVc`mX;VXE%8@rMlb6a)`#j51x5ukGv{Q3mpH2U;nD{`?>+%j$ugBXL z7Z%h`q}7nC)o)9)VdwqYgR`BJ#wshXx|f&QOCMk9ZM!f~6^*Jfi`cT*zimlbbbeUx z1~4PJn4ks6VfwtnuUAx zn}dX%`)f9?6q%=KpHSNik;Czh9u$8EbatoKXYQ29{IB9uyWF8);h;E$N7bhacwUOz!*kje$F%W3pAi^qbS2n_N1ytnYxGh z3x+fQj!(IIhQT@E$CtfoIGwQ3`Sp3+&4JNHU_!yew7)dTE4uU=6QsI%1trg?Q&4^W zy3>tts?kP2zSX`p((49k5>?O-+l#D9`sL&MN`G7GZMQEy!7Ql?Y{WToH%_!R4d?O} z9VwpTh7>H=k}Q~*$(i&&b+I^l1fUH^*j&yXmsuxGH-ErbXl;jShg5ckmfm$22oe7p zePLc6IeF@eq|;`=;`eTY!HXb!6$L!;2;$zkCcRcQp*t3B@z2A1kMl1X%)03r<2)DK zZ856mh1(d|3nY%&i%vvXBRpyPY&Q?@5-@Cdjz zJqOEYTIY`@VJpO0yaSpU=?kacEUom)`t^0TNxzPTRCgKoJ$c@(vwnm1%)!TX7l_fC ceOlTm{h|D*C7ZgP=6-HiOb3I0wqCy z!3>Q23VQlBwhmt1eYfuY{5{iEJ`pHq?djqeA`zaPaDZ(_B})LKytp#+dIq+H)B_^I z4-y!ZI2_V?1RU%9cpS^v^3DV#B_*UhbYRW#;fY{mFg(I>XZB}78K5x?p00i_>zopr E08lno+yDRo diff --git a/client/ayon_core/tools/pyblish_pype/img/tab-terminal.png b/client/ayon_core/tools/pyblish_pype/img/tab-terminal.png deleted file mode 100644 index ea1bcff98df5beddb9ba5303f6e1d5f1eeca529b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAd3?%E9GuQzs-2k5uS0Me4Xz=L5)g+(>zLFrn zUiH0 diff --git a/client/ayon_core/tools/pyblish_pype/mock.py b/client/ayon_core/tools/pyblish_pype/mock.py deleted file mode 100644 index c85ff0f2ba..0000000000 --- a/client/ayon_core/tools/pyblish_pype/mock.py +++ /dev/null @@ -1,732 +0,0 @@ -import os -import time -import subprocess - -import pyblish.api - - -class MyAction(pyblish.api.Action): - label = "My Action" - on = "processed" - - def process(self, context, plugin): - self.log.info("Running!") - - -class MyOtherAction(pyblish.api.Action): - label = "My Other Action" - - def process(self, context, plugin): - self.log.info("Running!") - - -class CollectComment(pyblish.api.ContextPlugin): - """This collector has a very long comment. - - The idea is that this comment should either be elided, or word- - wrapped in the corresponding view. - - """ - - order = pyblish.api.CollectorOrder - - def process(self, context): - context.data["comment"] = "" - - -class MyCollector(pyblish.api.ContextPlugin): - label = "My Collector" - order = pyblish.api.CollectorOrder - - def process(self, context): - context.create_instance("MyInstance 1", families=["myFamily"]) - context.create_instance("MyInstance 2", families=["myFamily 2"]) - context.create_instance( - "MyInstance 3", - families=["myFamily 2"], - publish=False - ) - - -class MyValidator(pyblish.api.InstancePlugin): - order = pyblish.api.ValidatorOrder - active = False - label = "My Validator" - actions = [MyAction, - MyOtherAction] - - def process(self, instance): - self.log.info("Validating: %s" % instance) - - -class MyExtractor(pyblish.api.InstancePlugin): - order = pyblish.api.ExtractorOrder - families = ["myFamily"] - label = "My Extractor" - - def process(self, instance): - self.log.info("Extracting: %s" % instance) - - -class CollectRenamed(pyblish.api.Collector): - def process(self, context): - i = context.create_instance("MyInstanceXYZ", family="MyFamily") - i.set_data("name", "My instance") - - -class CollectNegatron(pyblish.api.Collector): - """Negative collector adds Negatron""" - - order = pyblish.api.Collector.order - 0.49 - - def process_context(self, context): - self.log.info("Collecting Negatron") - context.create_instance("Negatron", family="MyFamily") - - -class CollectPositron(pyblish.api.Collector): - """Positive collector adds Positron""" - - order = pyblish.api.Collector.order + 0.49 - - def process_context(self, context): - self.log.info("Collecting Positron") - context.create_instance("Positron", family="MyFamily") - - -class SelectInstances(pyblish.api.Selector): - """Select debugging instances - - These instances are part of the evil plan to destroy the world. - Be weary, be vigilant, be sexy. - - """ - - def process_context(self, context): - self.log.info("Selecting instances..") - - for instance in instances[:-1]: - name, data = instance["name"], instance["data"] - self.log.info("Selecting: %s" % name) - instance = context.create_instance(name) - - for key, value in data.items(): - instance.set_data(key, value) - - -class SelectDiInstances(pyblish.api.Selector): - """Select DI instances""" - - name = "Select Dependency Instances" - - def process(self, context): - name, data = instances[-1]["name"], instances[-1]["data"] - self.log.info("Selecting: %s" % name) - instance = context.create_instance(name) - - for key, value in data.items(): - instance.set_data(key, value) - - -class SelectInstancesFailure(pyblish.api.Selector): - """Select some instances, but fail before adding anything to the context. - - That's right. I'm programmed to fail. Try me. - - """ - - __fail__ = True - - def process_context(self, context): - self.log.warning("I'm about to fail") - raise AssertionError("I was programmed to fail") - - -class SelectInstances2(pyblish.api.Selector): - def process(self, context): - self.log.warning("I'm good") - - -class ValidateNamespace(pyblish.api.Validator): - """Namespaces must be orange - - In case a namespace is not orange, report immediately to - your officer in charge, ask for a refund, do a backflip. - - This has been an example of: - - - A long doc-string - - With a list - - And plenty of newlines and tabs. - - """ - - families = ["B"] - - def process(self, instance): - self.log.info("Validating the namespace of %s" % instance.data("name")) - self.log.info("""And here's another message, quite long, in fact it's -too long to be displayed in a single row of text. -But that's how we roll down here. It's got \nnew lines\nas well. - -- And lists -- And more lists - - """) - - -class ValidateContext(pyblish.api.Validator): - families = ["A", "B"] - - def process_context(self, context): - self.log.info("Processing context..") - - -class ValidateContextFailure(pyblish.api.Validator): - optional = True - families = ["C"] - __fail__ = True - - def process_context(self, context): - self.log.info("About to fail..") - raise AssertionError("""I was programmed to fail - -The reason I failed was because the sun was not aligned with the tides, -and the moon is gray; not yellow. Try again when the moon is yellow.""") - - -class Validator1(pyblish.api.Validator): - """Test of the order attribute""" - order = pyblish.api.Validator.order + 0.1 - families = ["A"] - - def process_instance(self, instance): - pass - - -class Validator2(pyblish.api.Validator): - order = pyblish.api.Validator.order + 0.2 - families = ["B"] - - def process_instance(self, instance): - pass - - -class Validator3(pyblish.api.Validator): - order = pyblish.api.Validator.order + 0.3 - families = ["B"] - - def process_instance(self, instance): - pass - - -class ValidateFailureMock(pyblish.api.Validator): - """Plug-in that always fails""" - optional = True - order = pyblish.api.Validator.order + 0.1 - families = ["C"] - __fail__ = True - - def process_instance(self, instance): - self.log.debug("e = mc^2") - self.log.info("About to fail..") - self.log.warning("Failing.. soooon..") - self.log.critical("Ok, you're done.") - raise AssertionError("""ValidateFailureMock was destined to fail.. - -Here's some extended information about what went wrong. - -It has quite the long string associated with it, including -a few newlines and a list. - -- Item 1 -- Item 2 - -""") - - -class ValidateIsIncompatible(pyblish.api.Validator): - """This plug-in should never appear..""" - requires = False # This is invalid - - -class ValidateWithRepair(pyblish.api.Validator): - """A validator with repair functionality""" - optional = True - families = ["C"] - __fail__ = True - - def process_instance(self, instance): - raise AssertionError( - "%s is invalid, try repairing it!" % instance.name - ) - - def repair_instance(self, instance): - self.log.info("Attempting to repair..") - self.log.info("Success!") - - -class ValidateWithRepairFailure(pyblish.api.Validator): - """A validator with repair functionality that fails""" - optional = True - families = ["C"] - __fail__ = True - - def process_instance(self, instance): - raise AssertionError( - "%s is invalid, try repairing it!" % instance.name - ) - - def repair_instance(self, instance): - self.log.info("Attempting to repair..") - raise AssertionError("Could not repair due to X") - - -class ValidateWithVeryVeryVeryLongLongNaaaaame(pyblish.api.Validator): - """A validator with repair functionality that fails""" - families = ["A"] - - -class ValidateWithRepairContext(pyblish.api.Validator): - """A validator with repair functionality that fails""" - optional = True - families = ["C"] - __fail__ = True - - def process_context(self, context): - raise AssertionError("Could not validate context, try repairing it") - - def repair_context(self, context): - self.log.info("Attempting to repair..") - raise AssertionError("Could not repair") - - -class ExtractAsMa(pyblish.api.Extractor): - """Extract contents of each instance into .ma - - Serialise scene using Maya's own facilities and then put - it on the hard-disk. Once complete, this plug-in relies - on a Conformer to put it in it's final location, as this - extractor merely positions it in the users local temp- - directory. - - """ - - optional = True - __expected__ = { - "logCount": ">=4" - } - - def process_instance(self, instance): - self.log.info("About to extract scene to .ma..") - self.log.info("Extraction went well, now verifying the data..") - - if instance.name == "Richard05": - self.log.warning("You're almost running out of disk space!") - - self.log.info("About to finish up") - self.log.info("Finished successfully") - - -class ConformAsset(pyblish.api.Conformer): - """Conform the world - - Step 1: Conform all humans and Step 2: Conform all non-humans. - Once conforming has completed, rinse and repeat. - - """ - - optional = True - - def process_instance(self, instance): - self.log.info("About to conform all humans..") - - if instance.name == "Richard05": - self.log.warning("Richard05 is a conformist!") - - self.log.info("About to conform all non-humans..") - self.log.info("Conformed Successfully") - - -class ValidateInstancesDI(pyblish.api.Validator): - """Validate using the DI interface""" - families = ["diFamily"] - - def process(self, instance): - self.log.info("Validating %s.." % instance.data("name")) - - -class ValidateDIWithRepair(pyblish.api.Validator): - families = ["diFamily"] - optional = True - __fail__ = True - - def process(self, instance): - raise AssertionError("I was programmed to fail, for repair") - - def repair(self, instance): - self.log.info("Repairing %s" % instance.data("name")) - - -class ExtractInstancesDI(pyblish.api.Extractor): - """Extract using the DI interface""" - families = ["diFamily"] - - def process(self, instance): - self.log.info("Extracting %s.." % instance.data("name")) - - -class ValidateWithLabel(pyblish.api.Validator): - """Validate using the DI interface""" - label = "Validate with Label" - - -class ValidateWithLongLabel(pyblish.api.Validator): - """Validate using the DI interface""" - label = "Validate with Loooooooooooooooooooooong Label" - - -class SimplePlugin1(pyblish.api.Plugin): - """Validate using the simple-plugin interface""" - - def process(self): - self.log.info("I'm a simple plug-in, only processed once") - - -class SimplePlugin2(pyblish.api.Plugin): - """Validate using the simple-plugin interface - - It doesn't have an order, and will likely end up *before* all - other plug-ins. (due to how sorted([1, 2, 3, None]) works) - - """ - - def process(self, context): - self.log.info("Processing the context, simply: %s" % context) - - -class SimplePlugin3(pyblish.api.Plugin): - """Simply process every instance""" - - def process(self, instance): - self.log.info("Processing the instance, simply: %s" % instance) - - -class ContextAction(pyblish.api.Action): - label = "Context action" - - def process(self, context): - self.log.info("I have access to the context") - self.log.info("Context.instances: %s" % str(list(context))) - - -class FailingAction(pyblish.api.Action): - label = "Failing action" - - def process(self): - self.log.info("About to fail..") - raise Exception("I failed") - - -class LongRunningAction(pyblish.api.Action): - label = "Long-running action" - - def process(self): - self.log.info("Sleeping for 2 seconds..") - time.sleep(2) - self.log.info("Ah, that's better") - - -class IconAction(pyblish.api.Action): - label = "Icon action" - icon = "crop" - - def process(self): - self.log.info("I have an icon") - - -class PluginAction(pyblish.api.Action): - label = "Plugin action" - - def process(self, plugin): - self.log.info("I have access to my parent plug-in") - self.log.info("Which is %s" % plugin.id) - - -class LaunchExplorerAction(pyblish.api.Action): - label = "Open in Explorer" - icon = "folder-open" - - def process(self, context): - cwd = os.getcwd() - self.log.info("Opening %s in Explorer" % cwd) - result = subprocess.call("start .", cwd=cwd, shell=True) - self.log.debug(result) - - -class ProcessedAction(pyblish.api.Action): - label = "Success action" - icon = "check" - on = "processed" - - def process(self): - self.log.info("I am only available on a successful plug-in") - - -class FailedAction(pyblish.api.Action): - label = "Failure action" - icon = "close" - on = "failed" - - -class SucceededAction(pyblish.api.Action): - label = "Success action" - icon = "check" - on = "succeeded" - - def process(self): - self.log.info("I am only available on a successful plug-in") - - -class LongLabelAction(pyblish.api.Action): - label = "An incredibly, incredicly looooon label. Very long." - icon = "close" - - -class BadEventAction(pyblish.api.Action): - label = "Bad event action" - on = "not exist" - - -class InactiveAction(pyblish.api.Action): - active = False - - -class PluginWithActions(pyblish.api.Validator): - optional = True - actions = [ - pyblish.api.Category("General"), - ContextAction, - FailingAction, - LongRunningAction, - IconAction, - PluginAction, - pyblish.api.Category("Empty"), - pyblish.api.Category("OS"), - LaunchExplorerAction, - pyblish.api.Separator, - FailedAction, - SucceededAction, - pyblish.api.Category("Debug"), - BadEventAction, - InactiveAction, - LongLabelAction, - pyblish.api.Category("Empty"), - ] - - def process(self): - self.log.info("Ran PluginWithActions") - - -class FailingPluginWithActions(pyblish.api.Validator): - optional = True - actions = [ - FailedAction, - SucceededAction, - ] - - def process(self): - raise Exception("I was programmed to fail") - - -class ValidateDefaultOff(pyblish.api.Validator): - families = ["A", "B"] - active = False - optional = True - - def process(self, instance): - self.log.info("Processing instance..") - - -class ValidateWithHyperlinks(pyblish.api.Validator): - """To learn about Pyblish - - click here (http://pyblish.com) - - """ - - families = ["A", "B"] - - def process(self, instance): - self.log.info("Processing instance..") - - msg = "To learn about Pyblish, " - msg += "click here (http://pyblish.com)" - - self.log.info(msg) - - -class LongRunningCollector(pyblish.api.Collector): - """I will take at least 2 seconds...""" - def process(self, context): - self.log.info("Sleeping for 2 seconds..") - time.sleep(2) - self.log.info("Good morning") - - -class LongRunningValidator(pyblish.api.Validator): - """I will take at least 2 seconds...""" - def process(self, context): - self.log.info("Sleeping for 2 seconds..") - time.sleep(2) - self.log.info("Good morning") - - -class RearrangingPlugin(pyblish.api.ContextPlugin): - """Sort plug-ins by family, and then reverse it""" - order = pyblish.api.CollectorOrder + 0.2 - - def process(self, context): - self.log.info("Reversing instances in the context..") - context[:] = sorted( - context, - key=lambda i: i.data["family"], - reverse=True - ) - self.log.info("Reversed!") - - -class InactiveInstanceCollectorPlugin(pyblish.api.InstancePlugin): - """Special case of an InstancePlugin running as a Collector""" - order = pyblish.api.CollectorOrder + 0.1 - active = False - - def process(self, instance): - raise TypeError("I shouldn't have run in the first place") - - -class CollectWithIcon(pyblish.api.ContextPlugin): - order = pyblish.api.CollectorOrder - - def process(self, context): - instance = context.create_instance("With Icon") - instance.data["icon"] = "play" - - -instances = [ - { - "name": "Peter01", - "data": { - "family": "A", - "publish": False - } - }, - { - "name": "Richard05", - "data": { - "family": "A", - } - }, - { - "name": "Steven11", - "data": { - "family": "B", - } - }, - { - "name": "Piraya12", - "data": { - "family": "B", - } - }, - { - "name": "Marcus", - "data": { - "family": "C", - } - }, - { - "name": "Extra1", - "data": { - "family": "C", - } - }, - { - "name": "DependencyInstance", - "data": { - "family": "diFamily" - } - }, - { - "name": "NoFamily", - "data": {} - }, - { - "name": "Failure 1", - "data": { - "family": "failure", - "fail": False - } - }, - { - "name": "Failure 2", - "data": { - "family": "failure", - "fail": True - } - } -] - -plugins = [ - MyCollector, - MyValidator, - MyExtractor, - - CollectRenamed, - CollectNegatron, - CollectPositron, - SelectInstances, - SelectInstances2, - SelectDiInstances, - SelectInstancesFailure, - ValidateFailureMock, - ValidateNamespace, - # ValidateIsIncompatible, - ValidateWithVeryVeryVeryLongLongNaaaaame, - ValidateContext, - ValidateContextFailure, - Validator1, - Validator2, - Validator3, - ValidateWithRepair, - ValidateWithRepairFailure, - ValidateWithRepairContext, - ValidateWithLabel, - ValidateWithLongLabel, - ValidateDefaultOff, - ValidateWithHyperlinks, - ExtractAsMa, - ConformAsset, - - SimplePlugin1, - SimplePlugin2, - SimplePlugin3, - - ValidateInstancesDI, - ExtractInstancesDI, - ValidateDIWithRepair, - - PluginWithActions, - FailingPluginWithActions, - - # LongRunningCollector, - # LongRunningValidator, - - RearrangingPlugin, - InactiveInstanceCollectorPlugin, - - CollectComment, - CollectWithIcon, -] - -pyblish.api.sort_plugins(plugins) diff --git a/client/ayon_core/tools/pyblish_pype/model.py b/client/ayon_core/tools/pyblish_pype/model.py deleted file mode 100644 index 44f951fe14..0000000000 --- a/client/ayon_core/tools/pyblish_pype/model.py +++ /dev/null @@ -1,1116 +0,0 @@ -"""Qt models - -Description: - The model contains the original objects from Pyblish, such as - pyblish.api.Instance and pyblish.api.Plugin. The model then - provides an interface for reading and writing to those. - -GUI data: - Aside from original data, such as pyblish.api.Plugin.optional, - the GUI also hosts data internal to itself, such as whether or - not an item has processed such that it may be colored appropriately - in the view. This data is prefixed with two underscores (__). - - E.g. - - _has_processed - - This is so that the the GUI-only data doesn't accidentally overwrite - or cause confusion with existing data in plug-ins and instances. - -Roles: - Data is accessed via standard Qt "roles". You can think of a role - as the key of a dictionary, except they can only be integers. - -""" -from __future__ import unicode_literals - -import pyblish - -from . import settings, util -from .awesome import tags as awesome -from qtpy import QtCore, QtGui -import qtawesome -from .constants import PluginStates, InstanceStates, GroupStates, Roles - - -# ItemTypes -UserType = QtGui.QStandardItem.UserType -if hasattr(UserType, "value"): - UserType = UserType.value -InstanceType = UserType -PluginType = UserType + 1 -GroupType = UserType + 2 -TerminalLabelType = UserType + 3 -TerminalDetailType = UserType + 4 - - -class QAwesomeTextIconFactory: - icons = {} - - @classmethod - def icon(cls, icon_name): - if icon_name not in cls.icons: - cls.icons[icon_name] = awesome.get(icon_name) - return cls.icons[icon_name] - - -class QAwesomeIconFactory: - icons = {} - - @classmethod - def icon(cls, icon_name, icon_color): - if icon_name not in cls.icons: - cls.icons[icon_name] = {} - - if icon_color not in cls.icons[icon_name]: - cls.icons[icon_name][icon_color] = qtawesome.icon( - icon_name, - color=icon_color - ) - return cls.icons[icon_name][icon_color] - - -class IntentModel(QtGui.QStandardItemModel): - """Model for QComboBox with intents. - - It is expected that one inserted item is dictionary. - Key represents #Label and Value represent #Value. - - Example: - { - "Testing": "test", - "Publishing": "publish" - } - - First and default value is {"< Not Set >": None} - """ - - default_empty_label = "< Not set >" - - def __init__(self, parent=None): - super(IntentModel, self).__init__(parent) - self._item_count = 0 - self.default_index = 0 - - @property - def has_items(self): - return self._item_count > 0 - - def reset(self): - self.clear() - self._item_count = 0 - self.default_index = 0 - - # Intent settings are not available in core addon - intent_settings = {} - - items = intent_settings.get("items", {}) - if not items: - return - - allow_empty_intent = intent_settings.get("allow_empty_intent", True) - empty_intent_label = ( - intent_settings.get("empty_intent_label") - or self.default_empty_label - ) - listed_items = list(items.items()) - if allow_empty_intent: - listed_items.insert(0, ("", empty_intent_label)) - - default = intent_settings.get("default") - - for idx, item in enumerate(listed_items): - item_value = item[0] - if item_value == default: - self.default_index = idx - break - - self._add_items(listed_items) - - def _add_items(self, items): - for item in items: - value, label = item - new_item = QtGui.QStandardItem() - new_item.setData(label, QtCore.Qt.DisplayRole) - new_item.setData(value, Roles.IntentItemValue) - - self.setItem(self._item_count, new_item) - self._item_count += 1 - - -class PluginItem(QtGui.QStandardItem): - """Plugin item implementation.""" - - def __init__(self, plugin): - super(PluginItem, self).__init__() - - item_text = plugin.__name__ - if settings.UseLabel: - if hasattr(plugin, "label") and plugin.label: - item_text = plugin.label - - self.plugin = plugin - - self.setData(item_text, QtCore.Qt.DisplayRole) - self.setData(False, Roles.IsEnabledRole) - self.setData(0, Roles.PublishFlagsRole) - self.setData(0, Roles.PluginActionProgressRole) - icon_name = "" - if hasattr(plugin, "icon") and plugin.icon: - icon_name = plugin.icon - icon = QAwesomeTextIconFactory.icon(icon_name) - self.setData(icon, QtCore.Qt.DecorationRole) - - actions = [] - if hasattr(plugin, "actions") and plugin.actions: - actions = list(plugin.actions) - plugin.actions = actions - - is_checked = True - is_optional = getattr(plugin, "optional", False) - if is_optional: - is_checked = getattr(plugin, "active", True) - - plugin.active = is_checked - plugin.optional = is_optional - - self.setData( - "{}.{}".format(plugin.__module__, plugin.__name__), - Roles.ObjectUIdRole - ) - - self.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - ) - - def type(self): - return PluginType - - def data(self, role=QtCore.Qt.DisplayRole): - if role == Roles.IsOptionalRole: - return self.plugin.optional - - if role == Roles.ObjectIdRole: - return self.plugin.id - - if role == Roles.TypeRole: - return self.type() - - if role == QtCore.Qt.CheckStateRole: - return self.plugin.active - - if role == Roles.PathModuleRole: - return self.plugin.__module__ - - if role == Roles.FamiliesRole: - return self.plugin.families - - if role == Roles.DocstringRole: - return self.plugin.__doc__ - - if role == Roles.PluginActionsVisibleRole: - return self._data_actions_visible() - - if role == Roles.PluginValidActionsRole: - return self._data_valid_actions() - - return super(PluginItem, self).data(role) - - def _data_actions_visible(self): - # Can only run actions on active plug-ins. - if not self.plugin.active or not self.plugin.actions: - return False - - publish_states = self.data(Roles.PublishFlagsRole) - if ( - not publish_states & PluginStates.IsCompatible - or publish_states & PluginStates.WasSkipped - ): - return False - - # Context specific actions - for action in self.plugin.actions: - if action.on == "failed": - if publish_states & PluginStates.HasError: - return True - - elif action.on == "succeeded": - if ( - publish_states & PluginStates.WasProcessed - and not publish_states & PluginStates.HasError - ): - return True - - elif action.on == "processed": - if publish_states & PluginStates.WasProcessed: - return True - - elif action.on == "notProcessed": - if not publish_states & PluginStates.WasProcessed: - return True - return False - - def _data_valid_actions(self): - valid_actions = [] - - # Can only run actions on active plug-ins. - if not self.plugin.active or not self.plugin.actions: - return valid_actions - - if not self.plugin.active or not self.plugin.actions: - return False - - publish_states = self.data(Roles.PublishFlagsRole) - if ( - not publish_states & PluginStates.IsCompatible - or publish_states & PluginStates.WasSkipped - ): - return False - - # Context specific actions - for action in self.plugin.actions: - valid = False - if action.on == "failed": - if publish_states & PluginStates.HasError: - valid = True - - elif action.on == "succeeded": - if ( - publish_states & PluginStates.WasProcessed - and not publish_states & PluginStates.HasError - ): - valid = True - - elif action.on == "processed": - if publish_states & PluginStates.WasProcessed: - valid = True - - elif action.on == "notProcessed": - if not publish_states & PluginStates.WasProcessed: - valid = True - - if valid: - valid_actions.append(action) - - if not valid_actions: - return valid_actions - - actions_len = len(valid_actions) - # Discard empty groups - indexex_to_remove = [] - for idx, action in enumerate(valid_actions): - if action.__type__ != "category": - continue - - next_id = idx + 1 - if next_id >= actions_len: - indexex_to_remove.append(idx) - continue - - next = valid_actions[next_id] - if next.__type__ != "action": - indexex_to_remove.append(idx) - - for idx in reversed(indexex_to_remove): - valid_actions.pop(idx) - - return valid_actions - - def setData(self, value, role=None): - if role is None: - role = QtCore.Qt.UserRole + 1 - - if role == QtCore.Qt.CheckStateRole: - if not self.data(Roles.IsEnabledRole): - return False - self.plugin.active = value - self.emitDataChanged() - return - - elif role == Roles.PluginActionProgressRole: - if isinstance(value, list): - _value = self.data(Roles.PluginActionProgressRole) - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.data(Roles.PluginActionProgressRole) - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - - elif role == Roles.PublishFlagsRole: - if isinstance(value, list): - _value = self.data(Roles.PublishFlagsRole) - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.data(Roles.PublishFlagsRole) - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - - if value & PluginStates.HasWarning: - if self.parent(): - self.parent().setData( - {GroupStates.HasWarning: True}, - Roles.PublishFlagsRole - ) - if value & PluginStates.HasError: - if self.parent(): - self.parent().setData( - {GroupStates.HasError: True}, - Roles.PublishFlagsRole - ) - - return super(PluginItem, self).setData(value, role) - - -class GroupItem(QtGui.QStandardItem): - def __init__(self, *args, **kwargs): - self.order = kwargs.pop("order", None) - self.publish_states = 0 - super(GroupItem, self).__init__(*args, **kwargs) - - def flags(self): - return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - - def data(self, role=QtCore.Qt.DisplayRole): - if role == Roles.PublishFlagsRole: - return self.publish_states - - if role == Roles.TypeRole: - return self.type() - - return super(GroupItem, self).data(role) - - def setData(self, value, role=(QtCore.Qt.UserRole + 1)): - if role == Roles.PublishFlagsRole: - if isinstance(value, list): - _value = self.data(Roles.PublishFlagsRole) - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.data(Roles.PublishFlagsRole) - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - self.publish_states = value - self.emitDataChanged() - return True - - return super(GroupItem, self).setData(value, role) - - def type(self): - return GroupType - - -class PluginModel(QtGui.QStandardItemModel): - def __init__(self, controller, *args, **kwargs): - super(PluginModel, self).__init__(*args, **kwargs) - - self.controller = controller - self.checkstates = {} - self.group_items = {} - self.plugin_items = {} - - def reset(self): - self.group_items = {} - self.plugin_items = {} - self.clear() - - def append(self, plugin): - plugin_groups = self.controller.order_groups.groups - label = None - order = None - for _order, item in reversed(plugin_groups.items()): - if _order is None or plugin.order < _order: - label = item["label"] - order = _order - else: - break - - if label is None: - label = "Other" - - group_item = self.group_items.get(label) - if not group_item: - group_item = GroupItem(label, order=order) - self.appendRow(group_item) - self.group_items[label] = group_item - - new_item = PluginItem(plugin) - group_item.appendRow(new_item) - - self.plugin_items[plugin._id] = new_item - - def store_checkstates(self): - self.checkstates.clear() - - for plugin_item in self.plugin_items.values(): - if not plugin_item.plugin.optional: - continue - - uid = plugin_item.data(Roles.ObjectUIdRole) - self.checkstates[uid] = plugin_item.data(QtCore.Qt.CheckStateRole) - - def restore_checkstates(self): - for plugin_item in self.plugin_items.values(): - if not plugin_item.plugin.optional: - continue - - uid = plugin_item.data(Roles.ObjectUIdRole) - state = self.checkstates.get(uid) - if state is not None: - plugin_item.setData(state, QtCore.Qt.CheckStateRole) - - def update_with_result(self, result): - plugin = result["plugin"] - item = self.plugin_items[plugin.id] - - new_flag_states = { - PluginStates.InProgress: False, - PluginStates.WasProcessed: True - } - - publish_states = item.data(Roles.PublishFlagsRole) - - has_warning = publish_states & PluginStates.HasWarning - new_records = result.get("records") or [] - if not has_warning: - for record in new_records: - level_no = record.get("levelno") - if level_no and level_no >= 30: - new_flag_states[PluginStates.HasWarning] = True - break - - if ( - not publish_states & PluginStates.HasError - and not result["success"] - ): - new_flag_states[PluginStates.HasError] = True - - if not publish_states & PluginStates.IsCompatible: - new_flag_states[PluginStates.IsCompatible] = True - - item.setData(new_flag_states, Roles.PublishFlagsRole) - - records = item.data(Roles.LogRecordsRole) or [] - records.extend(new_records) - - item.setData(records, Roles.LogRecordsRole) - - return item - - def update_compatibility(self): - context = self.controller.context - - families = util.collect_families_from_instances(context, True) - for plugin_item in self.plugin_items.values(): - publish_states = plugin_item.data(Roles.PublishFlagsRole) - if ( - publish_states & PluginStates.WasProcessed - or publish_states & PluginStates.WasSkipped - ): - continue - - is_compatible = False - # A plugin should always show if it has processed. - if plugin_item.plugin.__instanceEnabled__: - compatible_instances = pyblish.logic.instances_by_plugin( - context, plugin_item.plugin - ) - for instance in context: - if not instance.data.get("publish"): - continue - - if instance in compatible_instances: - is_compatible = True - break - else: - plugins = pyblish.logic.plugins_by_families( - [plugin_item.plugin], families - ) - if plugins: - is_compatible = True - - current_is_compatible = publish_states & PluginStates.IsCompatible - if ( - (is_compatible and not current_is_compatible) - or (not is_compatible and current_is_compatible) - ): - new_flag = { - PluginStates.IsCompatible: is_compatible - } - plugin_item.setData(new_flag, Roles.PublishFlagsRole) - - -class PluginFilterProxy(QtCore.QSortFilterProxyModel): - def filterAcceptsRow(self, source_row, source_parent): - index = self.sourceModel().index(source_row, 0, source_parent) - item_type = index.data(Roles.TypeRole) - if item_type != PluginType: - return True - - publish_states = index.data(Roles.PublishFlagsRole) - if ( - publish_states & PluginStates.WasSkipped - or not publish_states & PluginStates.IsCompatible - ): - return False - return True - - -class InstanceItem(QtGui.QStandardItem): - """Instance item implementation.""" - - def __init__(self, instance): - super(InstanceItem, self).__init__() - - self.instance = instance - self.is_context = False - publish_states = getattr(instance, "_publish_states", 0) - if publish_states & InstanceStates.ContextType: - self.is_context = True - - instance._publish_states = publish_states - instance._logs = [] - instance.optional = getattr(instance, "optional", True) - instance.data["publish"] = instance.data.get("publish", True) - - family = self.data(Roles.FamiliesRole)[0] - self.setData( - "{}.{}".format(family, self.instance.data["name"]), - Roles.ObjectUIdRole - ) - - def flags(self): - return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - - def type(self): - return InstanceType - - def data(self, role=QtCore.Qt.DisplayRole): - if role == QtCore.Qt.DisplayRole: - label = None - if settings.UseLabel: - label = self.instance.data.get("label") - - if not label: - if self.is_context: - label = "Context" - else: - label = self.instance.data["name"] - return label - - if role == QtCore.Qt.DecorationRole: - icon_name = self.instance.data.get("icon") or "file" - return QAwesomeTextIconFactory.icon(icon_name) - - if role == Roles.TypeRole: - return self.type() - - if role == Roles.ObjectIdRole: - return self.instance.id - - if role == Roles.FamiliesRole: - if self.is_context: - return ["Context"] - - families = [] - family = self.instance.data.get("family") - if family: - families.append(family) - - _families = self.instance.data.get("families") or [] - for _family in _families: - if _family not in families: - families.append(_family) - - return families - - if role == Roles.IsOptionalRole: - return self.instance.optional - - if role == QtCore.Qt.CheckStateRole: - return self.instance.data["publish"] - - if role == Roles.PublishFlagsRole: - return self.instance._publish_states - - if role == Roles.LogRecordsRole: - return self.instance._logs - - return super(InstanceItem, self).data(role) - - def setData(self, value, role=(QtCore.Qt.UserRole + 1)): - if role == QtCore.Qt.CheckStateRole: - if not self.data(Roles.IsEnabledRole): - return - self.instance.data["publish"] = value - self.emitDataChanged() - return - - if role == Roles.IsEnabledRole: - if not self.instance.optional: - return - - if role == Roles.PublishFlagsRole: - if isinstance(value, list): - _value = self.instance._publish_states - for flag in value: - _value |= flag - value = _value - - elif isinstance(value, dict): - _value = self.instance._publish_states - for flag, _bool in value.items(): - if _bool is True: - _value |= flag - elif _value & flag: - _value ^= flag - value = _value - - if value & InstanceStates.HasWarning: - if self.parent(): - self.parent().setData( - {GroupStates.HasWarning: True}, - Roles.PublishFlagsRole - ) - if value & InstanceStates.HasError: - if self.parent(): - self.parent().setData( - {GroupStates.HasError: True}, - Roles.PublishFlagsRole - ) - - self.instance._publish_states = value - self.emitDataChanged() - return - - if role == Roles.LogRecordsRole: - self.instance._logs = value - self.emitDataChanged() - return - - return super(InstanceItem, self).setData(value, role) - - -class InstanceModel(QtGui.QStandardItemModel): - - group_created = QtCore.Signal(QtCore.QModelIndex) - - def __init__(self, controller, *args, **kwargs): - super(InstanceModel, self).__init__(*args, **kwargs) - - self.controller = controller - self.checkstates = {} - self.group_items = {} - self.instance_items = {} - - def reset(self): - self.group_items = {} - self.instance_items = {} - self.clear() - - def append(self, instance): - new_item = InstanceItem(instance) - if new_item.is_context: - self.appendRow(new_item) - else: - families = new_item.data(Roles.FamiliesRole) - group_item = self.group_items.get(families[0]) - if not group_item: - group_item = GroupItem(families[0]) - self.appendRow(group_item) - self.group_items[families[0]] = group_item - self.group_created.emit(group_item.index()) - - group_item.appendRow(new_item) - instance_id = instance.id - self.instance_items[instance_id] = new_item - - def remove(self, instance_id): - instance_item = self.instance_items.pop(instance_id) - parent_item = instance_item.parent() - parent_item.removeRow(instance_item.row()) - if parent_item.rowCount(): - return - - self.group_items.pop(parent_item.data(QtCore.Qt.DisplayRole)) - self.removeRow(parent_item.row()) - - def store_checkstates(self): - self.checkstates.clear() - - for instance_item in self.instance_items.values(): - if not instance_item.instance.optional: - continue - - uid = instance_item.data(Roles.ObjectUIdRole) - self.checkstates[uid] = instance_item.data( - QtCore.Qt.CheckStateRole - ) - - def restore_checkstates(self): - for instance_item in self.instance_items.values(): - if not instance_item.instance.optional: - continue - - uid = instance_item.data(Roles.ObjectUIdRole) - state = self.checkstates.get(uid) - if state is not None: - instance_item.setData(state, QtCore.Qt.CheckStateRole) - - def update_with_result(self, result): - instance = result["instance"] - if instance is None: - instance_id = self.controller.context.id - else: - instance_id = instance.id - - item = self.instance_items.get(instance_id) - if not item: - return - - new_flag_states = { - InstanceStates.InProgress: False - } - - publish_states = item.data(Roles.PublishFlagsRole) - has_warning = publish_states & InstanceStates.HasWarning - new_records = result.get("records") or [] - if not has_warning: - for record in new_records: - level_no = record.get("levelno") - if level_no and level_no >= 30: - new_flag_states[InstanceStates.HasWarning] = True - break - - if ( - not publish_states & InstanceStates.HasError - and not result["success"] - ): - new_flag_states[InstanceStates.HasError] = True - - item.setData(new_flag_states, Roles.PublishFlagsRole) - - records = item.data(Roles.LogRecordsRole) or [] - records.extend(new_records) - - item.setData(records, Roles.LogRecordsRole) - - return item - - def update_compatibility(self, context, instances): - families = util.collect_families_from_instances(context, True) - for plugin_item in self.plugin_items.values(): - publish_states = plugin_item.data(Roles.PublishFlagsRole) - if ( - publish_states & PluginStates.WasProcessed - or publish_states & PluginStates.WasSkipped - ): - continue - - is_compatible = False - # A plugin should always show if it has processed. - if plugin_item.plugin.__instanceEnabled__: - compatibleInstances = pyblish.logic.instances_by_plugin( - context, plugin_item.plugin - ) - for instance in instances: - if not instance.data.get("publish"): - continue - - if instance in compatibleInstances: - is_compatible = True - break - else: - plugins = pyblish.logic.plugins_by_families( - [plugin_item.plugin], families - ) - if plugins: - is_compatible = True - - current_is_compatible = publish_states & PluginStates.IsCompatible - if ( - (is_compatible and not current_is_compatible) - or (not is_compatible and current_is_compatible) - ): - plugin_item.setData( - {PluginStates.IsCompatible: is_compatible}, - Roles.PublishFlagsRole - ) - - -class InstanceSortProxy(QtCore.QSortFilterProxyModel): - def __init__(self, *args, **kwargs): - super(InstanceSortProxy, self).__init__(*args, **kwargs) - # Do not care about lower/upper case - self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) - - def lessThan(self, x_index, y_index): - x_type = x_index.data(Roles.TypeRole) - y_type = y_index.data(Roles.TypeRole) - if x_type != y_type: - if x_type == GroupType: - return False - return True - return super(InstanceSortProxy, self).lessThan(x_index, y_index) - - -class TerminalDetailItem(QtGui.QStandardItem): - key_label_record_map = ( - ("instance", "Instance"), - ("msg", "Message"), - ("name", "Plugin"), - ("pathname", "Path"), - ("lineno", "Line"), - ("traceback", "Traceback"), - ("levelname", "Level"), - ("threadName", "Thread"), - ("msecs", "Millis") - ) - - def __init__(self, record_item): - self.record_item = record_item - self.msg = None - msg = record_item.get("msg") - if msg is None: - msg = record_item["label"].split("\n")[0] - - super(TerminalDetailItem, self).__init__(msg) - - def data(self, role=QtCore.Qt.DisplayRole): - if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole): - if self.msg is None: - self.msg = self.compute_detail_text(self.record_item) - return self.msg - return super(TerminalDetailItem, self).data(role) - - def compute_detail_text(self, item_data): - if item_data["type"] == "info": - return item_data["label"] - - html_text = "" - for key, title in self.key_label_record_map: - if key not in item_data: - continue - value = item_data[key] - text = ( - str(value) - .replace("<", "<") - .replace(">", ">") - .replace('\n', '
    ') - .replace(' ', ' ') - ) - - title_tag = ( - '{}: ' - ' color:#fff;\" >{}: ' - ).format(title) - - html_text += ( - '{}' - '{}' - ).format(title_tag, text) - - html_text = '{}
    '.format( - html_text - ) - return html_text - - -class TerminalModel(QtGui.QStandardItemModel): - item_icon_name = { - "info": "fa.info", - "record": "fa.circle", - "error": "fa.exclamation-triangle", - } - - item_icon_colors = { - "info": "#ffffff", - "error": "#ff4a4a", - "log_debug": "#ff66e8", - "log_info": "#66abff", - "log_warning": "#ffba66", - "log_error": "#ff4d58", - "log_critical": "#ff4f75", - None: "#333333" - } - - level_to_record = ( - (10, "log_debug"), - (20, "log_info"), - (30, "log_warning"), - (40, "log_error"), - (50, "log_critical") - - ) - - def __init__(self, *args, **kwargs): - super(TerminalModel, self).__init__(*args, **kwargs) - self.reset() - - def reset(self): - self.clear() - - def prepare_records(self, result, suspend_logs): - prepared_records = [] - instance_name = None - instance = result["instance"] - if instance is not None: - instance_name = instance.data["name"] - - if not suspend_logs: - for record in result.get("records") or []: - if isinstance(record, dict): - record_item = record - else: - record_item = { - "label": str(record.msg), - "type": "record", - "levelno": record.levelno, - "threadName": record.threadName, - "name": record.name, - "filename": record.filename, - "pathname": record.pathname, - "lineno": record.lineno, - "msg": str(record.msg), - "msecs": record.msecs, - "levelname": record.levelname - } - - if instance_name is not None: - record_item["instance"] = instance_name - - prepared_records.append(record_item) - - error = result.get("error") - if error: - fname, line_no, func, exc = error.traceback - error_item = { - "label": str(error), - "type": "error", - "filename": str(fname), - "lineno": str(line_no), - "func": str(func), - "traceback": error.formatted_traceback, - } - - if instance_name is not None: - error_item["instance"] = instance_name - - prepared_records.append(error_item) - - return prepared_records - - def append(self, record_items): - all_record_items = [] - for record_item in record_items: - record_type = record_item["type"] - # Add error message to detail - if record_type == "error": - record_item["msg"] = record_item["label"] - terminal_item_type = None - if record_type == "record": - for level, _type in self.level_to_record: - if level > record_item["levelno"]: - break - terminal_item_type = _type - - else: - terminal_item_type = record_type - - icon_color = self.item_icon_colors.get(terminal_item_type) - icon_name = self.item_icon_name.get(record_type) - - top_item_icon = None - if icon_color and icon_name: - top_item_icon = QAwesomeIconFactory.icon(icon_name, icon_color) - - label = record_item["label"].split("\n")[0] - - top_item = QtGui.QStandardItem() - all_record_items.append(top_item) - - detail_item = TerminalDetailItem(record_item) - top_item.appendRow(detail_item) - - top_item.setData(TerminalLabelType, Roles.TypeRole) - top_item.setData(terminal_item_type, Roles.TerminalItemTypeRole) - top_item.setData(label, QtCore.Qt.DisplayRole) - top_item.setFlags( - QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled - ) - - if top_item_icon: - top_item.setData(top_item_icon, QtCore.Qt.DecorationRole) - - detail_item.setData(TerminalDetailType, Roles.TypeRole) - - self.invisibleRootItem().appendRows(all_record_items) - - def update_with_result(self, result): - self.append(result["records"]) - - -class TerminalProxy(QtCore.QSortFilterProxyModel): - filter_buttons_checks = { - "info": settings.TerminalFilters.get("info", True), - "log_debug": settings.TerminalFilters.get("log_debug", True), - "log_info": settings.TerminalFilters.get("log_info", True), - "log_warning": settings.TerminalFilters.get("log_warning", True), - "log_error": settings.TerminalFilters.get("log_error", True), - "log_critical": settings.TerminalFilters.get("log_critical", True), - "error": settings.TerminalFilters.get("error", True) - } - - instances = [] - - def __init__(self, view, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - self.__class__.instances.append(self) - # Store parent because by own `QSortFilterProxyModel` has `parent` - # method not returning parent QObject in PySide and PyQt4 - self.view = view - - @classmethod - def change_filter(cls, name, value): - cls.filter_buttons_checks[name] = value - - for instance in cls.instances: - try: - instance.invalidate() - if instance.view: - instance.view.updateGeometry() - - except RuntimeError: - # C++ Object was deleted - cls.instances.remove(instance) - - def filterAcceptsRow(self, source_row, source_parent): - index = self.sourceModel().index(source_row, 0, source_parent) - item_type = index.data(Roles.TypeRole) - if not item_type == TerminalLabelType: - return True - terminal_item_type = index.data(Roles.TerminalItemTypeRole) - return self.__class__.filter_buttons_checks.get( - terminal_item_type, True - ) diff --git a/client/ayon_core/tools/pyblish_pype/settings.py b/client/ayon_core/tools/pyblish_pype/settings.py deleted file mode 100644 index 5b69eb6a50..0000000000 --- a/client/ayon_core/tools/pyblish_pype/settings.py +++ /dev/null @@ -1,30 +0,0 @@ -from .util import env_variable_to_bool - -# Customize the window of the pyblish-lite window. -WindowTitle = "Pyblish" - -# Customize whether to show label names for plugins. -UseLabel = True - -# Customize which tab to start on. Possible choices are: "artist", "overview" -# and "terminal". -InitialTab = "overview" - -# Customize the window size. -WindowSize = (430, 600) - -TerminalFilters = { - "info": True, - "log_debug": True, - "log_info": True, - "log_warning": True, - "log_error": True, - "log_critical": True, - "traceback": True, -} - -# Allow animations in GUI -Animated = env_variable_to_bool("AYON_PYBLISH_ANIMATED", True) - -# Print UI info message to console -PrintInfo = env_variable_to_bool("AYON_PYBLISH_PRINT_INFO", True) diff --git a/client/ayon_core/tools/pyblish_pype/util.py b/client/ayon_core/tools/pyblish_pype/util.py deleted file mode 100644 index 081f7775d5..0000000000 --- a/client/ayon_core/tools/pyblish_pype/util.py +++ /dev/null @@ -1,144 +0,0 @@ -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals -) - -import os -import sys -import collections - -from qtpy import QtCore -import pyblish.api - -root = os.path.dirname(__file__) - - -def get_asset(*path): - """Return path to asset, relative the install directory - - Usage: - >>> path = get_asset("dir", "to", "asset.png") - >>> path == os.path.join(root, "dir", "to", "asset.png") - True - - Arguments: - path (str): One or more paths, to be concatenated - - """ - - return os.path.join(root, *path) - - -def defer(delay, func): - """Append artificial delay to `func` - - This aids in keeping the GUI responsive, but complicates logic - when producing tests. To combat this, the environment variable ensures - that every operation is synchronous. - - Arguments: - delay (float): Delay multiplier; default 1, 0 means no delay - func (callable): Any callable - - """ - - delay *= float(os.getenv("PYBLISH_DELAY", 1)) - if delay > 0: - return QtCore.QTimer.singleShot(delay, func) - else: - return func() - - -def u_print(msg, **kwargs): - """`print` with encoded unicode. - - `print` unicode may cause UnicodeEncodeError - or non-readable result when `PYTHONIOENCODING` is not set. - this will fix it. - - Arguments: - msg (unicode): Message to print. - **kwargs: Keyword argument for `print` function. - """ - - if isinstance(msg, str): - encoding = None - try: - encoding = os.getenv('PYTHONIOENCODING', sys.stdout.encoding) - except AttributeError: - # `sys.stdout.encoding` may not exists. - pass - msg = msg.encode(encoding or 'utf-8', 'replace') - print(msg, **kwargs) - - -def collect_families_from_instances(instances, only_active=False): - all_families = set() - for instance in instances: - if only_active: - if instance.data.get("publish") is False: - continue - family = instance.data.get("family") - if family: - all_families.add(family) - - families = instance.data.get("families") or tuple() - for family in families: - all_families.add(family) - - return list(all_families) - - -class OrderGroups: - validation_order = pyblish.api.ValidatorOrder + 0.5 - groups = collections.OrderedDict(( - ( - pyblish.api.CollectorOrder + 0.5, - { - "label": "Collect", - "state": "Collecting.." - } - ), - ( - pyblish.api.ValidatorOrder + 0.5, - { - "label": "Validate", - "state": "Validating.." - } - ), - ( - pyblish.api.ExtractorOrder + 0.5, - { - "label": "Extract", - "state": "Extracting.." - } - ), - ( - pyblish.api.IntegratorOrder + 0.5, - { - "label": "Integrate", - "state": "Integrating.." - } - ), - ( - None, - { - "label": "Other", - "state": "Finishing.." - } - ) - )) - - -def env_variable_to_bool(env_key, default=False): - """Boolean based on environment variable value.""" - value = os.environ.get(env_key) - if value is not None: - value = value.lower() - if value in ("true", "1", "yes", "on"): - return True - elif value in ("false", "0", "no", "off"): - return False - return default diff --git a/client/ayon_core/tools/pyblish_pype/vendor/__init__.py b/client/ayon_core/tools/pyblish_pype/vendor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py deleted file mode 100644 index 4a0001ebb7..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -qtawesome - use font-awesome in PyQt / PySide applications - -This is a port to Python of the C++ QtAwesome library by Rick Blommers -""" -from .iconic_font import IconicFont, set_global_defaults -from .animation import Pulse, Spin -from ._version import version_info, __version__ - -_resource = {'iconic': None, } - - -def _instance(): - if _resource['iconic'] is None: - _resource['iconic'] = IconicFont(('fa', 'fontawesome-webfont.ttf', 'fontawesome-webfont-charmap.json'), - ('ei', 'elusiveicons-webfont.ttf', 'elusiveicons-webfont-charmap.json')) - return _resource['iconic'] - - -def icon(*args, **kwargs): - return _instance().icon(*args, **kwargs) - - -def load_font(*args, **kwargs): - return _instance().load_font(*args, **kwargs) - - -def charmap(prefixed_name): - prefix, name = prefixed_name.split('.') - return _instance().charmap[prefix][name] - - -def font(*args, **kwargs): - return _instance().font(*args, **kwargs) - - -def set_defaults(**kwargs): - return set_global_defaults(**kwargs) - diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py deleted file mode 100644 index 7af886d1a0..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/_version.py +++ /dev/null @@ -1,2 +0,0 @@ -version_info = (0, 3, 0, 'dev') -__version__ = '.'.join(map(str, version_info)) diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py deleted file mode 100644 index ac69507444..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/animation.py +++ /dev/null @@ -1,41 +0,0 @@ -from qtpy import QtCore - - -class Spin: - - def __init__(self, parent_widget, interval=10, step=1): - self.parent_widget = parent_widget - self.interval, self.step = interval, step - self.info = {} - - def _update(self, parent_widget): - if self.parent_widget in self.info: - timer, angle, step = self.info[self.parent_widget] - - if angle >= 360: - angle = 0 - - angle += step - self.info[parent_widget] = timer, angle, step - parent_widget.update() - - def setup(self, icon_painter, painter, rect): - - if self.parent_widget not in self.info: - timer = QtCore.QTimer() - timer.timeout.connect(lambda: self._update(self.parent_widget)) - self.info[self.parent_widget] = [timer, 0, self.step] - timer.start(self.interval) - else: - timer, angle, self.step = self.info[self.parent_widget] - x_center = rect.width() * 0.5 - y_center = rect.height() * 0.5 - painter.translate(x_center, y_center) - painter.rotate(angle) - painter.translate(-x_center, -y_center) - - -class Pulse(Spin): - - def __init__(self, parent_widget): - Spin.__init__(self, parent_widget, interval=300, step=45) diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json deleted file mode 100644 index 099bcb818c..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont-charmap.json +++ /dev/null @@ -1,306 +0,0 @@ -{ - "address-book": "0xf102", - "address-book-alt": "0xf101", - "adjust": "0xf104", - "adjust-alt": "0xf103", - "adult": "0xf105", - "align-center": "0xf106", - "align-justify": "0xf107", - "align-left": "0xf108", - "align-right": "0xf109", - "arrow-down": "0xf10a", - "arrow-left": "0xf10b", - "arrow-right": "0xf10c", - "arrow-up": "0xf10d", - "asl": "0xf10e", - "asterisk": "0xf10f", - "backward": "0xf110", - "ban-circle": "0xf111", - "barcode": "0xf112", - "behance": "0xf113", - "bell": "0xf114", - "blind": "0xf115", - "blogger": "0xf116", - "bold": "0xf117", - "book": "0xf118", - "bookmark": "0xf11a", - "bookmark-empty": "0xf119", - "braille": "0xf11b", - "briefcase": "0xf11c", - "broom": "0xf11d", - "brush": "0xf11e", - "bulb": "0xf11f", - "bullhorn": "0xf120", - "calendar": "0xf122", - "calendar-sign": "0xf121", - "camera": "0xf123", - "car": "0xf124", - "caret-down": "0xf125", - "caret-left": "0xf126", - "caret-right": "0xf127", - "caret-up": "0xf128", - "cc": "0xf129", - "certificate": "0xf12a", - "check": "0xf12c", - "check-empty": "0xf12b", - "chevron-down": "0xf12d", - "chevron-left": "0xf12e", - "chevron-right": "0xf12f", - "chevron-up": "0xf130", - "child": "0xf131", - "circle-arrow-down": "0xf132", - "circle-arrow-left": "0xf133", - "circle-arrow-right": "0xf134", - "circle-arrow-up": "0xf135", - "cloud": "0xf137", - "cloud-alt": "0xf136", - "cog": "0xf139", - "cog-alt": "0xf138", - "cogs": "0xf13a", - "comment": "0xf13c", - "comment-alt": "0xf13b", - "compass": "0xf13e", - "compass-alt": "0xf13d", - "credit-card": "0xf13f", - "css": "0xf140", - "dashboard": "0xf141", - "delicious": "0xf142", - "deviantart": "0xf143", - "digg": "0xf144", - "download": "0xf146", - "download-alt": "0xf145", - "dribbble": "0xf147", - "edit": "0xf148", - "eject": "0xf149", - "envelope": "0xf14b", - "envelope-alt": "0xf14a", - "error": "0xf14d", - "error-alt": "0xf14c", - "eur": "0xf14e", - "exclamation-sign": "0xf14f", - "eye-close": "0xf150", - "eye-open": "0xf151", - "facebook": "0xf152", - "facetime-video": "0xf153", - "fast-backward": "0xf154", - "fast-forward": "0xf155", - "female": "0xf156", - "file": "0xf15c", - "file-alt": "0xf157", - "file-edit": "0xf159", - "file-edit-alt": "0xf158", - "file-new": "0xf15b", - "file-new-alt": "0xf15a", - "film": "0xf15d", - "filter": "0xf15e", - "fire": "0xf15f", - "flag": "0xf161", - "flag-alt": "0xf160", - "flickr": "0xf162", - "folder": "0xf166", - "folder-close": "0xf163", - "folder-open": "0xf164", - "folder-sign": "0xf165", - "font": "0xf167", - "fontsize": "0xf168", - "fork": "0xf169", - "forward": "0xf16b", - "forward-alt": "0xf16a", - "foursquare": "0xf16c", - "friendfeed": "0xf16e", - "friendfeed-rect": "0xf16d", - "fullscreen": "0xf16f", - "gbp": "0xf170", - "gift": "0xf171", - "github": "0xf173", - "github-text": "0xf172", - "glass": "0xf174", - "glasses": "0xf175", - "globe": "0xf177", - "globe-alt": "0xf176", - "googleplus": "0xf178", - "graph": "0xf17a", - "graph-alt": "0xf179", - "group": "0xf17c", - "group-alt": "0xf17b", - "guidedog": "0xf17d", - "hand-down": "0xf17e", - "hand-left": "0xf17f", - "hand-right": "0xf180", - "hand-up": "0xf181", - "hdd": "0xf182", - "headphones": "0xf183", - "hearing-impaired": "0xf184", - "heart": "0xf187", - "heart-alt": "0xf185", - "heart-empty": "0xf186", - "home": "0xf189", - "home-alt": "0xf188", - "hourglass": "0xf18a", - "idea": "0xf18c", - "idea-alt": "0xf18b", - "inbox": "0xf18f", - "inbox-alt": "0xf18d", - "inbox-box": "0xf18e", - "indent-left": "0xf190", - "indent-right": "0xf191", - "info-circle": "0xf192", - "instagram": "0xf193", - "iphone-home": "0xf194", - "italic": "0xf195", - "key": "0xf196", - "laptop": "0xf198", - "laptop-alt": "0xf197", - "lastfm": "0xf199", - "leaf": "0xf19a", - "lines": "0xf19b", - "link": "0xf19c", - "linkedin": "0xf19d", - "list": "0xf19f", - "list-alt": "0xf19e", - "livejournal": "0xf1a0", - "lock": "0xf1a2", - "lock-alt": "0xf1a1", - "magic": "0xf1a3", - "magnet": "0xf1a4", - "male": "0xf1a5", - "map-marker": "0xf1a7", - "map-marker-alt": "0xf1a6", - "mic": "0xf1a9", - "mic-alt": "0xf1a8", - "minus": "0xf1ab", - "minus-sign": "0xf1aa", - "move": "0xf1ac", - "music": "0xf1ad", - "myspace": "0xf1ae", - "network": "0xf1af", - "off": "0xf1b0", - "ok": "0xf1b3", - "ok-circle": "0xf1b1", - "ok-sign": "0xf1b2", - "opensource": "0xf1b4", - "paper-clip": "0xf1b6", - "paper-clip-alt": "0xf1b5", - "path": "0xf1b7", - "pause": "0xf1b9", - "pause-alt": "0xf1b8", - "pencil": "0xf1bb", - "pencil-alt": "0xf1ba", - "person": "0xf1bc", - "phone": "0xf1be", - "phone-alt": "0xf1bd", - "photo": "0xf1c0", - "photo-alt": "0xf1bf", - "picasa": "0xf1c1", - "picture": "0xf1c2", - "pinterest": "0xf1c3", - "plane": "0xf1c4", - "play": "0xf1c7", - "play-alt": "0xf1c5", - "play-circle": "0xf1c6", - "plurk": "0xf1c9", - "plurk-alt": "0xf1c8", - "plus": "0xf1cb", - "plus-sign": "0xf1ca", - "podcast": "0xf1cc", - "print": "0xf1cd", - "puzzle": "0xf1ce", - "qrcode": "0xf1cf", - "question": "0xf1d1", - "question-sign": "0xf1d0", - "quote-alt": "0xf1d2", - "quote-right": "0xf1d4", - "quote-right-alt": "0xf1d3", - "quotes": "0xf1d5", - "random": "0xf1d6", - "record": "0xf1d7", - "reddit": "0xf1d8", - "redux": "0xf1d9", - "refresh": "0xf1da", - "remove": "0xf1dd", - "remove-circle": "0xf1db", - "remove-sign": "0xf1dc", - "repeat": "0xf1df", - "repeat-alt": "0xf1de", - "resize-full": "0xf1e0", - "resize-horizontal": "0xf1e1", - "resize-small": "0xf1e2", - "resize-vertical": "0xf1e3", - "return-key": "0xf1e4", - "retweet": "0xf1e5", - "reverse-alt": "0xf1e6", - "road": "0xf1e7", - "rss": "0xf1e8", - "scissors": "0xf1e9", - "screen": "0xf1eb", - "screen-alt": "0xf1ea", - "screenshot": "0xf1ec", - "search": "0xf1ee", - "search-alt": "0xf1ed", - "share": "0xf1f0", - "share-alt": "0xf1ef", - "shopping-cart": "0xf1f2", - "shopping-cart-sign": "0xf1f1", - "signal": "0xf1f3", - "skype": "0xf1f4", - "slideshare": "0xf1f5", - "smiley": "0xf1f7", - "smiley-alt": "0xf1f6", - "soundcloud": "0xf1f8", - "speaker": "0xf1f9", - "spotify": "0xf1fa", - "stackoverflow": "0xf1fb", - "star": "0xf1fe", - "star-alt": "0xf1fc", - "star-empty": "0xf1fd", - "step-backward": "0xf1ff", - "step-forward": "0xf200", - "stop": "0xf202", - "stop-alt": "0xf201", - "stumbleupon": "0xf203", - "tag": "0xf204", - "tags": "0xf205", - "tasks": "0xf206", - "text-height": "0xf207", - "text-width": "0xf208", - "th": "0xf20b", - "th-large": "0xf209", - "th-list": "0xf20a", - "thumbs-down": "0xf20c", - "thumbs-up": "0xf20d", - "time": "0xf20f", - "time-alt": "0xf20e", - "tint": "0xf210", - "torso": "0xf211", - "trash": "0xf213", - "trash-alt": "0xf212", - "tumblr": "0xf214", - "twitter": "0xf215", - "universal-access": "0xf216", - "unlock": "0xf218", - "unlock-alt": "0xf217", - "upload": "0xf219", - "usd": "0xf21a", - "user": "0xf21b", - "viadeo": "0xf21c", - "video": "0xf21f", - "video-alt": "0xf21d", - "video-chat": "0xf21e", - "view-mode": "0xf220", - "vimeo": "0xf221", - "vkontakte": "0xf222", - "volume-down": "0xf223", - "volume-off": "0xf224", - "volume-up": "0xf225", - "w3c": "0xf226", - "warning-sign": "0xf227", - "website": "0xf229", - "website-alt": "0xf228", - "wheelchair": "0xf22a", - "wordpress": "0xf22b", - "wrench": "0xf22d", - "wrench-alt": "0xf22c", - "youtube": "0xf22e", - "zoom-in": "0xf22f", - "zoom-out": "0xf230" -} diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont.ttf b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/elusiveicons-webfont.ttf deleted file mode 100644 index b6fe85d4b265552431967d873b5c59a3e8e76333..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79556 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%0DliXV~&SY@w9 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 F{{~orV;TSe diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json deleted file mode 100644 index 0e97d031e6..0000000000 --- a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont-charmap.json +++ /dev/null @@ -1,696 +0,0 @@ -{ - "500px": "f26e", - "adjust": "f042", - "adn": "f170", - "align-center": "f037", - "align-justify": "f039", - "align-left": "f036", - "align-right": "f038", - "amazon": "f270", - "ambulance": "f0f9", - "anchor": "f13d", - "android": "f17b", - "angellist": "f209", - "angle-double-down": "f103", - "angle-double-left": "f100", - "angle-double-right": "f101", - "angle-double-up": "f102", - "angle-down": "f107", - "angle-left": "f104", - "angle-right": "f105", - "angle-up": "f106", - "apple": "f179", - "archive": "f187", - "area-chart": "f1fe", - "arrow-circle-down": "f0ab", - "arrow-circle-left": "f0a8", - "arrow-circle-o-down": "f01a", - "arrow-circle-o-left": "f190", - "arrow-circle-o-right": "f18e", - "arrow-circle-o-up": "f01b", - "arrow-circle-right": "f0a9", - "arrow-circle-up": "f0aa", - "arrow-down": "f063", - "arrow-left": "f060", - "arrow-right": "f061", - "arrow-up": "f062", - "arrows": "f047", - "arrows-alt": "f0b2", - "arrows-h": "f07e", - "arrows-v": "f07d", - "asterisk": "f069", - "at": "f1fa", - "automobile": "f1b9", - "backward": "f04a", - "balance-scale": "f24e", - "ban": "f05e", - "bank": "f19c", - "bar-chart": "f080", - "bar-chart-o": "f080", - "barcode": "f02a", - "bars": "f0c9", - "battery-0": "f244", - "battery-1": "f243", - "battery-2": "f242", - "battery-3": "f241", - "battery-4": "f240", - "battery-empty": "f244", - "battery-full": "f240", - "battery-half": "f242", - "battery-quarter": "f243", - "battery-three-quarters": "f241", - "bed": "f236", - "beer": "f0fc", - "behance": "f1b4", - "behance-square": "f1b5", - "bell": "f0f3", - "bell-o": "f0a2", - "bell-slash": "f1f6", - "bell-slash-o": "f1f7", - "bicycle": "f206", - "binoculars": "f1e5", - "birthday-cake": "f1fd", - "bitbucket": "f171", - "bitbucket-square": "f172", - "bitcoin": "f15a", - "black-tie": "f27e", - "bluetooth": "f293", - "bluetooth-b": "f294", - "bold": "f032", - "bolt": "f0e7", - "bomb": "f1e2", - "book": "f02d", - "bookmark": "f02e", - "bookmark-o": "f097", - "briefcase": "f0b1", - "btc": "f15a", - "bug": "f188", - "building": "f1ad", - "building-o": "f0f7", - "bullhorn": "f0a1", - "bullseye": "f140", - "bus": "f207", - "buysellads": "f20d", - "cab": "f1ba", - "calculator": "f1ec", - "calendar": "f073", - "calendar-check-o": "f274", - "calendar-minus-o": "f272", - "calendar-o": "f133", - "calendar-plus-o": "f271", - "calendar-times-o": "f273", - "camera": "f030", - "camera-retro": "f083", - "car": "f1b9", - "caret-down": "f0d7", - "caret-left": "f0d9", - "caret-right": "f0da", - "caret-square-o-down": "f150", - "caret-square-o-left": "f191", - "caret-square-o-right": "f152", - "caret-square-o-up": "f151", - "caret-up": "f0d8", - "cart-arrow-down": "f218", - "cart-plus": "f217", - "cc": "f20a", - "cc-amex": "f1f3", - "cc-diners-club": "f24c", - "cc-discover": "f1f2", - "cc-jcb": "f24b", - "cc-mastercard": "f1f1", - "cc-paypal": "f1f4", - "cc-stripe": "f1f5", - "cc-visa": "f1f0", - "certificate": "f0a3", - "chain": "f0c1", - "chain-broken": "f127", - "check": "f00c", - "check-circle": "f058", - "check-circle-o": "f05d", - "check-square": "f14a", - "check-square-o": "f046", - "chevron-circle-down": "f13a", - "chevron-circle-left": "f137", - "chevron-circle-right": "f138", - "chevron-circle-up": "f139", - "chevron-down": "f078", - "chevron-left": "f053", - "chevron-right": "f054", - "chevron-up": "f077", - "child": "f1ae", - "chrome": "f268", - "circle": "f111", - "circle-o": "f10c", - "circle-o-notch": "f1ce", - "circle-thin": "f1db", - "clipboard": "f0ea", - "clock-o": "f017", - "clone": "f24d", - "close": "f00d", - "cloud": "f0c2", - "cloud-download": "f0ed", - "cloud-upload": "f0ee", - "cny": "f157", - "code": "f121", - "code-fork": "f126", - "codepen": "f1cb", - "codiepie": "f284", - "coffee": "f0f4", - "cog": "f013", - "cogs": "f085", - "columns": "f0db", - "comment": "f075", - "comment-o": "f0e5", - "commenting": "f27a", - "commenting-o": "f27b", - "comments": "f086", - "comments-o": "f0e6", - "compass": "f14e", - "compress": "f066", - "connectdevelop": "f20e", - "contao": "f26d", - "copy": "f0c5", - "copyright": "f1f9", - "creative-commons": "f25e", - "credit-card": "f09d", - "credit-card-alt": "f283", - "crop": "f125", - "crosshairs": "f05b", - "css3": "f13c", - "cube": "f1b2", - "cubes": "f1b3", - "cut": "f0c4", - "cutlery": "f0f5", - "dashboard": "f0e4", - "dashcube": "f210", - "database": "f1c0", - "dedent": "f03b", - "delicious": "f1a5", - "desktop": "f108", - "deviantart": "f1bd", - "diamond": "f219", - "digg": "f1a6", - "dollar": "f155", - "dot-circle-o": "f192", - "download": "f019", - "dribbble": "f17d", - "dropbox": "f16b", - "drupal": "f1a9", - "edge": "f282", - "edit": "f044", - "eject": "f052", - "ellipsis-h": "f141", - "ellipsis-v": "f142", - "empire": "f1d1", - "envelope": "f0e0", - "envelope-o": "f003", - "envelope-square": "f199", - "eraser": "f12d", - "eur": "f153", - "euro": "f153", - "exchange": "f0ec", - "exclamation": "f12a", - "exclamation-circle": "f06a", - "exclamation-triangle": "f071", - "expand": "f065", - "expeditedssl": "f23e", - "external-link": "f08e", - "external-link-square": "f14c", - "eye": "f06e", - "eye-slash": "f070", - "eyedropper": "f1fb", - "facebook": "f09a", - "facebook-f": "f09a", - "facebook-official": "f230", - "facebook-square": "f082", - "fast-backward": "f049", - "fast-forward": "f050", - "fax": "f1ac", - "feed": "f09e", - "female": "f182", - "fighter-jet": "f0fb", - "file": "f15b", - "file-archive-o": "f1c6", - "file-audio-o": "f1c7", - "file-code-o": "f1c9", - "file-excel-o": "f1c3", - "file-image-o": "f1c5", - "file-movie-o": "f1c8", - "file-o": "f016", - "file-pdf-o": "f1c1", - "file-photo-o": "f1c5", - "file-picture-o": "f1c5", - "file-powerpoint-o": "f1c4", - "file-sound-o": "f1c7", - "file-text": "f15c", - "file-text-o": "f0f6", - "file-video-o": "f1c8", - "file-word-o": "f1c2", - "file-zip-o": "f1c6", - "files-o": "f0c5", - "film": "f008", - "filter": "f0b0", - "fire": "f06d", - "fire-extinguisher": "f134", - "firefox": "f269", - "flag": "f024", - "flag-checkered": "f11e", - "flag-o": "f11d", - "flash": "f0e7", - "flask": "f0c3", - "flickr": "f16e", - "floppy-o": "f0c7", - "folder": "f07b", - "folder-o": "f114", - "folder-open": "f07c", - "folder-open-o": "f115", - "font": "f031", - "fonticons": "f280", - "fort-awesome": "f286", - "forumbee": "f211", - "forward": "f04e", - "foursquare": "f180", - "frown-o": "f119", - "futbol-o": "f1e3", - "gamepad": "f11b", - "gavel": "f0e3", - "gbp": "f154", - "ge": "f1d1", - "gear": "f013", - "gears": "f085", - "genderless": "f22d", - "get-pocket": "f265", - "gg": "f260", - "gg-circle": "f261", - "gift": "f06b", - "git": "f1d3", - "git-square": "f1d2", - "github": "f09b", - "github-alt": "f113", - "github-square": "f092", - "gittip": "f184", - "glass": "f000", - "globe": "f0ac", - "google": "f1a0", - "google-plus": "f0d5", - "google-plus-square": "f0d4", - "google-wallet": "f1ee", - "graduation-cap": "f19d", - "gratipay": "f184", - "group": "f0c0", - "h-square": "f0fd", - "hacker-news": "f1d4", - "hand-grab-o": "f255", - "hand-lizard-o": "f258", - "hand-o-down": "f0a7", - "hand-o-left": "f0a5", - "hand-o-right": "f0a4", - "hand-o-up": "f0a6", - "hand-paper-o": "f256", - "hand-peace-o": "f25b", - "hand-pointer-o": "f25a", - "hand-rock-o": "f255", - "hand-scissors-o": "f257", - "hand-spock-o": "f259", - "hand-stop-o": "f256", - "hashtag": "f292", - "hdd-o": "f0a0", - "header": "f1dc", - "headphones": "f025", - "heart": "f004", - "heart-o": "f08a", - "heartbeat": "f21e", - "history": "f1da", - "home": "f015", - "hospital-o": "f0f8", - "hotel": "f236", - "hourglass": "f254", - "hourglass-1": "f251", - "hourglass-2": "f252", - "hourglass-3": "f253", - "hourglass-end": "f253", - "hourglass-half": "f252", - "hourglass-o": "f250", - "hourglass-start": "f251", - "houzz": "f27c", - "html5": "f13b", - "i-cursor": "f246", - "ils": "f20b", - "image": "f03e", - "inbox": "f01c", - "indent": "f03c", - "industry": "f275", - "info": "f129", - "info-circle": "f05a", - "inr": "f156", - "instagram": "f16d", - "institution": "f19c", - "internet-explorer": "f26b", - "intersex": "f224", - "ioxhost": "f208", - "italic": "f033", - "joomla": "f1aa", - "jpy": "f157", - "jsfiddle": "f1cc", - "key": "f084", - "keyboard-o": "f11c", - "krw": "f159", - "language": "f1ab", - "laptop": "f109", - "lastfm": "f202", - "lastfm-square": "f203", - "leaf": "f06c", - "leanpub": "f212", - "legal": "f0e3", - "lemon-o": "f094", - "level-down": "f149", - "level-up": "f148", - "life-bouy": "f1cd", - "life-buoy": "f1cd", - "life-ring": "f1cd", - "life-saver": "f1cd", - "lightbulb-o": "f0eb", - "line-chart": "f201", - "link": "f0c1", - "linkedin": "f0e1", - "linkedin-square": "f08c", - "linux": "f17c", - "list": "f03a", - "list-alt": "f022", - "list-ol": "f0cb", - "list-ul": "f0ca", - "location-arrow": "f124", - "lock": "f023", - "long-arrow-down": "f175", - "long-arrow-left": "f177", - "long-arrow-right": "f178", - "long-arrow-up": "f176", - "magic": "f0d0", - "magnet": "f076", - "mail-forward": "f064", - "mail-reply": "f112", - "mail-reply-all": "f122", - "male": "f183", - "map": "f279", - "map-marker": "f041", - "map-o": "f278", - "map-pin": "f276", - "map-signs": "f277", - "mars": "f222", - "mars-double": "f227", - "mars-stroke": "f229", - "mars-stroke-h": "f22b", - "mars-stroke-v": "f22a", - "maxcdn": "f136", - "meanpath": "f20c", - "medium": "f23a", - "medkit": "f0fa", - "meh-o": "f11a", - "mercury": "f223", - "microphone": "f130", - "microphone-slash": "f131", - "minus": "f068", - "minus-circle": "f056", - "minus-square": "f146", - "minus-square-o": "f147", - "mixcloud": "f289", - "mobile": "f10b", - "mobile-phone": "f10b", - "modx": "f285", - "money": "f0d6", - "moon-o": "f186", - "mortar-board": "f19d", - "motorcycle": "f21c", - "mouse-pointer": "f245", - "music": "f001", - "navicon": "f0c9", - "neuter": "f22c", - "newspaper-o": "f1ea", - "object-group": "f247", - "object-ungroup": "f248", - "odnoklassniki": "f263", - "odnoklassniki-square": "f264", - "opencart": "f23d", - "openid": "f19b", - "opera": "f26a", - "optin-monster": "f23c", - "outdent": "f03b", - "pagelines": "f18c", - "paint-brush": "f1fc", - "paper-plane": "f1d8", - "paper-plane-o": "f1d9", - "paperclip": "f0c6", - "paragraph": "f1dd", - "paste": "f0ea", - "pause": "f04c", - "pause-circle": "f28b", - "pause-circle-o": "f28c", - "paw": "f1b0", - "paypal": "f1ed", - "pencil": "f040", - "pencil-square": "f14b", - "pencil-square-o": "f044", - "percent": "f295", - "phone": "f095", - "phone-square": "f098", - "photo": "f03e", - "picture-o": "f03e", - "pie-chart": "f200", - "pied-piper": "f1a7", - "pied-piper-alt": "f1a8", - "pinterest": "f0d2", - "pinterest-p": "f231", - "pinterest-square": "f0d3", - "plane": "f072", - "play": "f04b", - "play-circle": "f144", - "play-circle-o": "f01d", - "plug": "f1e6", - "plus": "f067", - "plus-circle": "f055", - "plus-square": "f0fe", - "plus-square-o": "f196", - "power-off": "f011", - "print": "f02f", - "product-hunt": "f288", - "puzzle-piece": "f12e", - "qq": "f1d6", - "qrcode": "f029", - "question": "f128", - "question-circle": "f059", - "quote-left": "f10d", - "quote-right": "f10e", - "ra": "f1d0", - "random": "f074", - "rebel": "f1d0", - "recycle": "f1b8", - "reddit": "f1a1", - "reddit-alien": "f281", - "reddit-square": "f1a2", - "refresh": "f021", - "registered": "f25d", - "remove": "f00d", - "renren": "f18b", - "reorder": "f0c9", - "repeat": "f01e", - "reply": "f112", - "reply-all": "f122", - "retweet": "f079", - "rmb": "f157", - "road": "f018", - "rocket": "f135", - "rotate-left": "f0e2", - "rotate-right": "f01e", - "rouble": "f158", - "rss": "f09e", - "rss-square": "f143", - "rub": "f158", - "ruble": "f158", - "rupee": "f156", - "safari": "f267", - "save": "f0c7", - "scissors": "f0c4", - "scribd": "f28a", - "search": "f002", - "search-minus": "f010", - "search-plus": "f00e", - "sellsy": "f213", - "send": "f1d8", - "send-o": "f1d9", - "server": "f233", - "share": "f064", - "share-alt": "f1e0", - "share-alt-square": "f1e1", - "share-square": "f14d", - "share-square-o": "f045", - "shekel": "f20b", - "sheqel": "f20b", - "shield": "f132", - "ship": "f21a", - "shirtsinbulk": "f214", - "shopping-bag": "f290", - "shopping-basket": "f291", - "shopping-cart": "f07a", - "sign-in": "f090", - "sign-out": "f08b", - "signal": "f012", - "simplybuilt": "f215", - "sitemap": "f0e8", - "skyatlas": "f216", - "skype": "f17e", - "slack": "f198", - "sliders": "f1de", - "slideshare": "f1e7", - "smile-o": "f118", - "soccer-ball-o": "f1e3", - "sort": "f0dc", - "sort-alpha-asc": "f15d", - "sort-alpha-desc": "f15e", - "sort-amount-asc": "f160", - "sort-amount-desc": "f161", - "sort-asc": "f0de", - "sort-desc": "f0dd", - "sort-down": "f0dd", - "sort-numeric-asc": "f162", - "sort-numeric-desc": "f163", - "sort-up": "f0de", - "soundcloud": "f1be", - "space-shuttle": "f197", - "spinner": "f110", - "spoon": "f1b1", - "spotify": "f1bc", - "square": "f0c8", - "square-o": "f096", - "stack-exchange": "f18d", - "stack-overflow": "f16c", - "star": "f005", - "star-half": "f089", - "star-half-empty": "f123", - "star-half-full": "f123", - "star-half-o": "f123", - "star-o": "f006", - "steam": "f1b6", - "steam-square": "f1b7", - "step-backward": "f048", - "step-forward": "f051", - "stethoscope": "f0f1", - "sticky-note": "f249", - "sticky-note-o": "f24a", - "stop": "f04d", - "stop-circle": "f28d", - "stop-circle-o": "f28e", - "street-view": "f21d", - "strikethrough": "f0cc", - "stumbleupon": "f1a4", - "stumbleupon-circle": "f1a3", - "subscript": "f12c", - "subway": "f239", - "suitcase": "f0f2", - "sun-o": "f185", - "superscript": "f12b", - "support": "f1cd", - "table": "f0ce", - "tablet": "f10a", - "tachometer": "f0e4", - "tag": "f02b", - "tags": "f02c", - "tasks": "f0ae", - "taxi": "f1ba", - "television": "f26c", - "tencent-weibo": "f1d5", - "terminal": "f120", - "text-height": "f034", - "text-width": "f035", - "th": "f00a", - "th-large": "f009", - "th-list": "f00b", - "thumb-tack": "f08d", - "thumbs-down": "f165", - "thumbs-o-down": "f088", - "thumbs-o-up": "f087", - "thumbs-up": "f164", - "ticket": "f145", - "times": "f00d", - "times-circle": "f057", - "times-circle-o": "f05c", - "tint": "f043", - "toggle-down": "f150", - "toggle-left": "f191", - "toggle-off": "f204", - "toggle-on": "f205", - "toggle-right": "f152", - "toggle-up": "f151", - "trademark": "f25c", - "train": "f238", - "transgender": "f224", - "transgender-alt": "f225", - "trash": "f1f8", - "trash-o": "f014", - "tree": "f1bb", - "trello": "f181", - "tripadvisor": "f262", - "trophy": "f091", - "truck": "f0d1", - "try": "f195", - "tty": "f1e4", - "tumblr": "f173", - "tumblr-square": "f174", - "turkish-lira": "f195", - "tv": "f26c", - "twitch": "f1e8", - "twitter": "f099", - "twitter-square": "f081", - "umbrella": "f0e9", - "underline": "f0cd", - "undo": "f0e2", - "university": "f19c", - "unlink": "f127", - "unlock": "f09c", - "unlock-alt": "f13e", - "unsorted": "f0dc", - "upload": "f093", - "usb": "f287", - "usd": "f155", - "user": "f007", - "user-md": "f0f0", - "user-plus": "f234", - "user-secret": "f21b", - "user-times": "f235", - "users": "f0c0", - "venus": "f221", - "venus-double": "f226", - "venus-mars": "f228", - "viacoin": "f237", - "video-camera": "f03d", - "vimeo": "f27d", - "vimeo-square": "f194", - "vine": "f1ca", - "vk": "f189", - "volume-down": "f027", - "volume-off": "f026", - "volume-up": "f028", - "warning": "f071", - "wechat": "f1d7", - "weibo": "f18a", - "weixin": "f1d7", - "whatsapp": "f232", - "wheelchair": "f193", - "wifi": "f1eb", - "wikipedia-w": "f266", - "windows": "f17a", - "won": "f159", - "wordpress": "f19a", - "wrench": "f0ad", - "xing": "f168", - "xing-square": "f169", - "y-combinator": "f23b", - "y-combinator-square": "f1d4", - "yahoo": "f19e", - "yc": "f23b", - "yc-square": "f1d4", - "yelp": "f1e9", - "yen": "f157", - "youtube": "f167", - "youtube-play": "f16a", - "youtube-square": "f166" -} \ No newline at end of file diff --git a/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont.ttf b/client/ayon_core/tools/pyblish_pype/vendor/qtawesome/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 26dea7951a73079223b50653c455c5adf46a4648..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142072 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 +|+&Vdls@0SAya!8#E9iVniRwHu0GY;H*n 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�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%0D0!#Bo^>xe$Z4SmykflG_VnkLvLv4@e#4_y4Q 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 self.width() - 20: - self.show_perspective.emit(index) - - return super(PluginView, self).mouseReleaseEvent(event) - - -class InstanceView(OverviewView): - def __init__(self, *args, **kwargs): - super(InstanceView, self).__init__(*args, **kwargs) - self.setSortingEnabled(True) - self.sortByColumn(0, QtCore.Qt.AscendingOrder) - self.viewport().setMouseTracking(True) - self._pressed_group_index = None - self._pressed_expander = None - - def mouseMoveEvent(self, event): - index = self.indexAt(event.pos()) - if index.data(Roles.TypeRole) == model.GroupType: - self.update(index) - super(InstanceView, self).mouseMoveEvent(event) - - def item_expand(self, index, expand=None): - if expand is None: - expand = not self.isExpanded(index) - - if expand: - self.expand(index) - else: - self.collapse(index) - - def group_toggle(self, index): - if not index.isValid(): - return - model = index.model() - - chilren_indexes_checked = [] - chilren_indexes_unchecked = [] - for idx in range(model.rowCount(index)): - child_index = model.index(idx, 0, index) - if not child_index.data(Roles.IsEnabledRole): - continue - - if child_index.data(QtCore.Qt.CheckStateRole): - chilren_indexes_checked.append(child_index) - else: - chilren_indexes_unchecked.append(child_index) - - if chilren_indexes_checked: - to_change_indexes = chilren_indexes_checked - new_state = False - else: - to_change_indexes = chilren_indexes_unchecked - new_state = True - - for index in to_change_indexes: - model.setData(index, new_state, QtCore.Qt.CheckStateRole) - self.toggled.emit(index, new_state) - - def _mouse_press(self, event): - if event.button() != QtCore.Qt.LeftButton: - return - - self._pressed_group_index = None - self._pressed_expander = None - - pos_index = self.indexAt(event.pos()) - if not pos_index.isValid(): - return - - if pos_index.data(Roles.TypeRole) != model.InstanceType: - self._pressed_group_index = pos_index - if event.pos().x() < 20: - self._pressed_expander = True - else: - self._pressed_expander = False - - elif event.pos().x() < 20: - indexes = self.selectionModel().selectedIndexes() - any_checked = False - if len(indexes) <= 1: - return - - if pos_index in indexes: - for index in indexes: - if index.data(QtCore.Qt.CheckStateRole): - any_checked = True - break - - for index in indexes: - self.toggled.emit(index, not any_checked) - return True - self.toggled.emit(pos_index, not any_checked) - - def mousePressEvent(self, event): - if self._mouse_press(event): - return - return super(InstanceView, self).mousePressEvent(event) - - def _mouse_release(self, event, pressed_expander, pressed_index): - if event.button() != QtCore.Qt.LeftButton: - return - - pos_index = self.indexAt(event.pos()) - if not pos_index.isValid(): - return - - if pos_index.data(Roles.TypeRole) == model.InstanceType: - indexes = self.selectionModel().selectedIndexes() - if len(indexes) == 1 and indexes[0] == pos_index: - if event.pos().x() < 20: - self.toggled.emit(indexes[0], None) - elif event.pos().x() > self.width() - 20: - self.show_perspective.emit(indexes[0]) - return True - return - - if pressed_index != pos_index: - return - - if self.state() == QtWidgets.QTreeView.State.DragSelectingState: - indexes = self.selectionModel().selectedIndexes() - if len(indexes) != 1 or indexes[0] != pos_index: - return - - if event.pos().x() < EXPANDER_WIDTH: - if pressed_expander is True: - self.item_expand(pos_index) - return True - else: - if pressed_expander is False: - self.group_toggle(pos_index) - self.item_expand(pos_index, True) - return True - - def mouseReleaseEvent(self, event): - pressed_index = self._pressed_group_index - pressed_expander = self._pressed_expander is True - self._pressed_group_index = None - self._pressed_expander = None - result = self._mouse_release(event, pressed_expander, pressed_index) - if result: - return - return super(InstanceView, self).mouseReleaseEvent(event) - - -class TerminalView(QtWidgets.QTreeView): - # An item is requesting to be toggled, with optional forced-state - def __init__(self, parent=None): - super(TerminalView, self).__init__(parent) - self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) - self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) - self.setAutoScroll(False) - self.setHeaderHidden(True) - self.setIndentation(0) - self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) - self.verticalScrollBar().setSingleStep(10) - self.setRootIsDecorated(False) - - self.clicked.connect(self.item_expand) - - _import_widgets() - - def event(self, event): - if not event.type() == QtCore.QEvent.KeyPress: - return super(TerminalView, self).event(event) - - elif event.key() == QtCore.Qt.Key_Space: - for index in self.selectionModel().selectedIndexes(): - if self.isExpanded(index): - self.collapse(index) - else: - self.expand(index) - - elif event.key() == QtCore.Qt.Key_Backspace: - for index in self.selectionModel().selectedIndexes(): - self.collapse(index) - - elif event.key() == QtCore.Qt.Key_Return: - for index in self.selectionModel().selectedIndexes(): - self.expand(index) - - return super(TerminalView, self).event(event) - - def focusOutEvent(self, event): - self.selectionModel().clear() - - def item_expand(self, index): - if index.data(Roles.TypeRole) == model.TerminalLabelType: - if self.isExpanded(index): - self.collapse(index) - else: - self.expand(index) - self.model().layoutChanged.emit() - self.updateGeometry() - - def rowsInserted(self, parent, start, end): - """Automatically scroll to bottom on each new item added.""" - super(TerminalView, self).rowsInserted(parent, start, end) - self.updateGeometry() - self.scrollToBottom() - - def expand(self, index): - """Wrapper to set widget for expanded index.""" - model = index.model() - row_count = model.rowCount(index) - is_new = False - for child_idx in range(row_count): - child_index = model.index(child_idx, index.column(), index) - widget = self.indexWidget(child_index) - if widget is None: - is_new = True - msg = child_index.data(QtCore.Qt.DisplayRole) - widget = widgets.TerminalDetail(msg) - self.setIndexWidget(child_index, widget) - super(TerminalView, self).expand(index) - if is_new: - self.updateGeometries() - - def resizeEvent(self, event): - super(self.__class__, self).resizeEvent(event) - self.model().layoutChanged.emit() - - def sizeHint(self): - size = super(TerminalView, self).sizeHint() - height = ( - self.contentsMargins().top() - + self.contentsMargins().bottom() - ) - for idx_i in range(self.model().rowCount()): - index = self.model().index(idx_i, 0) - height += self.rowHeight(index) - if self.isExpanded(index): - for idx_j in range(index.model().rowCount(index)): - child_index = index.child(idx_j, 0) - height += self.rowHeight(child_index) - - size.setHeight(height) - return size diff --git a/client/ayon_core/tools/pyblish_pype/widgets.py b/client/ayon_core/tools/pyblish_pype/widgets.py deleted file mode 100644 index 6adcc55f06..0000000000 --- a/client/ayon_core/tools/pyblish_pype/widgets.py +++ /dev/null @@ -1,555 +0,0 @@ -import sys -from qtpy import QtCore, QtWidgets, QtGui -from . import model, delegate, view, awesome -from .constants import PluginStates, InstanceStates, Roles - - -class EllidableLabel(QtWidgets.QLabel): - def __init__(self, *args, **kwargs): - super(EllidableLabel, self).__init__(*args, **kwargs) - self.setObjectName("EllidableLabel") - - def paintEvent(self, event): - painter = QtGui.QPainter(self) - - metrics = QtGui.QFontMetrics(self.font()) - elided = metrics.elidedText( - self.text(), QtCore.Qt.ElideRight, self.width() - ) - painter.drawText(self.rect(), self.alignment(), elided) - - -class PerspectiveLabel(QtWidgets.QTextEdit): - def __init__(self, parent=None): - super(PerspectiveLabel, self).__init__(parent) - self.setObjectName("PerspectiveLabel") - - size_policy = self.sizePolicy() - size_policy.setHeightForWidth(True) - size_policy.setVerticalPolicy(QtWidgets.QSizePolicy.Preferred) - self.setSizePolicy(size_policy) - - self.textChanged.connect(self.on_text_changed) - - def on_text_changed(self, *args, **kwargs): - self.updateGeometry() - - def hasHeightForWidth(self): - return True - - def heightForWidth(self, width): - margins = self.contentsMargins() - - document_width = 0 - if width >= margins.left() + margins.right(): - document_width = width - margins.left() - margins.right() - - document = self.document().clone() - document.setTextWidth(document_width) - - return margins.top() + document.size().height() + margins.bottom() - - def sizeHint(self): - width = super(PerspectiveLabel, self).sizeHint().width() - return QtCore.QSize(width, self.heightForWidth(width)) - - -class PerspectiveWidget(QtWidgets.QWidget): - l_doc = "Documentation" - l_rec = "Records" - l_path = "Path" - - def __init__(self, parent): - super(PerspectiveWidget, self).__init__(parent) - - self.parent_widget = parent - main_layout = QtWidgets.QVBoxLayout(self) - - header_widget = QtWidgets.QWidget() - toggle_button = QtWidgets.QPushButton(parent=header_widget) - toggle_button.setObjectName("PerspectiveToggleBtn") - toggle_button.setText(delegate.icons["angle-left"]) - toggle_button.setMinimumHeight(50) - toggle_button.setFixedWidth(40) - - indicator = QtWidgets.QLabel("", parent=header_widget) - indicator.setFixedWidth(30) - indicator.setAlignment(QtCore.Qt.AlignCenter) - indicator.setObjectName("PerspectiveIndicator") - - name = EllidableLabel('*Name of inspected', parent=header_widget) - - header_layout = QtWidgets.QHBoxLayout(header_widget) - header_layout.setAlignment(QtCore.Qt.AlignLeft) - header_layout.addWidget(toggle_button) - header_layout.addWidget(indicator) - header_layout.addWidget(name) - header_layout.setContentsMargins(0, 0, 0, 0) - header_layout.setSpacing(10) - header_widget.setLayout(header_layout) - - main_layout.setAlignment(QtCore.Qt.AlignTop) - main_layout.addWidget(header_widget) - - scroll_widget = QtWidgets.QScrollArea(self) - scroll_widget.setObjectName("PerspectiveScrollContent") - - contents_widget = QtWidgets.QWidget(scroll_widget) - contents_widget.setObjectName("PerspectiveWidgetContent") - - layout = QtWidgets.QVBoxLayout() - layout.setAlignment(QtCore.Qt.AlignTop) - layout.setContentsMargins(0, 0, 0, 0) - - documentation = ExpandableWidget(self, self.l_doc) - doc_label = PerspectiveLabel() - documentation.set_content(doc_label) - layout.addWidget(documentation) - - path = ExpandableWidget(self, self.l_path) - path_label = PerspectiveLabel() - path.set_content(path_label) - layout.addWidget(path) - - records = ExpandableWidget(self, self.l_rec) - layout.addWidget(records) - - contents_widget.setLayout(layout) - - terminal_view = view.TerminalView() - terminal_view.setObjectName("TerminalView") - terminal_model = model.TerminalModel() - terminal_proxy = model.TerminalProxy(terminal_view) - terminal_proxy.setSourceModel(terminal_model) - - terminal_view.setModel(terminal_proxy) - terminal_delegate = delegate.TerminalItem() - terminal_view.setItemDelegate(terminal_delegate) - records.set_content(terminal_view) - - scroll_widget.setWidgetResizable(True) - scroll_widget.setWidget(contents_widget) - - main_layout.setContentsMargins(0, 0, 0, 0) - main_layout.setSpacing(0) - main_layout.addWidget(scroll_widget) - self.setLayout(main_layout) - - self.terminal_view = terminal_view - self.terminal_model = terminal_model - self.terminal_proxy = terminal_proxy - - self.indicator = indicator - self.scroll_widget = scroll_widget - self.contents_widget = contents_widget - self.toggle_button = toggle_button - self.name_widget = name - self.documentation = documentation - self.path = path - self.records = records - - self.toggle_button.clicked.connect(self.toggle_me) - - self.last_type = None - self.last_item_id = None - self.last_id = None - - def trim(self, docstring): - if not docstring: - return "" - # Convert tabs to spaces (following the normal Python rules) - # and split into a list of lines: - lines = docstring.expandtabs().splitlines() - # Determine minimum indentation (first line doesn't count): - try: - indent = sys.maxint - max = sys.maxint - except Exception: - indent = sys.maxsize - max = sys.maxsize - - for line in lines[1:]: - stripped = line.lstrip() - if stripped: - indent = min(indent, len(line) - len(stripped)) - # Remove indentation (first line is special): - trimmed = [lines[0].strip()] - if indent < max: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - # Strip off trailing and leading blank lines: - while trimmed and not trimmed[-1]: - trimmed.pop() - while trimmed and not trimmed[0]: - trimmed.pop(0) - # Return a single string: - return "\n".join(trimmed) - - def set_indicator_state(self, state): - self.indicator.setProperty("state", state) - self.indicator.style().polish(self.indicator) - - def reset(self): - self.last_id = None - self.set_records(list()) - self.set_indicator_state(None) - - def update_context(self, plugin_item, instance_item): - if not self.last_item_id or not self.last_type: - return - - if self.last_type == model.PluginType: - if not self.last_id: - _item_id = plugin_item.data(Roles.ObjectUIdRole) - if _item_id != self.last_item_id: - return - self.last_id = plugin_item.plugin.id - - elif self.last_id != plugin_item.plugin.id: - return - - self.set_context(plugin_item.index()) - return - - if self.last_type == model.InstanceType: - if not self.last_id: - _item_id = instance_item.data(Roles.ObjectUIdRole) - if _item_id != self.last_item_id: - return - self.last_id = instance_item.instance.id - - elif self.last_id != instance_item.instance.id: - return - - self.set_context(instance_item.index()) - return - - def set_context(self, index): - if not index or not index.isValid(): - index_type = None - else: - index_type = index.data(Roles.TypeRole) - - if index_type == model.InstanceType: - item_id = index.data(Roles.ObjectIdRole) - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & InstanceStates.ContextType: - type_indicator = "C" - else: - type_indicator = "I" - - if publish_states & InstanceStates.InProgress: - self.set_indicator_state("active") - - elif publish_states & InstanceStates.HasError: - self.set_indicator_state("error") - - elif publish_states & InstanceStates.HasWarning: - self.set_indicator_state("warning") - - elif publish_states & InstanceStates.HasFinished: - self.set_indicator_state("ok") - else: - self.set_indicator_state(None) - - self.documentation.setVisible(False) - self.path.setVisible(False) - - elif index_type == model.PluginType: - item_id = index.data(Roles.ObjectIdRole) - type_indicator = "P" - - doc = index.data(Roles.DocstringRole) - doc_str = "" - if doc: - doc_str = self.trim(doc) - - publish_states = index.data(Roles.PublishFlagsRole) - if publish_states & PluginStates.InProgress: - self.set_indicator_state("active") - - elif publish_states & PluginStates.HasError: - self.set_indicator_state("error") - - elif publish_states & PluginStates.HasWarning: - self.set_indicator_state("warning") - - elif publish_states & PluginStates.WasProcessed: - self.set_indicator_state("ok") - - else: - self.set_indicator_state(None) - - self.documentation.toggle_content(bool(doc_str)) - self.documentation.content.setText(doc_str) - - path = index.data(Roles.PathModuleRole) or "" - self.path.toggle_content(path.strip() != "") - self.path.content.setText(path) - - self.documentation.setVisible(True) - self.path.setVisible(True) - - else: - self.last_type = None - self.last_id = None - self.indicator.setText("?") - self.set_indicator_state(None) - self.documentation.setVisible(False) - self.path.setVisible(False) - self.records.setVisible(False) - return - - self.last_type = index_type - self.last_id = item_id - self.last_item_id = index.data(Roles.ObjectUIdRole) - - self.indicator.setText(type_indicator) - - label = index.data(QtCore.Qt.DisplayRole) - self.name_widget.setText(label) - self.records.setVisible(True) - - records = index.data(Roles.LogRecordsRole) or [] - self.set_records(records) - - def set_records(self, records): - len_records = 0 - if records: - len_records += len(records) - - data = {"records": records} - self.terminal_model.reset() - self.terminal_model.update_with_result(data) - - self.records.button_toggle_text.setText( - "{} ({})".format(self.l_rec, len_records) - ) - self.records.toggle_content(len_records > 0) - - def toggle_me(self): - self.parent_widget.parent().toggle_perspective_widget() - - -class ClickableWidget(QtWidgets.QLabel): - clicked = QtCore.Signal() - - def mouseReleaseEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - self.clicked.emit() - super(ClickableWidget, self).mouseReleaseEvent(event) - - -class ExpandableWidget(QtWidgets.QWidget): - - content = None - - def __init__(self, parent, title): - super(ExpandableWidget, self).__init__(parent) - - top_part = ClickableWidget(parent=self) - top_part.setObjectName("ExpandableHeader") - - button_size = QtCore.QSize(5, 5) - button_toggle = QtWidgets.QToolButton(parent=top_part) - button_toggle.setIconSize(button_size) - button_toggle.setArrowType(QtCore.Qt.RightArrow) - button_toggle.setCheckable(True) - button_toggle.setChecked(False) - - button_toggle_text = QtWidgets.QLabel(title, parent=top_part) - - layout = QtWidgets.QHBoxLayout(top_part) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(5) - layout.addWidget(button_toggle) - layout.addWidget(button_toggle_text) - top_part.setLayout(layout) - - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.setContentsMargins(9, 9, 9, 0) - - content = QtWidgets.QFrame(self) - content.setObjectName("ExpandableWidgetContent") - content.setVisible(False) - - content_layout = QtWidgets.QVBoxLayout(content) - - main_layout.addWidget(top_part) - main_layout.addWidget(content) - self.setLayout(main_layout) - - self.setAttribute(QtCore.Qt.WA_StyledBackground) - - self.top_part = top_part - self.button_toggle = button_toggle - self.button_toggle_text = button_toggle_text - - self.content_widget = content - self.content_layout = content_layout - - self.top_part.clicked.connect(self.top_part_clicked) - self.button_toggle.clicked.connect(self.toggle_content) - - def top_part_clicked(self): - self.toggle_content(not self.button_toggle.isChecked()) - - def toggle_content(self, *args): - if len(args) > 0: - checked = args[0] - else: - checked = self.button_toggle.isChecked() - arrow_type = QtCore.Qt.RightArrow - if checked: - arrow_type = QtCore.Qt.DownArrow - self.button_toggle.setChecked(checked) - self.button_toggle.setArrowType(arrow_type) - self.content_widget.setVisible(checked) - - def resizeEvent(self, event): - super(ExpandableWidget, self).resizeEvent(event) - self.content.updateGeometry() - - def set_content(self, in_widget): - if self.content: - self.content.hide() - self.content_layout.removeWidget(self.content) - self.content_layout.addWidget(in_widget) - self.content = in_widget - - -class ButtonWithMenu(QtWidgets.QWidget): - def __init__(self, button_title, parent=None): - super(ButtonWithMenu, self).__init__(parent=parent) - self.setSizePolicy(QtWidgets.QSizePolicy( - QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum - )) - - self.layout = QtWidgets.QHBoxLayout(self) - self.layout.setContentsMargins(0, 0, 0, 0) - self.layout.setSpacing(0) - - self.menu = QtWidgets.QMenu() - # TODO move to stylesheets - self.menu.setStyleSheet(""" - *{color: #fff; background-color: #555; border: 1px solid #222;} - ::item {background-color: transparent;padding: 5px; - padding-left: 10px;padding-right: 10px;} - ::item:selected {background-color: #666;} - """) - - self.button = QtWidgets.QPushButton(button_title) - self.button.setObjectName("ButtonWithMenu") - - self.layout.addWidget(self.button) - - self.button.clicked.connect(self.btn_clicked) - - def btn_clicked(self): - self.menu.popup(self.button.mapToGlobal( - QtCore.QPoint(0, self.button.height()) - )) - - def addItem(self, text, callback): - self.menu.addAction(text, callback) - self.button.setToolTip("Select to apply predefined presets") - - def clearMenu(self): - self.menu.clear() - self.button.setToolTip("Presets not found") - - -class CommentBox(QtWidgets.QLineEdit): - - def __init__(self, placeholder_text, parent=None): - super(CommentBox, self).__init__(parent=parent) - self.placeholder = QtWidgets.QLabel(placeholder_text, self) - self.placeholder.move(2, 2) - - def focusInEvent(self, event): - self.placeholder.setVisible(False) - return super(CommentBox, self).focusInEvent(event) - - def focusOutEvent(self, event): - current_text = self.text() - current_text = current_text.strip(" ") - self.setText(current_text) - if not self.text(): - self.placeholder.setVisible(True) - return super(CommentBox, self).focusOutEvent(event) - - -class TerminalDetail(QtWidgets.QTextEdit): - def __init__(self, text, *args, **kwargs): - super(TerminalDetail, self).__init__(*args, **kwargs) - - self.setReadOnly(True) - self.setHtml(text) - self.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - self.setWordWrapMode( - QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere - ) - - def sizeHint(self): - content_margins = ( - self.contentsMargins().top() - + self.contentsMargins().bottom() - ) - size = self.document().documentLayout().documentSize().toSize() - size.setHeight(size.height() + content_margins) - return size - - -class FilterButton(QtWidgets.QPushButton): - def __init__(self, name, *args, **kwargs): - self.filter_name = name - - super(FilterButton, self).__init__(*args, **kwargs) - - self.toggled.connect(self.on_toggle) - - self.setProperty("type", name) - self.setObjectName("TerminalFilerBtn") - self.setCheckable(True) - self.setChecked( - model.TerminalProxy.filter_buttons_checks[name] - ) - - def on_toggle(self, toggle_state): - model.TerminalProxy.change_filter(self.filter_name, toggle_state) - - -class TerminalFilterWidget(QtWidgets.QWidget): - # timer.timeout.connect(lambda: self._update(self.parent_widget)) - def __init__(self, *args, **kwargs): - super(TerminalFilterWidget, self).__init__(*args, **kwargs) - self.setObjectName("TerminalFilterWidget") - self.filter_changed = QtCore.Signal() - - info_icon = awesome.tags["info"] - log_icon = awesome.tags["circle"] - error_icon = awesome.tags["exclamation-triangle"] - - filter_buttons = ( - FilterButton("info", info_icon, self), - FilterButton("log_debug", log_icon, self), - FilterButton("log_info", log_icon, self), - FilterButton("log_warning", log_icon, self), - FilterButton("log_error", log_icon, self), - FilterButton("log_critical", log_icon, self), - FilterButton("error", error_icon, self) - ) - - layout = QtWidgets.QHBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - # Add spacers - spacer = QtWidgets.QWidget() - spacer.setAttribute(QtCore.Qt.WA_TranslucentBackground) - layout.addWidget(spacer, 1) - - for btn in filter_buttons: - layout.addWidget(btn) - - self.setLayout(layout) - - self.filter_buttons = filter_buttons diff --git a/client/ayon_core/tools/pyblish_pype/window.py b/client/ayon_core/tools/pyblish_pype/window.py deleted file mode 100644 index 01d373d841..0000000000 --- a/client/ayon_core/tools/pyblish_pype/window.py +++ /dev/null @@ -1,1315 +0,0 @@ -"""Main Window - -States: - These are all possible states and their transitions. - - - reset - ' - ' - ' - ___v__ - | | reset - | Idle |--------------------. - | |<-------------------' - | | - | | _____________ - | | validate | | reset # TODO - | |----------------->| In-progress |-----------. - | | |_____________| ' - | |<-------------------------------------------' - | | - | | _____________ - | | publish | | - | |----------------->| In-progress |---. - | | |_____________| ' - | |<-----------------------------------' - |______| - - -Todo: - There are notes spread throughout this project with the syntax: - - - TODO(username) - - The `username` is a quick and dirty indicator of who made the note - and is by no means exclusive to that person in terms of seeing it - done. Feel free to do, or make your own TODO's as you code. Just - make sure the description is sufficient for anyone reading it for - the first time to understand how to actually to it! - -""" -import sys -from functools import partial - -from . import delegate, model, settings, util, view, widgets -from .awesome import tags as awesome - -from qtpy import QtCore, QtGui, QtWidgets -from .constants import ( - PluginStates, PluginActionStates, InstanceStates, GroupStates, Roles -) -if sys.version_info[0] == 3: - from queue import Queue -else: - from Queue import Queue - - -class Window(QtWidgets.QDialog): - def __init__(self, controller, parent=None): - super(Window, self).__init__(parent=parent) - - self._suspend_logs = False - - # Use plastique style for specific ocations - # TODO set style name via environment variable - low_keys = { - key.lower(): key - for key in QtWidgets.QStyleFactory.keys() - } - if "plastique" in low_keys: - self.setStyle( - QtWidgets.QStyleFactory.create(low_keys["plastique"]) - ) - - icon = QtGui.QIcon(util.get_asset("img", "logo-extrasmall.png")) - if parent is None: - on_top_flag = QtCore.Qt.WindowStaysOnTopHint - else: - on_top_flag = QtCore.Qt.Dialog - - self.setWindowFlags( - self.windowFlags() - | QtCore.Qt.WindowTitleHint - | QtCore.Qt.WindowMaximizeButtonHint - | QtCore.Qt.WindowMinimizeButtonHint - | QtCore.Qt.WindowCloseButtonHint - | on_top_flag - ) - self.setWindowIcon(icon) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - - self.controller = controller - - main_widget = QtWidgets.QWidget(self) - - # General layout - header_widget = QtWidgets.QWidget(parent=main_widget) - - header_tab_widget = QtWidgets.QWidget(header_widget) - header_tab_overview = QtWidgets.QRadioButton(header_tab_widget) - header_tab_terminal = QtWidgets.QRadioButton(header_tab_widget) - header_spacer = QtWidgets.QWidget(header_tab_widget) - - button_suspend_logs_widget = QtWidgets.QWidget() - button_suspend_logs_widget_layout = QtWidgets.QHBoxLayout( - button_suspend_logs_widget - ) - button_suspend_logs_widget_layout.setContentsMargins(0, 10, 0, 10) - button_suspend_logs = QtWidgets.QPushButton(header_widget) - button_suspend_logs.setFixedWidth(7) - button_suspend_logs.setSizePolicy( - QtWidgets.QSizePolicy.Preferred, - QtWidgets.QSizePolicy.Expanding - ) - button_suspend_logs_widget_layout.addWidget(button_suspend_logs) - header_aditional_btns = QtWidgets.QWidget(header_tab_widget) - - aditional_btns_layout = QtWidgets.QHBoxLayout(header_aditional_btns) - - presets_button = widgets.ButtonWithMenu(awesome["filter"]) - presets_button.setEnabled(False) - aditional_btns_layout.addWidget(presets_button) - - layout_tab = QtWidgets.QHBoxLayout(header_tab_widget) - layout_tab.setContentsMargins(0, 0, 0, 0) - layout_tab.setSpacing(0) - layout_tab.addWidget(header_tab_overview, 0) - layout_tab.addWidget(header_tab_terminal, 0) - layout_tab.addWidget(button_suspend_logs_widget, 0) - - # Compress items to the left - layout_tab.addWidget(header_spacer, 1) - layout_tab.addWidget(header_aditional_btns, 0) - - layout = QtWidgets.QHBoxLayout(header_widget) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - layout.addWidget(header_tab_widget) - - header_widget.setLayout(layout) - - # Overview Page - # TODO add parent - overview_page = QtWidgets.QWidget() - - overview_instance_view = view.InstanceView(parent=overview_page) - overview_instance_view.setAnimated(settings.Animated) - overview_instance_delegate = delegate.InstanceDelegate( - parent=overview_instance_view - ) - instance_model = model.InstanceModel(controller) - instance_sort_proxy = model.InstanceSortProxy() - instance_sort_proxy.setSourceModel(instance_model) - - overview_instance_view.setItemDelegate(overview_instance_delegate) - overview_instance_view.setModel(instance_sort_proxy) - - overview_plugin_view = view.PluginView(parent=overview_page) - overview_plugin_view.setAnimated(settings.Animated) - overview_plugin_delegate = delegate.PluginDelegate( - parent=overview_plugin_view - ) - overview_plugin_view.setItemDelegate(overview_plugin_delegate) - plugin_model = model.PluginModel(controller) - plugin_proxy = model.PluginFilterProxy() - plugin_proxy.setSourceModel(plugin_model) - overview_plugin_view.setModel(plugin_proxy) - - layout = QtWidgets.QHBoxLayout(overview_page) - layout.addWidget(overview_instance_view, 1) - layout.addWidget(overview_plugin_view, 1) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(0) - overview_page.setLayout(layout) - - # Terminal - terminal_container = QtWidgets.QWidget() - - terminal_view = view.TerminalView() - terminal_model = model.TerminalModel() - terminal_proxy = model.TerminalProxy(terminal_view) - terminal_proxy.setSourceModel(terminal_model) - - terminal_view.setModel(terminal_proxy) - terminal_delegate = delegate.TerminalItem() - terminal_view.setItemDelegate(terminal_delegate) - - layout = QtWidgets.QVBoxLayout(terminal_container) - layout.addWidget(terminal_view) - layout.setContentsMargins(5, 5, 5, 5) - layout.setSpacing(0) - - terminal_container.setLayout(layout) - - terminal_page = QtWidgets.QWidget() - layout = QtWidgets.QVBoxLayout(terminal_page) - layout.addWidget(terminal_container) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - - # Add some room between window borders and contents - body_widget = QtWidgets.QWidget(main_widget) - layout = QtWidgets.QHBoxLayout(body_widget) - layout.setContentsMargins(5, 5, 5, 1) - layout.addWidget(overview_page) - layout.addWidget(terminal_page) - - # Comment Box - comment_box = widgets.CommentBox("Comment...", self) - - intent_box = QtWidgets.QComboBox() - - intent_model = model.IntentModel() - intent_box.setModel(intent_model) - - comment_intent_widget = QtWidgets.QWidget() - comment_intent_layout = QtWidgets.QHBoxLayout(comment_intent_widget) - comment_intent_layout.setContentsMargins(0, 0, 0, 0) - comment_intent_layout.setSpacing(5) - comment_intent_layout.addWidget(comment_box) - comment_intent_layout.addWidget(intent_box) - - # Terminal filtering - terminal_filters_widget = widgets.TerminalFilterWidget() - - # Footer - footer_widget = QtWidgets.QWidget(main_widget) - - footer_info = QtWidgets.QLabel(footer_widget) - footer_spacer = QtWidgets.QWidget(footer_widget) - - footer_button_stop = QtWidgets.QPushButton( - awesome["stop"], footer_widget - ) - footer_button_stop.setToolTip("Stop publishing") - footer_button_reset = QtWidgets.QPushButton( - awesome["refresh"], footer_widget - ) - footer_button_reset.setToolTip("Restart publishing") - footer_button_validate = QtWidgets.QPushButton( - awesome["flask"], footer_widget - ) - footer_button_validate.setToolTip("Run validations") - footer_button_play = QtWidgets.QPushButton( - awesome["play"], footer_widget - ) - footer_button_play.setToolTip("Publish") - layout = QtWidgets.QHBoxLayout() - layout.setContentsMargins(5, 5, 5, 5) - layout.addWidget(footer_info, 0) - layout.addWidget(footer_spacer, 1) - - layout.addWidget(footer_button_stop, 0) - layout.addWidget(footer_button_reset, 0) - layout.addWidget(footer_button_validate, 0) - layout.addWidget(footer_button_play, 0) - - footer_layout = QtWidgets.QVBoxLayout(footer_widget) - footer_layout.addWidget(terminal_filters_widget) - footer_layout.addWidget(comment_intent_widget) - footer_layout.addLayout(layout) - - footer_widget.setProperty("success", -1) - - # Placeholder for when GUI is closing - # TODO(marcus): Fade to black and the the user about what's happening - closing_placeholder = QtWidgets.QWidget(main_widget) - closing_placeholder.setSizePolicy( - QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Expanding - ) - closing_placeholder.hide() - - perspective_widget = widgets.PerspectiveWidget(main_widget) - perspective_widget.hide() - - pages_widget = QtWidgets.QWidget(main_widget) - layout = QtWidgets.QVBoxLayout(pages_widget) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - layout.addWidget(header_widget, 0) - layout.addWidget(body_widget, 1) - - # Main layout - layout = QtWidgets.QVBoxLayout(main_widget) - layout.addWidget(pages_widget, 3) - layout.addWidget(perspective_widget, 3) - layout.addWidget(closing_placeholder, 1) - layout.addWidget(footer_widget, 0) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) - main_widget.setLayout(layout) - - self.main_layout = QtWidgets.QVBoxLayout(self) - self.main_layout.setContentsMargins(0, 0, 0, 0) - self.main_layout.setSpacing(0) - self.main_layout.addWidget(main_widget) - - """Setup - - Widgets are referred to in CSS via their object-name. We - use the same mechanism internally to refer to objects; so rather - than storing widgets as self.my_widget, it is referred to as: - - >>> my_widget = self.findChild(QtWidgets.QWidget, "MyWidget") - - This way there is only ever a single method of referring to any widget. - """ - - names = { - # Main - "Header": header_widget, - "Body": body_widget, - "Footer": footer_widget, - - # Pages - "Overview": overview_page, - "Terminal": terminal_page, - - # Tabs - "OverviewTab": header_tab_overview, - "TerminalTab": header_tab_terminal, - - # Views - "TerminalView": terminal_view, - - # Buttons - "SuspendLogsBtn": button_suspend_logs, - "Stop": footer_button_stop, - "Reset": footer_button_reset, - "Validate": footer_button_validate, - "Play": footer_button_play, - - # Misc - "HeaderSpacer": header_spacer, - "FooterSpacer": footer_spacer, - "FooterInfo": footer_info, - "CommentIntentWidget": comment_intent_widget, - "CommentBox": comment_box, - "CommentPlaceholder": comment_box.placeholder, - "ClosingPlaceholder": closing_placeholder, - "IntentBox": intent_box - } - - for name, _widget in names.items(): - _widget.setObjectName(name) - - # Enable CSS on plain QWidget objects - for _widget in ( - pages_widget, - header_widget, - body_widget, - comment_box, - overview_page, - terminal_page, - footer_widget, - button_suspend_logs, - footer_button_stop, - footer_button_reset, - footer_button_validate, - footer_button_play, - footer_spacer, - closing_placeholder - ): - _widget.setAttribute(QtCore.Qt.WA_StyledBackground) - - # Signals - header_tab_overview.toggled.connect( - lambda: self.on_tab_changed("overview") - ) - header_tab_terminal.toggled.connect( - lambda: self.on_tab_changed("terminal") - ) - - overview_instance_view.show_perspective.connect( - self.toggle_perspective_widget - ) - overview_plugin_view.show_perspective.connect( - self.toggle_perspective_widget - ) - - controller.switch_toggleability.connect(self.change_toggleability) - - controller.was_reset.connect(self.on_was_reset) - # This is called synchronously on each process - controller.was_processed.connect(self.on_was_processed) - controller.passed_group.connect(self.on_passed_group) - controller.was_stopped.connect(self.on_was_stopped) - controller.was_finished.connect(self.on_was_finished) - - controller.was_skipped.connect(self.on_was_skipped) - controller.was_acted.connect(self.on_was_acted) - - # NOTE: Listeners to this signal are run in the main thread - controller.about_to_process.connect( - self.on_about_to_process, - QtCore.Qt.DirectConnection - ) - - overview_instance_view.toggled.connect(self.on_instance_toggle) - overview_plugin_view.toggled.connect(self.on_plugin_toggle) - - button_suspend_logs.clicked.connect(self.on_suspend_clicked) - footer_button_stop.clicked.connect(self.on_stop_clicked) - footer_button_reset.clicked.connect(self.on_reset_clicked) - footer_button_validate.clicked.connect(self.on_validate_clicked) - footer_button_play.clicked.connect(self.on_play_clicked) - - comment_box.textChanged.connect(self.on_comment_entered) - comment_box.returnPressed.connect(self.on_play_clicked) - overview_plugin_view.customContextMenuRequested.connect( - self.on_plugin_action_menu_requested - ) - - instance_model.group_created.connect(self.on_instance_group_created) - - self.main_widget = main_widget - - self.pages_widget = pages_widget - self.header_widget = header_widget - self.body_widget = body_widget - - self.terminal_filters_widget = terminal_filters_widget - - self.footer_widget = footer_widget - self.button_suspend_logs = button_suspend_logs - self.footer_button_stop = footer_button_stop - self.footer_button_reset = footer_button_reset - self.footer_button_validate = footer_button_validate - self.footer_button_play = footer_button_play - - self.footer_info = footer_info - - self.overview_instance_view = overview_instance_view - self.overview_plugin_view = overview_plugin_view - self.plugin_model = plugin_model - self.plugin_proxy = plugin_proxy - self.instance_model = instance_model - self.instance_sort_proxy = instance_sort_proxy - - self.presets_button = presets_button - - self.terminal_model = terminal_model - self.terminal_proxy = terminal_proxy - self.terminal_view = terminal_view - - self.comment_main_widget = comment_intent_widget - self.comment_box = comment_box - self.intent_box = intent_box - self.intent_model = intent_model - - self.perspective_widget = perspective_widget - - self.tabs = { - "overview": header_tab_overview, - "terminal": header_tab_terminal - } - self.pages = ( - ("overview", overview_page), - ("terminal", terminal_page) - ) - - current_page = settings.InitialTab or "overview" - self.comment_main_widget.setVisible( - not current_page == "terminal" - ) - self.terminal_filters_widget.setVisible( - current_page == "terminal" - ) - - self._current_page = current_page - self._hidden_for_plugin_process = False - - self.tabs[current_page].setChecked(True) - - self.apply_log_suspend_value( - util.env_variable_to_bool("PYBLISH_SUSPEND_LOGS") - ) - - # ------------------------------------------------------------------------- - # - # Event handlers - # - # ------------------------------------------------------------------------- - def set_presets(self, key): - plugin_settings = self.controller.possible_presets.get(key) - if not plugin_settings: - return - - for plugin_item in self.plugin_model.plugin_items.values(): - if not plugin_item.plugin.optional: - continue - - value = plugin_settings.get( - plugin_item.plugin.__name__, - # if plugin is not in presets then set default value - self.controller.optional_default.get( - plugin_item.plugin.__name__ - ) - ) - if value is None: - continue - - plugin_item.setData(value, QtCore.Qt.CheckStateRole) - - def toggle_perspective_widget(self, index=None): - show = False - if index: - show = True - self.perspective_widget.set_context(index) - - self.pages_widget.setVisible(not show) - self.perspective_widget.setVisible(show) - self.footer_items_visibility() - - def change_toggleability(self, enable_value): - for plugin_item in self.plugin_model.plugin_items.values(): - plugin_item.setData(enable_value, Roles.IsEnabledRole) - - for instance_item in ( - self.instance_model.instance_items.values() - ): - instance_item.setData(enable_value, Roles.IsEnabledRole) - - def _add_intent_to_context(self): - context_value = None - if ( - self.intent_model.has_items - and "intent" not in self.controller.context.data - ): - idx = self.intent_model.index(self.intent_box.currentIndex(), 0) - intent_value = self.intent_model.data(idx, Roles.IntentItemValue) - intent_label = self.intent_model.data(idx, QtCore.Qt.DisplayRole) - if intent_value: - context_value = { - "value": intent_value, - "label": intent_label - } - - # Unset intent if is set to empty value - if context_value is None: - self.controller.context.data.pop("intent", None) - else: - self.controller.context.data["intent"] = context_value - - def on_instance_toggle(self, index, state=None): - """An item is requesting to be toggled""" - if not index.data(Roles.IsOptionalRole): - return self.info("This item is mandatory") - - if self.controller.collect_state != 1: - return self.info("Cannot toggle") - - current_state = index.data(QtCore.Qt.CheckStateRole) - if state is None: - state = not current_state - - instance_id = index.data(Roles.ObjectIdRole) - instance_item = self.instance_model.instance_items[instance_id] - instance_item.setData(state, QtCore.Qt.CheckStateRole) - - self.controller.instance_toggled.emit( - instance_item.instance, current_state, state - ) - - self.update_compatibility() - - def on_instance_group_created(self, index): - _index = self.instance_sort_proxy.mapFromSource(index) - self.overview_instance_view.expand(_index) - - def on_plugin_toggle(self, index, state=None): - """An item is requesting to be toggled""" - if not index.data(Roles.IsOptionalRole): - return self.info("This item is mandatory") - - if self.controller.collect_state != 1: - return self.info("Cannot toggle") - - if state is None: - state = not index.data(QtCore.Qt.CheckStateRole) - - plugin_id = index.data(Roles.ObjectIdRole) - plugin_item = self.plugin_model.plugin_items[plugin_id] - plugin_item.setData(state, QtCore.Qt.CheckStateRole) - - self.update_compatibility() - - def on_tab_changed(self, target): - previous_page = None - target_page = None - direction = None - for name, page in self.pages: - if name == target: - target_page = page - if direction is None: - direction = -1 - elif name == self._current_page: - previous_page = page - if direction is None: - direction = 1 - else: - page.setVisible(False) - - self._current_page = target - self.slide_page(previous_page, target_page, direction) - - def slide_page(self, previous_page, target_page, direction): - if previous_page is None: - for name, page in self.pages: - for _name, _page in self.pages: - if name != _name: - _page.hide() - page.show() - page.hide() - - if ( - previous_page == target_page - or previous_page is None - ): - if not target_page.isVisible(): - target_page.show() - return - - if not settings.Animated: - previous_page.setVisible(False) - target_page.setVisible(True) - return - - width = previous_page.frameGeometry().width() - offset = QtCore.QPoint(direction * width, 0) - - previous_rect = ( - previous_page.frameGeometry().x(), - previous_page.frameGeometry().y(), - width, - previous_page.frameGeometry().height() - ) - curr_pos = previous_page.pos() - - previous_page.hide() - target_page.show() - target_page.update() - target_rect = ( - target_page.frameGeometry().x(), - target_page.frameGeometry().y(), - target_page.frameGeometry().width(), - target_page.frameGeometry().height() - ) - previous_page.show() - - target_page.raise_() - previous_page.setGeometry(*previous_rect) - target_page.setGeometry(*target_rect) - - target_page.move(curr_pos + offset) - - duration = 250 - - anim_old = QtCore.QPropertyAnimation( - previous_page, b"pos", self - ) - anim_old.setDuration(duration) - anim_old.setStartValue(curr_pos) - anim_old.setEndValue(curr_pos - offset) - anim_old.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - anim_new = QtCore.QPropertyAnimation( - target_page, b"pos", self - ) - anim_new.setDuration(duration) - anim_new.setStartValue(curr_pos + offset) - anim_new.setEndValue(curr_pos) - anim_new.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - anim_group = QtCore.QParallelAnimationGroup(self) - anim_group.addAnimation(anim_old) - anim_group.addAnimation(anim_new) - - def slide_finished(): - previous_page.hide() - self.footer_items_visibility() - - anim_group.finished.connect(slide_finished) - anim_group.start() - - def footer_items_visibility( - self, - comment_visible=None, - terminal_filters_visibile=None - ): - target = self._current_page - comment_visibility = ( - not self.perspective_widget.isVisible() - and not target == "terminal" - and self.comment_box.isEnabled() - ) - terminal_filters_visibility = ( - target == "terminal" - or self.perspective_widget.isVisible() - ) - - if comment_visible is not None and comment_visibility: - comment_visibility = comment_visible - - if ( - terminal_filters_visibile is not None - and terminal_filters_visibility - ): - terminal_filters_visibility = terminal_filters_visibile - - duration = 150 - - hiding_widgets = [] - showing_widgets = [] - if (comment_visibility != ( - self.comment_main_widget.isVisible() - )): - if self.comment_main_widget.isVisible(): - hiding_widgets.append(self.comment_main_widget) - else: - showing_widgets.append(self.comment_main_widget) - - if (terminal_filters_visibility != ( - self.terminal_filters_widget.isVisible() - )): - if self.terminal_filters_widget.isVisible(): - hiding_widgets.append(self.terminal_filters_widget) - else: - showing_widgets.append(self.terminal_filters_widget) - - if not hiding_widgets and not showing_widgets: - return - - hiding_widgets_queue = Queue() - showing_widgets_queue = Queue() - widgets_by_pos_y = {} - for widget in hiding_widgets: - key = widget.mapToGlobal(widget.rect().topLeft()).x() - widgets_by_pos_y[key] = widget - - for key in sorted(widgets_by_pos_y.keys()): - widget = widgets_by_pos_y[key] - hiding_widgets_queue.put((widget, )) - - for widget in hiding_widgets: - widget.hide() - - for widget in showing_widgets: - widget.show() - - self.footer_widget.updateGeometry() - widgets_by_pos_y = {} - for widget in showing_widgets: - key = widget.mapToGlobal(widget.rect().topLeft()).x() - widgets_by_pos_y[key] = widget - - for key in reversed(sorted(widgets_by_pos_y.keys())): - widget = widgets_by_pos_y[key] - showing_widgets_queue.put(widget) - - for widget in showing_widgets: - widget.hide() - - for widget in hiding_widgets: - widget.show() - - def process_showing(): - if showing_widgets_queue.empty(): - return - - widget = showing_widgets_queue.get() - widget.show() - - widget_rect = widget.frameGeometry() - second_rect = QtCore.QRect(widget_rect) - second_rect.setTopLeft(second_rect.bottomLeft()) - - animation = QtCore.QPropertyAnimation( - widget, b"geometry", self - ) - animation.setDuration(duration) - animation.setStartValue(second_rect) - animation.setEndValue(widget_rect) - animation.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - animation.finished.connect(process_showing) - animation.start() - - def process_hiding(): - if hiding_widgets_queue.empty(): - return process_showing() - - item = hiding_widgets_queue.get() - if isinstance(item, tuple): - widget = item[0] - hiding_widgets_queue.put(widget) - widget_rect = widget.frameGeometry() - second_rect = QtCore.QRect(widget_rect) - second_rect.setTopLeft(second_rect.bottomLeft()) - - anim = QtCore.QPropertyAnimation( - widget, b"geometry", self - ) - anim.setDuration(duration) - anim.setStartValue(widget_rect) - anim.setEndValue(second_rect) - anim.setEasingCurve(QtCore.QEasingCurve.OutQuad) - - anim.finished.connect(process_hiding) - anim.start() - else: - item.hide() - return process_hiding() - - process_hiding() - - def on_validate_clicked(self): - self.comment_box.setEnabled(False) - self.footer_items_visibility() - self.intent_box.setEnabled(False) - - self._add_intent_to_context() - - self.validate() - - def on_play_clicked(self): - self.comment_box.setEnabled(False) - self.footer_items_visibility() - self.intent_box.setEnabled(False) - - self._add_intent_to_context() - - self.publish() - - def on_reset_clicked(self): - self.reset() - - def on_stop_clicked(self): - self.info("Stopping..") - self.controller.stop() - - # TODO checks - self.footer_button_reset.setEnabled(True) - self.footer_button_play.setEnabled(False) - self.footer_button_stop.setEnabled(False) - - def on_suspend_clicked(self, value=None): - self.apply_log_suspend_value(not self._suspend_logs) - - def apply_log_suspend_value(self, value): - self._suspend_logs = value - if self._current_page == "terminal": - self.tabs["overview"].setChecked(True) - - self.tabs["terminal"].setVisible(not self._suspend_logs) - - def on_comment_entered(self): - """The user has typed a comment.""" - self.controller.context.data["comment"] = self.comment_box.text() - - def on_about_to_process(self, plugin, instance): - """Reflect currently running pair in GUI""" - if instance is None: - instance_id = self.controller.context.id - else: - instance_id = instance.id - - instance_item = ( - self.instance_model.instance_items[instance_id] - ) - instance_item.setData( - {InstanceStates.InProgress: True}, - Roles.PublishFlagsRole - ) - - plugin_item = self.plugin_model.plugin_items[plugin._id] - plugin_item.setData( - {PluginStates.InProgress: True}, - Roles.PublishFlagsRole - ) - - self.info("{} {}".format( - self.tr("Processing"), plugin_item.data(QtCore.Qt.DisplayRole) - )) - - visibility = True - if hasattr(plugin, "hide_ui_on_process") and plugin.hide_ui_on_process: - visibility = False - self._hidden_for_plugin_process = not visibility - - self._ensure_visible(visibility) - - def _ensure_visible(self, visible): - if self.isVisible() == visible: - return - - if not visible: - self.setVisible(visible) - else: - self.show() - self.raise_() - self.activateWindow() - self.showNormal() - - def on_plugin_action_menu_requested(self, pos): - """The user right-clicked on a plug-in - __________ - | | - | Action 1 | - | Action 2 | - | Action 3 | - | | - |__________| - - """ - - index = self.overview_plugin_view.indexAt(pos) - actions = index.data(Roles.PluginValidActionsRole) - - if not actions: - return - - menu = QtWidgets.QMenu(self) - plugin_id = index.data(Roles.ObjectIdRole) - plugin_item = self.plugin_model.plugin_items[plugin_id] - print("plugin is: %s" % plugin_item.plugin) - - for action in actions: - qaction = QtWidgets.QAction(action.label or action.__name__, self) - qaction.triggered.connect(partial(self.act, plugin_item, action)) - menu.addAction(qaction) - - menu.popup(self.overview_plugin_view.viewport().mapToGlobal(pos)) - - def update_compatibility(self): - self.plugin_model.update_compatibility() - self.plugin_proxy.invalidateFilter() - - def on_was_reset(self): - # Append context object to instances model - self.instance_model.append(self.controller.context) - - for plugin in self.controller.plugins: - self.plugin_model.append(plugin) - - self.overview_instance_view.expandAll() - self.overview_plugin_view.expandAll() - - self.presets_button.clearMenu() - if self.controller.possible_presets: - self.presets_button.setEnabled(True) - for key in self.controller.possible_presets: - self.presets_button.addItem( - key, partial(self.set_presets, key) - ) - - self.instance_model.restore_checkstates() - self.plugin_model.restore_checkstates() - - self.perspective_widget.reset() - - # Append placeholder comment from Context - # This allows users to inject a comment from elsewhere, - # or to perhaps provide a placeholder comment/template - # for artists to fill in. - comment = self.controller.context.data.get("comment") - self.comment_box.setText(comment or None) - self.comment_box.setEnabled(True) - self.footer_items_visibility() - - self.intent_box.setEnabled(True) - - # Refresh tab - self.on_tab_changed(self._current_page) - self.update_compatibility() - - self.button_suspend_logs.setEnabled(False) - - self.footer_button_validate.setEnabled(False) - self.footer_button_reset.setEnabled(False) - self.footer_button_stop.setEnabled(True) - self.footer_button_play.setEnabled(False) - - self._update_state() - - def on_passed_group(self, order): - for group_item in self.instance_model.group_items.values(): - group_index = self.instance_sort_proxy.mapFromSource( - group_item.index() - ) - if self.overview_instance_view.isExpanded(group_index): - continue - - if group_item.publish_states & GroupStates.HasError: - self.overview_instance_view.expand(group_index) - - for group_item in self.plugin_model.group_items.values(): - # TODO check only plugins from the group - if group_item.publish_states & GroupStates.HasFinished: - continue - - if order != group_item.order: - continue - - group_index = self.plugin_proxy.mapFromSource(group_item.index()) - if group_item.publish_states & GroupStates.HasError: - self.overview_plugin_view.expand(group_index) - continue - - group_item.setData( - {GroupStates.HasFinished: True}, - Roles.PublishFlagsRole - ) - self.overview_plugin_view.setAnimated(False) - self.overview_plugin_view.collapse(group_index) - - self._update_state() - - def on_was_stopped(self): - self.overview_plugin_view.setAnimated(settings.Animated) - errored = self.controller.errored - if self.controller.collect_state == 0: - self.footer_button_play.setEnabled(False) - self.footer_button_validate.setEnabled(False) - else: - self.footer_button_play.setEnabled(not errored) - self.footer_button_validate.setEnabled( - not errored and not self.controller.validated - ) - self.footer_button_play.setFocus() - - self.footer_button_reset.setEnabled(True) - self.footer_button_stop.setEnabled(False) - if errored: - self.footer_widget.setProperty("success", 0) - self.footer_widget.style().polish(self.footer_widget) - - suspend_log_bool = ( - self.controller.collect_state == 1 - and not self.controller.stopped - ) - self.button_suspend_logs.setEnabled(suspend_log_bool) - - self._update_state() - - if self._hidden_for_plugin_process: - self._hidden_for_plugin_process = False - self._ensure_visible(True) - - def on_was_skipped(self, plugin): - plugin_item = self.plugin_model.plugin_items[plugin.id] - plugin_item.setData( - {PluginStates.WasSkipped: True}, - Roles.PublishFlagsRole - ) - - def on_was_finished(self): - self.overview_plugin_view.setAnimated(settings.Animated) - self.footer_button_play.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_reset.setEnabled(True) - self.footer_button_stop.setEnabled(False) - - if self.controller.errored: - success_val = 0 - self.info(self.tr("Stopped due to error(s), see Terminal.")) - self.comment_box.setEnabled(False) - self.intent_box.setEnabled(False) - - else: - success_val = 1 - self.info(self.tr("Finished successfully!")) - - self.footer_widget.setProperty("success", success_val) - self.footer_widget.style().polish(self.footer_widget) - - for instance_item in ( - self.instance_model.instance_items.values() - ): - instance_item.setData( - {InstanceStates.HasFinished: True}, - Roles.PublishFlagsRole - ) - - for group_item in self.instance_model.group_items.values(): - group_item.setData( - {GroupStates.HasFinished: True}, - Roles.PublishFlagsRole - ) - - self.update_compatibility() - self._update_state() - - def on_was_processed(self, result): - existing_ids = set(self.instance_model.instance_items.keys()) - existing_ids.remove(self.controller.context.id) - for instance in self.controller.context: - if instance.id not in existing_ids: - self.instance_model.append(instance) - else: - existing_ids.remove(instance.id) - - for instance_id in existing_ids: - self.instance_model.remove(instance_id) - - result["records"] = self.terminal_model.prepare_records( - result, - self._suspend_logs - ) - - plugin_item = self.plugin_model.update_with_result(result) - instance_item = self.instance_model.update_with_result(result) - - self.terminal_model.update_with_result(result) - - self.update_compatibility() - - if self.perspective_widget.isVisible(): - self.perspective_widget.update_context( - plugin_item, instance_item - ) - - if self._hidden_for_plugin_process: - self._hidden_for_plugin_process = False - self._ensure_visible(True) - - # ------------------------------------------------------------------------- - # - # Functions - # - # ------------------------------------------------------------------------- - - def reset(self): - """Prepare GUI for reset""" - self.info(self.tr("About to reset..")) - - self.presets_button.setEnabled(False) - self.footer_widget.setProperty("success", -1) - self.footer_widget.style().polish(self.footer_widget) - - self.instance_model.store_checkstates() - self.plugin_model.store_checkstates() - - # Reset current ids to secure no previous instances get mixed in. - self.instance_model.reset() - self.plugin_model.reset() - self.intent_model.reset() - self.terminal_model.reset() - - self.footer_button_stop.setEnabled(False) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - self.intent_box.setVisible(self.intent_model.has_items) - if self.intent_model.has_items: - self.intent_box.setCurrentIndex(self.intent_model.default_index) - - self.comment_box.placeholder.setVisible(False) - # Launch controller reset - self.controller.reset() - if not self.comment_box.text(): - self.comment_box.placeholder.setVisible(True) - - def validate(self): - self.info(self.tr("Preparing validate..")) - self.footer_button_stop.setEnabled(True) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - self.button_suspend_logs.setEnabled(False) - - self.controller.validate() - - self._update_state() - - def publish(self): - self.info(self.tr("Preparing publish..")) - self.footer_button_stop.setEnabled(True) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - self.button_suspend_logs.setEnabled(False) - - self.controller.publish() - - self._update_state() - - def act(self, plugin_item, action): - self.info("%s %s.." % (self.tr("Preparing"), action)) - - self.footer_button_stop.setEnabled(True) - self.footer_button_reset.setEnabled(False) - self.footer_button_validate.setEnabled(False) - self.footer_button_play.setEnabled(False) - - # Cause view to update, but it won't visually - # happen until Qt is given time to idle.. - plugin_item.setData( - PluginActionStates.InProgress, Roles.PluginActionProgressRole - ) - - # Give Qt time to draw - self.controller.act(plugin_item.plugin, action) - - self.info(self.tr("Action prepared.")) - - def on_was_acted(self, result): - self.footer_button_reset.setEnabled(True) - self.footer_button_stop.setEnabled(False) - - # Update action with result - plugin_item = self.plugin_model.plugin_items[result["plugin"].id] - action_state = plugin_item.data(Roles.PluginActionProgressRole) - action_state |= PluginActionStates.HasFinished - result["records"] = self.terminal_model.prepare_records( - result, - self._suspend_logs - ) - - if result.get("error"): - action_state |= PluginActionStates.HasFailed - - plugin_item.setData(action_state, Roles.PluginActionProgressRole) - - self.terminal_model.update_with_result(result) - plugin_item = self.plugin_model.update_with_result(result) - instance_item = self.instance_model.update_with_result(result) - - if self.perspective_widget.isVisible(): - self.perspective_widget.update_context( - plugin_item, instance_item - ) - - def closeEvent(self, event): - """Perform post-flight checks before closing - - Make sure processing of any kind is wrapped up before closing - - """ - - self.info(self.tr("Closing..")) - - if self.controller.is_running: - self.info(self.tr("..as soon as processing is finished..")) - self.controller.stop() - - self.info(self.tr("Cleaning up controller..")) - self.controller.cleanup() - - self.overview_instance_view.setModel(None) - self.overview_plugin_view.setModel(None) - self.terminal_view.setModel(None) - - event.accept() - - def reject(self): - """Handle ESC key""" - - if self.controller.is_running: - self.info(self.tr("Stopping..")) - self.controller.stop() - - # ------------------------------------------------------------------------- - # - # Feedback - # - # ------------------------------------------------------------------------- - - def _update_state(self): - self.footer_info.setText(self.controller.current_state) - - def info(self, message): - """Print user-facing information - - Arguments: - message (str): Text message for the user - - """ - # Include message in terminal - self.terminal_model.append([{ - "label": message, - "type": "info" - }]) - - if settings.PrintInfo: - # Print message to console - util.u_print(message) - - def warning(self, message): - """Block processing and print warning until user hits "Continue" - - Arguments: - message (str): Message to display - - """ - - # TODO(marcus): Implement this. - self.info(message) - - def heads_up(self, title, message, command=None): - """Provide a front-and-center message with optional command - - Arguments: - title (str): Bold and short message - message (str): Extended message - command (optional, callable): Function is provided as a button - - """ - - # TODO(marcus): Implement this. - self.info(message) From d473118d392c4f78618ceaece50538251870d7dd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Mar 2025 18:19:34 +0100 Subject: [PATCH 456/463] Used clique method Co-authored-by: Roy Nieterau --- client/ayon_core/plugins/publish/extract_color_transcode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_color_transcode.py b/client/ayon_core/plugins/publish/extract_color_transcode.py index 22997dc986..1f2c2a89af 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -280,8 +280,7 @@ class ExtractOIIOTranscode(publish.Extractor): collection = collections[0] frames = list(collection.indexes) - real_range = list(range(frames[0], frames[-1] + 1)) - if set(frames) != set(real_range): # check for gaps + if collection.holes(): return files_to_convert frame_str = "{}-{}#".format(frames[0], frames[-1]) From 58e77209b3556459826d19ae9288b31cd167eeac Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 11 Mar 2025 17:50:15 +0000 Subject: [PATCH 457/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index e533e08fe4..1157c53d56 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.3+dev" +__version__ = "1.1.4" diff --git a/package.py b/package.py index 02e2f25384..0ad98648d9 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.3+dev" +version = "1.1.4" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index f065ca0c39..a16d0ef4a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.3+dev" +version = "1.1.4" description = "" authors = ["Ynput Team "] readme = "README.md" From fbdc70f396cfebc4f1ed6a540e8d85cbeb0b5a56 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 11 Mar 2025 17:50:55 +0000 Subject: [PATCH 458/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 1157c53d56..332001aef7 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.4" +__version__ = "1.1.4+dev" diff --git a/package.py b/package.py index 0ad98648d9..2c9072d443 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.4" +version = "1.1.4+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index a16d0ef4a3..0bbe0f90e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.4" +version = "1.1.4+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From bb625ffef448e63be53e1e0ba9c364b6c2b40dda Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 17:39:16 +0100 Subject: [PATCH 459/463] LoaderPlugin takes no arguments on initialization --- client/ayon_core/pipeline/load/plugins.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1fb906fd65..b601914acd 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -13,15 +13,7 @@ from .utils import get_representation_path_from_context class LoaderPlugin(list): - """Load representation into host application - - Arguments: - context (dict): avalon-core:context-1.0 - - .. versionadded:: 4.0 - This class was introduced - - """ + """Load representation into host application""" product_types = set() representations = set() From 00214c1defcd1ed6556eb278f338234e6f5ba672 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 21:53:13 +0100 Subject: [PATCH 460/463] Remove `python_2_comp.py` (we do not support Py2 for quite some time now) --- client/ayon_core/lib/python_2_comp.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 client/ayon_core/lib/python_2_comp.py diff --git a/client/ayon_core/lib/python_2_comp.py b/client/ayon_core/lib/python_2_comp.py deleted file mode 100644 index 900db59062..0000000000 --- a/client/ayon_core/lib/python_2_comp.py +++ /dev/null @@ -1,17 +0,0 @@ -# Deprecated file -# - the file container 'WeakMethod' implementation for Python 2 which is not -# needed anymore. -import warnings -import weakref - - -WeakMethod = weakref.WeakMethod - -warnings.warn( - ( - "'ayon_core.lib.python_2_comp' is deprecated." - "Please use 'weakref.WeakMethod'." - ), - DeprecationWarning, - stacklevel=2 -) From 60c9229f41a604243a71c4ffe65bf6e5b0b7e686 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:47:08 +0100 Subject: [PATCH 461/463] use differnt varible name for aov frames --- client/ayon_core/pipeline/farm/pyblish_functions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index 0261a0c2b5..c6f3ae7115 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -808,14 +808,14 @@ def _create_instances_for_aov( frames_to_render is not None and isinstance(collected_files, (list, tuple)) # not single file ): - frames_to_render = convert_frames_str_to_list(frames_to_render) + aov_frames_to_render = convert_frames_str_to_list(frames_to_render) collections, _ = clique.assemble(collected_files) collected_files = _get_real_files_to_render( - collections[0], frames_to_render) + collections[0], aov_frames_to_render) else: frame_start = int(skeleton.get("frameStartHandle")) frame_end = int(skeleton.get("frameEndHandle")) - frames_to_render = list(range(frame_start, frame_end + 1)) + aov_frames_to_render = list(range(frame_start, frame_end + 1)) dynamic_data = { "aov": aov, @@ -937,8 +937,8 @@ def _create_instances_for_aov( "name": ext, "ext": ext, "files": collected_files, - "frameStart": frames_to_render[0], - "frameEnd": frames_to_render[-1], + "frameStart": aov_frames_to_render[0], + "frameEnd": aov_frames_to_render[-1], # If expectedFile are absolute, we need only filenames "stagingDir": staging_dir, "fps": new_instance.get("fps"), From 4a7f79a7bad5b533eb9112ab3ea0ada70a243566 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 13 Mar 2025 16:06:47 +0000 Subject: [PATCH 462/463] [Automated] Add generated package files from main --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 332001aef7..7717a7a215 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.4+dev" +__version__ = "1.1.5" diff --git a/package.py b/package.py index 2c9072d443..69e4b85ef2 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.4+dev" +version = "1.1.5" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 0bbe0f90e1..eca50f349d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.4+dev" +version = "1.1.5" description = "" authors = ["Ynput Team "] readme = "README.md" From 7c4fb2d2e2f317a3f63c475fee6d4018e5ced990 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Thu, 13 Mar 2025 16:07:28 +0000 Subject: [PATCH 463/463] [Automated] Update version in package.py for develop --- client/ayon_core/version.py | 2 +- package.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 7717a7a215..de5c199428 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'core' version.""" -__version__ = "1.1.5" +__version__ = "1.1.5+dev" diff --git a/package.py b/package.py index 69e4b85ef2..250b77fe52 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.5" +version = "1.1.5+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index eca50f349d..94badd2f1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.5" +version = "1.1.5+dev" description = "" authors = ["Ynput Team "] readme = "README.md"

r{ZX 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 diff --git a/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBold.ttf b/client/ayon_core/tools/pyblish_pype/font/opensans/OpenSans-ExtraBold.ttf deleted file mode 100644 index 21f6f84a0799946fc4ae02c52b27e61c3762c745..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222584 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