From 95743f29a3e67baa6d39f1975fd001575c8abdfb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 14:35:23 +0100 Subject: [PATCH 01/70] Add ACES 1.3 Studio OCIO config option - Introduced new OCIO config for ACES 1.3 Studio. - Updated paths with relevant labels and descriptions. - Ensured compatibility with OCIO v2 requirements. --- server/settings/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/settings/main.py b/server/settings/main.py index 249bab85fd..c2f5c63f42 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -71,6 +71,12 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ + { + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", + "label": "ACES 1.3 Studio (OCIO v2)", + "description": ( + "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") + }, { "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "label": "ACES 1.2", From 8ff258983a54a7f665a12460e05eecfaf7a4426c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 30 Jan 2025 15:01:11 +0100 Subject: [PATCH 02/70] Update OCIO config path for clarity Refined the OCIO built-in paths to improve readability. Added a comment to ignore line length warning for better code style adherence. --- server/settings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/main.py b/server/settings/main.py index c2f5c63f42..261bd7fc04 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,7 +72,7 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") From d0e591067156b381c3ced96ed6703b013aeced7d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 17:47:21 +0100 Subject: [PATCH 03/70] Remove long deprecated `LoaderPlugin.fname` property Make sure to remove all usages of `self.fname` from any `LoaderPlugin` in your addons --- client/ayon_core/pipeline/load/plugins.py | 13 ------------- client/ayon_core/pipeline/load/utils.py | 6 ------ 2 files changed, 19 deletions(-) diff --git a/client/ayon_core/pipeline/load/plugins.py b/client/ayon_core/pipeline/load/plugins.py index 1fb906fd65..6075916369 100644 --- a/client/ayon_core/pipeline/load/plugins.py +++ b/client/ayon_core/pipeline/load/plugins.py @@ -229,19 +229,6 @@ class LoaderPlugin(list): """ return cls.options or [] - @property - def fname(self): - """Backwards compatibility with deprecation warning""" - - self.log.warning(( - "DEPRECATION WARNING: Source - Loader plugin {}." - " The 'fname' property on the Loader plugin will be removed in" - " future versions of OpenPype. Planned version to drop the support" - " is 3.16.6 or 3.17.0." - ).format(self.__class__.__name__)) - if hasattr(self, "_fname"): - return self._fname - @classmethod def get_representation_name_aliases(cls, representation_name: str): """Return representation names to which switching is allowed from diff --git a/client/ayon_core/pipeline/load/utils.py b/client/ayon_core/pipeline/load/utils.py index de8e1676e7..b130161190 100644 --- a/client/ayon_core/pipeline/load/utils.py +++ b/client/ayon_core/pipeline/load/utils.py @@ -316,12 +316,6 @@ def load_with_repre_context( ) loader = Loader() - - # Backwards compatibility: Originally the loader's __init__ required the - # representation context to set `fname` attribute to the filename to load - # Deprecated - to be removed in OpenPype 3.16.6 or 3.17.0. - loader._fname = get_representation_path_from_context(repre_context) - return loader.load(repre_context, name, namespace, options) From ffc76e639a5d516140950da3c9a2c3a9d305c773 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 21:50:06 +0100 Subject: [PATCH 04/70] Remove deprecated `convert_for_ffmpeg` --- client/ayon_core/lib/__init__.py | 2 - client/ayon_core/lib/transcoding.py | 135 +--------------------------- 2 files changed, 2 insertions(+), 135 deletions(-) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index 03ed574081..0c64b88d11 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -97,7 +97,6 @@ from .profiles_filtering import ( from .transcoding import ( get_transcode_temp_directory, should_convert_for_ffmpeg, - convert_for_ffmpeg, convert_input_paths_for_ffmpeg, get_ffprobe_data, get_ffprobe_streams, @@ -196,7 +195,6 @@ __all__ = [ "get_transcode_temp_directory", "should_convert_for_ffmpeg", - "convert_for_ffmpeg", "convert_input_paths_for_ffmpeg", "get_ffprobe_data", "get_ffprobe_streams", diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 1fda014bd8..c4030b3f97 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -526,137 +526,6 @@ def should_convert_for_ffmpeg(src_filepath): return False -# Deprecated since 2022 4 20 -# - Reason - Doesn't convert sequences right way: Can't handle gaps, reuse -# first frame for all frames and changes filenames when input -# is sequence. -# - use 'convert_input_paths_for_ffmpeg' instead -def convert_for_ffmpeg( - first_input_path, - output_dir, - input_frame_start=None, - input_frame_end=None, - logger=None -): - """Convert source file to format supported in ffmpeg. - - Currently can convert only exrs. - - Args: - first_input_path (str): Path to first file of a sequence or a single - file path for non-sequential input. - output_dir (str): Path to directory where output will be rendered. - Must not be same as input's directory. - input_frame_start (int): Frame start of input. - input_frame_end (int): Frame end of input. - logger (logging.Logger): Logger used for logging. - - Raises: - ValueError: If input filepath has extension not supported by function. - Currently is supported only ".exr" extension. - """ - if logger is None: - logger = logging.getLogger(__name__) - - logger.warning(( - "DEPRECATED: 'ayon_core.lib.transcoding.convert_for_ffmpeg' is" - " deprecated function of conversion for FFMpeg. Please replace usage" - " with 'ayon_core.lib.transcoding.convert_input_paths_for_ffmpeg'" - )) - - ext = os.path.splitext(first_input_path)[1].lower() - if ext != ".exr": - raise ValueError(( - "Function 'convert_for_ffmpeg' currently support only" - " \".exr\" extension. Got \"{}\"." - ).format(ext)) - - is_sequence = False - if input_frame_start is not None and input_frame_end is not None: - is_sequence = int(input_frame_end) != int(input_frame_start) - - input_info = get_oiio_info_for_input(first_input_path, logger=logger) - - # Change compression only if source compression is "dwaa" or "dwab" - # - they're not supported in ffmpeg - compression = input_info["attribs"].get("compression") - if compression in ("dwaa", "dwab"): - compression = "none" - - # Prepare subprocess arguments - oiio_cmd = get_oiio_tool_args( - "oiiotool", - # Don't add any additional attributes - "--nosoftwareattrib", - ) - # Add input compression if available - if compression: - oiio_cmd.extend(["--compression", compression]) - - # Collect channels to export - input_arg, channels_arg = get_oiio_input_and_channel_args(input_info) - - oiio_cmd.extend([ - input_arg, first_input_path, - # Tell oiiotool which channels should be put to top stack (and output) - "--ch", channels_arg, - # Use first subimage - "--subimage", "0" - ]) - - # Add frame definitions to arguments - if is_sequence: - oiio_cmd.extend([ - "--frames", "{}-{}".format(input_frame_start, input_frame_end) - ]) - - for attr_name, attr_value in input_info["attribs"].items(): - if not isinstance(attr_value, str): - continue - - # Remove attributes that have string value longer than allowed length - # for ffmpeg or when contain prohibited symbols - erase_reason = "Missing reason" - erase_attribute = False - if len(attr_value) > MAX_FFMPEG_STRING_LEN: - erase_reason = "has too long value ({} chars).".format( - len(attr_value) - ) - erase_attribute = True - - if not erase_attribute: - for char in NOT_ALLOWED_FFMPEG_CHARS: - if char in attr_value: - erase_attribute = True - erase_reason = ( - "contains unsupported character \"{}\"." - ).format(char) - break - - if erase_attribute: - # Set attribute to empty string - logger.info(( - "Removed attribute \"{}\" from metadata because {}." - ).format(attr_name, erase_reason)) - oiio_cmd.extend(["--eraseattrib", attr_name]) - - # Add last argument - path to output - if is_sequence: - ext = os.path.splitext(first_input_path)[1] - base_filename = "tmp.%{:0>2}d{}".format( - len(str(input_frame_end)), ext - ) - else: - base_filename = os.path.basename(first_input_path) - output_path = os.path.join(output_dir, base_filename) - oiio_cmd.extend([ - "-o", output_path - ]) - - logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) - run_subprocess(oiio_cmd, logger=logger) - - def convert_input_paths_for_ffmpeg( input_paths, output_dir, @@ -664,7 +533,7 @@ def convert_input_paths_for_ffmpeg( ): """Convert source file to format supported in ffmpeg. - Currently can convert only exrs. The input filepaths should be files + Can currently convert only EXRs. The input filepaths should be files with same type. Information about input is loaded only from first found file. @@ -692,7 +561,7 @@ def convert_input_paths_for_ffmpeg( if ext != ".exr": raise ValueError(( - "Function 'convert_for_ffmpeg' currently support only" + "Function 'convert_input_paths_for_ffmpeg' currently only supports" " \".exr\" extension. Got \"{}\"." ).format(ext)) From 12f6d76043986b8965bd6fc8d6402299b9bf52ea Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Mar 2025 22:01:21 +0100 Subject: [PATCH 05/70] Remove deprecated `StdOutBroker` import fallback --- client/ayon_core/tools/stdout_broker/app.py | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 client/ayon_core/tools/stdout_broker/app.py diff --git a/client/ayon_core/tools/stdout_broker/app.py b/client/ayon_core/tools/stdout_broker/app.py deleted file mode 100644 index ae73db1bb9..0000000000 --- a/client/ayon_core/tools/stdout_broker/app.py +++ /dev/null @@ -1,12 +0,0 @@ -import warnings -from .broker import StdOutBroker - -warnings.warn( - ( - "Import of 'StdOutBroker' from 'ayon_core.tools.stdout_broker.app'" - " is deprecated. Please use 'ayon_core.tools.stdout_broker' instead." - ), - DeprecationWarning -) - -__all__ = ("StdOutBroker", ) From 5540e7923a5f3dc2cc6179deccc783e9c33888c0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Mar 2025 10:59:38 +0100 Subject: [PATCH 06/70] Update client/ayon_core/lib/transcoding.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/lib/transcoding.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index c4030b3f97..3e77f39a8f 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -561,9 +561,9 @@ def convert_input_paths_for_ffmpeg( if ext != ".exr": raise ValueError(( - "Function 'convert_input_paths_for_ffmpeg' currently only supports" - " \".exr\" extension. Got \"{}\"." - ).format(ext)) + "Function 'convert_input_paths_for_ffmpeg' currently supports" + f" only \".exr\" extension. Got \"{ext}\"." + )) input_info = get_oiio_info_for_input(first_input_path, logger=logger) From 8424ad39078b3610894b0e53142a9b01ea6c5811 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Mar 2025 11:00:59 +0100 Subject: [PATCH 07/70] Remove redundant brackets --- client/ayon_core/lib/transcoding.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 3e77f39a8f..8c84e1c4dc 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -560,10 +560,10 @@ def convert_input_paths_for_ffmpeg( ext = os.path.splitext(first_input_path)[1].lower() if ext != ".exr": - raise ValueError(( + raise ValueError( "Function 'convert_input_paths_for_ffmpeg' currently supports" f" only \".exr\" extension. Got \"{ext}\"." - )) + ) input_info = get_oiio_info_for_input(first_input_path, logger=logger) From 4283d0b4534a93d1a79c8c86350eb348aef0904e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 27 Mar 2025 12:59:15 +0100 Subject: [PATCH 08/70] Allow disabling removal of rendered files on farm renders With this disabled the `metadata.json` and work area renders will remain on disk after a publish even if the folder is not a persistent staging dir --- .../plugins/publish/collect_rendered_files.py | 7 +++++-- server/settings/publish_plugins.py | 21 +++++++++++++++++++ 2 files changed, 26 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 deecf7ba24..c69dddd6ec 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -31,6 +31,9 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): # Keep "filesequence" for backwards compatibility of older jobs targets = ["filesequence", "farm"] label = "Collect rendered frames" + settings_category = "core" + + remove_files = True _context = None @@ -120,7 +123,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): self._fill_staging_dir(repre_data, anatomy) representations.append(repre_data) - if not staging_dir_persistent: + if self.remove_files and not staging_dir_persistent: add_repre_files_for_cleanup(instance, repre_data) instance.data["representations"] = representations @@ -170,7 +173,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): os.environ.update(session_data) staging_dir_persistent = self._process_path(data, anatomy) - if not staging_dir_persistent: + if self.remove_files and not staging_dir_persistent: context.data["cleanupFullPaths"].append(path) context.data["cleanupEmptyDirs"].append( os.path.dirname(path) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index c9c66e65d9..029eab5fc4 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -925,6 +925,20 @@ class IntegrateHeroVersionModel(BaseSettingsModel): "hero versions.") +class CollectRenderedFilesModel(BaseSettingsModel): + remove_files: bool = SettingsField( + True, + title="Remove rendered files", + description=( + "Remove rendered files and metadata json on publish.\n\n" + "Note that when enabled but the render is to a configured " + "persistent staging directory the files will not be removed. " + "However with this disabled the files will **not** be removed in " + "either case." + ) + ) + + class CleanUpModel(BaseSettingsModel): _isGroup = True paterns: list[str] = SettingsField( # codespell:ignore paterns @@ -1026,6 +1040,10 @@ class PublishPuginsModel(BaseSettingsModel): default_factory=IntegrateHeroVersionModel, title="Integrate Hero Version" ) + CollectRenderedFiles: CollectRenderedFilesModel = SettingsField( + default_factory=CollectRenderedFilesModel, + title="Clean up farm rendered files" + ) CleanUp: CleanUpModel = SettingsField( default_factory=CleanUpModel, title="Clean Up" @@ -1410,6 +1428,9 @@ DEFAULT_PUBLISH_VALUES = { ], "use_hardlinks": False }, + "CollectRenderedFiles": { + "remove_files": True + }, "CleanUp": { "paterns": [], # codespell:ignore paterns "remove_temp_renders": False From d20942892f0f6b00c5b5abdf867351b25c2f247b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 31 Mar 2025 14:45:31 +0200 Subject: [PATCH 09/70] Used new enum for template names from Anatomy --- server/settings/publish_plugins.py | 13 +++++++++++-- server/settings/tools.py | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 39a9c028f9..c32d8d360c 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -7,6 +7,7 @@ from ayon_server.settings import ( normalize_name, ensure_unique_names, task_types_enum, + anatomy_template_items_enum ) from ayon_server.types import ColorRGBA_uint8 @@ -889,7 +890,11 @@ class IntegrateANTemplateNameProfileModel(BaseSettingsModel): default_factory=list, title="Task names" ) - template_name: str = SettingsField("", title="Template name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="publish") + ) class IntegrateHeroTemplateNameProfileModel(BaseSettingsModel): @@ -910,7 +915,11 @@ class IntegrateHeroTemplateNameProfileModel(BaseSettingsModel): default_factory=list, title="Task names" ) - template_name: str = SettingsField("", title="Template name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="publish") + ) class IntegrateHeroVersionModel(BaseSettingsModel): diff --git a/server/settings/tools.py b/server/settings/tools.py index 32c72e7a98..d1e6cb50eb 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -5,6 +5,7 @@ from ayon_server.settings import ( normalize_name, ensure_unique_names, task_types_enum, + anatomy_template_items_enum ) @@ -283,7 +284,11 @@ class PublishTemplateNameProfile(BaseSettingsModel): task_names: list[str] = SettingsField( default_factory=list, title="Task names" ) - template_name: str = SettingsField("", title="Template name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="publish") + ) class CustomStagingDirProfileModel(BaseSettingsModel): @@ -306,7 +311,11 @@ class CustomStagingDirProfileModel(BaseSettingsModel): custom_staging_dir_persistent: bool = SettingsField( False, title="Custom Staging Folder Persistent" ) - template_name: str = SettingsField("", title="Template Name") + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum + ) class PublishToolModel(BaseSettingsModel): From ff8d4f5ddaa0c974a4a9a96553778f2b18139c85 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 1 Apr 2025 11:54:56 +0200 Subject: [PATCH 10/70] Added missed argument --- server/settings/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index d1e6cb50eb..28ceeedbe4 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -314,7 +314,7 @@ class CustomStagingDirProfileModel(BaseSettingsModel): template_name: str = SettingsField( "", title="Template name", - enum_resolver=anatomy_template_items_enum + enum_resolver=anatomy_template_items_enum(category="publish") ) From 7dad20d9c98c71c37db2bbdba22b32249c0001e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 1 Apr 2025 12:07:10 +0200 Subject: [PATCH 11/70] Bump up required server version --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 9af45719a7..51851c0f5f 100644 --- a/package.py +++ b/package.py @@ -6,7 +6,7 @@ client_dir = "ayon_core" plugin_for = ["ayon_server"] -ayon_server_version = ">=1.0.3,<2.0.0" +ayon_server_version = ">=1.7.5,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = {} From 93ea50cac145de2e568c6f17480fe487e62ec10c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 1 Apr 2025 12:15:22 +0200 Subject: [PATCH 12/70] Update correct category --- server/settings/publish_plugins.py | 2 +- server/settings/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index c32d8d360c..028cf9fffa 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -918,7 +918,7 @@ class IntegrateHeroTemplateNameProfileModel(BaseSettingsModel): template_name: str = SettingsField( "", title="Template name", - enum_resolver=anatomy_template_items_enum(category="publish") + enum_resolver=anatomy_template_items_enum(category="hero") ) diff --git a/server/settings/tools.py b/server/settings/tools.py index 28ceeedbe4..b003ef2244 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -314,7 +314,7 @@ class CustomStagingDirProfileModel(BaseSettingsModel): template_name: str = SettingsField( "", title="Template name", - enum_resolver=anatomy_template_items_enum(category="publish") + enum_resolver=anatomy_template_items_enum(category="staging") ) From d8c442e7f56a511c22376dadd828cda715ae19c4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Apr 2025 15:00:44 +0200 Subject: [PATCH 13/70] Update OCIO config paths and compatible addons - Added a new compatible addon with version requirement. - Updated existing OCIO config paths for ACES 1.3 and added support for ACES 2.0. - Adjusted labels and descriptions for clarity on OCIO versions. --- package.py | 4 +++- server/settings/main.py | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package.py b/package.py index af3342f3f2..bb8278151d 100644 --- a/package.py +++ b/package.py @@ -9,4 +9,6 @@ plugin_for = ["ayon_server"] ayon_server_version = ">=1.0.3,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} -ayon_compatible_addons = {} +ayon_compatible_addons = { + "ayon_ocio": ">=1.2.0", +} diff --git a/server/settings/main.py b/server/settings/main.py index 261bd7fc04..520c533aab 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,18 +72,30 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") }, { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 + "label": "ACES 1.3 Studio (OCIO v2.1)", + "description": ( + "Aces 1.3 Studio OCIO config file. Requires OCIO v2.1.") + }, + { + "value": "{BUILTIN_OCIO_ROOT}/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "label": "ACES 2.0 Studio (OCIO v2.4)", + "description": ( + "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") + }, + { + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", "label": "Nuke default", }, ] From ee213afce408dfb90e1b3265f16cdb19e59cfd66 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Apr 2025 15:02:19 +0200 Subject: [PATCH 14/70] Update OCIO config paths and add comments - Added line length comments to OCIO config paths - No functional changes, just improved code clarity --- server/settings/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 520c533aab..a7d82ec363 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -90,12 +90,12 @@ def _ocio_built_in_paths(): "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", # noqa: E501 "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", + "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", # noqa: E501 "label": "Nuke default", }, ] From 89a494b43f5d1a3c27a95f88d13458462381ebb5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 3 Apr 2025 11:28:52 +0200 Subject: [PATCH 15/70] Added profile targeting Hero --- server/settings/tools.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index b003ef2244..6b07910454 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -291,6 +291,29 @@ class PublishTemplateNameProfile(BaseSettingsModel): ) +class HeroTemplateNameProfile(BaseSettingsModel): + _layout = "expanded" + product_types: list[str] = SettingsField( + default_factory=list, + title="Product types" + ) + # TODO this should use hosts enum + hosts: list[str] = SettingsField(default_factory=list, title="Hosts") + task_types: list[str] = SettingsField( + default_factory=list, + title="Task types", + enum_resolver=task_types_enum + ) + task_names: list[str] = SettingsField( + default_factory=list, title="Task names" + ) + template_name: str = SettingsField( + "", + title="Template name", + enum_resolver=anatomy_template_items_enum(category="hero") + ) + + class CustomStagingDirProfileModel(BaseSettingsModel): active: bool = SettingsField(True, title="Is active") hosts: list[str] = SettingsField(default_factory=list, title="Host names") @@ -323,7 +346,7 @@ class PublishToolModel(BaseSettingsModel): default_factory=list, title="Template name profiles" ) - hero_template_name_profiles: list[PublishTemplateNameProfile] = ( + hero_template_name_profiles: list[HeroTemplateNameProfile] = ( SettingsField( default_factory=list, title="Hero template name profiles" From 2cd0b0ddbfdc8086156a2dee50aada811a3233c8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Apr 2025 14:47:19 +0200 Subject: [PATCH 16/70] Update OCIO config paths for cleaner structure - Removed redundant directory from ACES 1.2 path - Simplified Nuke default config path --- server/settings/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index a7d82ec363..21612ee362 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -90,12 +90,12 @@ def _ocio_built_in_paths(): "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/aces_1.2/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", # noqa: E501 "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/OpenColorIOConfigs/nuke-default/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", # noqa: E501 "label": "Nuke default", }, ] From f66ff742f7171994e289cdb3b7a9a9a0501bf3c4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 9 Apr 2025 15:38:19 +0200 Subject: [PATCH 17/70] Updates review extract to improve quality Improves review extraction by: - Switches output extension to '.png' for better image quality. - Adds compression level to ffmpeg command. - Adds scaling to the video filter. - Forces re-encoding for lossy formats. --- .../plugins/publish/extract_otio_review.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index 7a9a020ff0..e96c1a1b6b 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -54,7 +54,7 @@ class ExtractOTIOReview( # plugin default attributes to_width = 1280 to_height = 720 - output_ext = ".jpg" + output_ext = ".png" def process(self, instance): # Not all hosts can import these modules. @@ -474,6 +474,7 @@ class ExtractOTIOReview( command.extend([ "-start_number", str(in_frame_start), + "-compression_level", "5", "-framerate", str(sequence_fps), "-i", input_path ]) @@ -510,6 +511,11 @@ class ExtractOTIOReview( "-tune", "stillimage" ]) + if video or sequence: + command.extend([ + "-vf", f"scale={self.to_width}:{self.to_height}:flags=lanczos" + ]) + # add output attributes command.extend([ "-start_number", str(out_frame_start) @@ -520,9 +526,12 @@ class ExtractOTIOReview( input_extension and self.output_ext == input_extension ): - command.extend([ - "-c", "copy" - ]) + if input_extension.lower() in [ + '.png', '.tif', '.tiff', '.dpx', '.exr']: + command.extend(["-c", "copy"]) + else: + # For lossy formats, force re-encode + command.extend(["-pix_fmt", "rgba"]) # add output path at the end command.append(output_path) From edc0aa5867684eceed3da3a809567ab60f3ecc17 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 9 Apr 2025 15:56:39 +0200 Subject: [PATCH 18/70] Remove unnecessary E501 comments --- server/settings/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 21612ee362..f49866dc95 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -90,12 +90,12 @@ def _ocio_built_in_paths(): "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", "label": "ACES 1.2", "description": "Aces 1.2 OCIO config file." }, { - "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/nuke-default/config.ocio", "label": "Nuke default", }, ] From 51f86e5c5743f3252bbc60745f44f806ff386457 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Apr 2025 11:20:55 +0200 Subject: [PATCH 19/70] Bump server dependency version --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 9f01049fb9..d9edc36cdd 100644 --- a/package.py +++ b/package.py @@ -6,7 +6,7 @@ client_dir = "ayon_core" plugin_for = ["ayon_server"] -ayon_server_version = ">=1.7.5,<2.0.0" +ayon_server_version = ">=1.7.6,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = {} From f8ab13dd2aff1b5d13367c1b460305e5c4422a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 14 Apr 2025 15:50:20 +0200 Subject: [PATCH 20/70] Update client/ayon_core/plugins/publish/extract_otio_review.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/extract_otio_review.py | 3 ++- 1 file changed, 2 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 e96c1a1b6b..f7babc2b7f 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -527,7 +527,8 @@ class ExtractOTIOReview( and self.output_ext == input_extension ): if input_extension.lower() in [ - '.png', '.tif', '.tiff', '.dpx', '.exr']: + ".png", ".tif", ".tiff", ".dpx", ".exr" + ]: command.extend(["-c", "copy"]) else: # For lossy formats, force re-encode From 6df129b93f3460778f4fec02f8736efd1a2c62d4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 15 Apr 2025 16:37:56 +0200 Subject: [PATCH 21/70] Optimizes review encoding for image sequences Simplifies the encoding process for image sequences by removing the conditional check for specific image formats when using the 'copy' codec. This ensures consistent and efficient handling of image sequence encoding for review purposes. --- .../ayon_core/plugins/publish/extract_otio_review.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_otio_review.py b/client/ayon_core/plugins/publish/extract_otio_review.py index f7babc2b7f..908d78ca0d 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -526,13 +526,10 @@ class ExtractOTIOReview( input_extension and self.output_ext == input_extension ): - if input_extension.lower() in [ - ".png", ".tif", ".tiff", ".dpx", ".exr" - ]: - command.extend(["-c", "copy"]) - else: - # For lossy formats, force re-encode - command.extend(["-pix_fmt", "rgba"]) + command.extend(["-c", "copy"]) + else: + # For lossy formats, force re-encode + command.extend(["-pix_fmt", "rgba"]) # add output path at the end command.append(output_path) From d0999af4efaf6abcee42b4165f7919e6253b97b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:14:55 +0200 Subject: [PATCH 22/70] added aces subfolders --- server/settings/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index f49866dc95..97434d0b93 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,19 +72,19 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") }, { - "value": "{BUILTIN_OCIO_ROOT}/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 "label": "ACES 1.3 Studio (OCIO v2.1)", "description": ( "Aces 1.3 Studio OCIO config file. Requires OCIO v2.1.") }, { - "value": "{BUILTIN_OCIO_ROOT}/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 "label": "ACES 2.0 Studio (OCIO v2.4)", "description": ( "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") From 8d253033332e55b46ca0bcbeca59da50d078cdd7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 16 Apr 2025 21:53:17 +0200 Subject: [PATCH 23/70] Revert defaults so removal of rendered files is disabled by default --- client/ayon_core/plugins/publish/collect_rendered_files.py | 2 +- server/settings/publish_plugins.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_rendered_files.py b/client/ayon_core/plugins/publish/collect_rendered_files.py index c69dddd6ec..5c68af888f 100644 --- a/client/ayon_core/plugins/publish/collect_rendered_files.py +++ b/client/ayon_core/plugins/publish/collect_rendered_files.py @@ -33,7 +33,7 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): label = "Collect rendered frames" settings_category = "core" - remove_files = True + remove_files = False _context = None diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index e6a146a9d8..5f5891e4f4 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -931,7 +931,7 @@ class IntegrateHeroVersionModel(BaseSettingsModel): class CollectRenderedFilesModel(BaseSettingsModel): remove_files: bool = SettingsField( - True, + False, title="Remove rendered files", description=( "Remove rendered files and metadata json on publish.\n\n" @@ -1447,7 +1447,7 @@ DEFAULT_PUBLISH_VALUES = { "enabled": True, }, "CollectRenderedFiles": { - "remove_files": True + "remove_files": False }, "CleanUp": { "paterns": [], # codespell:ignore paterns From 644765cb85b5c786a3a7a59af4a00a623587eb38 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 16 Apr 2025 22:03:02 +0200 Subject: [PATCH 24/70] Remove redundant duplicate lines of code --- client/ayon_core/pipeline/farm/pyblish_functions.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/client/ayon_core/pipeline/farm/pyblish_functions.py b/client/ayon_core/pipeline/farm/pyblish_functions.py index c6f3ae7115..e10bcc9bb1 100644 --- a/client/ayon_core/pipeline/farm/pyblish_functions.py +++ b/client/ayon_core/pipeline/farm/pyblish_functions.py @@ -660,14 +660,6 @@ def _get_legacy_product_name_and_group( warnings.warn("Using legacy product name for renders", DeprecationWarning) - if not source_product_name.startswith(product_type): - resulting_group_name = '{}{}{}{}{}'.format( - product_type, - task_name[0].upper(), task_name[1:], - source_product_name[0].upper(), source_product_name[1:]) - else: - resulting_group_name = source_product_name - # create product name `` if not source_product_name.startswith(product_type): resulting_group_name = '{}{}{}{}{}'.format( From a39462497423327691675d0735ef9ac3ecd6e5f5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:28:51 +0200 Subject: [PATCH 25/70] don't skip other validations for string match --- client/ayon_core/tools/utils/projects_widget.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/utils/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py index 88d8a6c9f5..b88b2f410a 100644 --- a/client/ayon_core/tools/utils/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -351,13 +351,14 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): return True string_pattern = self.filterRegularExpression().pattern() - if string_pattern: - return string_pattern.lower() in project_name.lower() + if ( + string_pattern + and string_pattern.lower() not in project_name.lower() + ): + return False # Current project keep always visible - default = super(ProjectSortFilterProxy, self).filterAcceptsRow( - source_row, source_parent - ) + default = super().filterAcceptsRow(source_row, source_parent) if not default: return default From 922d19137c0e7df497fc7be53fcf7472da8fd7b5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:19:48 +0200 Subject: [PATCH 26/70] change order of filters --- client/ayon_core/tools/utils/projects_widget.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/utils/projects_widget.py b/client/ayon_core/tools/utils/projects_widget.py index b88b2f410a..c340be2f83 100644 --- a/client/ayon_core/tools/utils/projects_widget.py +++ b/client/ayon_core/tools/utils/projects_widget.py @@ -350,6 +350,14 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): if project_name is None: return True + # Make sure current project is visible + if index.data(PROJECT_IS_CURRENT_ROLE): + return True + + default = super().filterAcceptsRow(source_row, source_parent) + if not default: + return default + string_pattern = self.filterRegularExpression().pattern() if ( string_pattern @@ -357,15 +365,6 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel): ): return False - # Current project keep always visible - default = super().filterAcceptsRow(source_row, source_parent) - if not default: - return default - - # Make sure current project is visible - if index.data(PROJECT_IS_CURRENT_ROLE): - return True - if ( self._filter_inactive and not index.data(PROJECT_IS_ACTIVE_ROLE) From 71dc3650ace89b6ee77583ad43dd05690be32fa9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 23 Apr 2025 10:25:51 +0200 Subject: [PATCH 27/70] Adds explicit resolution override to publisher Adds a plugin that allows users to explicitly override the resolution settings (width, height, pixel aspect) of instances during the publishing process. This provides a way to ensure consistency and accuracy in resolution values across different tasks and product types. The plugin is configurable through the AYON settings, allowing administrators to define the available resolution options and the product types for which the override is enabled. --- .../publish/collect_explicit_resolution.py | 104 ++++++++++++++++++ server/settings/publish_plugins.py | 92 +++++++++++++++- 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 client/ayon_core/plugins/publish/collect_explicit_resolution.py diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py new file mode 100644 index 0000000000..3ff08cbd34 --- /dev/null +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -0,0 +1,104 @@ +import pyblish.api +from ayon_core.lib import EnumDef +from ayon_core.pipeline import colorspace +from ayon_core.pipeline import publish +from ayon_core.pipeline.publish import PublishError + + +class CollectExplicitResolution( + pyblish.api.InstancePlugin, + publish.AYONPyblishPluginMixin +): + """Collect explicit user defined resolution attributes for instances""" + + label = "Choose Explicit Resolution" + order = pyblish.api.CollectorOrder + 0.49 + settings_category = "core" + + enabled = False + + default_resolution_item = (None, "Don't override") + # Settings + product_types = [] + options = [] + + # caching resoluton items + resolution_items = None + + def process(self, instance): + """Process the instance and collect explicit resolution attributes""" + + # Get the values from the instance data + values = self.get_attr_values_from_data(instance.data) + resolution_value = values.get("explicit_resolution", None) + if resolution_value is None: + return + + # Get the width, height and pixel_aspect from the resolution value + resolution_data = self._get_resolution_values(resolution_value) + + # Set the values to the instance data + instance.data.update(resolution_data) + + def _get_resolution_values(self, resolution_value): + """ + Returns width, height and pixel_aspect from the resolution value + + Arguments: + resolution_value (str): resolution value + + Returns: + dict: dictionary with width, height and pixel_aspect + """ + resolution_items = self._get_resolution_items() + item_values = None + # check if resolution_value is in cached items + if resolution_value in resolution_items: + item_values = resolution_items[resolution_value] + + if item_values: + # if the item is in the cache, get the values from it + return { + "resolutionWidth": item_values["width"], + "resolutionHeight": item_values["height"], + "pixelAspect": item_values["pixel_aspect"] + } + else: + raise PublishError( + f"Invalid resolution value: {resolution_value}") + + @classmethod + def _get_resolution_items(cls): + if cls.resolution_items is None: + resolution_items = {} + for item in cls.options: + item_text = f"{item['width']}x{item['height']}x{item['pixel_aspect']}" + resolution_items[item_text] = item + + cls.resolution_items = resolution_items + + return cls.resolution_items + + @classmethod + def get_attr_defs_for_instance( + cls, create_context, instance + ): + if instance.product_type not in cls.product_types: + return [] + + # Get the resolution items + resolution_items = cls._get_resolution_items() + + items = [cls.default_resolution_item] + # Add all cached resolution items to the dropdown options + for item_text in resolution_items: + items.append((item_text, item_text)) + + return [ + EnumDef( + "explicit_resolution", + items, + default="Don't override", + label="Override Resolution" + ) + ] diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 5f5891e4f4..7ad6c9c506 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1,4 +1,6 @@ +from collections.abc import Iterable from pydantic import validator +from typing import Any from ayon_server.settings import ( BaseSettingsModel, @@ -8,7 +10,7 @@ from ayon_server.settings import ( ensure_unique_names, task_types_enum, ) - +from ayon_server.exceptions import BadRequestException from ayon_server.types import ColorRGBA_uint8 @@ -157,6 +159,77 @@ class CollectUSDLayerContributionsModel(BaseSettingsModel): return value +class ResolutionOptionsModel(BaseSettingsModel): + _layout = "compact" + width: int = SettingsField( + 1920, + ge=0, + le=100000, + title="Width", + description=( + "Width resolution number value"), + placeholder="Width" + ) + height: int = SettingsField( + 1080, + title="Height", + ge=0, + le=100000, + description=( + "Height resolution number value"), + placeholder="Height" + ) + pixel_aspect: float = SettingsField( + 1.0, + title="Pixel aspect", + ge=0.0, + le=100000.0, + description=( + "Pixel Aspect resolution decimal number value"), + placeholder="Pixel aspect" + ) + + +def ensure_unique_resolution_option( + objects: Iterable[Any], field_name: str | None = None) -> None: # noqa: C901 + """Ensure a list of objects have unique option attributes. + + This function checks if the list of objects has unique 'width', + 'height' and 'pixel_aspect' properties. + """ + options = [] + for obj in objects: + item_test_text = f"{obj.width}x{obj.height}x{obj.pixel_aspect}" + if item_test_text not in options: + options.append(item_test_text) + else: + raise BadRequestException( + f"Duplicate option '{item_test_text}'") + + +class CollectExplicitResolutionModel(BaseSettingsModel): + enabled: bool = SettingsField(True, title="Enabled") + product_types: list[str] = SettingsField( + default_factory=list, + title="Product types", + description=( + "Only activate the attribute for following product types." + ) + ) + options: list[ResolutionOptionsModel] = SettingsField( + default_factory=list, + title="Resolution options", + description=( + "Options to be provided in publisher attribute" + ) + ) + + @validator("options") + def validate_unique_options(cls, value): + ensure_unique_resolution_option(value) + return value + + class AyonEntityURIModel(BaseSettingsModel): use_ayon_entity_uri: bool = SettingsField( title="Use AYON Entity URI", @@ -988,6 +1061,10 @@ class PublishPuginsModel(BaseSettingsModel): title="Collect USD Layer Contributions", ) ) + CollectExplicitResolution: CollectExplicitResolutionModel = SettingsField( + default_factory=CollectExplicitResolutionModel, + title="Collect Explicit Resolution" + ) ValidateEditorialAssetName: ValidateBaseModel = SettingsField( default_factory=ValidateBaseModel, title="Validate Editorial Asset Name" @@ -1162,6 +1239,19 @@ DEFAULT_PUBLISH_VALUES = { }, ] }, + "CollectExplicitResolution": { + "enabled": True, + "product_types": [ + "shot" + ], + "options": [ + { + "width": 2048, + "height": 1080, + "aspect_ratio": 1.5, + } + ] + }, "ValidateEditorialAssetName": { "enabled": True, "optional": False, From 4141ce30b403e40dddda5eb288c3a79ef3c4f45d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:39:14 +0200 Subject: [PATCH 28/70] add compatible harmony addon version to package.py --- package.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.py b/package.py index a4ffe1a20d..0358c2f4cd 100644 --- a/package.py +++ b/package.py @@ -9,4 +9,6 @@ plugin_for = ["ayon_server"] ayon_server_version = ">=1.0.3,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} -ayon_compatible_addons = {} +ayon_compatible_addons = { + "harmony": ">0.4.0", +} From 80e0fa6b17052cfdafc7635ae53cf2d5d1d4dec6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 23 Apr 2025 20:05:59 +0200 Subject: [PATCH 29/70] Correctly check whether sequence has any frames in the returned holes collection. As per [`clique.Collection.holes` documentation](https://clique.readthedocs.io/en/stable/api_reference/collection.html#clique.collection.Collection.holes): > Return Collection of missing indexes. --- 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 1f2c2a89af..6cf30857a4 100644 --- a/client/ayon_core/plugins/publish/extract_color_transcode.py +++ b/client/ayon_core/plugins/publish/extract_color_transcode.py @@ -280,7 +280,7 @@ class ExtractOIIOTranscode(publish.Extractor): collection = collections[0] frames = list(collection.indexes) - if collection.holes(): + if collection.holes().indexes: return files_to_convert frame_str = "{}-{}#".format(frames[0], frames[-1]) From 468ab32b9a8c21e2add24bbdce7aad64ffe9ae50 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 24 Apr 2025 11:44:43 +0200 Subject: [PATCH 30/70] Remove unused import and add trailing commas --- .../plugins/publish/collect_explicit_resolution.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 3ff08cbd34..1aa7147627 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -1,13 +1,12 @@ import pyblish.api from ayon_core.lib import EnumDef -from ayon_core.pipeline import colorspace from ayon_core.pipeline import publish from ayon_core.pipeline.publish import PublishError class CollectExplicitResolution( pyblish.api.InstancePlugin, - publish.AYONPyblishPluginMixin + publish.AYONPyblishPluginMixin, ): """Collect explicit user defined resolution attributes for instances""" @@ -61,7 +60,7 @@ class CollectExplicitResolution( return { "resolutionWidth": item_values["width"], "resolutionHeight": item_values["height"], - "pixelAspect": item_values["pixel_aspect"] + "pixelAspect": item_values["pixel_aspect"], } else: raise PublishError( @@ -72,7 +71,8 @@ class CollectExplicitResolution( if cls.resolution_items is None: resolution_items = {} for item in cls.options: - item_text = f"{item['width']}x{item['height']}x{item['pixel_aspect']}" + item_text = ( + f"{item['width']}x{item['height']}x{item['pixel_aspect']}") resolution_items[item_text] = item cls.resolution_items = resolution_items @@ -81,7 +81,7 @@ class CollectExplicitResolution( @classmethod def get_attr_defs_for_instance( - cls, create_context, instance + cls, create_context, instance, ): if instance.product_type not in cls.product_types: return [] @@ -99,6 +99,6 @@ class CollectExplicitResolution( "explicit_resolution", items, default="Don't override", - label="Override Resolution" - ) + label="Override Resolution", + ), ] From f47c0b4027eccc0ce17962c3298a21f5ce3745d0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:21:40 +0800 Subject: [PATCH 31/70] If there is no valid review representation for thumbnail creation, make sure the representation is with the image content so that it can create thumbnail --- .../plugins/publish/extract_thumbnail.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index b72862ea22..adcd7be846 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -17,7 +17,7 @@ from ayon_core.lib import ( ) from ayon_core.lib.transcoding import convert_colorspace -from ayon_core.lib.transcoding import VIDEO_EXTENSIONS +from ayon_core.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS class ExtractThumbnail(pyblish.api.InstancePlugin): @@ -349,7 +349,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): continue if "review" not in tags: - continue + if not self._is_valid_media_repre(repre): + continue if not repre.get("files"): self.log.debug(( @@ -360,6 +361,21 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): filtered_repres.append(repre) return filtered_repres + def _is_valid_media_repre(self, repre): + """Check if representation contains valid media files""" + files = repre.get("files") + if not files: + return False + + # Get first file's extension + if isinstance(files, (list, tuple)): + first_file = files[0] + else: + first_file = files + + ext = os.path.splitext(first_file)[1].lower() + return ext in IMAGE_EXTENSIONS + def _create_thumbnail_oiio( self, src_path, From 59e986dde23fb53cac335ca454b7405d9579ec1e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:23:27 +0800 Subject: [PATCH 32/70] add debug message into self._is_valid_media_repre --- client/ayon_core/plugins/publish/extract_thumbnail.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index adcd7be846..47710de5f6 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -365,6 +365,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): """Check if representation contains valid media files""" files = repre.get("files") if not files: + self.log.debug(( + "Representation \"{}\" doesn't have files. Skipping" + ).format(repre["name"])) return False # Get first file's extension From 637b157fd387c5d7943c2f57ec3508aaa79a3823 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:24:28 +0800 Subject: [PATCH 33/70] rename self._is_valid_media_repre to self._is_valid_image_repre --- client/ayon_core/plugins/publish/extract_thumbnail.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 47710de5f6..b48e6a69b7 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -349,7 +349,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): continue if "review" not in tags: - if not self._is_valid_media_repre(repre): + if not self._is_valid_images_repre(repre): continue if not repre.get("files"): @@ -361,8 +361,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): filtered_repres.append(repre) return filtered_repres - def _is_valid_media_repre(self, repre): - """Check if representation contains valid media files""" + def _is_valid_images_repre(self, repre): + """Check if representation contains valid image files""" files = repre.get("files") if not files: self.log.debug(( From 625e782d7e2065bde600f3cd6c99387b411a3d10 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 16:32:17 +0800 Subject: [PATCH 34/70] improve the docstring --- client/ayon_core/plugins/publish/extract_thumbnail.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index b48e6a69b7..6e0d899f30 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -362,7 +362,14 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): return filtered_repres def _is_valid_images_repre(self, repre): - """Check if representation contains valid image files""" + """Check if representation contains valid image files + + Args: + repre (dict): representation + + Returns: + bool: whether the representation has the valid image content + """ files = repre.get("files") if not files: self.log.debug(( From c0db02f7b519eb36ab249f60d5c122053cca862d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 29 Apr 2025 17:14:14 +0800 Subject: [PATCH 35/70] improve the check on the valid representations for thumbnail creation --- .../plugins/publish/extract_thumbnail.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 6e0d899f30..cb941a53a9 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -336,7 +336,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): return need_thumb_repres def _get_filtered_repres(self, instance): - filtered_repres = [] + review_repres = [] + other_repres = [] src_repres = instance.data.get("representations") or [] for repre in src_repres: @@ -348,18 +349,22 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): # to be published locally continue - if "review" not in tags: - if not self._is_valid_images_repre(repre): - continue - if not repre.get("files"): self.log.debug(( "Representation \"{}\" doesn't have files. Skipping" ).format(repre["name"])) continue - filtered_repres.append(repre) - return filtered_repres + has_review_tag = "review" in tags + if not has_review_tag and not self._is_valid_images_repre(repre): + continue + + if has_review_tag: + review_repres.append(repre) + else: + other_repres.append(repre) + + return review_repres + other_repres def _is_valid_images_repre(self, repre): """Check if representation contains valid image files @@ -372,9 +377,6 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): """ files = repre.get("files") if not files: - self.log.debug(( - "Representation \"{}\" doesn't have files. Skipping" - ).format(repre["name"])) return False # Get first file's extension @@ -384,7 +386,8 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): first_file = files ext = os.path.splitext(first_file)[1].lower() - return ext in IMAGE_EXTENSIONS + + return ext in IMAGE_EXTENSIONS or ext in VIDEO_EXTENSIONS def _create_thumbnail_oiio( self, From 4bb0e14106cb65e5937225695ef336c5e87a3a1d Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 29 Apr 2025 19:10:01 +0800 Subject: [PATCH 36/70] Update client/ayon_core/plugins/publish/extract_thumbnail.py Co-authored-by: Roy Nieterau --- client/ayon_core/plugins/publish/extract_thumbnail.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index cb941a53a9..9592264ee2 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -356,12 +356,9 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): continue has_review_tag = "review" in tags - if not has_review_tag and not self._is_valid_images_repre(repre): - continue - if has_review_tag: review_repres.append(repre) - else: + elif self._is_valid_images_repre(repre): other_repres.append(repre) return review_repres + other_repres From 212a918b3151b9d69ec0eafc69f911b61561e259 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:25:49 +0200 Subject: [PATCH 37/70] added base ruff config file --- ruff.toml | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 ruff.toml diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000000..153a5c7d88 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,74 @@ +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 79 +indent-width = 4 + +[lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = ["E", "F", "W"] +ignore = [] + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = false + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" From 644f79bf0d67341f05474240840fe129faa459eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:17 +0200 Subject: [PATCH 38/70] added specific ignores --- ruff.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ruff.toml b/ruff.toml index 153a5c7d88..e94728c0fc 100644 --- a/ruff.toml +++ b/ruff.toml @@ -26,6 +26,8 @@ exclude = [ "node_modules", "site-packages", "venv", + "vendor", + "generated", ] # Same as Black. @@ -46,6 +48,14 @@ unfixable = [] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" +exclude = [ + "client/ayon_core/modules/click_wrap.py", + "client/ayon_core/scripts/slates/__init__.py" +] + +[lint.per-file-ignores] +"client/ayon_core/lib/__init__.py" = ["E402"] + [format] # Like Black, use double quotes for strings. quote-style = "double" From 79dbdb38e2c682a126d0c7f75aad00f6ecabef02 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:36 +0200 Subject: [PATCH 39/70] added linting details --- ruff.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruff.toml b/ruff.toml index e94728c0fc..49dd7a8c17 100644 --- a/ruff.toml +++ b/ruff.toml @@ -35,9 +35,9 @@ line-length = 79 indent-width = 4 [lint] +preview = true +pydocstyle.convention = "google" # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or -# McCabe complexity (`C901`) by default. select = ["E", "F", "W"] ignore = [] From 9f7f1ed314d6e2419b587053229e2bb870812765 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:29:43 +0200 Subject: [PATCH 40/70] added target python --- ruff.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ruff.toml b/ruff.toml index 49dd7a8c17..701055a65a 100644 --- a/ruff.toml +++ b/ruff.toml @@ -34,6 +34,9 @@ exclude = [ line-length = 79 indent-width = 4 +# Assume Python 3.9 +target-version = "py39" + [lint] preview = true pydocstyle.convention = "google" From 87514ba2e30a13e30d2bccfe6db6bd5962a87b8c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:30:02 +0200 Subject: [PATCH 41/70] remove ruff config from pyproject.toml --- pyproject.toml | 76 -------------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4c4272cc30..309b0f91a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,82 +41,6 @@ pymdown-extensions = "^10.14.3" mike = "^2.1.3" mkdocstrings-shell = "^1.0.2" - -[tool.ruff] -# Exclude a variety of commonly ignored directories. -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".ipynb_checkpoints", - ".mypy_cache", - ".nox", - ".pants.d", - ".pyenv", - ".pytest_cache", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - ".vscode", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "site-packages", - "venv", - "vendor", - "generated", -] - -# Same as Black. -line-length = 79 -indent-width = 4 - -# Assume Python 3.9 -target-version = "py39" - -[tool.ruff.lint] -preview = true -pydocstyle.convention = "google" -# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. -select = ["E", "F", "W"] -ignore = [] - -# Allow fix for all enabled rules (when `--fix`) is provided. -fixable = ["ALL"] -unfixable = [] - -# Allow unused variables when underscore-prefixed. -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" - -exclude = [ - "client/ayon_core/modules/click_wrap.py", - "client/ayon_core/scripts/slates/__init__.py" -] - -[tool.ruff.lint.per-file-ignores] -"client/ayon_core/lib/__init__.py" = ["E402"] - -[tool.ruff.format] -# Like Black, use double quotes for strings. -quote-style = "double" - -# Like Black, indent with spaces, rather than tabs. -indent-style = "space" - -# Like Black, respect magic trailing commas. -skip-magic-trailing-comma = false - -# Like Black, automatically detect the appropriate line ending. -line-ending = "auto" - [tool.codespell] # Ignore words that are not in the dictionary. ignore-words-list = "ayon,ynput,parms,parm,hda,developpement" From 2cb2a71e51e37614a12fc0c23378b61d57eee716 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:31:07 +0200 Subject: [PATCH 42/70] remove outdated paths --- pyproject.toml | 2 +- ruff.toml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 309b0f91a1..51f6048d8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ ignore-words-list = "ayon,ynput,parms,parm,hda,developpement" # Remove with next codespell release (>2.2.6) ignore-regex = ".*codespell:ignore.*" -skip = "./.*,./package/*,*/vendor/*,*/unreal/integration/*,*/aftereffects/api/extension/js/libs/*" +skip = "./.*,./package/*,*/client/ayon_core/vendor/*" count = true quiet-level = 3 diff --git a/ruff.toml b/ruff.toml index 701055a65a..f9b073e818 100644 --- a/ruff.toml +++ b/ruff.toml @@ -52,7 +52,6 @@ unfixable = [] dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" exclude = [ - "client/ayon_core/modules/click_wrap.py", "client/ayon_core/scripts/slates/__init__.py" ] From 591bf7c57be83a34677e4df720a42dd02d6b6017 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:01:01 +0200 Subject: [PATCH 43/70] bump ruff version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 51f6048d8a..3d85c30eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ pytest = "^8.0" pytest-print = "^1.0" ayon-python-api = "^1.0" # linting dependencies -ruff = "^0.3.3" +ruff = "0.11.7" pre-commit = "^3.6.2" codespell = "^2.2.6" semver = "^3.0.2" From c32cdba660ae1d30bfb22831cb3d63dfec325d82 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:40:50 +0800 Subject: [PATCH 44/70] Update client/ayon_core/plugins/publish/extract_thumbnail.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/plugins/publish/extract_thumbnail.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/plugins/publish/extract_thumbnail.py b/client/ayon_core/plugins/publish/extract_thumbnail.py index 9592264ee2..f67c571c90 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -372,15 +372,10 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): Returns: bool: whether the representation has the valid image content """ - files = repre.get("files") - if not files: - return False - # Get first file's extension - if isinstance(files, (list, tuple)): - first_file = files[0] - else: - first_file = files + first_file = repre["files"] + if isinstance(first_file, (list, tuple)): + first_file = first_file[0] ext = os.path.splitext(first_file)[1].lower() From 6d517d5e87074d7b4d4295c96e9bb405b36306b2 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Tue, 29 Apr 2025 20:41:01 +0800 Subject: [PATCH 45/70] Update client/ayon_core/plugins/publish/extract_thumbnail.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- 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 f67c571c90..3a428c46a7 100644 --- a/client/ayon_core/plugins/publish/extract_thumbnail.py +++ b/client/ayon_core/plugins/publish/extract_thumbnail.py @@ -355,8 +355,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): ).format(repre["name"])) continue - has_review_tag = "review" in tags - if has_review_tag: + if "review" in tags: review_repres.append(repre) elif self._is_valid_images_repre(repre): other_repres.append(repre) From 0f977b92840eda1a55e7489d651232c3dbe512f5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 29 Apr 2025 15:52:48 +0200 Subject: [PATCH 46/70] Add addon version compatibility --- package.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.py b/package.py index 0358c2f4cd..d1e2fac5ca 100644 --- a/package.py +++ b/package.py @@ -11,4 +11,6 @@ ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = { "harmony": ">0.4.0", + "fusion": ">=0.3.3", + "openrv": ">=1.0.2" } From 5d8e3e37c1f32ad5e314588dc2210b13b3d57eb1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 29 Apr 2025 15:53:00 +0200 Subject: [PATCH 47/70] Cosmetics --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index d1e2fac5ca..9fbb6a7000 100644 --- a/package.py +++ b/package.py @@ -12,5 +12,5 @@ ayon_required_addons = {} ayon_compatible_addons = { "harmony": ">0.4.0", "fusion": ">=0.3.3", - "openrv": ">=1.0.2" + "openrv": ">=1.0.2", } From 20435c18bcb770f0fe03a1144181a917d09670ac Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 29 Apr 2025 13:56:28 +0000 Subject: [PATCH 48/70] [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 01e431577e..6bc1e253c4 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.8+dev" +__version__ = "1.1.9" diff --git a/package.py b/package.py index 0358c2f4cd..111cfd08d0 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.8+dev" +version = "1.1.9" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 3d85c30eda..dd85bba802 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.8+dev" +version = "1.1.9" description = "" authors = ["Ynput Team "] readme = "README.md" From 839550d0cf5d89cbe6e2dc3fbf4306d8b383b628 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Apr 2025 13:57:15 +0000 Subject: [PATCH 49/70] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c0ab04abef..f602156aea 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,16 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.1.9 + - 1.1.8 + - 1.1.7 + - 1.1.6 + - 1.1.5 + - 1.1.4 + - 1.1.3 + - 1.1.2 + - 1.1.1 + - 1.1.0 - 1.0.14 - 1.0.13 - 1.0.12 From abe038ee16d657d59e1bcb03b3c5e324e7fcff1c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:13:02 +0200 Subject: [PATCH 50/70] bump version to 1.1.9+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 6bc1e253c4..ed56f67bb4 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.9" +__version__ = "1.1.9+dev" diff --git a/package.py b/package.py index 111cfd08d0..d28ebd059f 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.9" +version = "1.1.9+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index dd85bba802..5a89fbf7e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.9" +version = "1.1.9+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From b85e6b72cbad55190a0a539ca0ef9fd3d5de5b4a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 29 Apr 2025 17:05:13 +0200 Subject: [PATCH 51/70] run update bug report after release trigger --- .github/workflows/update_bug_report.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update_bug_report.yml b/.github/workflows/update_bug_report.yml index 1e5da414bb..98a8454e4b 100644 --- a/.github/workflows/update_bug_report.yml +++ b/.github/workflows/update_bug_report.yml @@ -1,10 +1,11 @@ name: 🐞 Update Bug Report on: + workflow_run: + workflows: ["🚀 Release Trigger"] + types: + - completed workflow_dispatch: - release: - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release - types: [published] jobs: update-bug-report: From 52d0cc8748707fb694c333b8f78693e0928d5759 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 30 Apr 2025 17:00:09 +0200 Subject: [PATCH 52/70] :sparkles: add hook to filter paths --- .../hooks/pre_remove_launcher_paths.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 client/ayon_core/hooks/pre_remove_launcher_paths.py diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py new file mode 100644 index 0000000000..a96978b368 --- /dev/null +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -0,0 +1,36 @@ +""""Pre launch hook to remove launcher paths from the system.""" +import os +from ayon_applications import PreLaunchHook, LaunchTypes + + +class PreRemoveLauncherPaths(PreLaunchHook): + """Remove launcher paths from the system. + + This hook is used to remove launcher paths from the system before launching + an application. It is used to ensure that the application is launched with + the correct environment variables. Especially for Windows, where + paths in `PATH` are used to load DLLs. This is important to avoid + conflicts with other applications that may have the same DLLs in their + paths. + """ + + order = 1 + + platforms = {"linux", "windows", "darwin"} + launch_types = {LaunchTypes.local} + + def execute(self): + # Remove launcher paths from the system + paths = [] + try: + ayon_root = self.launch_context.env["AYON_ROOT"] + except KeyError: + self.log.warning("AYON_ROOT not found in environment variables.") + return + + paths.extend( + path + for path in self.launch_context.env.get("PATH", "").split(os.pathsep) + if not path.startswith(ayon_root) + ) + self.launch_context.env["PATH"] = os.pathsep.join(paths) From cd7c4a37783b3c53b8e934a8524a18941c9ac0c0 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 30 Apr 2025 17:20:44 +0200 Subject: [PATCH 53/70] :recycle: make checks safer --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index a96978b368..800df0c214 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -23,14 +23,14 @@ class PreRemoveLauncherPaths(PreLaunchHook): # Remove launcher paths from the system paths = [] try: - ayon_root = self.launch_context.env["AYON_ROOT"] + ayon_root = os.path.normpath(self.launch_context.env["AYON_ROOT"]) except KeyError: self.log.warning("AYON_ROOT not found in environment variables.") return paths.extend( path - for path in self.launch_context.env.get("PATH", "").split(os.pathsep) - if not path.startswith(ayon_root) + for path in self.launch_context.env.get("PATH").split(os.pathsep) + if not os.path.normpath(path).startswith(ayon_root) ) self.launch_context.env["PATH"] = os.pathsep.join(paths) From 1f48b1568d0a988e749e42436ac545547756d726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:02:55 +0200 Subject: [PATCH 54/70] Update client/ayon_core/hooks/pre_remove_launcher_paths.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index 800df0c214..ee6ebd8950 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -30,7 +30,7 @@ class PreRemoveLauncherPaths(PreLaunchHook): paths.extend( path - for path in self.launch_context.env.get("PATH").split(os.pathsep) + for path in self.launch_context.env.get("PATH", "").split(os.pathsep) if not os.path.normpath(path).startswith(ayon_root) ) self.launch_context.env["PATH"] = os.pathsep.join(paths) From edabad6c13ec98e05a3b8c3f4d0e1b32b462165c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:07:30 +0200 Subject: [PATCH 55/70] Update client/ayon_core/hooks/pre_remove_launcher_paths.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index ee6ebd8950..4e3835d08b 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -22,11 +22,7 @@ class PreRemoveLauncherPaths(PreLaunchHook): def execute(self): # Remove launcher paths from the system paths = [] - try: - ayon_root = os.path.normpath(self.launch_context.env["AYON_ROOT"]) - except KeyError: - self.log.warning("AYON_ROOT not found in environment variables.") - return + ayon_root = os.path.normpath(os.environ["AYON_ROOT"]) paths.extend( path From 624dfcccadb4595d90f5c92127a30fbca99be897 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 2 May 2025 14:46:23 +0200 Subject: [PATCH 56/70] :recycle: some refactoring --- .../hooks/pre_remove_launcher_paths.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index 4e3835d08b..96ee997501 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -1,6 +1,10 @@ """"Pre launch hook to remove launcher paths from the system.""" +from __future__ import annotations + import os -from ayon_applications import PreLaunchHook, LaunchTypes +from typing import ClassVar + +from ayon_applications import PreLaunchHook class PreRemoveLauncherPaths(PreLaunchHook): @@ -15,18 +19,17 @@ class PreRemoveLauncherPaths(PreLaunchHook): """ order = 1 + launch_types: ClassVar[set] = set() - platforms = {"linux", "windows", "darwin"} - launch_types = {LaunchTypes.local} - - def execute(self): + def execute(self) -> None: + """Execute the hook.""" # Remove launcher paths from the system - paths = [] ayon_root = os.path.normpath(os.environ["AYON_ROOT"]) - paths.extend( + paths = [ path - for path in self.launch_context.env.get("PATH", "").split(os.pathsep) + for path in self.launch_context.env.get( + "PATH", "").split(os.pathsep) if not os.path.normpath(path).startswith(ayon_root) - ) + ] self.launch_context.env["PATH"] = os.pathsep.join(paths) From 917e32cb13923dfdc77d4bbdd1b041ee8492f3f4 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 2 May 2025 14:47:46 +0200 Subject: [PATCH 57/70] :recycle: remove unnecessary type hinting --- client/ayon_core/hooks/pre_remove_launcher_paths.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client/ayon_core/hooks/pre_remove_launcher_paths.py b/client/ayon_core/hooks/pre_remove_launcher_paths.py index 96ee997501..df27e512d0 100644 --- a/client/ayon_core/hooks/pre_remove_launcher_paths.py +++ b/client/ayon_core/hooks/pre_remove_launcher_paths.py @@ -1,8 +1,5 @@ """"Pre launch hook to remove launcher paths from the system.""" -from __future__ import annotations - import os -from typing import ClassVar from ayon_applications import PreLaunchHook @@ -17,9 +14,7 @@ class PreRemoveLauncherPaths(PreLaunchHook): conflicts with other applications that may have the same DLLs in their paths. """ - order = 1 - launch_types: ClassVar[set] = set() def execute(self) -> None: """Execute the hook.""" From 0063465626bb81f4a8cf9579576d406b473e189b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 2 May 2025 12:51:59 +0000 Subject: [PATCH 58/70] [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 ed56f67bb4..af87dbce6c 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.9+dev" +__version__ = "1.2.0" diff --git a/package.py b/package.py index 6229131f63..d6c58f223a 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.1.9+dev" +version = "1.2.0" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 5a89fbf7e3..2ce6d971dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.1.9+dev" +version = "1.2.0" description = "" authors = ["Ynput Team "] readme = "README.md" From 75110078618beab1f636bf26c24dcfd9b0393c75 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Fri, 2 May 2025 12:52:33 +0000 Subject: [PATCH 59/70] [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 af87dbce6c..4fd7bde336 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.2.0" +__version__ = "1.2.0+dev" diff --git a/package.py b/package.py index d6c58f223a..1695cc7808 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "1.2.0" +version = "1.2.0+dev" client_dir = "ayon_core" diff --git a/pyproject.toml b/pyproject.toml index 2ce6d971dd..c7e2bb5000 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.poetry] name = "ayon-core" -version = "1.2.0" +version = "1.2.0+dev" description = "" authors = ["Ynput Team "] readme = "README.md" From 3bd3c2f6f5bf85c95f172b1b97e2e2a685b09fcf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 May 2025 12:53:24 +0000 Subject: [PATCH 60/70] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f602156aea..c1e18faf55 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to AYON Tray options: + - 1.2.0 - 1.1.9 - 1.1.8 - 1.1.7 From 47f8dcdce6871e96e295d0ab6d00957877360666 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 5 May 2025 12:05:01 +0200 Subject: [PATCH 61/70] Updates OCIO config paths Updates the built-in OCIO config paths to correct versioning and descriptions, ensuring accurate configuration options. --- server/settings/main.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/settings/main.py b/server/settings/main.py index 97434d0b93..a582763b4b 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,10 +72,10 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 - "label": "ACES 1.3 Studio (OCIO v2)", + "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "label": "ACES 2.0 Studio (OCIO v2.4)", "description": ( - "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") + "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") }, { "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.1.ocio", # noqa: E501 @@ -84,10 +84,10 @@ def _ocio_built_in_paths(): "Aces 1.3 Studio OCIO config file. Requires OCIO v2.1.") }, { - "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 - "label": "ACES 2.0 Studio (OCIO v2.4)", + "value": "{BUILTIN_OCIO_ROOT}/aces_1.3/studio-config-v1.0.0_aces-v1.3_ocio-v2.0.ocio", # noqa: E501 + "label": "ACES 1.3 Studio (OCIO v2)", "description": ( - "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") + "Aces 1.3 Studio OCIO config file. Requires OCIO v2.") }, { "value": "{BUILTIN_OCIO_ROOT}/aces_1.2/config.ocio", From 93b59710b25303d105960e3ef370086f167c1b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 8 May 2025 17:27:52 +0200 Subject: [PATCH 62/70] Apply suggestions from code review Co-authored-by: Robin De Lillo Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../publish/collect_explicit_resolution.py | 18 +++++++++--------- server/settings/publish_plugins.py | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 1aa7147627..1a388d5487 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -50,21 +50,21 @@ class CollectExplicitResolution( dict: dictionary with width, height and pixel_aspect """ resolution_items = self._get_resolution_items() - item_values = None - # check if resolution_value is in cached items - if resolution_value in resolution_items: - item_values = resolution_items[resolution_value] + # ensure resolution_value is part of expected items + item_values = resolution_items.get(resolution_value) + # if the item is in the cache, get the values from it if item_values: - # if the item is in the cache, get the values from it return { "resolutionWidth": item_values["width"], "resolutionHeight": item_values["height"], "pixelAspect": item_values["pixel_aspect"], } - else: - raise PublishError( - f"Invalid resolution value: {resolution_value}") + + raise PublishError( + f"Invalid resolution value: {resolution_value} " + f"expected choices: {resolution_items}" + ) @classmethod def _get_resolution_items(cls): @@ -72,7 +72,7 @@ class CollectExplicitResolution( resolution_items = {} for item in cls.options: item_text = ( - f"{item['width']}x{item['height']}x{item['pixel_aspect']}") + f"{item['width']}x{item['height']} ({item['pixel_aspect']})") resolution_items[item_text] = item cls.resolution_items = resolution_items diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 7ad6c9c506..cce312c2f8 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -191,21 +191,21 @@ class ResolutionOptionsModel(BaseSettingsModel): def ensure_unique_resolution_option( - objects: Iterable[Any], field_name: str | None = None) -> None: # noqa: C901 + objects: list[Any], field_name: str | None = None) -> None: # noqa: C901 """Ensure a list of objects have unique option attributes. This function checks if the list of objects has unique 'width', 'height' and 'pixel_aspect' properties. """ - options = [] + options = set() for obj in objects: item_test_text = f"{obj.width}x{obj.height}x{obj.pixel_aspect}" - if item_test_text not in options: - options.append(item_test_text) - else: + if item_test_text in options: raise BadRequestException( f"Duplicate option '{item_test_text}'") + options.add(item_test_text) + class CollectExplicitResolutionModel(BaseSettingsModel): enabled: bool = SettingsField(True, title="Enabled") @@ -218,14 +218,14 @@ class CollectExplicitResolutionModel(BaseSettingsModel): ) options: list[ResolutionOptionsModel] = SettingsField( default_factory=list, - title="Resolution options", + title="Resolution choices", description=( - "Options to be provided in publisher attribute" + "Available resolution choices to be displayed in the publishers attribute." ) ) @validator("options") - def validate_unique_options(cls, value): + def validate_unique_resolution_options(cls, value): ensure_unique_resolution_option(value) return value From 753960ca9ace6aafe067e85a0e7384f868a999a6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 8 May 2025 17:33:08 +0200 Subject: [PATCH 63/70] Refactors explicit resolution collection Changes the collector order to ensure correct execution. Renames the "Override Resolution" label to "Force product resolution" for clarity. Removes default resolution values from server settings. The explicit resolution is intended to be defined on the instance level, not as a global default. --- .../plugins/publish/collect_explicit_resolution.py | 4 ++-- server/settings/publish_plugins.py | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 1a388d5487..7d70271846 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -11,7 +11,7 @@ class CollectExplicitResolution( """Collect explicit user defined resolution attributes for instances""" label = "Choose Explicit Resolution" - order = pyblish.api.CollectorOrder + 0.49 + order = pyblish.api.CollectorOrder - 0.091 settings_category = "core" enabled = False @@ -99,6 +99,6 @@ class CollectExplicitResolution( "explicit_resolution", items, default="Don't override", - label="Override Resolution", + label="Force product resolution", ), ] diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index cce312c2f8..4b75fb46b6 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1244,13 +1244,7 @@ DEFAULT_PUBLISH_VALUES = { "product_types": [ "shot" ], - "options": [ - { - "width": 2048, - "height": 1080, - "aspect_ratio": 1.5, - } - ] + "options": [] }, "ValidateEditorialAssetName": { "enabled": True, From 824dc0cc81d30618afc0afead8c584c99be8f53a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 9 May 2025 10:50:20 +0200 Subject: [PATCH 64/70] Improves readability of resolution display Updates the format of resolution items displayed in the publisher's attribute for better readability. Removes an unused import from server settings. --- .../ayon_core/plugins/publish/collect_explicit_resolution.py | 4 +++- server/settings/publish_plugins.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/plugins/publish/collect_explicit_resolution.py b/client/ayon_core/plugins/publish/collect_explicit_resolution.py index 7d70271846..3ea3d42102 100644 --- a/client/ayon_core/plugins/publish/collect_explicit_resolution.py +++ b/client/ayon_core/plugins/publish/collect_explicit_resolution.py @@ -72,7 +72,9 @@ class CollectExplicitResolution( resolution_items = {} for item in cls.options: item_text = ( - f"{item['width']}x{item['height']} ({item['pixel_aspect']})") + f"{item['width']}x{item['height']} " + f"({item['pixel_aspect']})" + ) resolution_items[item_text] = item cls.resolution_items = resolution_items diff --git a/server/settings/publish_plugins.py b/server/settings/publish_plugins.py index 56c8a929b1..0d8489d8ff 100644 --- a/server/settings/publish_plugins.py +++ b/server/settings/publish_plugins.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from pydantic import validator from typing import Any @@ -221,7 +220,8 @@ class CollectExplicitResolutionModel(BaseSettingsModel): default_factory=list, title="Resolution choices", description=( - "Available resolution choices to be displayed in the publishers attribute." + "Available resolution choices to be displayed in " + "the publishers attribute." ) ) From 92aa7e1ccb89103da0e7addfa5405b77fd4258b1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 9 May 2025 11:53:15 +0200 Subject: [PATCH 65/70] Updates ayon_ocio addon version Updates the minimum compatible version of the 'ayon_ocio' addon. This ensures compatibility with the latest features and fixes in the addon. --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 0dad0b9792..601d703857 100644 --- a/package.py +++ b/package.py @@ -10,7 +10,7 @@ ayon_server_version = ">=1.7.6,<2.0.0" ayon_launcher_version = ">=1.0.2" ayon_required_addons = {} ayon_compatible_addons = { - "ayon_ocio": ">=1.2.0", + "ayon_ocio": ">=1.2.1", "harmony": ">0.4.0", "fusion": ">=0.3.3", "openrv": ">=1.0.2", From e4c5b0d0a53d2382199f4cbc92ed3785a8bb8a12 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 9 May 2025 11:59:52 +0200 Subject: [PATCH 66/70] Fixes typo in default OCIO config path Corrects a typo in the built-in OCIO config path. It appends the missing ".ocio" extension to the ACES 2.0 Studio config path, ensuring that the OCIO configuration is correctly recognized and loaded. --- server/settings/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/main.py b/server/settings/main.py index a582763b4b..dd6af0a104 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -72,7 +72,7 @@ def _fallback_ocio_config_profile_types(): def _ocio_built_in_paths(): return [ { - "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4", # noqa: E501 + "value": "{BUILTIN_OCIO_ROOT}/aces_2.0/studio-config-v3.0.0_aces-v2.0_ocio-v2.4.ocio", # noqa: E501 "label": "ACES 2.0 Studio (OCIO v2.4)", "description": ( "Aces 2.0 Studio OCIO config file. Requires OCIO v2.4.") From c7faefa99489fe67b3014ebed5b9f8d6e6a0092e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 9 May 2025 14:22:00 +0200 Subject: [PATCH 67/70] reverse the replacements --- .../ayon_core/pipeline/create/product_name.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/pipeline/create/product_name.py b/client/ayon_core/pipeline/create/product_name.py index 0daec8a7ad..ecffa4a340 100644 --- a/client/ayon_core/pipeline/create/product_name.py +++ b/client/ayon_core/pipeline/create/product_name.py @@ -52,15 +52,15 @@ def get_product_name_template( # TODO remove formatting keys replacement template = ( matching_profile["template"] - .replace("{task[name]}", "{task}") - .replace("{Task[name]}", "{Task}") - .replace("{TASK[NAME]}", "{TASK}") - .replace("{product[type]}", "{family}") - .replace("{Product[type]}", "{Family}") - .replace("{PRODUCT[TYPE]}", "{FAMILY}") - .replace("{folder[name]}", "{asset}") - .replace("{Folder[name]}", "{Asset}") - .replace("{FOLDER[NAME]}", "{ASSET}") + .replace("{task}", "{task[name]}") + .replace("{Task}", "{Task[name]}") + .replace("{TASK}", "{TASK[NAME]}") + .replace("{family}", "{product[type]}") + .replace("{Family}", "{Product[type]}") + .replace("{FAMILY}", "{PRODUCT[TYPE]}") + .replace("{asset}", "{folder[name]}") + .replace("{Asset}", "{Folder[name]}") + .replace("{ASSET}", "{FOLDER[NAME]}") ) # Make sure template is set (matching may have empty string) From 2ac35d6dd8021c892c6c664b59066d29a9a950ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Mon, 12 May 2025 14:57:42 +0200 Subject: [PATCH 68/70] Apply suggestions from code review Co-authored-by: Robin De Lillo --- client/ayon_core/plugins/publish/extract_otio_review.py | 4 ++-- 1 file changed, 2 insertions(+), 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 908d78ca0d..f217be551c 100644 --- a/client/ayon_core/plugins/publish/extract_otio_review.py +++ b/client/ayon_core/plugins/publish/extract_otio_review.py @@ -474,7 +474,6 @@ class ExtractOTIOReview( command.extend([ "-start_number", str(in_frame_start), - "-compression_level", "5", "-framerate", str(sequence_fps), "-i", input_path ]) @@ -513,7 +512,8 @@ class ExtractOTIOReview( if video or sequence: command.extend([ - "-vf", f"scale={self.to_width}:{self.to_height}:flags=lanczos" + "-vf", f"scale={self.to_width}:{self.to_height}:flags=lanczos", + "-compression_level", "5", ]) # add output attributes From ce40d020d9a0c51f86066401e267ad3961fed91f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 12 May 2025 15:38:14 +0200 Subject: [PATCH 69/70] Updates image format to png and adds scaling Updates the image format for review outputs to PNG, adds scaling and compression to the ffmpeg calls, and includes pixel format specification for better compatibility and quality. --- .../editorial/test_extract_otio_review.py | 82 +++++++++++-------- 1 file changed, 50 insertions(+), 32 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 45191a2c53..a46ea149d7 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,18 @@ 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.%04d.jpg", + "-pix_fmt rgba C:/result/output.%04d.png", # 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.%04d.jpg", + "-pix_fmt rgba C:/result/output.%04d.png", # 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.%04d.jpg" + f"C:\\exr_embedded_tc{os.sep}output.%04d.exr " + "-vf scale=1280:720:flags=lanczos -compression_level 5 " + "-start_number 1001 -pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -130,20 +131,22 @@ 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.%04d.jpg", + "/path/to/ffmpeg -t 0.2 -r 25.0 -f lavfi -i color=c=black:s=1280x720 " + "-tune stillimage -start_number 991 -pix_fmt rgba " + "C:/result/output.%04d.png", # 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.%04d.jpg", + "/path/to/ffmpeg -t 0.36 -r 25.0 -f lavfi -i color=c=black:s=1280x720 " + "-tune stillimage -start_number 1097 -pix_fmt rgba C:/result/output.%04d.png", # 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" - f" C:/result/output.%04d.jpg" + f"C:\\tif_seq{os.sep}output.%04d.tif " + "-vf scale=1280:720:flags=lanczos -compression_level 5 -start_number 996 " + "-pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -164,7 +167,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.%04d.jpg" + "-pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -181,12 +184,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.%04d.jpg", + " -tune stillimage -start_number 991 -pix_fmt rgba C:/result/output.%04d.png", # 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.%04d.jpg" + " -start_number 1001 -pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -204,13 +207,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.%04d.jpg", + "-pix_fmt rgba C:/result/output.%04d.png", # 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.%04d.jpg" + " -pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -239,62 +242,75 @@ 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.%04d.jpg', + 'stillimage -start_number 991 -pix_fmt rgba C:/result/output.%04d.png', # 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.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1001 -pix_fmt rgba C:/result/output.%04d.png', '/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.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1102 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1198 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1198 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 24.0 -i ' f'C:\\with_tc{os.sep}output.%04d.exr ' - '-start_number 1299 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1299 -pix_fmt rgba C:/result/output.%04d.png', # 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 1395 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1395 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1496 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1496 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1597 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1597 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1698 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1698 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1799 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1799 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 1900 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1900 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2001 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 2001 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2102 C:/result/output.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 2102 -pix_fmt rgba C:/result/output.%04d.png', '/path/to/ffmpeg -start_number 1000 -framerate 25.0 -i ' f'C:\\no_tc{os.sep}output.%04d.tif ' - '-start_number 2203 C:/result/output.%04d.jpg' + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 2203 -pix_fmt rgba C:/result/output.%04d.png' ] assert calls == expected @@ -323,15 +339,17 @@ 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.%04d.jpg', + 'stillimage -start_number 991 -pix_fmt rgba C:/result/output.%04d.png', '/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.%04d.jpg', + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1003 -pix_fmt rgba C:/result/output.%04d.png', '/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.%04d.jpg' + '-vf scale=1280:720:flags=lanczos -compression_level 5 ' + '-start_number 1091 -pix_fmt rgba C:/result/output.%04d.png' ] assert calls == expected From 9137d1c0bb7336a0d849f5488d30531aa16372b7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 12 May 2025 15:50:06 +0200 Subject: [PATCH 70/70] Adds scaling and compression to ffmpeg calls Updates the ffmpeg calls within the editorial extraction tests to include scaling and compression parameters. This ensures consistent image quality and size across different source media. --- .../editorial/test_extract_otio_review.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 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 a46ea149d7..6a74df7f43 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 @@ -137,7 +137,8 @@ def test_image_sequence_and_handles_out_of_range(): # 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 -pix_fmt rgba C:/result/output.%04d.png", + "-tune stillimage -start_number 1097 -pix_fmt rgba " + "C:/result/output.%04d.png", # Report from source tiff (996-1096) # 996-1000 = additional 5 head frames @@ -145,8 +146,8 @@ 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 " - "-vf scale=1280:720:flags=lanczos -compression_level 5 -start_number 996 " - "-pix_fmt rgba C:/result/output.%04d.png" + "-vf scale=1280:720:flags=lanczos -compression_level 5 " + "-start_number 996 -pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -166,8 +167,9 @@ def test_movie_with_embedded_tc_no_gap_handles(): # - first_frame = 14 src - 10 (head tail) = frame 4 = 0.1666s # - 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 " - "-pix_fmt rgba C:/result/output.%04d.png" + "-i C:\\data\\qt_embedded_tc.mov -vf scale=1280:720:flags=lanczos " + "-compression_level 5 -start_number 991 -pix_fmt rgba " + "C:/result/output.%04d.png" ] assert calls == expected @@ -184,12 +186,14 @@ 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 -pix_fmt rgba C:/result/output.%04d.png", + " -tune stillimage -start_number 991 -pix_fmt rgba " + "C:/result/output.%04d.png", # 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 -pix_fmt rgba C:/result/output.%04d.png" + "/path/to/ffmpeg -ss 0.0 -t 2.4 -i C:\\data\\movie.mp4 -vf " + "scale=1280:720:flags=lanczos -compression_level 5 " + "-start_number 1001 -pix_fmt rgba C:/result/output.%04d.png" ] assert calls == expected @@ -212,8 +216,9 @@ 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" - " -pix_fmt rgba C:/result/output.%04d.png" + "C:\\data\\qt_no_tc_24fps.mov -vf scale=1280:720:flags=lanczos " + "-compression_level 5 -start_number 991 -pix_fmt rgba " + "C:/result/output.%04d.png" ] assert calls == expected