From ae5ec70d0538b4c659cdfa1eff6f0a89e62bf887 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 23 Sep 2022 21:37:47 +0200 Subject: [PATCH 01/55] Move `imageio` settings from project anatomy to project settings - Note: There is no backwards compatibility implemented --- openpype/hosts/flame/hooks/pre_flame_setup.py | 13 +- .../fusion/hooks/pre_fusion_ocio_hook.py | 7 +- openpype/hosts/hiero/api/lib.py | 8 +- openpype/hosts/maya/api/lib.py | 4 +- openpype/hosts/nuke/api/lib.py | 2 +- openpype/hosts/nuke/plugins/load/load_clip.py | 2 +- .../defaults/project_anatomy/imageio.json | 258 --------- .../defaults/project_settings/flame.json | 19 + .../defaults/project_settings/hiero.json | 25 + .../defaults/project_settings/maya.json | 22 + .../defaults/project_settings/nuke.json | 190 +++++++ .../schemas/projects_schema/schema_main.json | 4 - .../projects_schema/schema_project_flame.json | 63 +++ .../projects_schema/schema_project_hiero.json | 110 ++++ .../projects_schema/schema_project_maya.json | 70 +++ .../projects_schema/schema_project_nuke.json | 248 +++++++++ .../schemas/schema_anatomy_imageio.json | 493 ------------------ openpype/settings/lib.py | 17 - 18 files changed, 763 insertions(+), 792 deletions(-) delete mode 100644 openpype/settings/defaults/project_anatomy/imageio.json delete mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 0173eb8e3b..8f2edf59a6 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -42,17 +42,16 @@ class FlamePrelaunch(PreLaunchHook): volume_name = _env.get("FLAME_WIRETAP_VOLUME") # get image io - project_anatomy = self.data["anatomy"] + project_settings = self.data["project_settings"] # make sure anatomy settings are having flame key - if not project_anatomy["imageio"].get("flame"): - raise ApplicationLaunchFailed(( - "Anatomy project settings are missing `flame` key. " - "Please make sure you remove project overides on " - "Anatomy Image io") + if not project_settings["flame"].get("imageio"): + raise ApplicationLaunchFailed( + "Project settings are missing `flame/imageio` key. " + "Please make sure to update project settings." ) - imageio_flame = project_anatomy["imageio"]["flame"] + imageio_flame = project_settings["flame"]["imageio"] # get user name and host name user_name = get_openpype_username() diff --git a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py b/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py index 12fc640f5c..83cd070924 100644 --- a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py +++ b/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py @@ -17,10 +17,9 @@ class FusionPreLaunchOCIO(PreLaunchHook): # make sure anatomy settings are having flame key imageio_fusion = project_settings.get("fusion", {}).get("imageio") if not imageio_fusion: - raise ApplicationLaunchFailed(( - "Anatomy project settings are missing `fusion` key. " - "Please make sure you remove project overrides on " - "Anatomy ImageIO") + raise ApplicationLaunchFailed( + "Project settings are missing `fusion/imageio` key. " + "Please make sure you update your project settings. " ) ocio = imageio_fusion.get("ocio") diff --git a/openpype/hosts/hiero/api/lib.py b/openpype/hosts/hiero/api/lib.py index 895e95e0c0..e5d35945af 100644 --- a/openpype/hosts/hiero/api/lib.py +++ b/openpype/hosts/hiero/api/lib.py @@ -14,7 +14,7 @@ import hiero from Qt import QtWidgets from openpype.client import get_project -from openpype.settings import get_anatomy_settings +from openpype.settings import get_project_settings from openpype.pipeline import legacy_io, Anatomy from openpype.pipeline.load import filter_containers from openpype.lib import Logger @@ -878,8 +878,7 @@ def apply_colorspace_project(): project.close() # get presets for hiero - imageio = get_anatomy_settings( - project_name)["imageio"].get("hiero", None) + imageio = get_project_settings(project_name)["hiero"]["imageio"] presets = imageio.get("workfile") # save the workfile as subversion "comment:_colorspaceChange" @@ -932,8 +931,7 @@ def apply_colorspace_clips(): clips = project.clips() # get presets for hiero - imageio = get_anatomy_settings( - project_name)["imageio"].get("hiero", None) + imageio = get_project_settings(project_name)["hiero"]["imageio"] from pprint import pprint presets = imageio.get("regexInputs", {}).get("inputs", {}) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 6a8447d6ad..789dec31fa 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -23,7 +23,7 @@ from openpype.client import ( get_last_versions, get_representation_by_name ) -from openpype.api import get_anatomy_settings +from openpype.api import get_project_settings from openpype.pipeline import ( legacy_io, discover_loader_plugins, @@ -3159,7 +3159,7 @@ def set_colorspace(): """Set Colorspace from project configuration """ project_name = os.getenv("AVALON_PROJECT") - imageio = get_anatomy_settings(project_name)["imageio"]["maya"] + imageio = get_project_settings(project_name)["maya"]["imageio"] # Maya 2022+ introduces new OCIO v2 color management settings that # can override the old color managenement preferences. OpenPype has diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index e55fdbfcb2..6297da884c 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -563,7 +563,7 @@ def get_node_path(path, padding=4): def get_nuke_imageio_settings(): - return get_anatomy_settings(Context.project_name)["imageio"]["nuke"] + return get_project_settings(Context.project_name)["nuke"]["imageio"] def get_created_node_imageio_setting_legacy(nodeclass, creator, subset): diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 346773b5af..654ea367c8 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -425,7 +425,7 @@ class LoadClip(plugin.NukeLoader): colorspace = repre_data.get("colorspace") colorspace = colorspace or version_data.get("colorspace") - # colorspace from `project_anatomy/imageio/nuke/regexInputs` + # colorspace from `project_settings/nuke/imageio/regexInputs` iio_colorspace = get_imageio_input_colorspace(path) # Set colorspace defined in version data diff --git a/openpype/settings/defaults/project_anatomy/imageio.json b/openpype/settings/defaults/project_anatomy/imageio.json deleted file mode 100644 index f0be8f95f4..0000000000 --- a/openpype/settings/defaults/project_anatomy/imageio.json +++ /dev/null @@ -1,258 +0,0 @@ -{ - "hiero": { - "workfile": { - "ocioConfigName": "nuke-default", - "ocioconfigpath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpace": "linear", - "sixteenBitLut": "sRGB", - "eightBitLut": "sRGB", - "floatLut": "linear", - "logLut": "Cineon", - "viewerLut": "sRGB", - "thumbnailLut": "sRGB" - }, - "regexInputs": { - "inputs": [ - { - "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", - "colorspace": "sRGB" - } - ] - } - }, - "nuke": { - "viewer": { - "viewerProcess": "sRGB" - }, - "baking": { - "viewerProcess": "rec709" - }, - "workfile": { - "colorManagement": "Nuke", - "OCIO_config": "nuke-default", - "customOCIOConfigPath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "workingSpaceLUT": "linear", - "monitorLut": "sRGB", - "int8Lut": "sRGB", - "int16Lut": "sRGB", - "logLut": "Cineon", - "floatLut": "linear" - }, - "nodes": { - "requiredNodes": [ - { - "plugins": [ - "CreateWriteRender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "exr" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit half" - }, - { - "type": "text", - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "type": "bool", - "name": "autocrop", - "value": true - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 186, - 35, - 35, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "linear" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - }, - { - "plugins": [ - "CreateWritePrerender" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "exr" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit half" - }, - { - "type": "text", - "name": "compression", - "value": "Zip (1 scanline)" - }, - { - "type": "bool", - "name": "autocrop", - "value": true - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 171, - 171, - 10, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "linear" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - }, - { - "plugins": [ - "CreateWriteStill" - ], - "nukeNodeClass": "Write", - "knobs": [ - { - "type": "text", - "name": "file_type", - "value": "tiff" - }, - { - "type": "text", - "name": "datatype", - "value": "16 bit" - }, - { - "type": "text", - "name": "compression", - "value": "Deflate" - }, - { - "type": "color_gui", - "name": "tile_color", - "value": [ - 56, - 162, - 7, - 255 - ] - }, - { - "type": "text", - "name": "channels", - "value": "rgb" - }, - { - "type": "text", - "name": "colorspace", - "value": "sRGB" - }, - { - "type": "bool", - "name": "create_directories", - "value": true - } - ] - } - ], - "overrideNodes": [] - }, - "regexInputs": { - "inputs": [ - { - "regex": "(beauty).*(?=.exr)", - "colorspace": "linear" - } - ] - } - }, - "maya": { - "colorManagementPreference_v2": { - "enabled": true, - "configFilePath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "renderSpace": "ACEScg", - "displayName": "sRGB", - "viewName": "ACES 1.0 SDR-video" - }, - "colorManagementPreference": { - "configFilePath": { - "windows": [], - "darwin": [], - "linux": [] - }, - "renderSpace": "scene-linear Rec 709/sRGB", - "viewTransform": "sRGB gamma" - } - }, - "flame": { - "project": { - "colourPolicy": "ACES 1.1", - "frameDepth": "16-bit fp", - "fieldDominance": "PROGRESSIVE" - }, - "profilesMapping": { - "inputs": [ - { - "flameName": "ACEScg", - "ocioName": "ACES - ACEScg" - }, - { - "flameName": "Rec.709 video", - "ocioName": "Output - Rec.709" - } - ] - } - } -} \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index c90193fe13..0f3080ad64 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -1,4 +1,23 @@ { + "imageio": { + "project": { + "colourPolicy": "ACES 1.1", + "frameDepth": "16-bit fp", + "fieldDominance": "PROGRESSIVE" + }, + "profilesMapping": { + "inputs": [ + { + "flameName": "ACEScg", + "ocioName": "ACES - ACEScg" + }, + { + "flameName": "Rec.709 video", + "ocioName": "Output - Rec.709" + } + ] + } + }, "create": { "CreateShotClip": { "hierarchy": "{folder}/{sequence}", diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index e9e7199330..d2ba697305 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -1,4 +1,29 @@ { + "imageio": { + "workfile": { + "ocioConfigName": "nuke-default", + "ocioconfigpath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpace": "linear", + "sixteenBitLut": "sRGB", + "eightBitLut": "sRGB", + "floatLut": "linear", + "logLut": "Cineon", + "viewerLut": "sRGB", + "thumbnailLut": "sRGB" + }, + "regexInputs": { + "inputs": [ + { + "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", + "colorspace": "sRGB" + } + ] + } + }, "create": { "CreateShotClip": { "hierarchy": "{folder}/{sequence}", diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 76ef0a7338..c8a32d6bdf 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1,4 +1,26 @@ { + "imageio": { + "colorManagementPreference_v2": { + "enabled": true, + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "ACEScg", + "displayName": "sRGB", + "viewName": "ACES 1.0 SDR-video" + }, + "colorManagementPreference": { + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "scene-linear Rec 709/sRGB", + "viewTransform": "sRGB gamma" + } + }, "mel_workspace": "workspace -fr \"shaders\" \"renderData/shaders\";\nworkspace -fr \"images\" \"renders\";\nworkspace -fr \"particles\" \"particles\";\nworkspace -fr \"mayaAscii\" \"\";\nworkspace -fr \"mayaBinary\" \"\";\nworkspace -fr \"scene\" \"\";\nworkspace -fr \"alembicCache\" \"cache/alembic\";\nworkspace -fr \"renderData\" \"renderData\";\nworkspace -fr \"sourceImages\" \"sourceimages\";\nworkspace -fr \"fileCache\" \"cache/nCache\";\n", "ext_mapping": { "model": "ma", diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index c3eda2cbb4..e0feb06eb6 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -8,6 +8,196 @@ "build_workfile": "ctrl+alt+b" } }, + "imageio": { + "viewer": { + "viewerProcess": "sRGB" + }, + "baking": { + "viewerProcess": "rec709" + }, + "workfile": { + "colorManagement": "Nuke", + "OCIO_config": "nuke-default", + "customOCIOConfigPath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpaceLUT": "linear", + "monitorLut": "sRGB", + "int8Lut": "sRGB", + "int16Lut": "sRGB", + "logLut": "Cineon", + "floatLut": "linear" + }, + "nodes": { + "requiredNodes": [ + { + "plugins": [ + "CreateWriteRender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "type": "text", + "name": "file_type", + "value": "exr" + }, + { + "type": "text", + "name": "datatype", + "value": "16 bit half" + }, + { + "type": "text", + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "type": "bool", + "name": "autocrop", + "value": true + }, + { + "type": "color_gui", + "name": "tile_color", + "value": [ + 186, + 35, + 35, + 255 + ] + }, + { + "type": "text", + "name": "channels", + "value": "rgb" + }, + { + "type": "text", + "name": "colorspace", + "value": "linear" + }, + { + "type": "bool", + "name": "create_directories", + "value": true + } + ] + }, + { + "plugins": [ + "CreateWritePrerender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "type": "text", + "name": "file_type", + "value": "exr" + }, + { + "type": "text", + "name": "datatype", + "value": "16 bit half" + }, + { + "type": "text", + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "type": "bool", + "name": "autocrop", + "value": true + }, + { + "type": "color_gui", + "name": "tile_color", + "value": [ + 171, + 171, + 10, + 255 + ] + }, + { + "type": "text", + "name": "channels", + "value": "rgb" + }, + { + "type": "text", + "name": "colorspace", + "value": "linear" + }, + { + "type": "bool", + "name": "create_directories", + "value": true + } + ] + }, + { + "plugins": [ + "CreateWriteStill" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "type": "text", + "name": "file_type", + "value": "tiff" + }, + { + "type": "text", + "name": "datatype", + "value": "16 bit" + }, + { + "type": "text", + "name": "compression", + "value": "Deflate" + }, + { + "type": "color_gui", + "name": "tile_color", + "value": [ + 56, + 162, + 7, + 255 + ] + }, + { + "type": "text", + "name": "channels", + "value": "rgb" + }, + { + "type": "text", + "name": "colorspace", + "value": "sRGB" + }, + { + "type": "bool", + "name": "create_directories", + "value": true + } + ] + } + ], + "overrideNodes": [] + }, + "regexInputs": { + "inputs": [ + { + "regex": "(beauty).*(?=.exr)", + "colorspace": "linear" + } + ] + } + }, "nuke-dirmap": { "enabled": false, "paths": { diff --git a/openpype/settings/entities/schemas/projects_schema/schema_main.json b/openpype/settings/entities/schemas/projects_schema/schema_main.json index 0b9fbf7470..0f4afc54ce 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_main.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_main.json @@ -43,10 +43,6 @@ } ] } - }, - { - "type": "schema", - "name": "schema_anatomy_imageio" } ] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 5f05bef0e1..73664300aa 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -5,6 +5,69 @@ "label": "Flame", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "children": [ + { + "key": "project", + "type": "dict", + "label": "Project", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "text", + "key": "colourPolicy", + "label": "Colour Policy (name or path)" + }, + { + "type": "text", + "key": "frameDepth", + "label": "Image Depth" + }, + { + "type": "text", + "key": "fieldDominance", + "label": "Field Dominance" + } + ] + } + ] + }, + { + "key": "profilesMapping", + "type": "dict", + "label": "Profile names mapping", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "flameName", + "label": "Flame name" + }, + { + "type": "text", + "key": "ocioName", + "label": "OCIO name" + } + ] + } + } + ] + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json index 3108d2197e..9e18522def 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json @@ -5,6 +5,116 @@ "label": "Hiero", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "is_group": true, + "collapsible": true, + "children": [ + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "ocioConfigName", + "label": "OpenColorIO Config", + "enum_items": [ + { + "nuke-default": "nuke-default" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" + } + ] + }, + { + "type": "path", + "key": "ocioconfigpath", + "label": "Custom OCIO path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "workingSpace", + "label": "Working Space" + }, + { + "type": "text", + "key": "sixteenBitLut", + "label": "16 Bit Files" + }, + { + "type": "text", + "key": "eightBitLut", + "label": "8 Bit Files" + }, + { + "type": "text", + "key": "floatLut", + "label": "Floating Point Files" + }, + { + "type": "text", + "key": "logLut", + "label": "Log Files" + }, + { + "type": "text", + "key": "viewerLut", + "label": "Viewer" + }, + { + "type": "text", + "key": "thumbnailLut", + "label": "Thumbnails" + } + ] + } + ] + }, + { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, + { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json index d7a2b086d9..b2d79797a3 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -5,6 +5,76 @@ "label": "Maya", "is_file": true, "children": [ + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "collapsible": true, + "is_group": true, + "children": [ + { + "key": "colorManagementPreference_v2", + "type": "dict", + "label": "Color Management Preference v2 (Maya 2022+)", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Use Color Management Preference v2" + }, + { + "type": "path", + "key": "configFilePath", + "label": "OCIO Config File Path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "renderSpace", + "label": "Rendering Space" + }, + { + "type": "text", + "key": "displayName", + "label": "Display" + }, + { + "type": "text", + "key": "viewName", + "label": "View" + } + ] + }, + { + "key": "colorManagementPreference", + "type": "dict", + "label": "Color Management Preference (legacy)", + "collapsible": true, + "children": [ + { + "type": "path", + "key": "configFilePath", + "label": "OCIO Config File Path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "renderSpace", + "label": "Rendering Space" + }, + { + "type": "text", + "key": "viewTransform", + "label": "Viewer Transform" + } + ] + } + ] + }, { "type": "text", "multiline" : true, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json index 7cf82b9e69..ff341fb919 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -46,6 +46,254 @@ } ] }, + { + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "collapsible": true, + "is_group": true, + "children": [ + { + "key": "viewer", + "type": "dict", + "label": "Viewer", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, + { + "key": "baking", + "type": "dict", + "label": "Extract-review baking profile", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "colorManagement", + "label": "color management", + "enum_items": [ + { + "Nuke": "Nuke" + }, + { + "OCIO": "OCIO" + } + ] + }, + { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [ + { + "nuke-default": "nuke-default" + }, + { + "spi-vfx": "spi-vfx" + }, + { + "spi-anim": "spi-anim" + }, + { + "aces_0.1.1": "aces_0.1.1" + }, + { + "aces_0.7.1": "aces_0.7.1" + }, + { + "aces_1.0.1": "aces_1.0.1" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "aces_1.2": "aces_1.2" + }, + { + "custom": "custom" + } + ] + }, + { + "type": "path", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "workingSpaceLUT", + "label": "Working Space" + }, + { + "type": "text", + "key": "monitorLut", + "label": "monitor" + }, + { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, + { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, + { + "type": "text", + "key": "logLut", + "label": "log files" + }, + { + "type": "text", + "key": "floatLut", + "label": "float files" + } + ] + } + ] + }, + { + "key": "nodes", + "type": "dict", + "label": "Nodes", + "collapsible": true, + "children": [ + { + "key": "requiredNodes", + "type": "list", + "label": "Plugin required", + "object_type": { + "type": "dict", + "children": [ + { + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass" + } + }, + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Knobs", + "key": "knobs" + } + ] + } + + ] + } + }, + { + "type": "splitter" + }, + { + "type": "list", + "key": "overrideNodes", + "label": "Plugin's node overrides", + "object_type": { + "type": "dict", + "children": [ + { + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass" + } + }, + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, + { + "key": "subsets", + "label": "Subsets", + "type": "list", + "object_type": "text" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Knobs overrides", + "key": "knobs" + } + ] + } + ] + } + } + ] + }, + { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, + { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json deleted file mode 100644 index ef8c907dda..0000000000 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ /dev/null @@ -1,493 +0,0 @@ -{ - "type": "dict", - "key": "imageio", - "label": "Color Management and Output Formats", - "is_file": true, - "is_group": true, - "children": [ - { - "key": "hiero", - "type": "dict", - "label": "Hiero", - "children": [ - { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsible": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "ocioConfigName", - "label": "OpenColorIO Config", - "enum_items": [ - { - "nuke-default": "nuke-default" - }, - { - "aces_1.0.3": "aces_1.0.3" - }, - { - "aces_1.1": "aces_1.1" - }, - { - "custom": "custom" - } - ] - }, - { - "type": "path", - "key": "ocioconfigpath", - "label": "Custom OCIO path", - "multiplatform": true, - "multipath": true - }, - { - "type": "text", - "key": "workingSpace", - "label": "Working Space" - }, - { - "type": "text", - "key": "sixteenBitLut", - "label": "16 Bit Files" - }, - { - "type": "text", - "key": "eightBitLut", - "label": "8 Bit Files" - }, - { - "type": "text", - "key": "floatLut", - "label": "Floating Point Files" - }, - { - "type": "text", - "key": "logLut", - "label": "Log Files" - }, - { - "type": "text", - "key": "viewerLut", - "label": "Viewer" - }, - { - "type": "text", - "key": "thumbnailLut", - "label": "Thumbnails" - } - ] - } - ] - }, - { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsible": true, - "children": [ - { - "type": "list", - "key": "inputs", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "regex", - "label": "Regex" - }, - { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - } - ] - } - } - ] - } - ] - }, - { - "key": "nuke", - "type": "dict", - "label": "Nuke", - "children": [ - { - "key": "viewer", - "type": "dict", - "label": "Viewer", - "collapsible": false, - "children": [ - { - "type": "text", - "key": "viewerProcess", - "label": "Viewer Process" - } - ] - }, - { - "key": "baking", - "type": "dict", - "label": "Extract-review baking profile", - "collapsible": false, - "children": [ - { - "type": "text", - "key": "viewerProcess", - "label": "Viewer Process" - } - ] - }, - { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsible": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "colorManagement", - "label": "color management", - "enum_items": [ - { - "Nuke": "Nuke" - }, - { - "OCIO": "OCIO" - } - ] - }, - { - "type": "enum", - "key": "OCIO_config", - "label": "OpenColorIO Config", - "enum_items": [ - { - "nuke-default": "nuke-default" - }, - { - "spi-vfx": "spi-vfx" - }, - { - "spi-anim": "spi-anim" - }, - { - "aces_0.1.1": "aces_0.1.1" - }, - { - "aces_0.7.1": "aces_0.7.1" - }, - { - "aces_1.0.1": "aces_1.0.1" - }, - { - "aces_1.0.3": "aces_1.0.3" - }, - { - "aces_1.1": "aces_1.1" - }, - { - "aces_1.2": "aces_1.2" - }, - { - "custom": "custom" - } - ] - }, - { - "type": "path", - "key": "customOCIOConfigPath", - "label": "Custom OCIO config path", - "multiplatform": true, - "multipath": true - }, - { - "type": "text", - "key": "workingSpaceLUT", - "label": "Working Space" - }, - { - "type": "text", - "key": "monitorLut", - "label": "monitor" - }, - { - "type": "text", - "key": "int8Lut", - "label": "8-bit files" - }, - { - "type": "text", - "key": "int16Lut", - "label": "16-bit files" - }, - { - "type": "text", - "key": "logLut", - "label": "log files" - }, - { - "type": "text", - "key": "floatLut", - "label": "float files" - } - ] - } - ] - }, - { - "key": "nodes", - "type": "dict", - "label": "Nodes", - "collapsible": true, - "children": [ - { - "key": "requiredNodes", - "type": "list", - "label": "Plugin required", - "object_type": { - "type": "dict", - "children": [ - { - "type": "list", - "key": "plugins", - "label": "Used in plugins", - "object_type": { - "type": "text", - "key": "pluginClass" - } - }, - { - "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, - { - "type": "schema_template", - "name": "template_nuke_knob_inputs", - "template_data": [ - { - "label": "Knobs", - "key": "knobs" - } - ] - } - - ] - } - }, - { - "type": "splitter" - }, - { - "type": "list", - "key": "overrideNodes", - "label": "Plugin's node overrides", - "object_type": { - "type": "dict", - "children": [ - { - "type": "list", - "key": "plugins", - "label": "Used in plugins", - "object_type": { - "type": "text", - "key": "pluginClass" - } - }, - { - "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, - { - "key": "subsets", - "label": "Subsets", - "type": "list", - "object_type": "text" - }, - { - "type": "schema_template", - "name": "template_nuke_knob_inputs", - "template_data": [ - { - "label": "Knobs overrides", - "key": "knobs" - } - ] - } - ] - } - } - ] - }, - { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsible": true, - "children": [ - { - "type": "list", - "key": "inputs", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "regex", - "label": "Regex" - }, - { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - } - ] - } - } - ] - } - ] - }, - { - "key": "maya", - "type": "dict", - "label": "Maya", - "children": [ - { - "key": "colorManagementPreference_v2", - "type": "dict", - "label": "Color Management Preference v2 (Maya 2022+)", - "collapsible": true, - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Use Color Management Preference v2" - }, - { - "type": "path", - "key": "configFilePath", - "label": "OCIO Config File Path", - "multiplatform": true, - "multipath": true - }, - { - "type": "text", - "key": "renderSpace", - "label": "Rendering Space" - }, - { - "type": "text", - "key": "displayName", - "label": "Display" - }, - { - "type": "text", - "key": "viewName", - "label": "View" - } - ] - }, - { - "key": "colorManagementPreference", - "type": "dict", - "label": "Color Management Preference (legacy)", - "collapsible": true, - "children": [ - { - "type": "path", - "key": "configFilePath", - "label": "OCIO Config File Path", - "multiplatform": true, - "multipath": true - }, - { - "type": "text", - "key": "renderSpace", - "label": "Rendering Space" - }, - { - "type": "text", - "key": "viewTransform", - "label": "Viewer Transform" - } - ] - } - ] - }, - { - "key": "flame", - "type": "dict", - "label": "Flame & Flare", - "children": [ - { - "key": "project", - "type": "dict", - "label": "Project", - "collapsible": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "text", - "key": "colourPolicy", - "label": "Colour Policy (name or path)" - }, - { - "type": "text", - "key": "frameDepth", - "label": "Image Depth" - }, - { - "type": "text", - "key": "fieldDominance", - "label": "Field Dominance" - } - ] - } - ] - }, - { - "key": "profilesMapping", - "type": "dict", - "label": "Profile names mapping", - "collapsible": true, - "children": [ - { - "type": "list", - "key": "inputs", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "flameName", - "label": "Flame name" - }, - { - "type": "text", - "key": "ocioName", - "label": "OCIO name" - } - ] - } - } - ] - } - ] - } - ] -} diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index 5eaddf6e6e..3112400dbf 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -316,22 +316,6 @@ def _system_settings_backwards_compatible_conversion(studio_overrides): } -def _project_anatomy_backwards_compatible_conversion(project_anatomy): - # Backwards compatibility of node settings in Nuke 3.9.x - 3.10.0 - # - source PR - https://github.com/pypeclub/OpenPype/pull/3143 - value = project_anatomy - for key in ("imageio", "nuke", "nodes", "requiredNodes"): - if key not in value: - return - value = value[key] - - for item in value: - for node in item.get("knobs") or []: - if "type" in node: - break - node["type"] = "__legacy__" - - @require_handler def get_studio_system_settings_overrides(return_version=False): output = _SETTINGS_HANDLER.get_studio_system_settings_overrides( @@ -368,7 +352,6 @@ def get_project_settings_overrides(project_name, return_version=False): @require_handler def get_project_anatomy_overrides(project_name): output = _SETTINGS_HANDLER.get_project_anatomy_overrides(project_name) - _project_anatomy_backwards_compatible_conversion(output) return output From 843b52cbdb0433a3284a4f894749d2f8503043f7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 23 Sep 2022 21:39:39 +0200 Subject: [PATCH 02/55] Remove imageio from config-2.0 --- schema/config-2.0.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/schema/config-2.0.json b/schema/config-2.0.json index 54b226711a..c20f0a3f46 100644 --- a/schema/config-2.0.json +++ b/schema/config-2.0.json @@ -23,9 +23,6 @@ "roots": { "type": "object" }, - "imageio": { - "type": "object" - }, "tasks": { "type": "object", "items": { From 4a98d8de3916db4aa34a362e1cd89b97f73101f7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 24 Sep 2022 12:03:35 +0200 Subject: [PATCH 03/55] Revert removal of anatomy imageio settings - This way project save will not delete the old settings --- .../defaults/project_anatomy/imageio.json | 258 +++++++++ .../schemas/projects_schema/schema_main.json | 4 + .../schemas/schema_anatomy_imageio.json | 493 ++++++++++++++++++ 3 files changed, 755 insertions(+) create mode 100644 openpype/settings/defaults/project_anatomy/imageio.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json diff --git a/openpype/settings/defaults/project_anatomy/imageio.json b/openpype/settings/defaults/project_anatomy/imageio.json new file mode 100644 index 0000000000..f0be8f95f4 --- /dev/null +++ b/openpype/settings/defaults/project_anatomy/imageio.json @@ -0,0 +1,258 @@ +{ + "hiero": { + "workfile": { + "ocioConfigName": "nuke-default", + "ocioconfigpath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpace": "linear", + "sixteenBitLut": "sRGB", + "eightBitLut": "sRGB", + "floatLut": "linear", + "logLut": "Cineon", + "viewerLut": "sRGB", + "thumbnailLut": "sRGB" + }, + "regexInputs": { + "inputs": [ + { + "regex": "[^-a-zA-Z0-9](plateRef).*(?=mp4)", + "colorspace": "sRGB" + } + ] + } + }, + "nuke": { + "viewer": { + "viewerProcess": "sRGB" + }, + "baking": { + "viewerProcess": "rec709" + }, + "workfile": { + "colorManagement": "Nuke", + "OCIO_config": "nuke-default", + "customOCIOConfigPath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "workingSpaceLUT": "linear", + "monitorLut": "sRGB", + "int8Lut": "sRGB", + "int16Lut": "sRGB", + "logLut": "Cineon", + "floatLut": "linear" + }, + "nodes": { + "requiredNodes": [ + { + "plugins": [ + "CreateWriteRender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "type": "text", + "name": "file_type", + "value": "exr" + }, + { + "type": "text", + "name": "datatype", + "value": "16 bit half" + }, + { + "type": "text", + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "type": "bool", + "name": "autocrop", + "value": true + }, + { + "type": "color_gui", + "name": "tile_color", + "value": [ + 186, + 35, + 35, + 255 + ] + }, + { + "type": "text", + "name": "channels", + "value": "rgb" + }, + { + "type": "text", + "name": "colorspace", + "value": "linear" + }, + { + "type": "bool", + "name": "create_directories", + "value": true + } + ] + }, + { + "plugins": [ + "CreateWritePrerender" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "type": "text", + "name": "file_type", + "value": "exr" + }, + { + "type": "text", + "name": "datatype", + "value": "16 bit half" + }, + { + "type": "text", + "name": "compression", + "value": "Zip (1 scanline)" + }, + { + "type": "bool", + "name": "autocrop", + "value": true + }, + { + "type": "color_gui", + "name": "tile_color", + "value": [ + 171, + 171, + 10, + 255 + ] + }, + { + "type": "text", + "name": "channels", + "value": "rgb" + }, + { + "type": "text", + "name": "colorspace", + "value": "linear" + }, + { + "type": "bool", + "name": "create_directories", + "value": true + } + ] + }, + { + "plugins": [ + "CreateWriteStill" + ], + "nukeNodeClass": "Write", + "knobs": [ + { + "type": "text", + "name": "file_type", + "value": "tiff" + }, + { + "type": "text", + "name": "datatype", + "value": "16 bit" + }, + { + "type": "text", + "name": "compression", + "value": "Deflate" + }, + { + "type": "color_gui", + "name": "tile_color", + "value": [ + 56, + 162, + 7, + 255 + ] + }, + { + "type": "text", + "name": "channels", + "value": "rgb" + }, + { + "type": "text", + "name": "colorspace", + "value": "sRGB" + }, + { + "type": "bool", + "name": "create_directories", + "value": true + } + ] + } + ], + "overrideNodes": [] + }, + "regexInputs": { + "inputs": [ + { + "regex": "(beauty).*(?=.exr)", + "colorspace": "linear" + } + ] + } + }, + "maya": { + "colorManagementPreference_v2": { + "enabled": true, + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "ACEScg", + "displayName": "sRGB", + "viewName": "ACES 1.0 SDR-video" + }, + "colorManagementPreference": { + "configFilePath": { + "windows": [], + "darwin": [], + "linux": [] + }, + "renderSpace": "scene-linear Rec 709/sRGB", + "viewTransform": "sRGB gamma" + } + }, + "flame": { + "project": { + "colourPolicy": "ACES 1.1", + "frameDepth": "16-bit fp", + "fieldDominance": "PROGRESSIVE" + }, + "profilesMapping": { + "inputs": [ + { + "flameName": "ACEScg", + "ocioName": "ACES - ACEScg" + }, + { + "flameName": "Rec.709 video", + "ocioName": "Output - Rec.709" + } + ] + } + } +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_main.json b/openpype/settings/entities/schemas/projects_schema/schema_main.json index 0f4afc54ce..0b9fbf7470 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_main.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_main.json @@ -43,6 +43,10 @@ } ] } + }, + { + "type": "schema", + "name": "schema_anatomy_imageio" } ] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json new file mode 100644 index 0000000000..ef8c907dda --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -0,0 +1,493 @@ +{ + "type": "dict", + "key": "imageio", + "label": "Color Management and Output Formats", + "is_file": true, + "is_group": true, + "children": [ + { + "key": "hiero", + "type": "dict", + "label": "Hiero", + "children": [ + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "ocioConfigName", + "label": "OpenColorIO Config", + "enum_items": [ + { + "nuke-default": "nuke-default" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "custom": "custom" + } + ] + }, + { + "type": "path", + "key": "ocioconfigpath", + "label": "Custom OCIO path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "workingSpace", + "label": "Working Space" + }, + { + "type": "text", + "key": "sixteenBitLut", + "label": "16 Bit Files" + }, + { + "type": "text", + "key": "eightBitLut", + "label": "8 Bit Files" + }, + { + "type": "text", + "key": "floatLut", + "label": "Floating Point Files" + }, + { + "type": "text", + "key": "logLut", + "label": "Log Files" + }, + { + "type": "text", + "key": "viewerLut", + "label": "Viewer" + }, + { + "type": "text", + "key": "thumbnailLut", + "label": "Thumbnails" + } + ] + } + ] + }, + { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, + { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + }, + { + "key": "nuke", + "type": "dict", + "label": "Nuke", + "children": [ + { + "key": "viewer", + "type": "dict", + "label": "Viewer", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, + { + "key": "baking", + "type": "dict", + "label": "Extract-review baking profile", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "colorManagement", + "label": "color management", + "enum_items": [ + { + "Nuke": "Nuke" + }, + { + "OCIO": "OCIO" + } + ] + }, + { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [ + { + "nuke-default": "nuke-default" + }, + { + "spi-vfx": "spi-vfx" + }, + { + "spi-anim": "spi-anim" + }, + { + "aces_0.1.1": "aces_0.1.1" + }, + { + "aces_0.7.1": "aces_0.7.1" + }, + { + "aces_1.0.1": "aces_1.0.1" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "aces_1.2": "aces_1.2" + }, + { + "custom": "custom" + } + ] + }, + { + "type": "path", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "workingSpaceLUT", + "label": "Working Space" + }, + { + "type": "text", + "key": "monitorLut", + "label": "monitor" + }, + { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, + { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, + { + "type": "text", + "key": "logLut", + "label": "log files" + }, + { + "type": "text", + "key": "floatLut", + "label": "float files" + } + ] + } + ] + }, + { + "key": "nodes", + "type": "dict", + "label": "Nodes", + "collapsible": true, + "children": [ + { + "key": "requiredNodes", + "type": "list", + "label": "Plugin required", + "object_type": { + "type": "dict", + "children": [ + { + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass" + } + }, + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Knobs", + "key": "knobs" + } + ] + } + + ] + } + }, + { + "type": "splitter" + }, + { + "type": "list", + "key": "overrideNodes", + "label": "Plugin's node overrides", + "object_type": { + "type": "dict", + "children": [ + { + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass" + } + }, + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, + { + "key": "subsets", + "label": "Subsets", + "type": "list", + "object_type": "text" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Knobs overrides", + "key": "knobs" + } + ] + } + ] + } + } + ] + }, + { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, + { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] + }, + { + "key": "maya", + "type": "dict", + "label": "Maya", + "children": [ + { + "key": "colorManagementPreference_v2", + "type": "dict", + "label": "Color Management Preference v2 (Maya 2022+)", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Use Color Management Preference v2" + }, + { + "type": "path", + "key": "configFilePath", + "label": "OCIO Config File Path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "renderSpace", + "label": "Rendering Space" + }, + { + "type": "text", + "key": "displayName", + "label": "Display" + }, + { + "type": "text", + "key": "viewName", + "label": "View" + } + ] + }, + { + "key": "colorManagementPreference", + "type": "dict", + "label": "Color Management Preference (legacy)", + "collapsible": true, + "children": [ + { + "type": "path", + "key": "configFilePath", + "label": "OCIO Config File Path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "renderSpace", + "label": "Rendering Space" + }, + { + "type": "text", + "key": "viewTransform", + "label": "Viewer Transform" + } + ] + } + ] + }, + { + "key": "flame", + "type": "dict", + "label": "Flame & Flare", + "children": [ + { + "key": "project", + "type": "dict", + "label": "Project", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "text", + "key": "colourPolicy", + "label": "Colour Policy (name or path)" + }, + { + "type": "text", + "key": "frameDepth", + "label": "Image Depth" + }, + { + "type": "text", + "key": "fieldDominance", + "label": "Field Dominance" + } + ] + } + ] + }, + { + "key": "profilesMapping", + "type": "dict", + "label": "Profile names mapping", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "flameName", + "label": "Flame name" + }, + { + "type": "text", + "key": "ocioName", + "label": "OCIO name" + } + ] + } + } + ] + } + ] + } + ] +} From 9ff7d5665304ccf16533a2a3e17f272cc0ab3697 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 24 Sep 2022 12:04:03 +0200 Subject: [PATCH 04/55] Revert imageio removal from project anatomy in config schema --- schema/config-2.0.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/schema/config-2.0.json b/schema/config-2.0.json index c20f0a3f46..54b226711a 100644 --- a/schema/config-2.0.json +++ b/schema/config-2.0.json @@ -23,6 +23,9 @@ "roots": { "type": "object" }, + "imageio": { + "type": "object" + }, "tasks": { "type": "object", "items": { From 92371d54fa30b53ca43c630062e4ace522137a82 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 24 Sep 2022 12:09:52 +0200 Subject: [PATCH 05/55] Add deprecation labels --- .../projects_schema/schemas/schema_anatomy_imageio.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json index ef8c907dda..93b6adae6b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -1,10 +1,14 @@ { "type": "dict", "key": "imageio", - "label": "Color Management and Output Formats", + "label": "Color Management and Output Formats (Deprecated)", "is_file": true, "is_group": true, "children": [ + { + "type": "label", + "label": "These settings are deprecated and have moved to: project_settings/{app}/imageio.
You can right click to copy each host's values and paste them to apply to each host as needed.
Changing these values here will not do anything." + }, { "key": "hiero", "type": "dict", From a3e795aa37d99452728e67e7bb2cea0b27f514e9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Oct 2022 11:51:52 +0200 Subject: [PATCH 06/55] import setting functions from 'openpype.settings' --- openpype/hosts/flame/api/plugin.py | 4 ++-- openpype/hosts/hiero/api/plugin.py | 4 ++-- openpype/hosts/maya/api/lib.py | 2 +- openpype/hosts/maya/plugins/create/create_render.py | 2 +- .../hosts/maya/plugins/create/create_unreal_staticmesh.py | 2 +- openpype/hosts/maya/plugins/create/create_vrayscene.py | 2 +- openpype/hosts/maya/plugins/load/load_ass.py | 2 +- openpype/hosts/maya/plugins/load/load_gpucache.py | 2 +- openpype/hosts/maya/plugins/load/load_redshift_proxy.py | 2 +- openpype/hosts/maya/plugins/load/load_reference.py | 2 +- openpype/hosts/maya/plugins/load/load_vdb_to_arnold.py | 2 +- openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py | 2 +- openpype/hosts/maya/plugins/load/load_vdb_to_vray.py | 2 +- openpype/hosts/maya/plugins/load/load_vrayproxy.py | 2 +- openpype/hosts/maya/plugins/load/load_vrayscene.py | 2 +- openpype/hosts/maya/plugins/load/load_yeti_cache.py | 2 +- openpype/hosts/maya/plugins/load/load_yeti_rig.py | 2 +- openpype/hosts/maya/plugins/publish/submit_maya_muster.py | 2 +- openpype/hosts/maya/startup/userSetup.py | 2 +- openpype/hosts/nuke/api/pipeline.py | 4 +--- openpype/hosts/nuke/api/plugin.py | 2 +- openpype/hosts/resolve/api/plugin.py | 2 +- .../traypublisher/plugins/create/create_from_settings.py | 2 +- openpype/hosts/tvpaint/api/pipeline.py | 2 +- openpype/hosts/unreal/plugins/load/load_layout.py | 2 +- .../event_handlers_user/action_create_cust_attrs.py | 2 +- .../modules/ftrack/launch_hooks/post_ftrack_changes.py | 2 +- openpype/modules/job_queue/module.py | 2 +- openpype/modules/kitsu/utils/update_zou_with_op.py | 2 +- openpype/modules/shotgrid/lib/settings.py | 2 +- openpype/plugins/publish/collect_settings.py | 5 ++++- openpype/tools/creator/window.py | 2 +- openpype/tools/pyblish_pype/control.py | 2 +- openpype/tools/pyblish_pype/model.py | 2 +- openpype/tools/settings/local_settings/window.py | 8 ++++---- openpype/tools/utils/lib.py | 2 +- openpype/widgets/password_dialog.py | 2 +- 37 files changed, 45 insertions(+), 44 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 4bbdc79621..092ce9d106 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -6,9 +6,9 @@ from xml.etree import ElementTree as ET from Qt import QtCore, QtWidgets -import openpype.api as openpype import qargparse from openpype import style +from openpype.settings import get_current_project_settings from openpype.lib import Logger from openpype.pipeline import LegacyCreator, LoaderPlugin @@ -306,7 +306,7 @@ class Creator(LegacyCreator): def __init__(self, *args, **kwargs): super(Creator, self).__init__(*args, **kwargs) - self.presets = openpype.get_current_project_settings()[ + self.presets = get_current_project_settings()[ "flame"]["create"].get(self.__class__.__name__, {}) # adding basic current context flame objects diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py index 77fedbbbdc..ea8a9e836a 100644 --- a/openpype/hosts/hiero/api/plugin.py +++ b/openpype/hosts/hiero/api/plugin.py @@ -8,7 +8,7 @@ import hiero from Qt import QtWidgets, QtCore import qargparse -import openpype.api as openpype +from openpype.settings import get_current_project_settings from openpype.lib import Logger from openpype.pipeline import LoaderPlugin, LegacyCreator from openpype.pipeline.context_tools import get_current_project_asset @@ -606,7 +606,7 @@ class Creator(LegacyCreator): def __init__(self, *args, **kwargs): super(Creator, self).__init__(*args, **kwargs) import openpype.hosts.hiero.api as phiero - self.presets = openpype.get_current_project_settings()[ + self.presets = get_current_project_settings()[ "hiero"]["create"].get(self.__class__.__name__, {}) # adding basic current context resolve objects diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 6a8447d6ad..e86ec56bbc 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -23,7 +23,7 @@ from openpype.client import ( get_last_versions, get_representation_by_name ) -from openpype.api import get_anatomy_settings +from openpype.settings import get_anatomy_settings from openpype.pipeline import ( legacy_io, discover_loader_plugins, diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 5418ec1f2f..2b2c978d3c 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -9,7 +9,7 @@ import requests from maya import cmds from maya.app.renderSetup.model import renderSetup -from openpype.api import ( +from openpype.settings import ( get_system_settings, get_project_settings, ) diff --git a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py index 4e4417ff34..44cbee0502 100644 --- a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator for Unreal Static Meshes.""" from openpype.hosts.maya.api import plugin, lib -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import legacy_io from maya import cmds # noqa diff --git a/openpype/hosts/maya/plugins/create/create_vrayscene.py b/openpype/hosts/maya/plugins/create/create_vrayscene.py index 45c4b7e443..59d80e6d5b 100644 --- a/openpype/hosts/maya/plugins/create/create_vrayscene.py +++ b/openpype/hosts/maya/plugins/create/create_vrayscene.py @@ -12,7 +12,7 @@ from openpype.hosts.maya.api import ( lib, plugin ) -from openpype.api import ( +from openpype.settings import ( get_system_settings, get_project_settings ) diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index d1b12ceaba..5db6fc3dfa 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -1,7 +1,7 @@ import os import clique -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_gpucache.py b/openpype/hosts/maya/plugins/load/load_gpucache.py index 179819f904..a09f924c7b 100644 --- a/openpype/hosts/maya/plugins/load/load_gpucache.py +++ b/openpype/hosts/maya/plugins/load/load_gpucache.py @@ -4,7 +4,7 @@ from openpype.pipeline import ( load, get_representation_path ) -from openpype.api import get_project_settings +from openpype.settings import get_project_settings class GpuCacheLoader(load.LoaderPlugin): diff --git a/openpype/hosts/maya/plugins/load/load_redshift_proxy.py b/openpype/hosts/maya/plugins/load/load_redshift_proxy.py index d93a9f02a2..c288e23ded 100644 --- a/openpype/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/openpype/hosts/maya/plugins/load/load_redshift_proxy.py @@ -5,7 +5,7 @@ import clique import maya.cmds as cmds -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 5a06661df9..c762a29326 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -1,7 +1,7 @@ import os from maya import cmds -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import legacy_io from openpype.pipeline.create import ( legacy_create, diff --git a/openpype/hosts/maya/plugins/load/load_vdb_to_arnold.py b/openpype/hosts/maya/plugins/load/load_vdb_to_arnold.py index d458c5abda..8a386cecfd 100644 --- a/openpype/hosts/maya/plugins/load/load_vdb_to_arnold.py +++ b/openpype/hosts/maya/plugins/load/load_vdb_to_arnold.py @@ -1,6 +1,6 @@ import os -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py b/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py index c6a69dfe35..1f02321dc8 100644 --- a/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -1,6 +1,6 @@ import os -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py b/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py index 3a16264ec0..9267c59c02 100644 --- a/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -1,6 +1,6 @@ import os -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index e3d6166d3a..720a132aa7 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -10,7 +10,7 @@ import os import maya.cmds as cmds from openpype.client import get_representation_by_name -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( legacy_io, load, diff --git a/openpype/hosts/maya/plugins/load/load_vrayscene.py b/openpype/hosts/maya/plugins/load/load_vrayscene.py index 61132088cc..d87992f9a7 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayscene.py +++ b/openpype/hosts/maya/plugins/load/load_vrayscene.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os import maya.cmds as cmds # noqa -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_yeti_cache.py b/openpype/hosts/maya/plugins/load/load_yeti_cache.py index 8435ba2493..e928136f71 100644 --- a/openpype/hosts/maya/plugins/load/load_yeti_cache.py +++ b/openpype/hosts/maya/plugins/load/load_yeti_cache.py @@ -6,7 +6,7 @@ from collections import defaultdict import clique from maya import cmds -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( load, get_representation_path diff --git a/openpype/hosts/maya/plugins/load/load_yeti_rig.py b/openpype/hosts/maya/plugins/load/load_yeti_rig.py index 4b730ad2c1..651607de8a 100644 --- a/openpype/hosts/maya/plugins/load/load_yeti_rig.py +++ b/openpype/hosts/maya/plugins/load/load_yeti_rig.py @@ -1,7 +1,7 @@ import os from collections import defaultdict -from openpype.api import get_project_settings +from openpype.settings import get_project_settings import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api import lib diff --git a/openpype/hosts/maya/plugins/publish/submit_maya_muster.py b/openpype/hosts/maya/plugins/publish/submit_maya_muster.py index c4250a20bd..df06220fd3 100644 --- a/openpype/hosts/maya/plugins/publish/submit_maya_muster.py +++ b/openpype/hosts/maya/plugins/publish/submit_maya_muster.py @@ -11,7 +11,7 @@ import pyblish.api from openpype.lib import requests_post from openpype.hosts.maya.api import lib from openpype.pipeline import legacy_io -from openpype.api import get_system_settings +from openpype.settings import get_system_settings # mapping between Maya renderer names and Muster template ids diff --git a/openpype/hosts/maya/startup/userSetup.py b/openpype/hosts/maya/startup/userSetup.py index 10e68c2ddb..40cd51f2d8 100644 --- a/openpype/hosts/maya/startup/userSetup.py +++ b/openpype/hosts/maya/startup/userSetup.py @@ -1,5 +1,5 @@ import os -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import install_host from openpype.hosts.maya.api import MayaHost from maya import cmds diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index b347fc0d09..7db420f6af 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -7,9 +7,7 @@ import nuke import pyblish.api import openpype -from openpype.api import ( - get_current_project_settings -) +from openpype.settings import get_current_project_settings from openpype.lib import register_event_callback, Logger from openpype.pipeline import ( register_loader_plugin_path, diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 37ce03dc55..91bb90ff99 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -6,7 +6,7 @@ from abc import abstractmethod import nuke -from openpype.api import get_current_project_settings +from openpype.settings import get_current_project_settings from openpype.pipeline import ( LegacyCreator, LoaderPlugin, diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index b03125d502..3995077d21 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -504,7 +504,7 @@ class Creator(LegacyCreator): def __init__(self, *args, **kwargs): super(Creator, self).__init__(*args, **kwargs) - from openpype.api import get_current_project_settings + from openpype.settings import get_current_project_settings resolve_p_settings = get_current_project_settings().get("resolve") self.presets = {} if resolve_p_settings: diff --git a/openpype/hosts/traypublisher/plugins/create/create_from_settings.py b/openpype/hosts/traypublisher/plugins/create/create_from_settings.py index 5d80c20309..df6253b0c2 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_from_settings.py +++ b/openpype/hosts/traypublisher/plugins/create/create_from_settings.py @@ -1,6 +1,6 @@ import os from openpype.lib import Logger -from openpype.api import get_project_settings +from openpype.settings import get_project_settings log = Logger.get_logger(__name__) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 427c927264..cbaa059809 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -10,7 +10,7 @@ import pyblish.api from openpype.client import get_project, get_asset_by_name from openpype.hosts import tvpaint -from openpype.api import get_current_project_settings +from openpype.settings import get_current_project_settings from openpype.lib import register_event_callback from openpype.pipeline import ( legacy_io, diff --git a/openpype/hosts/unreal/plugins/load/load_layout.py b/openpype/hosts/unreal/plugins/load/load_layout.py index 926c932a85..c1d66ddf2a 100644 --- a/openpype/hosts/unreal/plugins/load/load_layout.py +++ b/openpype/hosts/unreal/plugins/load/load_layout.py @@ -24,7 +24,7 @@ from openpype.pipeline import ( legacy_io, ) from openpype.pipeline.context_tools import get_current_project_asset -from openpype.api import get_current_project_settings +from openpype.settings import get_current_project_settings from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline diff --git a/openpype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py b/openpype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py index d04440a564..c19cfd1502 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py +++ b/openpype/modules/ftrack/event_handlers_user/action_create_cust_attrs.py @@ -18,7 +18,7 @@ from openpype_modules.ftrack.lib import ( tool_definitions_from_app_manager ) -from openpype.api import get_system_settings +from openpype.settings import get_system_settings from openpype.lib import ApplicationManager """ diff --git a/openpype/modules/ftrack/launch_hooks/post_ftrack_changes.py b/openpype/modules/ftrack/launch_hooks/post_ftrack_changes.py index d5a95fad91..86ecffd5b8 100644 --- a/openpype/modules/ftrack/launch_hooks/post_ftrack_changes.py +++ b/openpype/modules/ftrack/launch_hooks/post_ftrack_changes.py @@ -1,7 +1,7 @@ import os import ftrack_api -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.lib import PostLaunchHook diff --git a/openpype/modules/job_queue/module.py b/openpype/modules/job_queue/module.py index f1d7251e85..7075fcea14 100644 --- a/openpype/modules/job_queue/module.py +++ b/openpype/modules/job_queue/module.py @@ -43,7 +43,7 @@ import platform import click from openpype.modules import OpenPypeModule -from openpype.api import get_system_settings +from openpype.settings import get_system_settings class JobQueueModule(OpenPypeModule): diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index da924aa5ee..39baf31b93 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -12,7 +12,7 @@ from openpype.client import ( get_assets, ) from openpype.pipeline import AvalonMongoDB -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.modules.kitsu.utils.credentials import validate_credentials diff --git a/openpype/modules/shotgrid/lib/settings.py b/openpype/modules/shotgrid/lib/settings.py index 924099f04b..5b0b728f55 100644 --- a/openpype/modules/shotgrid/lib/settings.py +++ b/openpype/modules/shotgrid/lib/settings.py @@ -1,4 +1,4 @@ -from openpype.api import get_system_settings, get_project_settings +from openpype.settings import get_system_settings, get_project_settings from openpype.modules.shotgrid.lib.const import MODULE_NAME diff --git a/openpype/plugins/publish/collect_settings.py b/openpype/plugins/publish/collect_settings.py index d56eabd1b5..a418a6400c 100644 --- a/openpype/plugins/publish/collect_settings.py +++ b/openpype/plugins/publish/collect_settings.py @@ -1,5 +1,8 @@ from pyblish import api -from openpype.api import get_current_project_settings, get_system_settings +from openpype.settings import ( + get_current_project_settings, + get_system_settings, +) class CollectSettings(api.ContextPlugin): diff --git a/openpype/tools/creator/window.py b/openpype/tools/creator/window.py index a3937d6a40..e2396ed29e 100644 --- a/openpype/tools/creator/window.py +++ b/openpype/tools/creator/window.py @@ -6,7 +6,7 @@ from Qt import QtWidgets, QtCore from openpype.client import get_asset_by_name, get_subsets from openpype import style -from openpype.api import get_current_project_settings +from openpype.settings import get_current_project_settings from openpype.tools.utils.lib import qt_app_context from openpype.pipeline import legacy_io from openpype.pipeline.create import ( diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 05e53a989a..888051d2a1 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -22,7 +22,7 @@ import pyblish.version from . import util from .constants import InstanceStates -from openpype.api import get_project_settings +from openpype.settings import get_project_settings class IterationBreak(Exception): diff --git a/openpype/tools/pyblish_pype/model.py b/openpype/tools/pyblish_pype/model.py index 1479d91bb5..383d8304a5 100644 --- a/openpype/tools/pyblish_pype/model.py +++ b/openpype/tools/pyblish_pype/model.py @@ -34,7 +34,7 @@ import qtawesome from six import text_type from .constants import PluginStates, InstanceStates, GroupStates, Roles -from openpype.api import get_system_settings +from openpype.settings import get_system_settings # ItemTypes diff --git a/openpype/tools/settings/local_settings/window.py b/openpype/tools/settings/local_settings/window.py index 761b978ab4..76c2d851e9 100644 --- a/openpype/tools/settings/local_settings/window.py +++ b/openpype/tools/settings/local_settings/window.py @@ -2,6 +2,10 @@ from Qt import QtWidgets, QtGui from openpype import style +from openpype.settings import ( + SystemSettings, + ProjectSettings +) from openpype.settings.lib import ( get_local_settings, save_local_settings @@ -9,10 +13,6 @@ from openpype.settings.lib import ( from openpype.lib import Logger from openpype.tools.settings import CHILD_OFFSET from openpype.tools.utils import MessageOverlayObject -from openpype.api import ( - SystemSettings, - ProjectSettings -) from openpype.modules import ModulesManager from .widgets import ( diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index caf568f0c2..5683b2f04a 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -17,7 +17,7 @@ from openpype.style import ( ) from openpype.resources import get_image_path from openpype.lib import filter_profiles, Logger -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import registered_host log = Logger.get_logger(__name__) diff --git a/openpype/widgets/password_dialog.py b/openpype/widgets/password_dialog.py index 9990642ca1..58add7832f 100644 --- a/openpype/widgets/password_dialog.py +++ b/openpype/widgets/password_dialog.py @@ -3,7 +3,7 @@ from Qt import QtWidgets, QtCore, QtGui from openpype import style from openpype.resources import get_resource -from openpype.api import get_system_settings +from openpype.settings import get_system_settings from openpype.settings.lib import ( get_local_settings, save_local_settings From 35645690cf5a0d6d2935890adf712da3a4b81105 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Oct 2022 12:20:36 +0200 Subject: [PATCH 07/55] one more settings import change --- openpype/hosts/maya/api/lib_rendersettings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 777a6ffbc9..b0a283da5a 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -6,7 +6,7 @@ import six import sys from openpype.lib import Logger -from openpype.api import ( +from openpype.settings import ( get_project_settings, get_current_project_settings ) From 13a53e31381d4901df766fe889e1bf74231580ca Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 4 Oct 2022 16:10:38 +0200 Subject: [PATCH 08/55] use 'get_current_project_settings' instead of 'get_project_settings' in pyblish pype --- openpype/tools/pyblish_pype/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 888051d2a1..90bb002ba5 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -22,7 +22,7 @@ import pyblish.version from . import util from .constants import InstanceStates -from openpype.settings import get_project_settings +from openpype.settings import get_current_project_settings class IterationBreak(Exception): @@ -204,7 +204,7 @@ class Controller(QtCore.QObject): def presets_by_hosts(self): # Get global filters as base - presets = get_project_settings(os.environ['AVALON_PROJECT']) or {} + presets = get_current_project_settings() if not presets: return {} From a9413b1ecf8694be012ce28dca5c7f738efe63f6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 5 Oct 2022 13:50:27 +0200 Subject: [PATCH 09/55] OP-4181 - changed legacy way to update database Operations used instead. --- openpype/client/operations.py | 43 +++++++ .../plugins/publish/integrate_hero_version.py | 115 +++++++++--------- 2 files changed, 98 insertions(+), 60 deletions(-) diff --git a/openpype/client/operations.py b/openpype/client/operations.py index 48e8645726..1f2727599c 100644 --- a/openpype/client/operations.py +++ b/openpype/client/operations.py @@ -23,6 +23,7 @@ CURRENT_PROJECT_CONFIG_SCHEMA = "openpype:config-2.0" CURRENT_ASSET_DOC_SCHEMA = "openpype:asset-3.0" CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0" CURRENT_VERSION_SCHEMA = "openpype:version-3.0" +CURRENT_HERO_VERSION_SCHEMA = "openpype:hero_version-1.0" CURRENT_REPRESENTATION_SCHEMA = "openpype:representation-2.0" CURRENT_WORKFILE_INFO_SCHEMA = "openpype:workfile-1.0" CURRENT_THUMBNAIL_SCHEMA = "openpype:thumbnail-1.0" @@ -162,6 +163,34 @@ def new_version_doc(version, subset_id, data=None, entity_id=None): } +def new_hero_version_doc(version_id, parent_id, data=None, entity_id=None): + """Create skeleton data of hero version document. + + Args: + version_id (ObjectId): Is considered as unique identifier of version + under subset. + parent_id (Union[str, ObjectId]): Id of parent subset. + data (Dict[str, Any]): Version document data. + entity_id (Union[str, ObjectId]): Predefined id of document. New id is + created if not passed. + + Returns: + Dict[str, Any]: Skeleton of version document. + """ + + if data is None: + data = {} + + return { + "_id": _create_or_convert_to_mongo_id(entity_id), + "schema": CURRENT_HERO_VERSION_SCHEMA, + "type": "hero_version", + "version_id": version_id, + "parent": parent_id, + "data": data + } + + def new_representation_doc( name, version_id, context, data=None, entity_id=None ): @@ -293,6 +322,20 @@ def prepare_version_update_data(old_doc, new_doc, replace=True): return _prepare_update_data(old_doc, new_doc, replace) +def prepare_hero_version_update_data(old_doc, new_doc, replace=True): + """Compare two hero version documents and prepare update data. + + Based on compared values will create update data for 'UpdateOperation'. + + Empty output means that documents are identical. + + Returns: + Dict[str, Any]: Changes between old and new document. + """ + + return _prepare_update_data(old_doc, new_doc, replace) + + def prepare_representation_update_data(old_doc, new_doc, replace=True): """Compare two representation documents and prepare update data. diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index c0760a5471..26327ccc97 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -4,8 +4,6 @@ import clique import errno import shutil -from bson.objectid import ObjectId -from pymongo import InsertOne, ReplaceOne import pyblish.api from openpype.client import ( @@ -14,10 +12,16 @@ from openpype.client import ( get_archived_representations, get_representations, ) +from openpype.client.operations import ( + OperationsSession, + _create_or_convert_to_mongo_id, + new_hero_version_doc, + prepare_hero_version_update_data, + prepare_representation_update_data, +) from openpype.lib import create_hard_link from openpype.pipeline import ( - schema, - legacy_io, + schema ) from openpype.pipeline.publish import get_publish_template_name @@ -187,35 +191,29 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): repre["name"].lower(): repre for repre in old_repres } - if old_version: - new_version_id = old_version["_id"] - else: - new_version_id = ObjectId() + op_session = OperationsSession() - new_hero_version = { - "_id": new_version_id, - "version_id": src_version_entity["_id"], - "parent": src_version_entity["parent"], - "type": "hero_version", - "schema": "openpype:hero_version-1.0" - } - schema.validate(new_hero_version) - - # Don't make changes in database until everything is O.K. - bulk_writes = [] + new_hero_version = new_hero_version_doc( + src_version_entity["_id"], + src_version_entity["parent"] + ) if old_version: self.log.debug("Replacing old hero version.") - bulk_writes.append( - ReplaceOne( - {"_id": new_hero_version["_id"]}, - new_hero_version - ) + new_hero_version["_id"] = old_version["_id"] + update_data = prepare_hero_version_update_data( + old_version, new_hero_version + ) + op_session.update_entity( + project_name, + new_hero_version["type"], + old_version["_id"], + update_data ) else: self.log.debug("Creating first hero version.") - bulk_writes.append( - InsertOne(new_hero_version) + op_session.create_entity( + project_name, new_hero_version["type"], new_hero_version ) # Separate old representations into `to replace` and `to delete` @@ -235,7 +233,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): archived_repres = list(get_archived_representations( project_name, # Check what is type of archived representation - version_ids=[new_version_id] + version_ids=[new_hero_version["_id"]] )) archived_repres_by_name = {} for repre in archived_repres: @@ -382,12 +380,15 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Replace current representation if repre_name_low in old_repres_to_replace: old_repre = old_repres_to_replace.pop(repre_name_low) + repre["_id"] = old_repre["_id"] - bulk_writes.append( - ReplaceOne( - {"_id": old_repre["_id"]}, - repre - ) + update_data = prepare_representation_update_data( + old_repre, repre) + op_session.update_entity( + project_name, + "representation", + old_repre["_id"], + update_data ) # Unarchive representation @@ -395,21 +396,21 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): archived_repre = archived_repres_by_name.pop( repre_name_low ) - old_id = archived_repre["old_id"] - repre["_id"] = old_id - bulk_writes.append( - ReplaceOne( - {"old_id": old_id}, - repre - ) + repre["_id"] = archived_repre["old_id"] + update_data = prepare_representation_update_data( + archived_repre, repre) + op_session.update_entity( + project_name, + "representation", + archived_repre["_id"], + update_data ) # Create representation else: - repre["_id"] = ObjectId() - bulk_writes.append( - InsertOne(repre) - ) + repre["_id"] = _create_or_convert_to_mongo_id(None) + op_session.create_entity(project_name, "representation", + repre) self.path_checks = [] @@ -430,28 +431,22 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): archived_repre = archived_repres_by_name.pop( repre_name_low ) - repre["old_id"] = repre["_id"] - repre["_id"] = archived_repre["_id"] - repre["type"] = archived_repre["type"] - bulk_writes.append( - ReplaceOne( - {"_id": archived_repre["_id"]}, - repre - ) - ) + changes["old_id"] = repre["_id"] + changes["_id"] = archived_repre["_id"] + changes["type"] = archived_repre["type"] + op_session.update_entity(project_name, + archived_repre["type"], + archived_repre["_id"], + changes) else: repre["old_id"] = repre["_id"] - repre["_id"] = ObjectId() + repre["_id"] = _create_or_convert_to_mongo_id(None) repre["type"] = "archived_representation" - bulk_writes.append( - InsertOne(repre) - ) + op_session.create_entity(project_name, "representation", + repre) - if bulk_writes: - legacy_io.database[project_name].bulk_write( - bulk_writes - ) + op_session.commit() # Remove backuped previous hero if ( From 5c401fb17ff893420137bfbd872997fea954d857 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 5 Oct 2022 14:13:52 +0200 Subject: [PATCH 10/55] OP-4181 - Hound --- openpype/plugins/publish/integrate_hero_version.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 26327ccc97..adc629352e 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -432,9 +432,9 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): repre_name_low ) - changes["old_id"] = repre["_id"] - changes["_id"] = archived_repre["_id"] - changes["type"] = archived_repre["type"] + changes = {"old_id": repre["_id"], + "_id": archived_repre["_id"], + "type": archived_repre["type"]} op_session.update_entity(project_name, archived_repre["type"], archived_repre["_id"], From 870e3394514e562740148b246812841262a13656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 5 Oct 2022 16:37:16 +0200 Subject: [PATCH 11/55] :bug: set default value for render setup option RenderSetupIncludeLights must be either set to 1 or 0 or not set at all --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 44f2b5b2b4..5021c0796b 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -190,7 +190,9 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): Version=cmds.about(version=True), RenderLayer=instance.data['setMembers'], Renderer=instance.data["renderer"], - RenderSetupIncludeLights=instance.data.get("renderSetupIncludeLights"), # noqa + # Set it to default Maya behaviour if it cannot be determined + # from instance (but it should be, by the Collector). + RenderSetupIncludeLights=instance.data.get("renderSetupIncludeLights", 1), # noqa ProjectPath=context.data["workspaceDir"], UsingRenderLayers=True, ) From 640971a49bf2398a5216b0083d018cf794cc4bcf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 13:40:37 +0200 Subject: [PATCH 12/55] OP-4181 - modified signature of new_hero_version_doc --- openpype/client/operations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/client/operations.py b/openpype/client/operations.py index 1f2727599c..fd639c34a7 100644 --- a/openpype/client/operations.py +++ b/openpype/client/operations.py @@ -163,13 +163,13 @@ def new_version_doc(version, subset_id, data=None, entity_id=None): } -def new_hero_version_doc(version_id, parent_id, data=None, entity_id=None): +def new_hero_version_doc(version_id, subset_id, data=None, entity_id=None): """Create skeleton data of hero version document. Args: version_id (ObjectId): Is considered as unique identifier of version under subset. - parent_id (Union[str, ObjectId]): Id of parent subset. + subset_id (Union[str, ObjectId]): Id of parent subset. data (Dict[str, Any]): Version document data. entity_id (Union[str, ObjectId]): Predefined id of document. New id is created if not passed. @@ -186,7 +186,7 @@ def new_hero_version_doc(version_id, parent_id, data=None, entity_id=None): "schema": CURRENT_HERO_VERSION_SCHEMA, "type": "hero_version", "version_id": version_id, - "parent": parent_id, + "parent": subset_id, "data": data } From 2a2326971a677dc4bad6f87cfd05b9a03cf49574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 6 Oct 2022 16:30:41 +0200 Subject: [PATCH 13/55] :sparkles: add validator for RenderSetupIncludeLights --- .../plugins/publish/submit_maya_deadline.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 5021c0796b..e232571122 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -36,6 +36,17 @@ from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo +def _validate_deadline_bool_value(instance, attribute, value): + if not isinstance(value, (str, bool)): + raise TypeError( + "Attribute {} must be str or bool.".format(attribute)) + if value not in {"1", "0", True, False}: + raise ValueError( + ("Value of {} must be one of " + "'0', '1', True, False").format(attribute) + ) + + @attr.s class MayaPluginInfo: SceneFile = attr.ib(default=None) # Input @@ -46,7 +57,8 @@ class MayaPluginInfo: RenderLayer = attr.ib(default=None) # Render only this layer Renderer = attr.ib(default=None) ProjectPath = attr.ib(default=None) # Resolve relative references - RenderSetupIncludeLights = attr.ib(default=None) # Include all lights flag + RenderSetupIncludeLights = attr.ib( + default="1", validator=_validate_deadline_bool_value) # Include all lights flag @attr.s @@ -185,14 +197,17 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): instance = self._instance context = instance.context + # Set it to default Maya behaviour if it cannot be determined + # from instance (but it should be, by the Collector). Also + rs_include_lights = instance.data.get("renderSetupIncludeLights", "1") + if rs_include_lights not in {"1", "0", True, False}: + rs_include_lights = "1" plugin_info = MayaPluginInfo( SceneFile=self.scene_path, Version=cmds.about(version=True), RenderLayer=instance.data['setMembers'], Renderer=instance.data["renderer"], - # Set it to default Maya behaviour if it cannot be determined - # from instance (but it should be, by the Collector). - RenderSetupIncludeLights=instance.data.get("renderSetupIncludeLights", 1), # noqa + RenderSetupIncludeLights=rs_include_lights, # noqa ProjectPath=context.data["workspaceDir"], UsingRenderLayers=True, ) From 87584b5f4976db087d04e738f0fdfec7fd56f773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 6 Oct 2022 16:34:19 +0200 Subject: [PATCH 14/55] :rotating_light: fix hound :dog: --- .../modules/deadline/plugins/publish/submit_maya_deadline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index e232571122..3f0905c586 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -57,8 +57,9 @@ class MayaPluginInfo: RenderLayer = attr.ib(default=None) # Render only this layer Renderer = attr.ib(default=None) ProjectPath = attr.ib(default=None) # Resolve relative references + # Include all lights flag RenderSetupIncludeLights = attr.ib( - default="1", validator=_validate_deadline_bool_value) # Include all lights flag + default="1", validator=_validate_deadline_bool_value) @attr.s From 99e9c2d14f90fa1e7782169c8f67b36240426153 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 6 Oct 2022 16:47:09 +0200 Subject: [PATCH 15/55] pass instance to get_subset_name on update of existing instance subset name --- openpype/pipeline/create/creator_plugins.py | 13 ++++++++++++- openpype/tools/publisher/widgets/widgets.py | 6 +++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 945a97a99c..4e77146838 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -257,7 +257,13 @@ class BaseCreator: return {} def get_subset_name( - self, variant, task_name, asset_doc, project_name, host_name=None + self, + variant, + task_name, + asset_doc, + project_name, + host_name=None, + instance=None ): """Return subset name for passed context. @@ -271,12 +277,17 @@ class BaseCreator: Asset document is not used yet but is required if would like to use task type in subset templates. + Method is also called on subset name update. In that case origin + instance is passed in. + Args: variant(str): Subset name variant. In most of cases user input. task_name(str): For which task subset is created. asset_doc(dict): Asset document for which subset is created. project_name(str): Project name. host_name(str): Which host creates subset. + instance(str|None): Object of 'CreatedInstance' for which is + subset name updated. Passed only on subset name update. """ dynamic_data = self.get_dynamic_data( diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index d1fa71343c..7fdceff68f 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1080,7 +1080,11 @@ class GlobalAttrsWidget(QtWidgets.QWidget): try: new_subset_name = instance.creator.get_subset_name( - new_variant_value, new_task_name, asset_doc, project_name + new_variant_value, + new_task_name, + asset_doc, + project_name, + instance=instance ) except TaskNotSetError: invalid_tasks = True From a7077a0abd65c4df0506e5d22be502ee03dc2c40 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 6 Oct 2022 16:53:17 +0200 Subject: [PATCH 16/55] pass the instance to 'get_dynamic_data' too --- openpype/pipeline/create/creator_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 4e77146838..05ba8902aa 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -246,7 +246,7 @@ class BaseCreator: return self.icon def get_dynamic_data( - self, variant, task_name, asset_doc, project_name, host_name + self, variant, task_name, asset_doc, project_name, host_name, instance ): """Dynamic data for subset name filling. @@ -291,7 +291,7 @@ class BaseCreator: """ dynamic_data = self.get_dynamic_data( - variant, task_name, asset_doc, project_name, host_name + variant, task_name, asset_doc, project_name, host_name, instance ) return get_subset_name( From a7150bd6f1c9494734f03265eecbe86ff284d882 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:28:30 +0200 Subject: [PATCH 17/55] OP-4181 - clean up after review comments --- igniter/GPUCache/data_0 | Bin 0 -> 8192 bytes igniter/GPUCache/data_1 | Bin 0 -> 270336 bytes igniter/GPUCache/data_2 | Bin 0 -> 8192 bytes igniter/GPUCache/data_3 | Bin 0 -> 8192 bytes igniter/GPUCache/index | Bin 0 -> 262512 bytes openpype/hooks/pre_python2_prelaunch.py | 35 + openpype/hosts/photoshop/tests/expr.py | 51 + openpype/lib/token | 1 + .../event_handlers_user/action_edl_create.py | 275 ++++ openpype/pipeline/temp_anatomy.py | 1330 +++++++++++++++++ .../plugins/publish/integrate_hero_version.py | 9 +- .../_process_referenced_pipeline_result.json | 92 ++ tests/unit/test_unzip.py | 11 + vendor/configs/OpenColorIO-Configs | 1 + vendor/instance.json | 1133 ++++++++++++++ vendor/response.json | 1 + vendor/temp.json | 46 + 17 files changed, 2982 insertions(+), 3 deletions(-) create mode 100644 igniter/GPUCache/data_0 create mode 100644 igniter/GPUCache/data_1 create mode 100644 igniter/GPUCache/data_2 create mode 100644 igniter/GPUCache/data_3 create mode 100644 igniter/GPUCache/index create mode 100644 openpype/hooks/pre_python2_prelaunch.py create mode 100644 openpype/hosts/photoshop/tests/expr.py create mode 100644 openpype/lib/token create mode 100644 openpype/modules/ftrack/event_handlers_user/action_edl_create.py create mode 100644 openpype/pipeline/temp_anatomy.py create mode 100644 tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json create mode 100644 tests/unit/test_unzip.py create mode 160000 vendor/configs/OpenColorIO-Configs create mode 100644 vendor/instance.json create mode 100644 vendor/response.json create mode 100644 vendor/temp.json diff --git a/igniter/GPUCache/data_0 b/igniter/GPUCache/data_0 new file mode 100644 index 0000000000000000000000000000000000000000..d76fb77e93ac8a536b5dbade616d63abd00626c5 GIT binary patch literal 8192 zcmeIuK?wjL5Jka{7-jo+5O1auw}mk8@B+*}b0s6M>Kg$91PBlyK!5-N0t5&UAV7cs W0RjXF5FkK+009C72oNCfo4^Gh&;oe? literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/data_1 b/igniter/GPUCache/data_1 new file mode 100644 index 0000000000000000000000000000000000000000..212f73166781160e472f8e76c3b9998b3775ecb7 GIT binary patch literal 270336 zcmeI%u?@m75CFhW@CfV>3QP3t%>amw0a8XffrJVo)0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N c0t5&UAV7cs0RjXF5FkK+009C72>hqO2eI!7!2kdN literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/data_2 b/igniter/GPUCache/data_2 new file mode 100644 index 0000000000000000000000000000000000000000..c7e2eb9adcfb2d3313ec85f5c28cedda950a3f9b GIT binary patch literal 8192 zcmeIu!3h8`2n0b1_TQ7_m#U&=2(t%Qz}%M=ae7_Oi2wlt1PBlyK!5-N0t5&UAV7cs V0RjXF5FkK+009C72oTsN@Bv`}0$Tt8 literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/data_3 b/igniter/GPUCache/data_3 new file mode 100644 index 0000000000000000000000000000000000000000..5eec97358cf550862fd343fc9a73c159d4c0ab10 GIT binary patch literal 8192 zcmeIuK@9*P5CpLeAOQbv2)|PW$RO!FMnHFsm9+HS=9>r*AV7cs0RjXF5FkK+009C7 W2oNAZfB*pk1PBlyK!5;&-vkZ-dID$w literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/index b/igniter/GPUCache/index new file mode 100644 index 0000000000000000000000000000000000000000..b2998cfef1a6457e5cfe9dc37e029bdbe0a7f778 GIT binary patch literal 262512 zcmeIuu?>JQ00XcT9zZ-&b?3`o&{Gf_S0S;K0~nnt$>{4|&t%Cr+dIlg%Diho_EzWC z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjYm G5qJP2>jZZI literal 0 HcmV?d00001 diff --git a/openpype/hooks/pre_python2_prelaunch.py b/openpype/hooks/pre_python2_prelaunch.py new file mode 100644 index 0000000000..84272d2e5d --- /dev/null +++ b/openpype/hooks/pre_python2_prelaunch.py @@ -0,0 +1,35 @@ +import os +from openpype.lib import PreLaunchHook + + +class PrePython2Vendor(PreLaunchHook): + """Prepend python 2 dependencies for py2 hosts.""" + order = 10 + + def execute(self): + if not self.application.use_python_2: + return + + # Prepare vendor dir path + self.log.info("adding global python 2 vendor") + pype_root = os.getenv("OPENPYPE_REPOS_ROOT") + python_2_vendor = os.path.join( + pype_root, + "openpype", + "vendor", + "python", + "python_2" + ) + + # Add Python 2 modules + python_paths = [ + python_2_vendor + ] + + # Load PYTHONPATH from current launch context + python_path = self.launch_context.env.get("PYTHONPATH") + if python_path: + python_paths.append(python_path) + + # Set new PYTHONPATH to launch context environments + self.launch_context.env["PYTHONPATH"] = os.pathsep.join(python_paths) diff --git a/openpype/hosts/photoshop/tests/expr.py b/openpype/hosts/photoshop/tests/expr.py new file mode 100644 index 0000000000..ff796f417c --- /dev/null +++ b/openpype/hosts/photoshop/tests/expr.py @@ -0,0 +1,51 @@ +import json + +data = [ + { + "schema": "openpype:container-2.0", + "id": "pyblish.avalon.container", + "name": "imageArtNeew", + "namespace": "Jungle_imageArtNeew_001", + "loader": "ReferenceLoader", + "representation": "61c1eb91e1a4d1e5a23582f6", + "members": [ + "131" + ] + }, + { + "id": "pyblish.avalon.instance", + "family": "image", + "asset": "Jungle", + "subset": "imageMainBg", + "active": True, + "variant": "Main", + "uuid": "199", + "long_name": "BG" + }, + { + "id": "pyblish.avalon.instance", + "family": "image", + "asset": "Jungle", + "subset": "imageMain", + "active": True, + "variant": "Main", + "uuid": "192", + "long_name": "imageMain" + }, + { + "id": "pyblish.avalon.instance", + "family": "workfile", + "subset": "workfile", + "active": True, + "creator_identifier": "workfile", + "asset": "Jungle", + "task": "art", + "variant": "", + "instance_id": "3ed19342-cd8e-4bb6-8cda-d6e74d9a7efe", + "creator_attributes": {}, + "publish_attributes": {} + } +] + +with open("C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\openpype\\hosts\\photoshop\\tests\\mock_get_layers_metadata.json", 'w') as fp: + fp.write(json.dumps(data, indent=4)) \ No newline at end of file diff --git a/openpype/lib/token b/openpype/lib/token new file mode 100644 index 0000000000..193a2aac95 --- /dev/null +++ b/openpype/lib/token @@ -0,0 +1 @@ +5d58370a7702b2efee5120704246baf4abb865323fc9db9a04827bfb478569d6 \ No newline at end of file diff --git a/openpype/modules/ftrack/event_handlers_user/action_edl_create.py b/openpype/modules/ftrack/event_handlers_user/action_edl_create.py new file mode 100644 index 0000000000..7ac139ae63 --- /dev/null +++ b/openpype/modules/ftrack/event_handlers_user/action_edl_create.py @@ -0,0 +1,275 @@ +import os +import subprocess +import tempfile +import shutil +import json +import sys + +import opentimelineio as otio +import ftrack_api +import requests + +from openpype_modules.ftrack.lib import BaseAction + + +def download_file(url, path): + with open(path, "wb") as f: + print("\nDownloading %s" % path) + response = requests.get(url, stream=True) + total_length = response.headers.get('content-length') + + if total_length is None: + f.write(response.content) + else: + dl = 0 + total_length = int(total_length) + for data in response.iter_content(chunk_size=4096): + dl += len(data) + f.write(data) + done = int(50 * dl / total_length) + sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50-done))) + sys.stdout.flush() + + +class ExportEditorialAction(BaseAction): + '''Export Editorial action''' + + label = "Export Editorial" + variant = None + identifier = "export-editorial" + description = None + component_name_order = ["exr", "mov", "ftrackreview-mp4_src"] + + def export_editorial(self, entity, output_path): + session = ftrack_api.Session() + unmanaged_location = session.query( + "Location where name is \"ftrack.unmanaged\"" + ).one() + temp_path = tempfile.mkdtemp() + + files = {} + for obj in entity["review_session_objects"]: + data = {} + parent_name = obj["asset_version"]["asset"]["parent"]["name"] + component_query = "Component where version_id is \"{}\"" + component_query += " and name is \"{}\"" + for name in self.component_name_order: + try: + component = session.query( + component_query.format( + obj["asset_version"]["id"], name + ) + ).one() + path = unmanaged_location.get_filesystem_path(component) + data["path"] = path.replace("\\", "/") + break + except ftrack_api.exception.NoResultFoundError: + pass + + # Download online review if not local path found. + if "path" not in data: + component = session.query( + component_query.format( + obj["asset_version"]["id"], "ftrackreview-mp4" + ) + ).one() + location = component["component_locations"][0] + component_url = location["location"].get_url(component) + asset_name = obj["asset_version"]["asset"]["name"] + version = obj["asset_version"]["version"] + filename = "{}_{}_v{:03d}.mp4".format( + parent_name, asset_name, version + ) + filepath = os.path.join( + output_path, "downloads", filename + ).replace("\\", "/") + + if not os.path.exists(os.path.dirname(filepath)): + os.makedirs(os.path.dirname(filepath)) + + download_file(component_url, filepath) + data["path"] = filepath + + # Get frame duration and framerate. + query = "Component where version_id is \"{}\"" + query += " and name is \"ftrackreview-mp4\"" + component = session.query( + query.format(obj["asset_version"]["id"]) + ).one() + metadata = json.loads(component["metadata"]["ftr_meta"]) + data["framerate"] = metadata["frameRate"] + data["frames"] = metadata["frameOut"] - metadata["frameIn"] + + # Find audio if it exists. + query = "Asset where parent.id is \"{}\"" + query += " and type.name is \"Audio\"" + asset = session.query( + query.format(obj["asset_version"]["asset"]["parent"]["id"]) + ) + if asset: + asset_version = asset[0]["versions"][-1] + query = "Component where version_id is \"{}\"" + query += " and name is \"{}\"" + comp = session.query( + query.format(asset_version["id"], "wav") + ).one() + src = unmanaged_location.get_filesystem_path(comp) + dst = os.path.join(temp_path, parent_name + ".wav") + shutil.copy(src, dst) + + # Collect data. + files[parent_name] = data + + clips = [] + for name, data in files.items(): + self.log.info("Processing {} with {}".format(name, data)) + f = data["path"] + range = otio.opentime.TimeRange( + start_time=otio.opentime.RationalTime(0, data["framerate"]), + duration=otio.opentime.RationalTime( + data["frames"], data["framerate"] + ) + ) + + media_reference = otio.schema.ExternalReference( + available_range=range, + target_url=f"file://{f}" + ) + + clip = otio.schema.Clip( + name=name, + media_reference=media_reference, + source_range=range + ) + clips.append(clip) + + # path = os.path.join(temp_path, name + ".wav").replace("\\", "/") + # if not os.path.exists(path): + # args = ["ffmpeg", "-y", "-i", f, path] + # self.log.info(subprocess.list2cmdline(args)) + # subprocess.call(args) + + timeline = otio.schema.timeline_from_clips(clips) + otio.adapters.write_to_file( + timeline, os.path.join(output_path, entity["name"] + ".xml") + ) + + data = "" + for f in os.listdir(temp_path): + f = f.replace("\\", "/") + data += f"file '{f}'\n" + + path = os.path.join(temp_path, "temp.txt") + with open(path, "w") as f: + f.write(data) + + args = [ + "ffmpeg", "-y", "-f", "concat", "-safe", "0", + "-i", os.path.basename(path), + os.path.join(output_path, entity["name"] + ".wav") + ] + self.log.info(subprocess.list2cmdline(args)) + subprocess.call(args, cwd=temp_path) + + shutil.rmtree(temp_path) + + def discover(self, session, entities, event): + '''Return true if we can handle the selected entities. + *session* is a `ftrack_api.Session` instance + *entities* is a list of tuples each containing the entity type and the + entity id. + If the entity is a hierarchical you will always get the entity + type TypedContext, once retrieved through a get operation you + will have the "real" entity type ie. example Shot, Sequence + or Asset Build. + *event* the unmodified original event + ''' + if len(entities) == 1: + if entities[0].entity_type == "ReviewSession": + return True + + return False + + def launch(self, session, entities, event): + '''Callback method for the custom action. + return either a bool ( True if successful or False if the action + failed ) or a dictionary with they keys `message` and `success`, the + message should be a string and will be displayed as feedback to the + user, success should be a bool, True if successful or False if the + action failed. + *session* is a `ftrack_api.Session` instance + *entities* is a list of tuples each containing the entity type and the + entity id. + If the entity is a hierarchical you will always get the entity + type TypedContext, once retrieved through a get operation you + will have the "real" entity type ie. example Shot, Sequence + or Asset Build. + *event* the unmodified original event + ''' + if 'values' in event['data']: + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() + job = session.create( + 'Job', + { + 'user': user, + 'status': 'running', + 'data': json.dumps({ + 'description': 'Export Editorial.' + }) + } + ) + session.commit() + + try: + output_path = event["data"]["values"]["output_path"] + + if not os.path.exists(output_path): + os.makedirs(output_path) + + self.export_editorial(entities[0], output_path) + + job['status'] = 'done' + session.commit() + except Exception: + session.rollback() + job["status"] = "failed" + session.commit() + self.log.error( + "Exporting editorial failed ({})", exc_info=True + ) + + return { + 'success': True, + 'message': 'Action completed successfully' + } + + items = [ + { + 'label': 'Output folder:', + 'type': 'text', + 'value': '', + 'name': 'output_path' + } + + ] + return { + 'success': True, + 'message': "", + 'items': items + } + + +def register(session): + '''Register action. Called when used as an event plugin.''' + + ExportEditorialAction(session).register() + + +if __name__ == "__main__": + session = ftrack_api.Session() + action = ExportEditorialAction(session) + id = "bfe0477c-d5a8-49d8-88b9-6d44d2e48fd9" + review_session = session.get("ReviewSession", id) + path = r"c:/projects" + action.export_editorial(review_session, path) \ No newline at end of file diff --git a/openpype/pipeline/temp_anatomy.py b/openpype/pipeline/temp_anatomy.py new file mode 100644 index 0000000000..27a9370928 --- /dev/null +++ b/openpype/pipeline/temp_anatomy.py @@ -0,0 +1,1330 @@ +import os +import re +import copy +import platform +import collections +import numbers + +import six +import time + +from openpype.settings.lib import ( + get_anatomy_settings, + get_project_settings, + get_default_project_settings, + get_local_settings +) + +from openpype.client import get_project +from openpype.lib.path_templates import ( + TemplateUnsolved, + TemplateResult, + TemplatesDict, + FormatObject, +) +from openpype.lib.log import Logger +from openpype.lib import get_local_site_id + +log = Logger.get_logger(__name__) + + +class ProjectNotSet(Exception): + """Exception raised when is created Anatomy without project name.""" + + +class RootCombinationError(Exception): + """This exception is raised when templates has combined root types.""" + + def __init__(self, roots): + joined_roots = ", ".join( + ["\"{}\"".format(_root) for _root in roots] + ) + # TODO better error message + msg = ( + "Combination of root with and" + " without root name in AnatomyTemplates. {}" + ).format(joined_roots) + + super(RootCombinationError, self).__init__(msg) + + +class BaseAnatomy(object): + """Anatomy module helps to keep project settings. + + Wraps key project specifications, AnatomyTemplates and Roots. + """ + + def __init__(self, project_doc, local_settings): + project_name = project_doc["name"] + self.project_name = project_name + + self._data = self._prepare_anatomy_data( + project_doc, local_settings + ) + self._templates_obj = AnatomyTemplates(self) + self._roots_obj = Roots(self) + + root_key_regex = re.compile(r"{(root?[^}]+)}") + root_name_regex = re.compile(r"root\[([^]]+)\]") + + # Anatomy used as dictionary + # - implemented only getters returning copy + def __getitem__(self, key): + return copy.deepcopy(self._data[key]) + + def get(self, key, default=None): + return copy.deepcopy(self._data).get(key, default) + + def keys(self): + return copy.deepcopy(self._data).keys() + + def values(self): + return copy.deepcopy(self._data).values() + + def items(self): + return copy.deepcopy(self._data).items() + + @staticmethod + def _prepare_anatomy_data(anatomy_data): + """Prepare anatomy data for further processing. + + Method added to replace `{task}` with `{task[name]}` in templates. + """ + templates_data = anatomy_data.get("templates") + if templates_data: + # Replace `{task}` with `{task[name]}` in templates + value_queue = collections.deque() + value_queue.append(templates_data) + while value_queue: + item = value_queue.popleft() + if not isinstance(item, dict): + continue + + for key in tuple(item.keys()): + value = item[key] + if isinstance(value, dict): + value_queue.append(value) + + elif isinstance(value, six.string_types): + item[key] = value.replace("{task}", "{task[name]}") + return anatomy_data + + def reset(self): + """Reset values of cached data in templates and roots objects.""" + self._data = self._prepare_anatomy_data( + get_anatomy_settings(self.project_name, self._site_name) + ) + self.templates_obj.reset() + self.roots_obj.reset() + + @property + def templates(self): + """Wrap property `templates` of Anatomy's AnatomyTemplates instance.""" + return self._templates_obj.templates + + @property + def templates_obj(self): + """Return `AnatomyTemplates` object of current Anatomy instance.""" + return self._templates_obj + + def format(self, *args, **kwargs): + """Wrap `format` method of Anatomy's `templates_obj`.""" + return self._templates_obj.format(*args, **kwargs) + + def format_all(self, *args, **kwargs): + """Wrap `format_all` method of Anatomy's `templates_obj`.""" + return self._templates_obj.format_all(*args, **kwargs) + + @property + def roots(self): + """Wrap `roots` property of Anatomy's `roots_obj`.""" + return self._roots_obj.roots + + @property + def roots_obj(self): + """Return `Roots` object of current Anatomy instance.""" + return self._roots_obj + + def root_environments(self): + """Return OPENPYPE_ROOT_* environments for current project in dict.""" + return self._roots_obj.root_environments() + + def root_environmets_fill_data(self, template=None): + """Environment variable values in dictionary for rootless path. + + Args: + template (str): Template for environment variable key fill. + By default is set to `"${}"`. + """ + return self.roots_obj.root_environmets_fill_data(template) + + def find_root_template_from_path(self, *args, **kwargs): + """Wrapper for Roots `find_root_template_from_path`.""" + return self.roots_obj.find_root_template_from_path(*args, **kwargs) + + def path_remapper(self, *args, **kwargs): + """Wrapper for Roots `path_remapper`.""" + return self.roots_obj.path_remapper(*args, **kwargs) + + def all_root_paths(self): + """Wrapper for Roots `all_root_paths`.""" + return self.roots_obj.all_root_paths() + + def set_root_environments(self): + """Set OPENPYPE_ROOT_* environments for current project.""" + self._roots_obj.set_root_environments() + + def root_names(self): + """Return root names for current project.""" + return self.root_names_from_templates(self.templates) + + def _root_keys_from_templates(self, data): + """Extract root key from templates in data. + + Args: + data (dict): Data that may contain templates as string. + + Return: + set: Set of all root names from templates as strings. + + Output example: `{"root[work]", "root[publish]"}` + """ + + output = set() + if isinstance(data, dict): + for value in data.values(): + for root in self._root_keys_from_templates(value): + output.add(root) + + elif isinstance(data, str): + for group in re.findall(self.root_key_regex, data): + output.add(group) + + return output + + def root_value_for_template(self, template): + """Returns value of root key from template.""" + root_templates = [] + for group in re.findall(self.root_key_regex, template): + root_templates.append("{" + group + "}") + + if not root_templates: + return None + + return root_templates[0].format(**{"root": self.roots}) + + def root_names_from_templates(self, templates): + """Extract root names form anatomy templates. + + Returns None if values in templates contain only "{root}". + Empty list is returned if there is no "root" in templates. + Else returns all root names from templates in list. + + RootCombinationError is raised when templates contain both root types, + basic "{root}" and with root name specification "{root[work]}". + + Args: + templates (dict): Anatomy templates where roots are not filled. + + Return: + list/None: List of all root names from templates as strings when + multiroot setup is used, otherwise None is returned. + """ + roots = list(self._root_keys_from_templates(templates)) + # Return empty list if no roots found in templates + if not roots: + return roots + + # Raise exception when root keys have roots with and without root name. + # Invalid output example: ["root", "root[project]", "root[render]"] + if len(roots) > 1 and "root" in roots: + raise RootCombinationError(roots) + + # Return None if "root" without root name in templates + if len(roots) == 1 and roots[0] == "root": + return None + + names = set() + for root in roots: + for group in re.findall(self.root_name_regex, root): + names.add(group) + return list(names) + + def fill_root(self, template_path): + """Fill template path where is only "root" key unfilled. + + Args: + template_path (str): Path with "root" key in. + Example path: "{root}/projects/MyProject/Shot01/Lighting/..." + + Return: + str: formatted path + """ + # NOTE does not care if there are different keys than "root" + return template_path.format(**{"root": self.roots}) + + @classmethod + def fill_root_with_path(cls, rootless_path, root_path): + """Fill path without filled "root" key with passed path. + + This is helper to fill root with different directory path than anatomy + has defined no matter if is single or multiroot. + + Output path is same as input path if `rootless_path` does not contain + unfilled root key. + + Args: + rootless_path (str): Path without filled "root" key. Example: + "{root[work]}/MyProject/..." + root_path (str): What should replace root key in `rootless_path`. + + Returns: + str: Path with filled root. + """ + output = str(rootless_path) + for group in re.findall(cls.root_key_regex, rootless_path): + replacement = "{" + group + "}" + output = output.replace(replacement, root_path) + + return output + + def replace_root_with_env_key(self, filepath, template=None): + """Replace root of path with environment key. + + # Example: + ## Project with roots: + ``` + { + "nas": { + "windows": P:/projects", + ... + } + ... + } + ``` + + ## Entered filepath + "P:/projects/project/asset/task/animation_v001.ma" + + ## Entered template + "<{}>" + + ## Output + "/project/asset/task/animation_v001.ma" + + Args: + filepath (str): Full file path where root should be replaced. + template (str): Optional template for environment key. Must + have one index format key. + Default value if not entered: "${}" + + Returns: + str: Path where root is replaced with environment root key. + + Raise: + ValueError: When project's roots were not found in entered path. + """ + success, rootless_path = self.find_root_template_from_path(filepath) + if not success: + raise ValueError( + "{}: Project's roots were not found in path: {}".format( + self.project_name, filepath + ) + ) + + data = self.root_environmets_fill_data(template) + return rootless_path.format(**data) + + +class Anatomy(BaseAnatomy): + _project_cache = {} + + def __init__(self, project_name=None, site_name=None): + if not project_name: + project_name = os.environ.get("AVALON_PROJECT") + + if not project_name: + raise ProjectNotSet(( + "Implementation bug: Project name is not set. Anatomy requires" + " to load data for specific project." + )) + + self._site_name = site_name + project_info = self.get_project_data_and_cache(project_name, site_name) + + super(Anatomy, self).__init__( + project_info["project_doc"], + project_info["local_settings"] + ) + + @classmethod + def get_project_data_and_cache(cls, project_name, site_name): + project_info = cls._project_cache.get(project_name) + if project_info is not None: + if time.time() - project_info["start"] > 10: + cls._project_cache.pop(project_name) + project_info = None + + if project_info is None: + if site_name is None: + if project_name: + project_settings = get_project_settings(project_name) + else: + project_settings = get_default_project_settings() + site_name = ( + project_settings["global"] + ["sync_server"] + ["config"] + ["active_site"] + ) + if site_name == "local": + site_name = get_local_site_id() + + project_info = { + "project_doc": get_project(project_name), + "local_settings": get_local_settings(site_name), + "site_name": site_name, + "start": time.time() + } + cls._project_cache[project_name] = project_info + + return project_info + + def reset(self): + """Reset values of cached data in templates and roots objects.""" + self._data = self._prepare_anatomy_data( + get_anatomy_settings(self.project_name, self._site_name) + ) + self.templates_obj.reset() + self.roots_obj.reset() + + +class AnatomyTemplateUnsolved(TemplateUnsolved): + """Exception for unsolved template when strict is set to True.""" + + msg = "Anatomy template \"{0}\" is unsolved.{1}{2}" + + +class AnatomyTemplateResult(TemplateResult): + rootless = None + + def __new__(cls, result, rootless_path): + new_obj = super(AnatomyTemplateResult, cls).__new__( + cls, + str(result), + result.template, + result.solved, + result.used_values, + result.missing_keys, + result.invalid_types + ) + new_obj.rootless = rootless_path + return new_obj + + def validate(self): + if not self.solved: + raise AnatomyTemplateUnsolved( + self.template, + self.missing_keys, + self.invalid_types + ) + + def copy(self): + tmp = TemplateResult( + str(self), + self.template, + self.solved, + self.used_values, + self.missing_keys, + self.invalid_types + ) + return self.__class__(tmp, self.rootless) + + def normalized(self): + """Convert to normalized path.""" + + tmp = TemplateResult( + os.path.normpath(self), + self.template, + self.solved, + self.used_values, + self.missing_keys, + self.invalid_types + ) + return self.__class__(tmp, self.rootless) + + +class AnatomyTemplates(TemplatesDict): + inner_key_pattern = re.compile(r"(\{@.*?[^{}0]*\})") + inner_key_name_pattern = re.compile(r"\{@(.*?[^{}0]*)\}") + + def __init__(self, anatomy): + super(AnatomyTemplates, self).__init__() + self.anatomy = anatomy + self.loaded_project = None + + def __getitem__(self, key): + return self.templates[key] + + def get(self, key, default=None): + return self.templates.get(key, default) + + def reset(self): + self._raw_templates = None + self._templates = None + self._objected_templates = None + + @property + def project_name(self): + return self.anatomy.project_name + + @property + def roots(self): + return self.anatomy.roots + + @property + def templates(self): + self._validate_discovery() + return self._templates + + @property + def objected_templates(self): + self._validate_discovery() + return self._objected_templates + + def _validate_discovery(self): + if self.project_name != self.loaded_project: + self.reset() + + if self._templates is None: + self._discover() + self.loaded_project = self.project_name + + def _format_value(self, value, data): + if isinstance(value, RootItem): + return self._solve_dict(value, data) + + result = super(AnatomyTemplates, self)._format_value(value, data) + if isinstance(result, TemplateResult): + rootless_path = self._rootless_path(result, data) + result = AnatomyTemplateResult(result, rootless_path) + return result + + def set_templates(self, templates): + if not templates: + self.reset() + return + + self._raw_templates = copy.deepcopy(templates) + templates = copy.deepcopy(templates) + v_queue = collections.deque() + v_queue.append(templates) + while v_queue: + item = v_queue.popleft() + if not isinstance(item, dict): + continue + + for key in tuple(item.keys()): + value = item[key] + if isinstance(value, dict): + v_queue.append(value) + + elif ( + isinstance(value, six.string_types) + and "{task}" in value + ): + item[key] = value.replace("{task}", "{task[name]}") + + solved_templates = self.solve_template_inner_links(templates) + self._templates = solved_templates + self._objected_templates = self.create_ojected_templates( + solved_templates + ) + + def default_templates(self): + """Return default templates data with solved inner keys.""" + return self.solve_template_inner_links( + self.anatomy["templates"] + ) + + def _discover(self): + """ Loads anatomy templates from yaml. + Default templates are loaded if project is not set or project does + not have set it's own. + TODO: create templates if not exist. + + Returns: + TemplatesResultDict: Contain templates data for current project of + default templates. + """ + + if self.project_name is None: + # QUESTION create project specific if not found? + raise AssertionError(( + "Project \"{0}\" does not have his own templates." + " Trying to use default." + ).format(self.project_name)) + + self.set_templates(self.anatomy["templates"]) + + @classmethod + def replace_inner_keys(cls, matches, value, key_values, key): + """Replacement of inner keys in template values.""" + for match in matches: + anatomy_sub_keys = ( + cls.inner_key_name_pattern.findall(match) + ) + if key in anatomy_sub_keys: + raise ValueError(( + "Unsolvable recursion in inner keys, " + "key: \"{}\" is in his own value." + " Can't determine source, please check Anatomy templates." + ).format(key)) + + for anatomy_sub_key in anatomy_sub_keys: + replace_value = key_values.get(anatomy_sub_key) + if replace_value is None: + raise KeyError(( + "Anatomy templates can't be filled." + " Anatomy key `{0}` has" + " invalid inner key `{1}`." + ).format(key, anatomy_sub_key)) + + if not ( + isinstance(replace_value, numbers.Number) + or isinstance(replace_value, six.string_types) + ): + raise ValueError(( + "Anatomy templates can't be filled." + " Anatomy key `{0}` has" + " invalid inner key `{1}`" + " with value `{2}`." + ).format(key, anatomy_sub_key, str(replace_value))) + + value = value.replace(match, str(replace_value)) + + return value + + @classmethod + def prepare_inner_keys(cls, key_values): + """Check values of inner keys. + + Check if inner key exist in template group and has valid value. + It is also required to avoid infinite loop with unsolvable recursion + when first inner key's value refers to second inner key's value where + first is used. + """ + keys_to_solve = set(key_values.keys()) + while True: + found = False + for key in tuple(keys_to_solve): + value = key_values[key] + + if isinstance(value, six.string_types): + matches = cls.inner_key_pattern.findall(value) + if not matches: + keys_to_solve.remove(key) + continue + + found = True + key_values[key] = cls.replace_inner_keys( + matches, value, key_values, key + ) + continue + + elif not isinstance(value, dict): + keys_to_solve.remove(key) + continue + + subdict_found = False + for _key, _value in tuple(value.items()): + matches = cls.inner_key_pattern.findall(_value) + if not matches: + continue + + subdict_found = True + found = True + key_values[key][_key] = cls.replace_inner_keys( + matches, _value, key_values, + "{}.{}".format(key, _key) + ) + + if not subdict_found: + keys_to_solve.remove(key) + + if not found: + break + + return key_values + + @classmethod + def solve_template_inner_links(cls, templates): + """Solve templates inner keys identified by "{@*}". + + Process is split into 2 parts. + First is collecting all global keys (keys in top hierarchy where value + is not dictionary). All global keys are set for all group keys (keys + in top hierarchy where value is dictionary). Value of a key is not + overridden in group if already contain value for the key. + + In second part all keys with "at" symbol in value are replaced with + value of the key afterward "at" symbol from the group. + + Args: + templates (dict): Raw templates data. + + Example: + templates:: + key_1: "value_1", + key_2: "{@key_1}/{filling_key}" + + group_1: + key_3: "value_3/{@key_2}" + + group_2: + key_2": "value_2" + key_4": "value_4/{@key_2}" + + output:: + key_1: "value_1" + key_2: "value_1/{filling_key}" + + group_1: { + key_1: "value_1" + key_2: "value_1/{filling_key}" + key_3: "value_3/value_1/{filling_key}" + + group_2: { + key_1: "value_1" + key_2: "value_2" + key_4: "value_3/value_2" + """ + default_key_values = templates.pop("defaults", {}) + for key, value in tuple(templates.items()): + if isinstance(value, dict): + continue + default_key_values[key] = templates.pop(key) + + # Pop "others" key before before expected keys are processed + other_templates = templates.pop("others") or {} + + keys_by_subkey = {} + for sub_key, sub_value in templates.items(): + key_values = {} + key_values.update(default_key_values) + key_values.update(sub_value) + keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) + + for sub_key, sub_value in other_templates.items(): + if sub_key in keys_by_subkey: + log.warning(( + "Key \"{}\" is duplicated in others. Skipping." + ).format(sub_key)) + continue + + key_values = {} + key_values.update(default_key_values) + key_values.update(sub_value) + keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) + + default_keys_by_subkeys = cls.prepare_inner_keys(default_key_values) + + for key, value in default_keys_by_subkeys.items(): + keys_by_subkey[key] = value + + return keys_by_subkey + + def _dict_to_subkeys_list(self, subdict, pre_keys=None): + if pre_keys is None: + pre_keys = [] + output = [] + for key in subdict: + value = subdict[key] + result = list(pre_keys) + result.append(key) + if isinstance(value, dict): + for item in self._dict_to_subkeys_list(value, result): + output.append(item) + else: + output.append(result) + return output + + def _keys_to_dicts(self, key_list, value): + if not key_list: + return None + if len(key_list) == 1: + return {key_list[0]: value} + return {key_list[0]: self._keys_to_dicts(key_list[1:], value)} + + def _rootless_path(self, result, final_data): + used_values = result.used_values + missing_keys = result.missing_keys + template = result.template + invalid_types = result.invalid_types + if ( + "root" not in used_values + or "root" in missing_keys + or "{root" not in template + ): + return + + for invalid_type in invalid_types: + if "root" in invalid_type: + return + + root_keys = self._dict_to_subkeys_list({"root": used_values["root"]}) + if not root_keys: + return + + output = str(result) + for used_root_keys in root_keys: + if not used_root_keys: + continue + + used_value = used_values + root_key = None + for key in used_root_keys: + used_value = used_value[key] + if root_key is None: + root_key = key + else: + root_key += "[{}]".format(key) + + root_key = "{" + root_key + "}" + output = output.replace(str(used_value), root_key) + + return output + + def format(self, data, strict=True): + copy_data = copy.deepcopy(data) + roots = self.roots + if roots: + copy_data["root"] = roots + result = super(AnatomyTemplates, self).format(copy_data) + result.strict = strict + return result + + def format_all(self, in_data, only_keys=True): + """ Solves templates based on entered data. + + Args: + data (dict): Containing keys to be filled into template. + + Returns: + TemplatesResultDict: Output `TemplateResult` have `strict` + attribute set to False so accessing unfilled keys in templates + won't raise any exceptions. + """ + return self.format(in_data, strict=False) + + +class RootItem(FormatObject): + """Represents one item or roots. + + Holds raw data of root item specification. Raw data contain value + for each platform, but current platform value is used when object + is used for formatting of template. + + Args: + root_raw_data (dict): Dictionary containing root values by platform + names. ["windows", "linux" and "darwin"] + name (str, optional): Root name which is representing. Used with + multi root setup otherwise None value is expected. + parent_keys (list, optional): All dictionary parent keys. Values of + `parent_keys` are used for get full key which RootItem is + representing. Used for replacing root value in path with + formattable key. e.g. parent_keys == ["work"] -> {root[work]} + parent (object, optional): It is expected to be `Roots` object. + Value of `parent` won't affect code logic much. + """ + + def __init__( + self, root_raw_data, name=None, parent_keys=None, parent=None + ): + lowered_platform_keys = {} + for key, value in root_raw_data.items(): + lowered_platform_keys[key.lower()] = value + self.raw_data = lowered_platform_keys + self.cleaned_data = self._clean_roots(lowered_platform_keys) + self.name = name + self.parent_keys = parent_keys or [] + self.parent = parent + + self.available_platforms = list(lowered_platform_keys.keys()) + self.value = lowered_platform_keys.get(platform.system().lower()) + self.clean_value = self.clean_root(self.value) + + def __format__(self, *args, **kwargs): + return self.value.__format__(*args, **kwargs) + + def __str__(self): + return str(self.value) + + def __repr__(self): + return self.__str__() + + def __getitem__(self, key): + if isinstance(key, numbers.Number): + return self.value[key] + + additional_info = "" + if self.parent and self.parent.project_name: + additional_info += " for project \"{}\"".format( + self.parent.project_name + ) + + raise AssertionError( + "Root key \"{}\" is missing{}.".format( + key, additional_info + ) + ) + + def full_key(self): + """Full key value for dictionary formatting in template. + + Returns: + str: Return full replacement key for formatting. This helps when + multiple roots are set. In that case e.g. `"root[work]"` is + returned. + """ + if not self.name: + return "root" + + joined_parent_keys = "".join( + ["[{}]".format(key) for key in self.parent_keys] + ) + return "root{}".format(joined_parent_keys) + + def clean_path(self, path): + """Just replace backslashes with forward slashes.""" + return str(path).replace("\\", "/") + + def clean_root(self, root): + """Makes sure root value does not end with slash.""" + if root: + root = self.clean_path(root) + while root.endswith("/"): + root = root[:-1] + return root + + def _clean_roots(self, raw_data): + """Clean all values of raw root item values.""" + cleaned = {} + for key, value in raw_data.items(): + cleaned[key] = self.clean_root(value) + return cleaned + + def path_remapper(self, path, dst_platform=None, src_platform=None): + """Remap path for specific platform. + + Args: + path (str): Source path which need to be remapped. + dst_platform (str, optional): Specify destination platform + for which remapping should happen. + src_platform (str, optional): Specify source platform. This is + recommended to not use and keep unset until you really want + to use specific platform. + roots (dict/RootItem/None, optional): It is possible to remap + path with different roots then instance where method was + called has. + + Returns: + str/None: When path does not contain known root then + None is returned else returns remapped path with "{root}" + or "{root[]}". + """ + cleaned_path = self.clean_path(path) + if dst_platform: + dst_root_clean = self.cleaned_data.get(dst_platform) + if not dst_root_clean: + key_part = "" + full_key = self.full_key() + if full_key != "root": + key_part += "\"{}\" ".format(full_key) + + log.warning( + "Root {}miss platform \"{}\" definition.".format( + key_part, dst_platform + ) + ) + return None + + if cleaned_path.startswith(dst_root_clean): + return cleaned_path + + if src_platform: + src_root_clean = self.cleaned_data.get(src_platform) + if src_root_clean is None: + log.warning( + "Root \"{}\" miss platform \"{}\" definition.".format( + self.full_key(), src_platform + ) + ) + return None + + if not cleaned_path.startswith(src_root_clean): + return None + + subpath = cleaned_path[len(src_root_clean):] + if dst_platform: + # `dst_root_clean` is used from upper condition + return dst_root_clean + subpath + return self.clean_value + subpath + + result, template = self.find_root_template_from_path(path) + if not result: + return None + + def parent_dict(keys, value): + if not keys: + return value + + key = keys.pop(0) + return {key: parent_dict(keys, value)} + + if dst_platform: + format_value = parent_dict(list(self.parent_keys), dst_root_clean) + else: + format_value = parent_dict(list(self.parent_keys), self.value) + + return template.format(**{"root": format_value}) + + def find_root_template_from_path(self, path): + """Replaces known root value with formattable key in path. + + All platform values are checked for this replacement. + + Args: + path (str): Path where root value should be found. + + Returns: + tuple: Tuple contain 2 values: `success` (bool) and `path` (str). + When success it True then path should contain replaced root + value with formattable key. + + Example: + When input path is:: + "C:/windows/path/root/projects/my_project/file.ext" + + And raw data of item looks like:: + { + "windows": "C:/windows/path/root", + "linux": "/mount/root" + } + + Output will be:: + (True, "{root}/projects/my_project/file.ext") + + If any of raw data value wouldn't match path's root output is:: + (False, "C:/windows/path/root/projects/my_project/file.ext") + """ + result = False + output = str(path) + + root_paths = list(self.cleaned_data.values()) + mod_path = self.clean_path(path) + for root_path in root_paths: + # Skip empty paths + if not root_path: + continue + + if mod_path.startswith(root_path): + result = True + replacement = "{" + self.full_key() + "}" + output = replacement + mod_path[len(root_path):] + break + + return (result, output) + + +class Roots: + """Object which should be used for formatting "root" key in templates. + + Args: + anatomy Anatomy: Anatomy object created for a specific project. + """ + + env_prefix = "OPENPYPE_PROJECT_ROOT" + roots_filename = "roots.json" + + def __init__(self, anatomy): + self.anatomy = anatomy + self.loaded_project = None + self._roots = None + + def __format__(self, *args, **kwargs): + return self.roots.__format__(*args, **kwargs) + + def __getitem__(self, key): + return self.roots[key] + + def reset(self): + """Reset current roots value.""" + self._roots = None + + def path_remapper( + self, path, dst_platform=None, src_platform=None, roots=None + ): + """Remap path for specific platform. + + Args: + path (str): Source path which need to be remapped. + dst_platform (str, optional): Specify destination platform + for which remapping should happen. + src_platform (str, optional): Specify source platform. This is + recommended to not use and keep unset until you really want + to use specific platform. + roots (dict/RootItem/None, optional): It is possible to remap + path with different roots then instance where method was + called has. + + Returns: + str/None: When path does not contain known root then + None is returned else returns remapped path with "{root}" + or "{root[]}". + """ + if roots is None: + roots = self.roots + + if roots is None: + raise ValueError("Roots are not set. Can't find path.") + + if "{root" in path: + path = path.format(**{"root": roots}) + # If `dst_platform` is not specified then return else continue. + if not dst_platform: + return path + + if isinstance(roots, RootItem): + return roots.path_remapper(path, dst_platform, src_platform) + + for _root in roots.values(): + result = self.path_remapper( + path, dst_platform, src_platform, _root + ) + if result is not None: + return result + + def find_root_template_from_path(self, path, roots=None): + """Find root value in entered path and replace it with formatting key. + + Args: + path (str): Source path where root will be searched. + roots (Roots/dict, optional): It is possible to use different + roots than instance where method was triggered has. + + Returns: + tuple: Output contains tuple with bool representing success as + first value and path with or without replaced root with + formatting key as second value. + + Raises: + ValueError: When roots are not entered and can't be loaded. + """ + if roots is None: + log.debug( + "Looking for matching root in path \"{}\".".format(path) + ) + roots = self.roots + + if roots is None: + raise ValueError("Roots are not set. Can't find path.") + + if isinstance(roots, RootItem): + return roots.find_root_template_from_path(path) + + for root_name, _root in roots.items(): + success, result = self.find_root_template_from_path(path, _root) + if success: + log.info("Found match in root \"{}\".".format(root_name)) + return success, result + + log.warning("No matching root was found in current setting.") + return (False, path) + + def set_root_environments(self): + """Set root environments for current project.""" + for key, value in self.root_environments().items(): + os.environ[key] = value + + def root_environments(self): + """Use root keys to create unique keys for environment variables. + + Concatenates prefix "OPENPYPE_ROOT" with root keys to create unique + keys. + + Returns: + dict: Result is `{(str): (str)}` dicitonary where key represents + unique key concatenated by keys and value is root value of + current platform root. + + Example: + With raw root values:: + "work": { + "windows": "P:/projects/work", + "linux": "/mnt/share/projects/work", + "darwin": "/darwin/path/work" + }, + "publish": { + "windows": "P:/projects/publish", + "linux": "/mnt/share/projects/publish", + "darwin": "/darwin/path/publish" + } + + Result on windows platform:: + { + "OPENPYPE_ROOT_WORK": "P:/projects/work", + "OPENPYPE_ROOT_PUBLISH": "P:/projects/publish" + } + + Short example when multiroot is not used:: + { + "OPENPYPE_ROOT": "P:/projects" + } + """ + return self._root_environments() + + def all_root_paths(self, roots=None): + """Return all paths for all roots of all platforms.""" + if roots is None: + roots = self.roots + + output = [] + if isinstance(roots, RootItem): + for value in roots.raw_data.values(): + output.append(value) + return output + + for _roots in roots.values(): + output.extend(self.all_root_paths(_roots)) + return output + + def _root_environments(self, keys=None, roots=None): + if not keys: + keys = [] + if roots is None: + roots = self.roots + + if isinstance(roots, RootItem): + key_items = [self.env_prefix] + for _key in keys: + key_items.append(_key.upper()) + + key = "_".join(key_items) + # Make sure key and value does not contain unicode + # - can happen in Python 2 hosts + return {str(key): str(roots.value)} + + output = {} + for _key, _value in roots.items(): + _keys = list(keys) + _keys.append(_key) + output.update(self._root_environments(_keys, _value)) + return output + + def root_environmets_fill_data(self, template=None): + """Environment variable values in dictionary for rootless path. + + Args: + template (str): Template for environment variable key fill. + By default is set to `"${}"`. + """ + if template is None: + template = "${}" + return self._root_environmets_fill_data(template) + + def _root_environmets_fill_data(self, template, keys=None, roots=None): + if keys is None and roots is None: + return { + "root": self._root_environmets_fill_data( + template, [], self.roots + ) + } + + if isinstance(roots, RootItem): + key_items = [Roots.env_prefix] + for _key in keys: + key_items.append(_key.upper()) + key = "_".join(key_items) + return template.format(key) + + output = {} + for key, value in roots.items(): + _keys = list(keys) + _keys.append(key) + output[key] = self._root_environmets_fill_data( + template, _keys, value + ) + return output + + @property + def project_name(self): + """Return project name which will be used for loading root values.""" + return self.anatomy.project_name + + @property + def roots(self): + """Property for filling "root" key in templates. + + This property returns roots for current project or default root values. + Warning: + Default roots value may cause issues when project use different + roots settings. That may happen when project use multiroot + templates but default roots miss their keys. + """ + if self.project_name != self.loaded_project: + self._roots = None + + if self._roots is None: + self._roots = self._discover() + self.loaded_project = self.project_name + return self._roots + + def _discover(self): + """ Loads current project's roots or default. + + Default roots are loaded if project override's does not contain roots. + + Returns: + `RootItem` or `dict` with multiple `RootItem`s when multiroot + setting is used. + """ + + return self._parse_dict(self.anatomy["roots"], parent=self) + + @staticmethod + def _parse_dict(data, key=None, parent_keys=None, parent=None): + """Parse roots raw data into RootItem or dictionary with RootItems. + + Converting raw roots data to `RootItem` helps to handle platform keys. + This method is recursive to be able handle multiroot setup and + is static to be able to load default roots without creating new object. + + Args: + data (dict): Should contain raw roots data to be parsed. + key (str, optional): Current root key. Set by recursion. + parent_keys (list): Parent dictionary keys. Set by recursion. + parent (Roots, optional): Parent object set in `RootItem` + helps to keep RootItem instance updated with `Roots` object. + + Returns: + `RootItem` or `dict` with multiple `RootItem`s when multiroot + setting is used. + """ + if not parent_keys: + parent_keys = [] + is_last = False + for value in data.values(): + if isinstance(value, six.string_types): + is_last = True + break + + if is_last: + return RootItem(data, key, parent_keys, parent=parent) + + output = {} + for _key, value in data.items(): + _parent_keys = list(parent_keys) + _parent_keys.append(_key) + output[_key] = Roots._parse_dict(value, _key, _parent_keys, parent) + return output diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index adc629352e..661975993b 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -14,7 +14,6 @@ from openpype.client import ( ) from openpype.client.operations import ( OperationsSession, - _create_or_convert_to_mongo_id, new_hero_version_doc, prepare_hero_version_update_data, prepare_representation_update_data, @@ -193,9 +192,13 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): op_session = OperationsSession() + entity_id = None + if old_version: + entity_id = old_version["_id"] new_hero_version = new_hero_version_doc( src_version_entity["_id"], - src_version_entity["parent"] + src_version_entity["parent"], + entity_id=entity_id ) if old_version: @@ -408,7 +411,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Create representation else: - repre["_id"] = _create_or_convert_to_mongo_id(None) + repre.pop("_id", None) op_session.create_entity(project_name, "representation", repre) diff --git a/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json b/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json new file mode 100644 index 0000000000..fb798524bc --- /dev/null +++ b/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json @@ -0,0 +1,92 @@ +[ + { + "_id": { + "$oid": "623c9d53db3f5046eb1ad5f4" + }, + "schema": "openpype:version-3.0", + "type": "version", + "parent": { + "$oid": "5f3e439a30a9464d6c181cbc" + }, + "name": 94, + "data": { + "families": [ + "workfile" + ], + "time": "20220324T173254Z", + "author": "petrk", + "source": "C:/projects_local/petr_test/assets/locations/Jungle/work/art/petr_test_Jungle_art_v009.psd", + "comment": "", + "machine": "LAPTOP-UB778LHG", + "fps": 25.0, + "intent": "-", + "inputLinks": [ + { + "type": "reference", + "id": { + "$oid": "618eb14f0a55a9c1591e913c" + }, + "linkedBy": "publish" + } + ] + }, + "outputs_recursive": [ + { + "_id": { + "$oid": "618eb14f0a55a9c1591e913c" + }, + "schema": "openpype:version-3.0", + "type": "version", + "parent": { + "$oid": "618e42a72ff49bd543bc1768" + }, + "name": 8, + "data": { + "families": [ + "image" + ], + "time": "20211112T192359Z", + "author": "petrk", + "source": "C:/projects_local/petr_test/assets/locations/Town/work/art/petr_test_Town_art_v005.psd", + "comment": "", + "machine": "LAPTOP-UB778LHG", + "fps": 25.0, + "intent": "-", + "inputLinks": [ + { + "type": "reference", + "id": { + "$oid": "5f3cd2d530a94638544837c3" + }, + "linkedBy": "publish" + } + ] + }, + "depth": 0 + }, + { + "_id": { + "$oid": "5f3cd2d530a94638544837c3" + }, + "schema": "pype:version-3.0", + "type": "version", + "parent": { + "$oid": "5f3a714030a9464bfc7d2382" + }, + "name": 7, + "data": { + "families": [ + "image" + ], + "time": "20200819T092032Z", + "author": "petrk", + "source": "/c/projects/petr_test/assets/characters/Hero/work/art/Hero_v019.psd", + "comment": "", + "machine": "LAPTOP-UB778LHG", + "fps": null + }, + "depth": 1 + } + ] + } +] \ No newline at end of file diff --git a/tests/unit/test_unzip.py b/tests/unit/test_unzip.py new file mode 100644 index 0000000000..586fc49b6f --- /dev/null +++ b/tests/unit/test_unzip.py @@ -0,0 +1,11 @@ + +from openpype.hosts.harmony.api.lib import _ZipFile +from pathlib import Path + +def test_zip(): + source = "c:/Users/petrk/Downloads/fbb_fbb100_sh0020_workfileAnimation_v010.zip" + dest = "c:/projects/temp/unzipped_with_python_111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\\2222222222222222222222222222222222222222222222222222222222222222222222222222222222" + + dest = Path(dest) + with _ZipFile(source, "r") as zip_ref: + zip_ref.extractall(dest.as_posix()) \ No newline at end of file diff --git a/vendor/configs/OpenColorIO-Configs b/vendor/configs/OpenColorIO-Configs new file mode 160000 index 0000000000..0bb079c08b --- /dev/null +++ b/vendor/configs/OpenColorIO-Configs @@ -0,0 +1 @@ +Subproject commit 0bb079c08be410030669cbf5f19ff869b88af953 diff --git a/vendor/instance.json b/vendor/instance.json new file mode 100644 index 0000000000..b1d623e85d --- /dev/null +++ b/vendor/instance.json @@ -0,0 +1,1133 @@ +{ + 'family': 'render', + 'name': 'renderLightingDefault', + 'label': 'renderLightingDefault - local', + 'version': 1, + 'time': '', + 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', + 'subset': 'renderLightingDefault', + 'asset': 'shot01', + 'attachTo': False, + 'setMembers': '', + 'publish': True, + 'resolutionWidth': 1920.0, + 'resolutionHeight': 1080.0, + 'pixelAspect': 1, + 'frameStart': 0, + 'frameEnd': 0, + 'frameStep': 1, + 'handleStart': 0, + 'handleEnd': 0, + 'ignoreFrameHandleCheck': False, + 'renderer': 'aerender', + 'review': True, + 'priority': 50, + 'families': [ + 'render', + 'review', + 'ftrack', + 'slack' + ], + 'multipartExr': False, + 'convertToScanline': False, + 'tileRendering': False, + 'tilesX': 0, + 'tilesY': 0, + 'toBeRenderedOn': 'deadline', + 'deadlineSubmissionJob': None, + 'anatomyData': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-' + }, + 'outputDir': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001', + 'comp_name': '℗ renderLightingDefault', + 'comp_id': 1, + 'fps': 25, + 'projectEntity': { + '_id': ObjectId( + '5f2a6d2311e06a9818a1958b' + ), + 'name': 'petr_test', + 'created_d': datetime.datetime(2020, + 9, + 17, + 15, + 27, + 27, + 927000), + 'data': { + 'ftrackId': 'e5eda2bc-d682-11ea-afc1-92591a5b5e3e', + 'entityType': 'Project', + 'applications': [ + 'maya_2019', + 'photoshop_2021', + 'photoshop_2022', + 'harmony_17', + 'aftereffects_2022', + 'harmony_20', + 'nukestudio_12.2', + 'nukex_12.2', + 'hiero_12.2', + 'blender_2.93' + ], + 'library_project': True, + 'clipIn': 1, + 'resolutionWidth': 1920.0, + 'handleEnd': 0, + 'frameEnd': 1001, + 'resolutionHeight': 1080.0, + 'frameStart': 1001.0, + 'pixelAspect': 1.0, + 'fps': 25.0, + 'handleStart': 0, + 'clipOut': 1, + 'tools_env': [], + 'code': 'petr_test', + 'active': True + }, + 'type': 'project', + 'config': { + 'apps': [ + { + 'name': 'aftereffects/2022' + }, + { + 'name': 'maya/2019' + }, + { + 'name': 'hiero/12-2' + }, + { + 'name': 'photoshop/2021' + }, + { + 'name': 'nuke/12-2' + }, + { + 'name': 'photoshop/2022' + } + ], + 'tasks': { + 'Layout': { + 'short_name': 'lay' + }, + 'Setdress': { + 'short_name': 'dress' + }, + 'Previz': { + 'short_name': '' + }, + 'Generic': { + 'short_name': 'gener' + }, + 'Animation': { + 'short_name': 'anim' + }, + 'Modeling': { + 'short_name': 'mdl' + }, + 'Lookdev': { + 'short_name': 'look' + }, + 'FX': { + 'short_name': 'fx' + }, + 'Lighting': { + 'short_name': 'lgt' + }, + 'Compositing': { + 'short_name': 'comp' + }, + 'Tracking': { + 'short_name': '' + }, + 'Rigging': { + 'short_name': 'rig' + }, + 'Paint': { + 'short_name': 'paint' + }, + 'schedulle': { + 'short_name': '' + }, + 'Art': { + 'short_name': 'art' + }, + 'Texture': { + 'short_name': 'tex' + }, + 'Edit': { + 'short_name': 'edit' + } + }, + 'imageio': { + 'hiero': { + 'workfile': { + 'ocioConfigName': 'nuke-default', + 'ocioconfigpath': { + 'windows': [], + 'darwin': [], + 'linux': [] + }, + 'workingSpace': 'linear', + 'sixteenBitLut': 'sRGB', + 'eightBitLut': 'sRGB', + 'floatLut': 'linear', + 'logLut': 'Cineon', + 'viewerLut': 'sRGB', + 'thumbnailLut': 'sRGB' + }, + 'regexInputs': { + 'inputs': [ + { + 'regex': '[^-a-zA-Z0-9](plateRef).*(?=mp4)', + 'colorspace': 'sRGB' + } + ] + } + }, + 'nuke': { + 'viewer': { + 'viewerProcess': 'sRGB' + }, + 'baking': { + 'viewerProcess': 'rec709' + }, + 'workfile': { + 'colorManagement': 'Nuke', + 'OCIO_config': 'nuke-default', + 'customOCIOConfigPath': { + 'windows': [], + 'darwin': [], + 'linux': [] + }, + 'workingSpaceLUT': 'linear', + 'monitorLut': 'sRGB', + 'int8Lut': 'sRGB', + 'int16Lut': 'sRGB', + 'logLut': 'Cineon', + 'floatLut': 'linear' + }, + 'nodes': { + 'requiredNodes': [ + { + 'plugins': [ + 'CreateWriteRender' + ], + 'nukeNodeClass': 'Write', + 'knobs': [ + { + 'name': 'file_type', + 'value': 'exr' + }, + { + 'name': 'datatype', + 'value': '16 bit half' + }, + { + 'name': 'compression', + 'value': 'Zip (1 scanline)' + }, + { + 'name': 'autocrop', + 'value': 'True' + }, + { + 'name': 'tile_color', + 'value': '0xff0000ff' + }, + { + 'name': 'channels', + 'value': 'rgb' + }, + { + 'name': 'colorspace', + 'value': 'linear' + }, + { + 'name': 'create_directories', + 'value': 'True' + } + ] + }, + { + 'plugins': [ + 'CreateWritePrerender' + ], + 'nukeNodeClass': 'Write', + 'knobs': [ + { + 'name': 'file_type', + 'value': 'exr' + }, + { + 'name': 'datatype', + 'value': '16 bit half' + }, + { + 'name': 'compression', + 'value': 'Zip (1 scanline)' + }, + { + 'name': 'autocrop', + 'value': 'False' + }, + { + 'name': 'tile_color', + 'value': '0xadab1dff' + }, + { + 'name': 'channels', + 'value': 'rgb' + }, + { + 'name': 'colorspace', + 'value': 'linear' + }, + { + 'name': 'create_directories', + 'value': 'True' + } + ] + } + ], + 'customNodes': [] + }, + 'regexInputs': { + 'inputs': [ + { + 'regex': '[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]', + 'colorspace': 'linear' + } + ] + } + }, + 'maya': { + 'colorManagementPreference': { + 'configFilePath': { + 'windows': [], + 'darwin': [], + 'linux': [] + }, + 'renderSpace': 'scene-linear Rec 709/sRGB', + 'viewTransform': 'sRGB gamma' + } + } + }, + 'roots': { + 'work': { + 'windows': 'C:/projects', + 'darwin': '/Volumes/path', + 'linux': '/mnt/share/projects' + } + }, + 'templates': { + 'defaults': { + 'version_padding': 3, + 'version': 'v{version:0>{@version_padding}}', + 'frame_padding': 4, + 'frame': '{frame:0>{@frame_padding}}' + }, + 'work': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task}', + 'file': '{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}', + 'path': '{@folder}/{@file}' + }, + 'render': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', + 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{ext}', + 'path': '{@folder}/{@file}' + }, + 'publish': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', + 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}', + 'path': '{@folder}/{@file}', + 'thumbnail': '{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}' + }, + 'hero': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/hero', + 'file': '{project[code]}_{asset}_{subset}_hero<_{output}><.{frame}>.{ext}', + 'path': '{@folder}/{@file}' + }, + 'delivery': {}, + 'others': {} + } + }, + 'parent': None, + 'schema': 'avalon-core:project-2.0' + }, + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'frameStartHandle': 0, + 'frameEndHandle': 0, + 'byFrameStep': 1, + 'author': 'petrk', + 'expectedFiles': [ + 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001\\shot01_renderLightingDefault_v001.mov' + ], + 'slack_channel_message_profiles': [ + { + 'channels': [ + 'test_integration' + ], + 'upload_thumbnail': True, + 'message': 'Test message' + } + ], + 'slack_token': 'xoxb-1494100953104-2176825439264-jGqvQzfq9uZJPmyX5Q4o4TnP', + 'representations': [ + { + 'frameStart': 0, + 'frameEnd': 0, + 'name': 'mov', + 'ext': 'mov', + 'files': ' renderLightingDefault.mov', + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'tags': [ + 'review' + ], + 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'publishedFiles': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' + ] + }, + { + 'name': 'thumbnail', + 'ext': 'jpg', + 'files': 'thumbnail.jpg', + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'tags': [ + 'thumbnail' + ], + 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'publishedFiles': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' + ] + }, + { + 'frameStart': 0, + 'frameEnd': 0, + 'name': 'h264_mp4', + 'ext': 'mp4', + 'files': ' renderLightingDefault_h264burnin.mp4', + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'tags': [ + 'review', + 'burnin', + 'ftrackreview' + ], + 'resolutionWidth': 1920, + 'resolutionHeight': 1080, + 'outputName': 'h264', + 'outputDef': { + 'ext': 'mp4', + 'tags': [ + 'burnin', + 'ftrackreview' + ], + 'burnins': [], + 'ffmpeg_args': { + 'video_filters': [], + 'audio_filters': [], + 'input': [ + '-apply_trc gamma22' + ], + 'output': [ + '-pix_fmt yuv420p', + '-crf 18', + '-intra' + ] + }, + 'filter': { + 'families': [ + 'render', + 'review', + 'ftrack' + ] + }, + 'overscan_crop': '', + 'overscan_color': [ + 0, + 0, + 0, + 255 + ], + 'width': 0, + 'height': 0, + 'bg_color': [ + 0, + 0, + 0, + 0 + ], + 'letter_box': { + 'enabled': False, + 'ratio': 0.0, + 'state': 'letterbox', + 'fill_color': [ + 0, + 0, + 0, + 255 + ], + 'line_thickness': 0, + 'line_color': [ + 255, + 0, + 0, + 255 + ] + }, + 'filename_suffix': 'h264' + }, + 'frameStartFtrack': 0, + 'frameEndFtrack': 0, + 'ffmpeg_cmd': 'C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\vendor\\bin\\ffmpeg\\windows\\bin\\ffmpeg -apply_trc gamma22 -i "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault.mov" -pix_fmt yuv420p -crf 18 -intra -y "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264.mp4"', + 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'publishedFiles': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ] + } + ], + 'assetEntity': { + '_id': ObjectId( + '5fabee9730a94666449245b7' + ), + 'name': 'shot01', + 'data': { + 'ftrackId': '0c5f548c-2425-11eb-b203-628b111fac3c', + 'entityType': 'Shot', + 'clipIn': 1, + 'resolutionWidth': 1920.0, + 'handleEnd': 0.0, + 'frameEnd': 1001, + 'resolutionHeight': 1080.0, + 'frameStart': 1001.0, + 'pixelAspect': 1.0, + 'fps': 25.0, + 'handleStart': 0.0, + 'clipOut': 1, + 'tools_env': [], + 'avalon_mongo_id': '5fabee9730a94666449245b7', + 'parents': [ + 'sequences', + 'seq01' + ], + 'hierarchy': 'sequences\\seq01', + 'tasks': { + 'lighting': { + 'type': 'Lighting' + }, + 'animation': { + 'type': 'Animation' + }, + 'compositing': { + 'type': 'Compositing' + } + }, + 'visualParent': ObjectId( + '5fabee9730a94666449245b6' + ) + }, + 'type': 'asset', + 'parent': ObjectId( + '5f2a6d2311e06a9818a1958b' + ), + 'schema': 'pype:asset-3.0' + }, + 'subsetEntity': { + '_id': ObjectId( + '61d723a271e6fce378bd428c' + ), + 'schema': 'openpype:subset-3.0', + 'type': 'subset', + 'name': 'renderLightingDefault', + 'data': { + 'families': [ + 'render', + 'review', + 'ftrack', + 'slack' + ] + }, + 'parent': ObjectId( + '5fabee9730a94666449245b7' + ) + }, + 'versionEntity': { + '_id': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'schema': 'openpype:version-3.0', + 'type': 'version', + 'parent': ObjectId( + '61d723a271e6fce378bd428c' + ), + 'name': 1, + 'data': { + 'families': [ + 'render', + 'render', + 'review', + 'ftrack', + 'slack' + ], + 'time': '20220106T181423Z', + 'author': 'petrk', + 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', + 'comment': '', + 'machine': 'LAPTOP-UB778LHG', + 'fps': 25.0, + 'intent': '-', + 'frameStart': 0, + 'frameEnd': 0, + 'handleEnd': 0, + 'handleStart': 0, + 'inputLinks': [ + OrderedDict( + [ + ( + 'type', + 'generative' + ), + ( + 'id', + ObjectId( + '600ab849c411725a626b8c35' + )), + ( + 'linkedBy', + 'publish' + ) + ] + ) + ] + } + }, + 'transfers': [ + [ + 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264burnin.mp4', + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ] + ], + 'destination_list': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ], + 'published_representations': { + ObjectId( + '61d723a371e6fce378bd428e' + ): { + 'representation': { + '_id': ObjectId( + '61d723a371e6fce378bd428e' + ), + 'schema': 'openpype:representation-2.0', + 'type': 'representation', + 'parent': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'name': 'mov', + 'data': { + 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' + }, + 'dependencies': [], + 'context': { + 'root': { + 'work': 'C:/projects' + }, + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'hierarchy': 'sequences/seq01', + 'asset': 'shot01', + 'family': 'render', + 'subset': 'renderLightingDefault', + 'version': 1, + 'ext': 'mov', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'representation': 'mov', + 'username': 'petrk' + }, + 'files': [ + { + '_id': ObjectId( + '61d723a371e6fce378bd4291' + ), + 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.mov', + 'size': 1654788, + 'hash': 'petr_test_shot01_renderLightingDefault_v001,mov|1641489300,6230524|1654788', + 'sites': [ + { + 'name': 'studio', + 'created_dt': datetime.datetime(2022, + 1, + 6, + 18, + 15, + 15, + 264448) + } + ] + } + ] + }, + 'anatomy_data': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-', + 'representation': 'mov', + 'ext': 'mov' + }, + 'published_files': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' + ] + }, + ObjectId( + '61d723a371e6fce378bd4292' + ): { + 'representation': { + '_id': ObjectId( + '61d723a371e6fce378bd4292' + ), + 'schema': 'openpype:representation-2.0', + 'type': 'representation', + 'parent': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'name': 'thumbnail', + 'data': { + 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' + }, + 'dependencies': [], + 'context': { + 'root': { + 'work': 'C:/projects' + }, + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'hierarchy': 'sequences/seq01', + 'asset': 'shot01', + 'family': 'render', + 'subset': 'renderLightingDefault', + 'version': 1, + 'ext': 'jpg', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'representation': 'jpg', + 'username': 'petrk' + }, + 'files': [ + { + '_id': ObjectId( + '61d723a371e6fce378bd4295' + ), + 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.jpg', + 'size': 871, + 'hash': 'petr_test_shot01_renderLightingDefault_v001,jpg|1641489301,1720147|871', + 'sites': [ + { + 'name': 'studio', + 'created_dt': datetime.datetime(2022, + 1, + 6, + 18, + 15, + 15, + 825446) + } + ] + } + ] + }, + 'anatomy_data': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-', + 'representation': 'jpg', + 'ext': 'jpg' + }, + 'published_files': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' + ] + }, + ObjectId( + '61d723a471e6fce378bd4296' + ): { + 'representation': { + '_id': ObjectId( + '61d723a471e6fce378bd4296' + ), + 'schema': 'openpype:representation-2.0', + 'type': 'representation', + 'parent': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'name': 'h264_mp4', + 'data': { + 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' + }, + 'dependencies': [], + 'context': { + 'root': { + 'work': 'C:/projects' + }, + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'hierarchy': 'sequences/seq01', + 'asset': 'shot01', + 'family': 'render', + 'subset': 'renderLightingDefault', + 'version': 1, + 'output': 'h264', + 'ext': 'mp4', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'representation': 'mp4', + 'username': 'petrk' + }, + 'files': [ + { + '_id': ObjectId( + '61d723a471e6fce378bd4299' + ), + 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'size': 10227, + 'hash': 'petr_test_shot01_renderLightingDefault_v001_h264,mp4|1641489313,659368|10227', + 'sites': [ + { + 'name': 'studio', + 'created_dt': datetime.datetime(2022, + 1, + 6, + 18, + 15, + 16, + 53445) + } + ] + } + ] + }, + 'anatomy_data': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-', + 'resolution_width': 1920, + 'resolution_height': 1080, + 'fps': 25, + 'output': 'h264', + 'representation': 'mp4', + 'ext': 'mp4' + }, + 'published_files': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ] + } + }, + 'ftrackComponentsList': [ + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': True, + 'component_data': { + 'name': 'thumbnail' + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'ftrackreview-mp4', + 'metadata': { + 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' + } + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'thumbnail_src' + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'ftrackreview-mp4_src', + 'metadata': { + 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' + } + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'mov' + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'component_location': , + 'component': + } + ], + 'ftrackIntegratedAssetVersions': [ + + ] +} \ No newline at end of file diff --git a/vendor/response.json b/vendor/response.json new file mode 100644 index 0000000000..26a4fae2fd --- /dev/null +++ b/vendor/response.json @@ -0,0 +1 @@ +{status: 200, headers: {'date': 'Tue, 11 Jan 2022 11:08:57 GMT', 'server': 'Apache', 'x-powered-by': 'HHVM/4.128.0', 'access-control-allow-origin': '*', 'referrer-policy': 'no-referrer', 'x-slack-backend': 'r', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'access-control-allow-headers': 'slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags', 'access-control-expose-headers': 'x-slack-req-id, retry-after', 'x-oauth-scopes': 'chat:write,chat:write.public,files:write,chat:write.customize', 'x-accepted-oauth-scopes': 'chat:write', 'expires': 'Mon, 26 Jul 1997 05:00:00 GMT', 'cache-control': 'private, no-cache, no-store, must-revalidate', 'pragma': 'no-cache', 'x-xss-protection': '0', 'x-content-type-options': 'nosniff', 'x-slack-req-id': '9d1d11399a44c8751f89bb4dcd2b91fb', 'vary': 'Accept-Encoding', 'content-type': 'application/json; charset=utf-8', 'x-envoy-upstream-service-time': '52', 'x-backend': 'main_normal main_bedrock_normal_with_overflow main_canary_with_overflow main_bedrock_canary_with_overflow main_control_with_overflow main_bedrock_control_with_overflow', 'x-server': 'slack-www-hhvm-main-iad-qno3', 'x-slack-shared-secret-outcome': 'no-match', 'via': 'envoy-www-iad-omsy, envoy-edge-iad-bgfx', 'x-edge-backend': 'envoy-www', 'x-slack-edge-shared-secret-outcome': 'no-match', 'connection': 'close', 'transfer-encoding': 'chunked'}, body: {"ok":true,"channel":"C024DUFM8MB","ts":"1641899337.001100","message":{"type":"message","subtype":"bot_message","text":"RenderCompositingDefault published for Jungle\n\nHere should be link to review C:\\projects\\petr_test\\assets\\locations\\Jungle\\publish\\render\\renderCompositingDefault\\v253\\petr_test_Jungle_renderCompositingDefault_v253_h264.mp4\n\n Attachment links: \n\n","ts":"1641899337.001100","username":"OpenPypeNotifier","icons":{"image_48":"https:\/\/s3-us-west-2.amazonaws.com\/slack-files2\/bot_icons\/2022-01-07\/2934353684385_48.png"},"bot_id":"B024H0P0CAE"}} \ No newline at end of file diff --git a/vendor/temp.json b/vendor/temp.json new file mode 100644 index 0000000000..089174d26c --- /dev/null +++ b/vendor/temp.json @@ -0,0 +1,46 @@ +{ + project(name: "demo_Big_Episodic") { + representations( + first: 0, + after: 0, + localSite: "local", + remoteSite: "local" + ) { + edges { + node { + id + name + # Sorry: totalSize is not implemented, but it will be + # totalSize + fileCount + # overal sync state + localState{ + status + size + timestamp + } + remoteState{ + status + size + timestamp + } + # crawl to the top to get parent info + version { + version + subset { + family + name + folder { + name + } + } + } + } + } + pageInfo { + hasNextPage + endCursor + } + } + } +} \ No newline at end of file From b47e480d7c5384862afbbfae3e7e0b779036e1da Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:31:02 +0200 Subject: [PATCH 18/55] Revert "OP-4181 - clean up after review comments" This reverts commit a7150bd6f1c9494734f03265eecbe86ff284d882. --- igniter/GPUCache/data_0 | Bin 8192 -> 0 bytes igniter/GPUCache/data_1 | Bin 270336 -> 0 bytes igniter/GPUCache/data_2 | Bin 8192 -> 0 bytes igniter/GPUCache/data_3 | Bin 8192 -> 0 bytes igniter/GPUCache/index | Bin 262512 -> 0 bytes openpype/hooks/pre_python2_prelaunch.py | 35 - openpype/hosts/photoshop/tests/expr.py | 51 - openpype/lib/token | 1 - .../event_handlers_user/action_edl_create.py | 275 ---- openpype/pipeline/temp_anatomy.py | 1330 ----------------- .../plugins/publish/integrate_hero_version.py | 9 +- .../_process_referenced_pipeline_result.json | 92 -- tests/unit/test_unzip.py | 11 - vendor/configs/OpenColorIO-Configs | 1 - vendor/instance.json | 1133 -------------- vendor/response.json | 1 - vendor/temp.json | 46 - 17 files changed, 3 insertions(+), 2982 deletions(-) delete mode 100644 igniter/GPUCache/data_0 delete mode 100644 igniter/GPUCache/data_1 delete mode 100644 igniter/GPUCache/data_2 delete mode 100644 igniter/GPUCache/data_3 delete mode 100644 igniter/GPUCache/index delete mode 100644 openpype/hooks/pre_python2_prelaunch.py delete mode 100644 openpype/hosts/photoshop/tests/expr.py delete mode 100644 openpype/lib/token delete mode 100644 openpype/modules/ftrack/event_handlers_user/action_edl_create.py delete mode 100644 openpype/pipeline/temp_anatomy.py delete mode 100644 tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json delete mode 100644 tests/unit/test_unzip.py delete mode 160000 vendor/configs/OpenColorIO-Configs delete mode 100644 vendor/instance.json delete mode 100644 vendor/response.json delete mode 100644 vendor/temp.json diff --git a/igniter/GPUCache/data_0 b/igniter/GPUCache/data_0 deleted file mode 100644 index d76fb77e93ac8a536b5dbade616d63abd00626c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeIuK?wjL5Jka{7-jo+5O1auw}mk8@B+*}b0s6M>Kg$91PBlyK!5-N0t5&UAV7cs W0RjXF5FkK+009C72oNCfo4^Gh&;oe? diff --git a/igniter/GPUCache/data_1 b/igniter/GPUCache/data_1 deleted file mode 100644 index 212f73166781160e472f8e76c3b9998b3775ecb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270336 zcmeI%u?@m75CFhW@CfV>3QP3t%>amw0a8XffrJVo)0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N c0t5&UAV7cs0RjXF5FkK+009C72>hqO2eI!7!2kdN diff --git a/igniter/GPUCache/data_2 b/igniter/GPUCache/data_2 deleted file mode 100644 index c7e2eb9adcfb2d3313ec85f5c28cedda950a3f9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeIu!3h8`2n0b1_TQ7_m#U&=2(t%Qz}%M=ae7_Oi2wlt1PBlyK!5-N0t5&UAV7cs V0RjXF5FkK+009C72oTsN@Bv`}0$Tt8 diff --git a/igniter/GPUCache/data_3 b/igniter/GPUCache/data_3 deleted file mode 100644 index 5eec97358cf550862fd343fc9a73c159d4c0ab10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeIuK@9*P5CpLeAOQbv2)|PW$RO!FMnHFsm9+HS=9>r*AV7cs0RjXF5FkK+009C7 W2oNAZfB*pk1PBlyK!5;&-vkZ-dID$w diff --git a/igniter/GPUCache/index b/igniter/GPUCache/index deleted file mode 100644 index b2998cfef1a6457e5cfe9dc37e029bdbe0a7f778..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262512 zcmeIuu?>JQ00XcT9zZ-&b?3`o&{Gf_S0S;K0~nnt$>{4|&t%Cr+dIlg%Diho_EzWC z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjYm G5qJP2>jZZI diff --git a/openpype/hooks/pre_python2_prelaunch.py b/openpype/hooks/pre_python2_prelaunch.py deleted file mode 100644 index 84272d2e5d..0000000000 --- a/openpype/hooks/pre_python2_prelaunch.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -from openpype.lib import PreLaunchHook - - -class PrePython2Vendor(PreLaunchHook): - """Prepend python 2 dependencies for py2 hosts.""" - order = 10 - - def execute(self): - if not self.application.use_python_2: - return - - # Prepare vendor dir path - self.log.info("adding global python 2 vendor") - pype_root = os.getenv("OPENPYPE_REPOS_ROOT") - python_2_vendor = os.path.join( - pype_root, - "openpype", - "vendor", - "python", - "python_2" - ) - - # Add Python 2 modules - python_paths = [ - python_2_vendor - ] - - # Load PYTHONPATH from current launch context - python_path = self.launch_context.env.get("PYTHONPATH") - if python_path: - python_paths.append(python_path) - - # Set new PYTHONPATH to launch context environments - self.launch_context.env["PYTHONPATH"] = os.pathsep.join(python_paths) diff --git a/openpype/hosts/photoshop/tests/expr.py b/openpype/hosts/photoshop/tests/expr.py deleted file mode 100644 index ff796f417c..0000000000 --- a/openpype/hosts/photoshop/tests/expr.py +++ /dev/null @@ -1,51 +0,0 @@ -import json - -data = [ - { - "schema": "openpype:container-2.0", - "id": "pyblish.avalon.container", - "name": "imageArtNeew", - "namespace": "Jungle_imageArtNeew_001", - "loader": "ReferenceLoader", - "representation": "61c1eb91e1a4d1e5a23582f6", - "members": [ - "131" - ] - }, - { - "id": "pyblish.avalon.instance", - "family": "image", - "asset": "Jungle", - "subset": "imageMainBg", - "active": True, - "variant": "Main", - "uuid": "199", - "long_name": "BG" - }, - { - "id": "pyblish.avalon.instance", - "family": "image", - "asset": "Jungle", - "subset": "imageMain", - "active": True, - "variant": "Main", - "uuid": "192", - "long_name": "imageMain" - }, - { - "id": "pyblish.avalon.instance", - "family": "workfile", - "subset": "workfile", - "active": True, - "creator_identifier": "workfile", - "asset": "Jungle", - "task": "art", - "variant": "", - "instance_id": "3ed19342-cd8e-4bb6-8cda-d6e74d9a7efe", - "creator_attributes": {}, - "publish_attributes": {} - } -] - -with open("C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\openpype\\hosts\\photoshop\\tests\\mock_get_layers_metadata.json", 'w') as fp: - fp.write(json.dumps(data, indent=4)) \ No newline at end of file diff --git a/openpype/lib/token b/openpype/lib/token deleted file mode 100644 index 193a2aac95..0000000000 --- a/openpype/lib/token +++ /dev/null @@ -1 +0,0 @@ -5d58370a7702b2efee5120704246baf4abb865323fc9db9a04827bfb478569d6 \ No newline at end of file diff --git a/openpype/modules/ftrack/event_handlers_user/action_edl_create.py b/openpype/modules/ftrack/event_handlers_user/action_edl_create.py deleted file mode 100644 index 7ac139ae63..0000000000 --- a/openpype/modules/ftrack/event_handlers_user/action_edl_create.py +++ /dev/null @@ -1,275 +0,0 @@ -import os -import subprocess -import tempfile -import shutil -import json -import sys - -import opentimelineio as otio -import ftrack_api -import requests - -from openpype_modules.ftrack.lib import BaseAction - - -def download_file(url, path): - with open(path, "wb") as f: - print("\nDownloading %s" % path) - response = requests.get(url, stream=True) - total_length = response.headers.get('content-length') - - if total_length is None: - f.write(response.content) - else: - dl = 0 - total_length = int(total_length) - for data in response.iter_content(chunk_size=4096): - dl += len(data) - f.write(data) - done = int(50 * dl / total_length) - sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50-done))) - sys.stdout.flush() - - -class ExportEditorialAction(BaseAction): - '''Export Editorial action''' - - label = "Export Editorial" - variant = None - identifier = "export-editorial" - description = None - component_name_order = ["exr", "mov", "ftrackreview-mp4_src"] - - def export_editorial(self, entity, output_path): - session = ftrack_api.Session() - unmanaged_location = session.query( - "Location where name is \"ftrack.unmanaged\"" - ).one() - temp_path = tempfile.mkdtemp() - - files = {} - for obj in entity["review_session_objects"]: - data = {} - parent_name = obj["asset_version"]["asset"]["parent"]["name"] - component_query = "Component where version_id is \"{}\"" - component_query += " and name is \"{}\"" - for name in self.component_name_order: - try: - component = session.query( - component_query.format( - obj["asset_version"]["id"], name - ) - ).one() - path = unmanaged_location.get_filesystem_path(component) - data["path"] = path.replace("\\", "/") - break - except ftrack_api.exception.NoResultFoundError: - pass - - # Download online review if not local path found. - if "path" not in data: - component = session.query( - component_query.format( - obj["asset_version"]["id"], "ftrackreview-mp4" - ) - ).one() - location = component["component_locations"][0] - component_url = location["location"].get_url(component) - asset_name = obj["asset_version"]["asset"]["name"] - version = obj["asset_version"]["version"] - filename = "{}_{}_v{:03d}.mp4".format( - parent_name, asset_name, version - ) - filepath = os.path.join( - output_path, "downloads", filename - ).replace("\\", "/") - - if not os.path.exists(os.path.dirname(filepath)): - os.makedirs(os.path.dirname(filepath)) - - download_file(component_url, filepath) - data["path"] = filepath - - # Get frame duration and framerate. - query = "Component where version_id is \"{}\"" - query += " and name is \"ftrackreview-mp4\"" - component = session.query( - query.format(obj["asset_version"]["id"]) - ).one() - metadata = json.loads(component["metadata"]["ftr_meta"]) - data["framerate"] = metadata["frameRate"] - data["frames"] = metadata["frameOut"] - metadata["frameIn"] - - # Find audio if it exists. - query = "Asset where parent.id is \"{}\"" - query += " and type.name is \"Audio\"" - asset = session.query( - query.format(obj["asset_version"]["asset"]["parent"]["id"]) - ) - if asset: - asset_version = asset[0]["versions"][-1] - query = "Component where version_id is \"{}\"" - query += " and name is \"{}\"" - comp = session.query( - query.format(asset_version["id"], "wav") - ).one() - src = unmanaged_location.get_filesystem_path(comp) - dst = os.path.join(temp_path, parent_name + ".wav") - shutil.copy(src, dst) - - # Collect data. - files[parent_name] = data - - clips = [] - for name, data in files.items(): - self.log.info("Processing {} with {}".format(name, data)) - f = data["path"] - range = otio.opentime.TimeRange( - start_time=otio.opentime.RationalTime(0, data["framerate"]), - duration=otio.opentime.RationalTime( - data["frames"], data["framerate"] - ) - ) - - media_reference = otio.schema.ExternalReference( - available_range=range, - target_url=f"file://{f}" - ) - - clip = otio.schema.Clip( - name=name, - media_reference=media_reference, - source_range=range - ) - clips.append(clip) - - # path = os.path.join(temp_path, name + ".wav").replace("\\", "/") - # if not os.path.exists(path): - # args = ["ffmpeg", "-y", "-i", f, path] - # self.log.info(subprocess.list2cmdline(args)) - # subprocess.call(args) - - timeline = otio.schema.timeline_from_clips(clips) - otio.adapters.write_to_file( - timeline, os.path.join(output_path, entity["name"] + ".xml") - ) - - data = "" - for f in os.listdir(temp_path): - f = f.replace("\\", "/") - data += f"file '{f}'\n" - - path = os.path.join(temp_path, "temp.txt") - with open(path, "w") as f: - f.write(data) - - args = [ - "ffmpeg", "-y", "-f", "concat", "-safe", "0", - "-i", os.path.basename(path), - os.path.join(output_path, entity["name"] + ".wav") - ] - self.log.info(subprocess.list2cmdline(args)) - subprocess.call(args, cwd=temp_path) - - shutil.rmtree(temp_path) - - def discover(self, session, entities, event): - '''Return true if we can handle the selected entities. - *session* is a `ftrack_api.Session` instance - *entities* is a list of tuples each containing the entity type and the - entity id. - If the entity is a hierarchical you will always get the entity - type TypedContext, once retrieved through a get operation you - will have the "real" entity type ie. example Shot, Sequence - or Asset Build. - *event* the unmodified original event - ''' - if len(entities) == 1: - if entities[0].entity_type == "ReviewSession": - return True - - return False - - def launch(self, session, entities, event): - '''Callback method for the custom action. - return either a bool ( True if successful or False if the action - failed ) or a dictionary with they keys `message` and `success`, the - message should be a string and will be displayed as feedback to the - user, success should be a bool, True if successful or False if the - action failed. - *session* is a `ftrack_api.Session` instance - *entities* is a list of tuples each containing the entity type and the - entity id. - If the entity is a hierarchical you will always get the entity - type TypedContext, once retrieved through a get operation you - will have the "real" entity type ie. example Shot, Sequence - or Asset Build. - *event* the unmodified original event - ''' - if 'values' in event['data']: - userId = event['source']['user']['id'] - user = session.query('User where id is ' + userId).one() - job = session.create( - 'Job', - { - 'user': user, - 'status': 'running', - 'data': json.dumps({ - 'description': 'Export Editorial.' - }) - } - ) - session.commit() - - try: - output_path = event["data"]["values"]["output_path"] - - if not os.path.exists(output_path): - os.makedirs(output_path) - - self.export_editorial(entities[0], output_path) - - job['status'] = 'done' - session.commit() - except Exception: - session.rollback() - job["status"] = "failed" - session.commit() - self.log.error( - "Exporting editorial failed ({})", exc_info=True - ) - - return { - 'success': True, - 'message': 'Action completed successfully' - } - - items = [ - { - 'label': 'Output folder:', - 'type': 'text', - 'value': '', - 'name': 'output_path' - } - - ] - return { - 'success': True, - 'message': "", - 'items': items - } - - -def register(session): - '''Register action. Called when used as an event plugin.''' - - ExportEditorialAction(session).register() - - -if __name__ == "__main__": - session = ftrack_api.Session() - action = ExportEditorialAction(session) - id = "bfe0477c-d5a8-49d8-88b9-6d44d2e48fd9" - review_session = session.get("ReviewSession", id) - path = r"c:/projects" - action.export_editorial(review_session, path) \ No newline at end of file diff --git a/openpype/pipeline/temp_anatomy.py b/openpype/pipeline/temp_anatomy.py deleted file mode 100644 index 27a9370928..0000000000 --- a/openpype/pipeline/temp_anatomy.py +++ /dev/null @@ -1,1330 +0,0 @@ -import os -import re -import copy -import platform -import collections -import numbers - -import six -import time - -from openpype.settings.lib import ( - get_anatomy_settings, - get_project_settings, - get_default_project_settings, - get_local_settings -) - -from openpype.client import get_project -from openpype.lib.path_templates import ( - TemplateUnsolved, - TemplateResult, - TemplatesDict, - FormatObject, -) -from openpype.lib.log import Logger -from openpype.lib import get_local_site_id - -log = Logger.get_logger(__name__) - - -class ProjectNotSet(Exception): - """Exception raised when is created Anatomy without project name.""" - - -class RootCombinationError(Exception): - """This exception is raised when templates has combined root types.""" - - def __init__(self, roots): - joined_roots = ", ".join( - ["\"{}\"".format(_root) for _root in roots] - ) - # TODO better error message - msg = ( - "Combination of root with and" - " without root name in AnatomyTemplates. {}" - ).format(joined_roots) - - super(RootCombinationError, self).__init__(msg) - - -class BaseAnatomy(object): - """Anatomy module helps to keep project settings. - - Wraps key project specifications, AnatomyTemplates and Roots. - """ - - def __init__(self, project_doc, local_settings): - project_name = project_doc["name"] - self.project_name = project_name - - self._data = self._prepare_anatomy_data( - project_doc, local_settings - ) - self._templates_obj = AnatomyTemplates(self) - self._roots_obj = Roots(self) - - root_key_regex = re.compile(r"{(root?[^}]+)}") - root_name_regex = re.compile(r"root\[([^]]+)\]") - - # Anatomy used as dictionary - # - implemented only getters returning copy - def __getitem__(self, key): - return copy.deepcopy(self._data[key]) - - def get(self, key, default=None): - return copy.deepcopy(self._data).get(key, default) - - def keys(self): - return copy.deepcopy(self._data).keys() - - def values(self): - return copy.deepcopy(self._data).values() - - def items(self): - return copy.deepcopy(self._data).items() - - @staticmethod - def _prepare_anatomy_data(anatomy_data): - """Prepare anatomy data for further processing. - - Method added to replace `{task}` with `{task[name]}` in templates. - """ - templates_data = anatomy_data.get("templates") - if templates_data: - # Replace `{task}` with `{task[name]}` in templates - value_queue = collections.deque() - value_queue.append(templates_data) - while value_queue: - item = value_queue.popleft() - if not isinstance(item, dict): - continue - - for key in tuple(item.keys()): - value = item[key] - if isinstance(value, dict): - value_queue.append(value) - - elif isinstance(value, six.string_types): - item[key] = value.replace("{task}", "{task[name]}") - return anatomy_data - - def reset(self): - """Reset values of cached data in templates and roots objects.""" - self._data = self._prepare_anatomy_data( - get_anatomy_settings(self.project_name, self._site_name) - ) - self.templates_obj.reset() - self.roots_obj.reset() - - @property - def templates(self): - """Wrap property `templates` of Anatomy's AnatomyTemplates instance.""" - return self._templates_obj.templates - - @property - def templates_obj(self): - """Return `AnatomyTemplates` object of current Anatomy instance.""" - return self._templates_obj - - def format(self, *args, **kwargs): - """Wrap `format` method of Anatomy's `templates_obj`.""" - return self._templates_obj.format(*args, **kwargs) - - def format_all(self, *args, **kwargs): - """Wrap `format_all` method of Anatomy's `templates_obj`.""" - return self._templates_obj.format_all(*args, **kwargs) - - @property - def roots(self): - """Wrap `roots` property of Anatomy's `roots_obj`.""" - return self._roots_obj.roots - - @property - def roots_obj(self): - """Return `Roots` object of current Anatomy instance.""" - return self._roots_obj - - def root_environments(self): - """Return OPENPYPE_ROOT_* environments for current project in dict.""" - return self._roots_obj.root_environments() - - def root_environmets_fill_data(self, template=None): - """Environment variable values in dictionary for rootless path. - - Args: - template (str): Template for environment variable key fill. - By default is set to `"${}"`. - """ - return self.roots_obj.root_environmets_fill_data(template) - - def find_root_template_from_path(self, *args, **kwargs): - """Wrapper for Roots `find_root_template_from_path`.""" - return self.roots_obj.find_root_template_from_path(*args, **kwargs) - - def path_remapper(self, *args, **kwargs): - """Wrapper for Roots `path_remapper`.""" - return self.roots_obj.path_remapper(*args, **kwargs) - - def all_root_paths(self): - """Wrapper for Roots `all_root_paths`.""" - return self.roots_obj.all_root_paths() - - def set_root_environments(self): - """Set OPENPYPE_ROOT_* environments for current project.""" - self._roots_obj.set_root_environments() - - def root_names(self): - """Return root names for current project.""" - return self.root_names_from_templates(self.templates) - - def _root_keys_from_templates(self, data): - """Extract root key from templates in data. - - Args: - data (dict): Data that may contain templates as string. - - Return: - set: Set of all root names from templates as strings. - - Output example: `{"root[work]", "root[publish]"}` - """ - - output = set() - if isinstance(data, dict): - for value in data.values(): - for root in self._root_keys_from_templates(value): - output.add(root) - - elif isinstance(data, str): - for group in re.findall(self.root_key_regex, data): - output.add(group) - - return output - - def root_value_for_template(self, template): - """Returns value of root key from template.""" - root_templates = [] - for group in re.findall(self.root_key_regex, template): - root_templates.append("{" + group + "}") - - if not root_templates: - return None - - return root_templates[0].format(**{"root": self.roots}) - - def root_names_from_templates(self, templates): - """Extract root names form anatomy templates. - - Returns None if values in templates contain only "{root}". - Empty list is returned if there is no "root" in templates. - Else returns all root names from templates in list. - - RootCombinationError is raised when templates contain both root types, - basic "{root}" and with root name specification "{root[work]}". - - Args: - templates (dict): Anatomy templates where roots are not filled. - - Return: - list/None: List of all root names from templates as strings when - multiroot setup is used, otherwise None is returned. - """ - roots = list(self._root_keys_from_templates(templates)) - # Return empty list if no roots found in templates - if not roots: - return roots - - # Raise exception when root keys have roots with and without root name. - # Invalid output example: ["root", "root[project]", "root[render]"] - if len(roots) > 1 and "root" in roots: - raise RootCombinationError(roots) - - # Return None if "root" without root name in templates - if len(roots) == 1 and roots[0] == "root": - return None - - names = set() - for root in roots: - for group in re.findall(self.root_name_regex, root): - names.add(group) - return list(names) - - def fill_root(self, template_path): - """Fill template path where is only "root" key unfilled. - - Args: - template_path (str): Path with "root" key in. - Example path: "{root}/projects/MyProject/Shot01/Lighting/..." - - Return: - str: formatted path - """ - # NOTE does not care if there are different keys than "root" - return template_path.format(**{"root": self.roots}) - - @classmethod - def fill_root_with_path(cls, rootless_path, root_path): - """Fill path without filled "root" key with passed path. - - This is helper to fill root with different directory path than anatomy - has defined no matter if is single or multiroot. - - Output path is same as input path if `rootless_path` does not contain - unfilled root key. - - Args: - rootless_path (str): Path without filled "root" key. Example: - "{root[work]}/MyProject/..." - root_path (str): What should replace root key in `rootless_path`. - - Returns: - str: Path with filled root. - """ - output = str(rootless_path) - for group in re.findall(cls.root_key_regex, rootless_path): - replacement = "{" + group + "}" - output = output.replace(replacement, root_path) - - return output - - def replace_root_with_env_key(self, filepath, template=None): - """Replace root of path with environment key. - - # Example: - ## Project with roots: - ``` - { - "nas": { - "windows": P:/projects", - ... - } - ... - } - ``` - - ## Entered filepath - "P:/projects/project/asset/task/animation_v001.ma" - - ## Entered template - "<{}>" - - ## Output - "/project/asset/task/animation_v001.ma" - - Args: - filepath (str): Full file path where root should be replaced. - template (str): Optional template for environment key. Must - have one index format key. - Default value if not entered: "${}" - - Returns: - str: Path where root is replaced with environment root key. - - Raise: - ValueError: When project's roots were not found in entered path. - """ - success, rootless_path = self.find_root_template_from_path(filepath) - if not success: - raise ValueError( - "{}: Project's roots were not found in path: {}".format( - self.project_name, filepath - ) - ) - - data = self.root_environmets_fill_data(template) - return rootless_path.format(**data) - - -class Anatomy(BaseAnatomy): - _project_cache = {} - - def __init__(self, project_name=None, site_name=None): - if not project_name: - project_name = os.environ.get("AVALON_PROJECT") - - if not project_name: - raise ProjectNotSet(( - "Implementation bug: Project name is not set. Anatomy requires" - " to load data for specific project." - )) - - self._site_name = site_name - project_info = self.get_project_data_and_cache(project_name, site_name) - - super(Anatomy, self).__init__( - project_info["project_doc"], - project_info["local_settings"] - ) - - @classmethod - def get_project_data_and_cache(cls, project_name, site_name): - project_info = cls._project_cache.get(project_name) - if project_info is not None: - if time.time() - project_info["start"] > 10: - cls._project_cache.pop(project_name) - project_info = None - - if project_info is None: - if site_name is None: - if project_name: - project_settings = get_project_settings(project_name) - else: - project_settings = get_default_project_settings() - site_name = ( - project_settings["global"] - ["sync_server"] - ["config"] - ["active_site"] - ) - if site_name == "local": - site_name = get_local_site_id() - - project_info = { - "project_doc": get_project(project_name), - "local_settings": get_local_settings(site_name), - "site_name": site_name, - "start": time.time() - } - cls._project_cache[project_name] = project_info - - return project_info - - def reset(self): - """Reset values of cached data in templates and roots objects.""" - self._data = self._prepare_anatomy_data( - get_anatomy_settings(self.project_name, self._site_name) - ) - self.templates_obj.reset() - self.roots_obj.reset() - - -class AnatomyTemplateUnsolved(TemplateUnsolved): - """Exception for unsolved template when strict is set to True.""" - - msg = "Anatomy template \"{0}\" is unsolved.{1}{2}" - - -class AnatomyTemplateResult(TemplateResult): - rootless = None - - def __new__(cls, result, rootless_path): - new_obj = super(AnatomyTemplateResult, cls).__new__( - cls, - str(result), - result.template, - result.solved, - result.used_values, - result.missing_keys, - result.invalid_types - ) - new_obj.rootless = rootless_path - return new_obj - - def validate(self): - if not self.solved: - raise AnatomyTemplateUnsolved( - self.template, - self.missing_keys, - self.invalid_types - ) - - def copy(self): - tmp = TemplateResult( - str(self), - self.template, - self.solved, - self.used_values, - self.missing_keys, - self.invalid_types - ) - return self.__class__(tmp, self.rootless) - - def normalized(self): - """Convert to normalized path.""" - - tmp = TemplateResult( - os.path.normpath(self), - self.template, - self.solved, - self.used_values, - self.missing_keys, - self.invalid_types - ) - return self.__class__(tmp, self.rootless) - - -class AnatomyTemplates(TemplatesDict): - inner_key_pattern = re.compile(r"(\{@.*?[^{}0]*\})") - inner_key_name_pattern = re.compile(r"\{@(.*?[^{}0]*)\}") - - def __init__(self, anatomy): - super(AnatomyTemplates, self).__init__() - self.anatomy = anatomy - self.loaded_project = None - - def __getitem__(self, key): - return self.templates[key] - - def get(self, key, default=None): - return self.templates.get(key, default) - - def reset(self): - self._raw_templates = None - self._templates = None - self._objected_templates = None - - @property - def project_name(self): - return self.anatomy.project_name - - @property - def roots(self): - return self.anatomy.roots - - @property - def templates(self): - self._validate_discovery() - return self._templates - - @property - def objected_templates(self): - self._validate_discovery() - return self._objected_templates - - def _validate_discovery(self): - if self.project_name != self.loaded_project: - self.reset() - - if self._templates is None: - self._discover() - self.loaded_project = self.project_name - - def _format_value(self, value, data): - if isinstance(value, RootItem): - return self._solve_dict(value, data) - - result = super(AnatomyTemplates, self)._format_value(value, data) - if isinstance(result, TemplateResult): - rootless_path = self._rootless_path(result, data) - result = AnatomyTemplateResult(result, rootless_path) - return result - - def set_templates(self, templates): - if not templates: - self.reset() - return - - self._raw_templates = copy.deepcopy(templates) - templates = copy.deepcopy(templates) - v_queue = collections.deque() - v_queue.append(templates) - while v_queue: - item = v_queue.popleft() - if not isinstance(item, dict): - continue - - for key in tuple(item.keys()): - value = item[key] - if isinstance(value, dict): - v_queue.append(value) - - elif ( - isinstance(value, six.string_types) - and "{task}" in value - ): - item[key] = value.replace("{task}", "{task[name]}") - - solved_templates = self.solve_template_inner_links(templates) - self._templates = solved_templates - self._objected_templates = self.create_ojected_templates( - solved_templates - ) - - def default_templates(self): - """Return default templates data with solved inner keys.""" - return self.solve_template_inner_links( - self.anatomy["templates"] - ) - - def _discover(self): - """ Loads anatomy templates from yaml. - Default templates are loaded if project is not set or project does - not have set it's own. - TODO: create templates if not exist. - - Returns: - TemplatesResultDict: Contain templates data for current project of - default templates. - """ - - if self.project_name is None: - # QUESTION create project specific if not found? - raise AssertionError(( - "Project \"{0}\" does not have his own templates." - " Trying to use default." - ).format(self.project_name)) - - self.set_templates(self.anatomy["templates"]) - - @classmethod - def replace_inner_keys(cls, matches, value, key_values, key): - """Replacement of inner keys in template values.""" - for match in matches: - anatomy_sub_keys = ( - cls.inner_key_name_pattern.findall(match) - ) - if key in anatomy_sub_keys: - raise ValueError(( - "Unsolvable recursion in inner keys, " - "key: \"{}\" is in his own value." - " Can't determine source, please check Anatomy templates." - ).format(key)) - - for anatomy_sub_key in anatomy_sub_keys: - replace_value = key_values.get(anatomy_sub_key) - if replace_value is None: - raise KeyError(( - "Anatomy templates can't be filled." - " Anatomy key `{0}` has" - " invalid inner key `{1}`." - ).format(key, anatomy_sub_key)) - - if not ( - isinstance(replace_value, numbers.Number) - or isinstance(replace_value, six.string_types) - ): - raise ValueError(( - "Anatomy templates can't be filled." - " Anatomy key `{0}` has" - " invalid inner key `{1}`" - " with value `{2}`." - ).format(key, anatomy_sub_key, str(replace_value))) - - value = value.replace(match, str(replace_value)) - - return value - - @classmethod - def prepare_inner_keys(cls, key_values): - """Check values of inner keys. - - Check if inner key exist in template group and has valid value. - It is also required to avoid infinite loop with unsolvable recursion - when first inner key's value refers to second inner key's value where - first is used. - """ - keys_to_solve = set(key_values.keys()) - while True: - found = False - for key in tuple(keys_to_solve): - value = key_values[key] - - if isinstance(value, six.string_types): - matches = cls.inner_key_pattern.findall(value) - if not matches: - keys_to_solve.remove(key) - continue - - found = True - key_values[key] = cls.replace_inner_keys( - matches, value, key_values, key - ) - continue - - elif not isinstance(value, dict): - keys_to_solve.remove(key) - continue - - subdict_found = False - for _key, _value in tuple(value.items()): - matches = cls.inner_key_pattern.findall(_value) - if not matches: - continue - - subdict_found = True - found = True - key_values[key][_key] = cls.replace_inner_keys( - matches, _value, key_values, - "{}.{}".format(key, _key) - ) - - if not subdict_found: - keys_to_solve.remove(key) - - if not found: - break - - return key_values - - @classmethod - def solve_template_inner_links(cls, templates): - """Solve templates inner keys identified by "{@*}". - - Process is split into 2 parts. - First is collecting all global keys (keys in top hierarchy where value - is not dictionary). All global keys are set for all group keys (keys - in top hierarchy where value is dictionary). Value of a key is not - overridden in group if already contain value for the key. - - In second part all keys with "at" symbol in value are replaced with - value of the key afterward "at" symbol from the group. - - Args: - templates (dict): Raw templates data. - - Example: - templates:: - key_1: "value_1", - key_2: "{@key_1}/{filling_key}" - - group_1: - key_3: "value_3/{@key_2}" - - group_2: - key_2": "value_2" - key_4": "value_4/{@key_2}" - - output:: - key_1: "value_1" - key_2: "value_1/{filling_key}" - - group_1: { - key_1: "value_1" - key_2: "value_1/{filling_key}" - key_3: "value_3/value_1/{filling_key}" - - group_2: { - key_1: "value_1" - key_2: "value_2" - key_4: "value_3/value_2" - """ - default_key_values = templates.pop("defaults", {}) - for key, value in tuple(templates.items()): - if isinstance(value, dict): - continue - default_key_values[key] = templates.pop(key) - - # Pop "others" key before before expected keys are processed - other_templates = templates.pop("others") or {} - - keys_by_subkey = {} - for sub_key, sub_value in templates.items(): - key_values = {} - key_values.update(default_key_values) - key_values.update(sub_value) - keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) - - for sub_key, sub_value in other_templates.items(): - if sub_key in keys_by_subkey: - log.warning(( - "Key \"{}\" is duplicated in others. Skipping." - ).format(sub_key)) - continue - - key_values = {} - key_values.update(default_key_values) - key_values.update(sub_value) - keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) - - default_keys_by_subkeys = cls.prepare_inner_keys(default_key_values) - - for key, value in default_keys_by_subkeys.items(): - keys_by_subkey[key] = value - - return keys_by_subkey - - def _dict_to_subkeys_list(self, subdict, pre_keys=None): - if pre_keys is None: - pre_keys = [] - output = [] - for key in subdict: - value = subdict[key] - result = list(pre_keys) - result.append(key) - if isinstance(value, dict): - for item in self._dict_to_subkeys_list(value, result): - output.append(item) - else: - output.append(result) - return output - - def _keys_to_dicts(self, key_list, value): - if not key_list: - return None - if len(key_list) == 1: - return {key_list[0]: value} - return {key_list[0]: self._keys_to_dicts(key_list[1:], value)} - - def _rootless_path(self, result, final_data): - used_values = result.used_values - missing_keys = result.missing_keys - template = result.template - invalid_types = result.invalid_types - if ( - "root" not in used_values - or "root" in missing_keys - or "{root" not in template - ): - return - - for invalid_type in invalid_types: - if "root" in invalid_type: - return - - root_keys = self._dict_to_subkeys_list({"root": used_values["root"]}) - if not root_keys: - return - - output = str(result) - for used_root_keys in root_keys: - if not used_root_keys: - continue - - used_value = used_values - root_key = None - for key in used_root_keys: - used_value = used_value[key] - if root_key is None: - root_key = key - else: - root_key += "[{}]".format(key) - - root_key = "{" + root_key + "}" - output = output.replace(str(used_value), root_key) - - return output - - def format(self, data, strict=True): - copy_data = copy.deepcopy(data) - roots = self.roots - if roots: - copy_data["root"] = roots - result = super(AnatomyTemplates, self).format(copy_data) - result.strict = strict - return result - - def format_all(self, in_data, only_keys=True): - """ Solves templates based on entered data. - - Args: - data (dict): Containing keys to be filled into template. - - Returns: - TemplatesResultDict: Output `TemplateResult` have `strict` - attribute set to False so accessing unfilled keys in templates - won't raise any exceptions. - """ - return self.format(in_data, strict=False) - - -class RootItem(FormatObject): - """Represents one item or roots. - - Holds raw data of root item specification. Raw data contain value - for each platform, but current platform value is used when object - is used for formatting of template. - - Args: - root_raw_data (dict): Dictionary containing root values by platform - names. ["windows", "linux" and "darwin"] - name (str, optional): Root name which is representing. Used with - multi root setup otherwise None value is expected. - parent_keys (list, optional): All dictionary parent keys. Values of - `parent_keys` are used for get full key which RootItem is - representing. Used for replacing root value in path with - formattable key. e.g. parent_keys == ["work"] -> {root[work]} - parent (object, optional): It is expected to be `Roots` object. - Value of `parent` won't affect code logic much. - """ - - def __init__( - self, root_raw_data, name=None, parent_keys=None, parent=None - ): - lowered_platform_keys = {} - for key, value in root_raw_data.items(): - lowered_platform_keys[key.lower()] = value - self.raw_data = lowered_platform_keys - self.cleaned_data = self._clean_roots(lowered_platform_keys) - self.name = name - self.parent_keys = parent_keys or [] - self.parent = parent - - self.available_platforms = list(lowered_platform_keys.keys()) - self.value = lowered_platform_keys.get(platform.system().lower()) - self.clean_value = self.clean_root(self.value) - - def __format__(self, *args, **kwargs): - return self.value.__format__(*args, **kwargs) - - def __str__(self): - return str(self.value) - - def __repr__(self): - return self.__str__() - - def __getitem__(self, key): - if isinstance(key, numbers.Number): - return self.value[key] - - additional_info = "" - if self.parent and self.parent.project_name: - additional_info += " for project \"{}\"".format( - self.parent.project_name - ) - - raise AssertionError( - "Root key \"{}\" is missing{}.".format( - key, additional_info - ) - ) - - def full_key(self): - """Full key value for dictionary formatting in template. - - Returns: - str: Return full replacement key for formatting. This helps when - multiple roots are set. In that case e.g. `"root[work]"` is - returned. - """ - if not self.name: - return "root" - - joined_parent_keys = "".join( - ["[{}]".format(key) for key in self.parent_keys] - ) - return "root{}".format(joined_parent_keys) - - def clean_path(self, path): - """Just replace backslashes with forward slashes.""" - return str(path).replace("\\", "/") - - def clean_root(self, root): - """Makes sure root value does not end with slash.""" - if root: - root = self.clean_path(root) - while root.endswith("/"): - root = root[:-1] - return root - - def _clean_roots(self, raw_data): - """Clean all values of raw root item values.""" - cleaned = {} - for key, value in raw_data.items(): - cleaned[key] = self.clean_root(value) - return cleaned - - def path_remapper(self, path, dst_platform=None, src_platform=None): - """Remap path for specific platform. - - Args: - path (str): Source path which need to be remapped. - dst_platform (str, optional): Specify destination platform - for which remapping should happen. - src_platform (str, optional): Specify source platform. This is - recommended to not use and keep unset until you really want - to use specific platform. - roots (dict/RootItem/None, optional): It is possible to remap - path with different roots then instance where method was - called has. - - Returns: - str/None: When path does not contain known root then - None is returned else returns remapped path with "{root}" - or "{root[]}". - """ - cleaned_path = self.clean_path(path) - if dst_platform: - dst_root_clean = self.cleaned_data.get(dst_platform) - if not dst_root_clean: - key_part = "" - full_key = self.full_key() - if full_key != "root": - key_part += "\"{}\" ".format(full_key) - - log.warning( - "Root {}miss platform \"{}\" definition.".format( - key_part, dst_platform - ) - ) - return None - - if cleaned_path.startswith(dst_root_clean): - return cleaned_path - - if src_platform: - src_root_clean = self.cleaned_data.get(src_platform) - if src_root_clean is None: - log.warning( - "Root \"{}\" miss platform \"{}\" definition.".format( - self.full_key(), src_platform - ) - ) - return None - - if not cleaned_path.startswith(src_root_clean): - return None - - subpath = cleaned_path[len(src_root_clean):] - if dst_platform: - # `dst_root_clean` is used from upper condition - return dst_root_clean + subpath - return self.clean_value + subpath - - result, template = self.find_root_template_from_path(path) - if not result: - return None - - def parent_dict(keys, value): - if not keys: - return value - - key = keys.pop(0) - return {key: parent_dict(keys, value)} - - if dst_platform: - format_value = parent_dict(list(self.parent_keys), dst_root_clean) - else: - format_value = parent_dict(list(self.parent_keys), self.value) - - return template.format(**{"root": format_value}) - - def find_root_template_from_path(self, path): - """Replaces known root value with formattable key in path. - - All platform values are checked for this replacement. - - Args: - path (str): Path where root value should be found. - - Returns: - tuple: Tuple contain 2 values: `success` (bool) and `path` (str). - When success it True then path should contain replaced root - value with formattable key. - - Example: - When input path is:: - "C:/windows/path/root/projects/my_project/file.ext" - - And raw data of item looks like:: - { - "windows": "C:/windows/path/root", - "linux": "/mount/root" - } - - Output will be:: - (True, "{root}/projects/my_project/file.ext") - - If any of raw data value wouldn't match path's root output is:: - (False, "C:/windows/path/root/projects/my_project/file.ext") - """ - result = False - output = str(path) - - root_paths = list(self.cleaned_data.values()) - mod_path = self.clean_path(path) - for root_path in root_paths: - # Skip empty paths - if not root_path: - continue - - if mod_path.startswith(root_path): - result = True - replacement = "{" + self.full_key() + "}" - output = replacement + mod_path[len(root_path):] - break - - return (result, output) - - -class Roots: - """Object which should be used for formatting "root" key in templates. - - Args: - anatomy Anatomy: Anatomy object created for a specific project. - """ - - env_prefix = "OPENPYPE_PROJECT_ROOT" - roots_filename = "roots.json" - - def __init__(self, anatomy): - self.anatomy = anatomy - self.loaded_project = None - self._roots = None - - def __format__(self, *args, **kwargs): - return self.roots.__format__(*args, **kwargs) - - def __getitem__(self, key): - return self.roots[key] - - def reset(self): - """Reset current roots value.""" - self._roots = None - - def path_remapper( - self, path, dst_platform=None, src_platform=None, roots=None - ): - """Remap path for specific platform. - - Args: - path (str): Source path which need to be remapped. - dst_platform (str, optional): Specify destination platform - for which remapping should happen. - src_platform (str, optional): Specify source platform. This is - recommended to not use and keep unset until you really want - to use specific platform. - roots (dict/RootItem/None, optional): It is possible to remap - path with different roots then instance where method was - called has. - - Returns: - str/None: When path does not contain known root then - None is returned else returns remapped path with "{root}" - or "{root[]}". - """ - if roots is None: - roots = self.roots - - if roots is None: - raise ValueError("Roots are not set. Can't find path.") - - if "{root" in path: - path = path.format(**{"root": roots}) - # If `dst_platform` is not specified then return else continue. - if not dst_platform: - return path - - if isinstance(roots, RootItem): - return roots.path_remapper(path, dst_platform, src_platform) - - for _root in roots.values(): - result = self.path_remapper( - path, dst_platform, src_platform, _root - ) - if result is not None: - return result - - def find_root_template_from_path(self, path, roots=None): - """Find root value in entered path and replace it with formatting key. - - Args: - path (str): Source path where root will be searched. - roots (Roots/dict, optional): It is possible to use different - roots than instance where method was triggered has. - - Returns: - tuple: Output contains tuple with bool representing success as - first value and path with or without replaced root with - formatting key as second value. - - Raises: - ValueError: When roots are not entered and can't be loaded. - """ - if roots is None: - log.debug( - "Looking for matching root in path \"{}\".".format(path) - ) - roots = self.roots - - if roots is None: - raise ValueError("Roots are not set. Can't find path.") - - if isinstance(roots, RootItem): - return roots.find_root_template_from_path(path) - - for root_name, _root in roots.items(): - success, result = self.find_root_template_from_path(path, _root) - if success: - log.info("Found match in root \"{}\".".format(root_name)) - return success, result - - log.warning("No matching root was found in current setting.") - return (False, path) - - def set_root_environments(self): - """Set root environments for current project.""" - for key, value in self.root_environments().items(): - os.environ[key] = value - - def root_environments(self): - """Use root keys to create unique keys for environment variables. - - Concatenates prefix "OPENPYPE_ROOT" with root keys to create unique - keys. - - Returns: - dict: Result is `{(str): (str)}` dicitonary where key represents - unique key concatenated by keys and value is root value of - current platform root. - - Example: - With raw root values:: - "work": { - "windows": "P:/projects/work", - "linux": "/mnt/share/projects/work", - "darwin": "/darwin/path/work" - }, - "publish": { - "windows": "P:/projects/publish", - "linux": "/mnt/share/projects/publish", - "darwin": "/darwin/path/publish" - } - - Result on windows platform:: - { - "OPENPYPE_ROOT_WORK": "P:/projects/work", - "OPENPYPE_ROOT_PUBLISH": "P:/projects/publish" - } - - Short example when multiroot is not used:: - { - "OPENPYPE_ROOT": "P:/projects" - } - """ - return self._root_environments() - - def all_root_paths(self, roots=None): - """Return all paths for all roots of all platforms.""" - if roots is None: - roots = self.roots - - output = [] - if isinstance(roots, RootItem): - for value in roots.raw_data.values(): - output.append(value) - return output - - for _roots in roots.values(): - output.extend(self.all_root_paths(_roots)) - return output - - def _root_environments(self, keys=None, roots=None): - if not keys: - keys = [] - if roots is None: - roots = self.roots - - if isinstance(roots, RootItem): - key_items = [self.env_prefix] - for _key in keys: - key_items.append(_key.upper()) - - key = "_".join(key_items) - # Make sure key and value does not contain unicode - # - can happen in Python 2 hosts - return {str(key): str(roots.value)} - - output = {} - for _key, _value in roots.items(): - _keys = list(keys) - _keys.append(_key) - output.update(self._root_environments(_keys, _value)) - return output - - def root_environmets_fill_data(self, template=None): - """Environment variable values in dictionary for rootless path. - - Args: - template (str): Template for environment variable key fill. - By default is set to `"${}"`. - """ - if template is None: - template = "${}" - return self._root_environmets_fill_data(template) - - def _root_environmets_fill_data(self, template, keys=None, roots=None): - if keys is None and roots is None: - return { - "root": self._root_environmets_fill_data( - template, [], self.roots - ) - } - - if isinstance(roots, RootItem): - key_items = [Roots.env_prefix] - for _key in keys: - key_items.append(_key.upper()) - key = "_".join(key_items) - return template.format(key) - - output = {} - for key, value in roots.items(): - _keys = list(keys) - _keys.append(key) - output[key] = self._root_environmets_fill_data( - template, _keys, value - ) - return output - - @property - def project_name(self): - """Return project name which will be used for loading root values.""" - return self.anatomy.project_name - - @property - def roots(self): - """Property for filling "root" key in templates. - - This property returns roots for current project or default root values. - Warning: - Default roots value may cause issues when project use different - roots settings. That may happen when project use multiroot - templates but default roots miss their keys. - """ - if self.project_name != self.loaded_project: - self._roots = None - - if self._roots is None: - self._roots = self._discover() - self.loaded_project = self.project_name - return self._roots - - def _discover(self): - """ Loads current project's roots or default. - - Default roots are loaded if project override's does not contain roots. - - Returns: - `RootItem` or `dict` with multiple `RootItem`s when multiroot - setting is used. - """ - - return self._parse_dict(self.anatomy["roots"], parent=self) - - @staticmethod - def _parse_dict(data, key=None, parent_keys=None, parent=None): - """Parse roots raw data into RootItem or dictionary with RootItems. - - Converting raw roots data to `RootItem` helps to handle platform keys. - This method is recursive to be able handle multiroot setup and - is static to be able to load default roots without creating new object. - - Args: - data (dict): Should contain raw roots data to be parsed. - key (str, optional): Current root key. Set by recursion. - parent_keys (list): Parent dictionary keys. Set by recursion. - parent (Roots, optional): Parent object set in `RootItem` - helps to keep RootItem instance updated with `Roots` object. - - Returns: - `RootItem` or `dict` with multiple `RootItem`s when multiroot - setting is used. - """ - if not parent_keys: - parent_keys = [] - is_last = False - for value in data.values(): - if isinstance(value, six.string_types): - is_last = True - break - - if is_last: - return RootItem(data, key, parent_keys, parent=parent) - - output = {} - for _key, value in data.items(): - _parent_keys = list(parent_keys) - _parent_keys.append(_key) - output[_key] = Roots._parse_dict(value, _key, _parent_keys, parent) - return output diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 661975993b..adc629352e 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -14,6 +14,7 @@ from openpype.client import ( ) from openpype.client.operations import ( OperationsSession, + _create_or_convert_to_mongo_id, new_hero_version_doc, prepare_hero_version_update_data, prepare_representation_update_data, @@ -192,13 +193,9 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): op_session = OperationsSession() - entity_id = None - if old_version: - entity_id = old_version["_id"] new_hero_version = new_hero_version_doc( src_version_entity["_id"], - src_version_entity["parent"], - entity_id=entity_id + src_version_entity["parent"] ) if old_version: @@ -411,7 +408,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Create representation else: - repre.pop("_id", None) + repre["_id"] = _create_or_convert_to_mongo_id(None) op_session.create_entity(project_name, "representation", repre) diff --git a/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json b/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json deleted file mode 100644 index fb798524bc..0000000000 --- a/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "_id": { - "$oid": "623c9d53db3f5046eb1ad5f4" - }, - "schema": "openpype:version-3.0", - "type": "version", - "parent": { - "$oid": "5f3e439a30a9464d6c181cbc" - }, - "name": 94, - "data": { - "families": [ - "workfile" - ], - "time": "20220324T173254Z", - "author": "petrk", - "source": "C:/projects_local/petr_test/assets/locations/Jungle/work/art/petr_test_Jungle_art_v009.psd", - "comment": "", - "machine": "LAPTOP-UB778LHG", - "fps": 25.0, - "intent": "-", - "inputLinks": [ - { - "type": "reference", - "id": { - "$oid": "618eb14f0a55a9c1591e913c" - }, - "linkedBy": "publish" - } - ] - }, - "outputs_recursive": [ - { - "_id": { - "$oid": "618eb14f0a55a9c1591e913c" - }, - "schema": "openpype:version-3.0", - "type": "version", - "parent": { - "$oid": "618e42a72ff49bd543bc1768" - }, - "name": 8, - "data": { - "families": [ - "image" - ], - "time": "20211112T192359Z", - "author": "petrk", - "source": "C:/projects_local/petr_test/assets/locations/Town/work/art/petr_test_Town_art_v005.psd", - "comment": "", - "machine": "LAPTOP-UB778LHG", - "fps": 25.0, - "intent": "-", - "inputLinks": [ - { - "type": "reference", - "id": { - "$oid": "5f3cd2d530a94638544837c3" - }, - "linkedBy": "publish" - } - ] - }, - "depth": 0 - }, - { - "_id": { - "$oid": "5f3cd2d530a94638544837c3" - }, - "schema": "pype:version-3.0", - "type": "version", - "parent": { - "$oid": "5f3a714030a9464bfc7d2382" - }, - "name": 7, - "data": { - "families": [ - "image" - ], - "time": "20200819T092032Z", - "author": "petrk", - "source": "/c/projects/petr_test/assets/characters/Hero/work/art/Hero_v019.psd", - "comment": "", - "machine": "LAPTOP-UB778LHG", - "fps": null - }, - "depth": 1 - } - ] - } -] \ No newline at end of file diff --git a/tests/unit/test_unzip.py b/tests/unit/test_unzip.py deleted file mode 100644 index 586fc49b6f..0000000000 --- a/tests/unit/test_unzip.py +++ /dev/null @@ -1,11 +0,0 @@ - -from openpype.hosts.harmony.api.lib import _ZipFile -from pathlib import Path - -def test_zip(): - source = "c:/Users/petrk/Downloads/fbb_fbb100_sh0020_workfileAnimation_v010.zip" - dest = "c:/projects/temp/unzipped_with_python_111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\\2222222222222222222222222222222222222222222222222222222222222222222222222222222222" - - dest = Path(dest) - with _ZipFile(source, "r") as zip_ref: - zip_ref.extractall(dest.as_posix()) \ No newline at end of file diff --git a/vendor/configs/OpenColorIO-Configs b/vendor/configs/OpenColorIO-Configs deleted file mode 160000 index 0bb079c08b..0000000000 --- a/vendor/configs/OpenColorIO-Configs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0bb079c08be410030669cbf5f19ff869b88af953 diff --git a/vendor/instance.json b/vendor/instance.json deleted file mode 100644 index b1d623e85d..0000000000 --- a/vendor/instance.json +++ /dev/null @@ -1,1133 +0,0 @@ -{ - 'family': 'render', - 'name': 'renderLightingDefault', - 'label': 'renderLightingDefault - local', - 'version': 1, - 'time': '', - 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', - 'subset': 'renderLightingDefault', - 'asset': 'shot01', - 'attachTo': False, - 'setMembers': '', - 'publish': True, - 'resolutionWidth': 1920.0, - 'resolutionHeight': 1080.0, - 'pixelAspect': 1, - 'frameStart': 0, - 'frameEnd': 0, - 'frameStep': 1, - 'handleStart': 0, - 'handleEnd': 0, - 'ignoreFrameHandleCheck': False, - 'renderer': 'aerender', - 'review': True, - 'priority': 50, - 'families': [ - 'render', - 'review', - 'ftrack', - 'slack' - ], - 'multipartExr': False, - 'convertToScanline': False, - 'tileRendering': False, - 'tilesX': 0, - 'tilesY': 0, - 'toBeRenderedOn': 'deadline', - 'deadlineSubmissionJob': None, - 'anatomyData': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-' - }, - 'outputDir': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001', - 'comp_name': '℗ renderLightingDefault', - 'comp_id': 1, - 'fps': 25, - 'projectEntity': { - '_id': ObjectId( - '5f2a6d2311e06a9818a1958b' - ), - 'name': 'petr_test', - 'created_d': datetime.datetime(2020, - 9, - 17, - 15, - 27, - 27, - 927000), - 'data': { - 'ftrackId': 'e5eda2bc-d682-11ea-afc1-92591a5b5e3e', - 'entityType': 'Project', - 'applications': [ - 'maya_2019', - 'photoshop_2021', - 'photoshop_2022', - 'harmony_17', - 'aftereffects_2022', - 'harmony_20', - 'nukestudio_12.2', - 'nukex_12.2', - 'hiero_12.2', - 'blender_2.93' - ], - 'library_project': True, - 'clipIn': 1, - 'resolutionWidth': 1920.0, - 'handleEnd': 0, - 'frameEnd': 1001, - 'resolutionHeight': 1080.0, - 'frameStart': 1001.0, - 'pixelAspect': 1.0, - 'fps': 25.0, - 'handleStart': 0, - 'clipOut': 1, - 'tools_env': [], - 'code': 'petr_test', - 'active': True - }, - 'type': 'project', - 'config': { - 'apps': [ - { - 'name': 'aftereffects/2022' - }, - { - 'name': 'maya/2019' - }, - { - 'name': 'hiero/12-2' - }, - { - 'name': 'photoshop/2021' - }, - { - 'name': 'nuke/12-2' - }, - { - 'name': 'photoshop/2022' - } - ], - 'tasks': { - 'Layout': { - 'short_name': 'lay' - }, - 'Setdress': { - 'short_name': 'dress' - }, - 'Previz': { - 'short_name': '' - }, - 'Generic': { - 'short_name': 'gener' - }, - 'Animation': { - 'short_name': 'anim' - }, - 'Modeling': { - 'short_name': 'mdl' - }, - 'Lookdev': { - 'short_name': 'look' - }, - 'FX': { - 'short_name': 'fx' - }, - 'Lighting': { - 'short_name': 'lgt' - }, - 'Compositing': { - 'short_name': 'comp' - }, - 'Tracking': { - 'short_name': '' - }, - 'Rigging': { - 'short_name': 'rig' - }, - 'Paint': { - 'short_name': 'paint' - }, - 'schedulle': { - 'short_name': '' - }, - 'Art': { - 'short_name': 'art' - }, - 'Texture': { - 'short_name': 'tex' - }, - 'Edit': { - 'short_name': 'edit' - } - }, - 'imageio': { - 'hiero': { - 'workfile': { - 'ocioConfigName': 'nuke-default', - 'ocioconfigpath': { - 'windows': [], - 'darwin': [], - 'linux': [] - }, - 'workingSpace': 'linear', - 'sixteenBitLut': 'sRGB', - 'eightBitLut': 'sRGB', - 'floatLut': 'linear', - 'logLut': 'Cineon', - 'viewerLut': 'sRGB', - 'thumbnailLut': 'sRGB' - }, - 'regexInputs': { - 'inputs': [ - { - 'regex': '[^-a-zA-Z0-9](plateRef).*(?=mp4)', - 'colorspace': 'sRGB' - } - ] - } - }, - 'nuke': { - 'viewer': { - 'viewerProcess': 'sRGB' - }, - 'baking': { - 'viewerProcess': 'rec709' - }, - 'workfile': { - 'colorManagement': 'Nuke', - 'OCIO_config': 'nuke-default', - 'customOCIOConfigPath': { - 'windows': [], - 'darwin': [], - 'linux': [] - }, - 'workingSpaceLUT': 'linear', - 'monitorLut': 'sRGB', - 'int8Lut': 'sRGB', - 'int16Lut': 'sRGB', - 'logLut': 'Cineon', - 'floatLut': 'linear' - }, - 'nodes': { - 'requiredNodes': [ - { - 'plugins': [ - 'CreateWriteRender' - ], - 'nukeNodeClass': 'Write', - 'knobs': [ - { - 'name': 'file_type', - 'value': 'exr' - }, - { - 'name': 'datatype', - 'value': '16 bit half' - }, - { - 'name': 'compression', - 'value': 'Zip (1 scanline)' - }, - { - 'name': 'autocrop', - 'value': 'True' - }, - { - 'name': 'tile_color', - 'value': '0xff0000ff' - }, - { - 'name': 'channels', - 'value': 'rgb' - }, - { - 'name': 'colorspace', - 'value': 'linear' - }, - { - 'name': 'create_directories', - 'value': 'True' - } - ] - }, - { - 'plugins': [ - 'CreateWritePrerender' - ], - 'nukeNodeClass': 'Write', - 'knobs': [ - { - 'name': 'file_type', - 'value': 'exr' - }, - { - 'name': 'datatype', - 'value': '16 bit half' - }, - { - 'name': 'compression', - 'value': 'Zip (1 scanline)' - }, - { - 'name': 'autocrop', - 'value': 'False' - }, - { - 'name': 'tile_color', - 'value': '0xadab1dff' - }, - { - 'name': 'channels', - 'value': 'rgb' - }, - { - 'name': 'colorspace', - 'value': 'linear' - }, - { - 'name': 'create_directories', - 'value': 'True' - } - ] - } - ], - 'customNodes': [] - }, - 'regexInputs': { - 'inputs': [ - { - 'regex': '[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]', - 'colorspace': 'linear' - } - ] - } - }, - 'maya': { - 'colorManagementPreference': { - 'configFilePath': { - 'windows': [], - 'darwin': [], - 'linux': [] - }, - 'renderSpace': 'scene-linear Rec 709/sRGB', - 'viewTransform': 'sRGB gamma' - } - } - }, - 'roots': { - 'work': { - 'windows': 'C:/projects', - 'darwin': '/Volumes/path', - 'linux': '/mnt/share/projects' - } - }, - 'templates': { - 'defaults': { - 'version_padding': 3, - 'version': 'v{version:0>{@version_padding}}', - 'frame_padding': 4, - 'frame': '{frame:0>{@frame_padding}}' - }, - 'work': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task}', - 'file': '{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}', - 'path': '{@folder}/{@file}' - }, - 'render': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', - 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{ext}', - 'path': '{@folder}/{@file}' - }, - 'publish': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', - 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}', - 'path': '{@folder}/{@file}', - 'thumbnail': '{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}' - }, - 'hero': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/hero', - 'file': '{project[code]}_{asset}_{subset}_hero<_{output}><.{frame}>.{ext}', - 'path': '{@folder}/{@file}' - }, - 'delivery': {}, - 'others': {} - } - }, - 'parent': None, - 'schema': 'avalon-core:project-2.0' - }, - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'frameStartHandle': 0, - 'frameEndHandle': 0, - 'byFrameStep': 1, - 'author': 'petrk', - 'expectedFiles': [ - 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001\\shot01_renderLightingDefault_v001.mov' - ], - 'slack_channel_message_profiles': [ - { - 'channels': [ - 'test_integration' - ], - 'upload_thumbnail': True, - 'message': 'Test message' - } - ], - 'slack_token': 'xoxb-1494100953104-2176825439264-jGqvQzfq9uZJPmyX5Q4o4TnP', - 'representations': [ - { - 'frameStart': 0, - 'frameEnd': 0, - 'name': 'mov', - 'ext': 'mov', - 'files': ' renderLightingDefault.mov', - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'tags': [ - 'review' - ], - 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'publishedFiles': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' - ] - }, - { - 'name': 'thumbnail', - 'ext': 'jpg', - 'files': 'thumbnail.jpg', - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'tags': [ - 'thumbnail' - ], - 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'publishedFiles': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' - ] - }, - { - 'frameStart': 0, - 'frameEnd': 0, - 'name': 'h264_mp4', - 'ext': 'mp4', - 'files': ' renderLightingDefault_h264burnin.mp4', - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'tags': [ - 'review', - 'burnin', - 'ftrackreview' - ], - 'resolutionWidth': 1920, - 'resolutionHeight': 1080, - 'outputName': 'h264', - 'outputDef': { - 'ext': 'mp4', - 'tags': [ - 'burnin', - 'ftrackreview' - ], - 'burnins': [], - 'ffmpeg_args': { - 'video_filters': [], - 'audio_filters': [], - 'input': [ - '-apply_trc gamma22' - ], - 'output': [ - '-pix_fmt yuv420p', - '-crf 18', - '-intra' - ] - }, - 'filter': { - 'families': [ - 'render', - 'review', - 'ftrack' - ] - }, - 'overscan_crop': '', - 'overscan_color': [ - 0, - 0, - 0, - 255 - ], - 'width': 0, - 'height': 0, - 'bg_color': [ - 0, - 0, - 0, - 0 - ], - 'letter_box': { - 'enabled': False, - 'ratio': 0.0, - 'state': 'letterbox', - 'fill_color': [ - 0, - 0, - 0, - 255 - ], - 'line_thickness': 0, - 'line_color': [ - 255, - 0, - 0, - 255 - ] - }, - 'filename_suffix': 'h264' - }, - 'frameStartFtrack': 0, - 'frameEndFtrack': 0, - 'ffmpeg_cmd': 'C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\vendor\\bin\\ffmpeg\\windows\\bin\\ffmpeg -apply_trc gamma22 -i "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault.mov" -pix_fmt yuv420p -crf 18 -intra -y "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264.mp4"', - 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'publishedFiles': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ] - } - ], - 'assetEntity': { - '_id': ObjectId( - '5fabee9730a94666449245b7' - ), - 'name': 'shot01', - 'data': { - 'ftrackId': '0c5f548c-2425-11eb-b203-628b111fac3c', - 'entityType': 'Shot', - 'clipIn': 1, - 'resolutionWidth': 1920.0, - 'handleEnd': 0.0, - 'frameEnd': 1001, - 'resolutionHeight': 1080.0, - 'frameStart': 1001.0, - 'pixelAspect': 1.0, - 'fps': 25.0, - 'handleStart': 0.0, - 'clipOut': 1, - 'tools_env': [], - 'avalon_mongo_id': '5fabee9730a94666449245b7', - 'parents': [ - 'sequences', - 'seq01' - ], - 'hierarchy': 'sequences\\seq01', - 'tasks': { - 'lighting': { - 'type': 'Lighting' - }, - 'animation': { - 'type': 'Animation' - }, - 'compositing': { - 'type': 'Compositing' - } - }, - 'visualParent': ObjectId( - '5fabee9730a94666449245b6' - ) - }, - 'type': 'asset', - 'parent': ObjectId( - '5f2a6d2311e06a9818a1958b' - ), - 'schema': 'pype:asset-3.0' - }, - 'subsetEntity': { - '_id': ObjectId( - '61d723a271e6fce378bd428c' - ), - 'schema': 'openpype:subset-3.0', - 'type': 'subset', - 'name': 'renderLightingDefault', - 'data': { - 'families': [ - 'render', - 'review', - 'ftrack', - 'slack' - ] - }, - 'parent': ObjectId( - '5fabee9730a94666449245b7' - ) - }, - 'versionEntity': { - '_id': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'schema': 'openpype:version-3.0', - 'type': 'version', - 'parent': ObjectId( - '61d723a271e6fce378bd428c' - ), - 'name': 1, - 'data': { - 'families': [ - 'render', - 'render', - 'review', - 'ftrack', - 'slack' - ], - 'time': '20220106T181423Z', - 'author': 'petrk', - 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', - 'comment': '', - 'machine': 'LAPTOP-UB778LHG', - 'fps': 25.0, - 'intent': '-', - 'frameStart': 0, - 'frameEnd': 0, - 'handleEnd': 0, - 'handleStart': 0, - 'inputLinks': [ - OrderedDict( - [ - ( - 'type', - 'generative' - ), - ( - 'id', - ObjectId( - '600ab849c411725a626b8c35' - )), - ( - 'linkedBy', - 'publish' - ) - ] - ) - ] - } - }, - 'transfers': [ - [ - 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264burnin.mp4', - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ] - ], - 'destination_list': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ], - 'published_representations': { - ObjectId( - '61d723a371e6fce378bd428e' - ): { - 'representation': { - '_id': ObjectId( - '61d723a371e6fce378bd428e' - ), - 'schema': 'openpype:representation-2.0', - 'type': 'representation', - 'parent': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'name': 'mov', - 'data': { - 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' - }, - 'dependencies': [], - 'context': { - 'root': { - 'work': 'C:/projects' - }, - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'hierarchy': 'sequences/seq01', - 'asset': 'shot01', - 'family': 'render', - 'subset': 'renderLightingDefault', - 'version': 1, - 'ext': 'mov', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'representation': 'mov', - 'username': 'petrk' - }, - 'files': [ - { - '_id': ObjectId( - '61d723a371e6fce378bd4291' - ), - 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.mov', - 'size': 1654788, - 'hash': 'petr_test_shot01_renderLightingDefault_v001,mov|1641489300,6230524|1654788', - 'sites': [ - { - 'name': 'studio', - 'created_dt': datetime.datetime(2022, - 1, - 6, - 18, - 15, - 15, - 264448) - } - ] - } - ] - }, - 'anatomy_data': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-', - 'representation': 'mov', - 'ext': 'mov' - }, - 'published_files': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' - ] - }, - ObjectId( - '61d723a371e6fce378bd4292' - ): { - 'representation': { - '_id': ObjectId( - '61d723a371e6fce378bd4292' - ), - 'schema': 'openpype:representation-2.0', - 'type': 'representation', - 'parent': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'name': 'thumbnail', - 'data': { - 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' - }, - 'dependencies': [], - 'context': { - 'root': { - 'work': 'C:/projects' - }, - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'hierarchy': 'sequences/seq01', - 'asset': 'shot01', - 'family': 'render', - 'subset': 'renderLightingDefault', - 'version': 1, - 'ext': 'jpg', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'representation': 'jpg', - 'username': 'petrk' - }, - 'files': [ - { - '_id': ObjectId( - '61d723a371e6fce378bd4295' - ), - 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.jpg', - 'size': 871, - 'hash': 'petr_test_shot01_renderLightingDefault_v001,jpg|1641489301,1720147|871', - 'sites': [ - { - 'name': 'studio', - 'created_dt': datetime.datetime(2022, - 1, - 6, - 18, - 15, - 15, - 825446) - } - ] - } - ] - }, - 'anatomy_data': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-', - 'representation': 'jpg', - 'ext': 'jpg' - }, - 'published_files': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' - ] - }, - ObjectId( - '61d723a471e6fce378bd4296' - ): { - 'representation': { - '_id': ObjectId( - '61d723a471e6fce378bd4296' - ), - 'schema': 'openpype:representation-2.0', - 'type': 'representation', - 'parent': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'name': 'h264_mp4', - 'data': { - 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' - }, - 'dependencies': [], - 'context': { - 'root': { - 'work': 'C:/projects' - }, - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'hierarchy': 'sequences/seq01', - 'asset': 'shot01', - 'family': 'render', - 'subset': 'renderLightingDefault', - 'version': 1, - 'output': 'h264', - 'ext': 'mp4', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'representation': 'mp4', - 'username': 'petrk' - }, - 'files': [ - { - '_id': ObjectId( - '61d723a471e6fce378bd4299' - ), - 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'size': 10227, - 'hash': 'petr_test_shot01_renderLightingDefault_v001_h264,mp4|1641489313,659368|10227', - 'sites': [ - { - 'name': 'studio', - 'created_dt': datetime.datetime(2022, - 1, - 6, - 18, - 15, - 16, - 53445) - } - ] - } - ] - }, - 'anatomy_data': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-', - 'resolution_width': 1920, - 'resolution_height': 1080, - 'fps': 25, - 'output': 'h264', - 'representation': 'mp4', - 'ext': 'mp4' - }, - 'published_files': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ] - } - }, - 'ftrackComponentsList': [ - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': True, - 'component_data': { - 'name': 'thumbnail' - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'ftrackreview-mp4', - 'metadata': { - 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' - } - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'thumbnail_src' - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'ftrackreview-mp4_src', - 'metadata': { - 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' - } - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'mov' - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'component_location': , - 'component': - } - ], - 'ftrackIntegratedAssetVersions': [ - - ] -} \ No newline at end of file diff --git a/vendor/response.json b/vendor/response.json deleted file mode 100644 index 26a4fae2fd..0000000000 --- a/vendor/response.json +++ /dev/null @@ -1 +0,0 @@ -{status: 200, headers: {'date': 'Tue, 11 Jan 2022 11:08:57 GMT', 'server': 'Apache', 'x-powered-by': 'HHVM/4.128.0', 'access-control-allow-origin': '*', 'referrer-policy': 'no-referrer', 'x-slack-backend': 'r', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'access-control-allow-headers': 'slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags', 'access-control-expose-headers': 'x-slack-req-id, retry-after', 'x-oauth-scopes': 'chat:write,chat:write.public,files:write,chat:write.customize', 'x-accepted-oauth-scopes': 'chat:write', 'expires': 'Mon, 26 Jul 1997 05:00:00 GMT', 'cache-control': 'private, no-cache, no-store, must-revalidate', 'pragma': 'no-cache', 'x-xss-protection': '0', 'x-content-type-options': 'nosniff', 'x-slack-req-id': '9d1d11399a44c8751f89bb4dcd2b91fb', 'vary': 'Accept-Encoding', 'content-type': 'application/json; charset=utf-8', 'x-envoy-upstream-service-time': '52', 'x-backend': 'main_normal main_bedrock_normal_with_overflow main_canary_with_overflow main_bedrock_canary_with_overflow main_control_with_overflow main_bedrock_control_with_overflow', 'x-server': 'slack-www-hhvm-main-iad-qno3', 'x-slack-shared-secret-outcome': 'no-match', 'via': 'envoy-www-iad-omsy, envoy-edge-iad-bgfx', 'x-edge-backend': 'envoy-www', 'x-slack-edge-shared-secret-outcome': 'no-match', 'connection': 'close', 'transfer-encoding': 'chunked'}, body: {"ok":true,"channel":"C024DUFM8MB","ts":"1641899337.001100","message":{"type":"message","subtype":"bot_message","text":"RenderCompositingDefault published for Jungle\n\nHere should be link to review C:\\projects\\petr_test\\assets\\locations\\Jungle\\publish\\render\\renderCompositingDefault\\v253\\petr_test_Jungle_renderCompositingDefault_v253_h264.mp4\n\n Attachment links: \n\n","ts":"1641899337.001100","username":"OpenPypeNotifier","icons":{"image_48":"https:\/\/s3-us-west-2.amazonaws.com\/slack-files2\/bot_icons\/2022-01-07\/2934353684385_48.png"},"bot_id":"B024H0P0CAE"}} \ No newline at end of file diff --git a/vendor/temp.json b/vendor/temp.json deleted file mode 100644 index 089174d26c..0000000000 --- a/vendor/temp.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - project(name: "demo_Big_Episodic") { - representations( - first: 0, - after: 0, - localSite: "local", - remoteSite: "local" - ) { - edges { - node { - id - name - # Sorry: totalSize is not implemented, but it will be - # totalSize - fileCount - # overal sync state - localState{ - status - size - timestamp - } - remoteState{ - status - size - timestamp - } - # crawl to the top to get parent info - version { - version - subset { - family - name - folder { - name - } - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } -} \ No newline at end of file From e0f46635f5045ead05226504ed48b9f972145060 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:32:18 +0200 Subject: [PATCH 19/55] OP-4181 - clean up after review comments --- openpype/plugins/publish/integrate_hero_version.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index adc629352e..661975993b 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -14,7 +14,6 @@ from openpype.client import ( ) from openpype.client.operations import ( OperationsSession, - _create_or_convert_to_mongo_id, new_hero_version_doc, prepare_hero_version_update_data, prepare_representation_update_data, @@ -193,9 +192,13 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): op_session = OperationsSession() + entity_id = None + if old_version: + entity_id = old_version["_id"] new_hero_version = new_hero_version_doc( src_version_entity["_id"], - src_version_entity["parent"] + src_version_entity["parent"], + entity_id=entity_id ) if old_version: @@ -408,7 +411,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # Create representation else: - repre["_id"] = _create_or_convert_to_mongo_id(None) + repre.pop("_id", None) op_session.create_entity(project_name, "representation", repre) From f6f5e77f3c4f8b384feba08cfb5713829192e532 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:34:46 +0200 Subject: [PATCH 20/55] OP-4181 - clean up after review comments - missed line --- openpype/plugins/publish/integrate_hero_version.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 661975993b..84960ec609 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -443,8 +443,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): archived_repre["_id"], changes) else: - repre["old_id"] = repre["_id"] - repre["_id"] = _create_or_convert_to_mongo_id(None) + repre["old_id"] = repre.pop("_id") repre["type"] = "archived_representation" op_session.create_entity(project_name, "representation", repre) From b4644457501ee22d1f37ea0217835662365b19f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:38:40 +0200 Subject: [PATCH 21/55] OP-4181 - fix - wrong entity type --- openpype/plugins/publish/integrate_hero_version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 84960ec609..6d553a7a3c 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -445,7 +445,8 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): else: repre["old_id"] = repre.pop("_id") repre["type"] = "archived_representation" - op_session.create_entity(project_name, "representation", + op_session.create_entity(project_name, + "archived_representation", repre) op_session.commit() From b198e922b250fbfe5957be7b6e7c7d3b95a5517d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:57:24 +0200 Subject: [PATCH 22/55] OP-4181 - fix - wrong entity type --- openpype/plugins/publish/integrate_hero_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 6d553a7a3c..398a0226df 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -389,7 +389,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): old_repre, repre) op_session.update_entity( project_name, - "representation", + old_repre["type"], old_repre["_id"], update_data ) @@ -404,7 +404,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): archived_repre, repre) op_session.update_entity( project_name, - "representation", + old_repre["type"], archived_repre["_id"], update_data ) From adc5d044d4047b9c403a359602205a195e68a9c5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Oct 2022 12:20:09 +0200 Subject: [PATCH 23/55] OP-4181 - removed unneeded line _id set already higher when item is created. Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/plugins/publish/integrate_hero_version.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 398a0226df..5f4d284740 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -203,7 +203,6 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): if old_version: self.log.debug("Replacing old hero version.") - new_hero_version["_id"] = old_version["_id"] update_data = prepare_hero_version_update_data( old_version, new_hero_version ) From 08d79e1a786ca0a01a50b7b781aaaf8c83b64371 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Oct 2022 12:28:40 +0200 Subject: [PATCH 24/55] OP-3939 - added configuration of integrate profiles to Setting Integration of thumbnail representation could be controlled from single location with use of profiles. --- .../defaults/project_settings/global.json | 4 ++ .../schemas/schema_global_publish.json | 67 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 115a719995..1b7dc7a41a 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -164,6 +164,10 @@ } ] }, + "PreIntegrateThumbnails": { + "enabled": true, + "integrate_profiles": [] + }, "IntegrateSubsetGroup": { "subset_grouping_profiles": [ { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 297f96aa8c..9a8d10a4e1 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -555,6 +555,73 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "PreIntegrateThumbnails", + "label": "Integrate Thumbnail Representations", + "is_group": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Explicitly set if Thumbnail representation should be integrated into DB.
If no matching profile set, existing state from Host implementation is kept." + }, + { + "type": "list", + "key": "integrate_profiles", + "label": "Integrate profiles", + "use_label_wrap": true, + "object_type": { + "type": "dict", + "children": [ + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "type": "hosts-enum", + "key": "hosts", + "label": "Hosts", + "multiselection": true + }, + { + "key": "task_types", + "label": "Task types", + "type": "task-types-enum" + }, + { + "key": "tasks", + "label": "Task names", + "type": "list", + "object_type": "text" + }, + { + "key": "subsets", + "label": "Subset names", + "type": "list", + "object_type": "text" + }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "integrate_thumbnail", + "label": "Integrate thumbnail" + } + ] + } + } + ] + }, { "type": "dict", "collapsible": true, From 8b0e6410167d34a21d71bec3665ac67cb240b032 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Oct 2022 12:30:21 +0200 Subject: [PATCH 25/55] OP-3939 - added plugin to update tags for thumbnail representation Integration of thumbnail could be explicitly controlled on one place, overwriting implicit state from host implementations. --- .../preintegrate_thumbnail_representation.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 openpype/plugins/publish/preintegrate_thumbnail_representation.py diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py new file mode 100644 index 0000000000..0c3ba4057c --- /dev/null +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -0,0 +1,68 @@ +""" Marks thumbnail representation for integrate to DB or not. + + Some hosts produce thumbnail representation, most of them do not create + them explicitly, but they created during extract phase. + + In some cases it might be useful to override implicit setting for host/task + + This plugin needs to run after extract phase, but before integrate.py as + thumbnail is part of review family and integrated there. + + It should be better to control integration of thumbnail in one place than + configure it in multiple places on host implementations. +""" +import pyblish.api + +from openpype.lib.profiles_filtering import filter_profiles + + +class PreIntegrateThumbnails(pyblish.api.InstancePlugin): + """Marks thumbnail representation for integrate to DB or not.""" + + label = "Should Integrate Thumbnails" + order = pyblish.api.IntegratorOrder + families = ["review"] + + integrate_profiles = {} + + def process(self, instance): + thumbnail_repre = None + for repre in instance.data["representations"]: + if repre["name"] == "thumbnail": + thumbnail_repre = repre + break + + if not thumbnail_repre: + return + + family = instance.data["family"] + subset_name = instance.data["subset"] + host_name = instance.context.data["hostName"] + + anatomy_data = instance.data["anatomyData"] + task = anatomy_data.get("task", {}) + + found_profile = filter_profiles( + self.integrate_profiles, + { + "hosts": host_name, + "tasks": task.get("name"), + "task_types": task.get("type"), + "families": family, + "subsets": subset_name, + }, + logger=self.log + ) + + if not found_profile: + return + + if not found_profile["integrate_thumbnail"]: + if "delete" not in thumbnail_repre["tags"]: + thumbnail_repre["tags"].append("delete") + else: + if "delete" in thumbnail_repre["tags"]: + thumbnail_repre["tags"].pop("delete") + + self.log.debug( + "Thumbnail repre tags {}".format(thumbnail_repre["tags"])) From 237a9fcf6cd990cfc72368cadb45b53b38565157 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Oct 2022 12:30:38 +0200 Subject: [PATCH 26/55] OP-3939 - added bit of documentation --- openpype/plugins/publish/integrate_thumbnail.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_thumbnail.py b/openpype/plugins/publish/integrate_thumbnail.py index d86cec10ad..e7046ba2ea 100644 --- a/openpype/plugins/publish/integrate_thumbnail.py +++ b/openpype/plugins/publish/integrate_thumbnail.py @@ -1,3 +1,13 @@ +""" Integrate Thumbnails for Openpype use in Loaders. + + This thumbnail is different from 'thumbnail' representation which could + be uploaded to Ftrack, or used as any other representation in Loaders to + pull into a scene. + + This one is used only as image describing content of published item and + shows up only in Loader in right column section. +""" + import os import sys import errno @@ -12,7 +22,7 @@ from openpype.client.operations import OperationsSession, new_thumbnail_doc class IntegrateThumbnails(pyblish.api.InstancePlugin): - """Integrate Thumbnails.""" + """Integrate Thumbnails for Openpype use in Loaders.""" label = "Integrate Thumbnails" order = pyblish.api.IntegratorOrder + 0.01 From 72e647a5b990411bd88d2f4e045fd66b906d0b7d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Oct 2022 13:10:56 +0200 Subject: [PATCH 27/55] OP-3939 - fix - must be before integrate.py --- .../plugins/publish/preintegrate_thumbnail_representation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py index 0c3ba4057c..a4ab0443c2 100644 --- a/openpype/plugins/publish/preintegrate_thumbnail_representation.py +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -20,7 +20,7 @@ class PreIntegrateThumbnails(pyblish.api.InstancePlugin): """Marks thumbnail representation for integrate to DB or not.""" label = "Should Integrate Thumbnails" - order = pyblish.api.IntegratorOrder + order = pyblish.api.IntegratorOrder - 0.1 families = ["review"] integrate_profiles = {} From 467a6d89c8cfdc30e86786914e8513bcbed2022a Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 7 Oct 2022 14:19:11 +0200 Subject: [PATCH 28/55] :recycle: set proper default value from the settings --- .../plugins/publish/submit_maya_deadline.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 3f0905c586..67771eae50 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -199,10 +199,18 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): context = instance.context # Set it to default Maya behaviour if it cannot be determined - # from instance (but it should be, by the Collector). Also - rs_include_lights = instance.data.get("renderSetupIncludeLights", "1") + # from instance (but it should be, by the Collector). + + default_rs_include_lights = instance.context.data\ + .get('project_settings')\ + .get('maya')\ + .get('RenderSettings')\ + .get('enable_all_lights') + + rs_include_lights = instance.data.get( + "renderSetupIncludeLights", default_rs_include_lights) if rs_include_lights not in {"1", "0", True, False}: - rs_include_lights = "1" + rs_include_lights = default_rs_include_lights plugin_info = MayaPluginInfo( SceneFile=self.scene_path, Version=cmds.about(version=True), From 53487001c53dfe760221e0deed4d5bc49077dab8 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 7 Oct 2022 18:31:13 +0200 Subject: [PATCH 29/55] :recycle: flipping default value and changing settings access --- .../deadline/plugins/publish/submit_maya_deadline.py | 11 ++++++----- openpype/settings/defaults/project_settings/maya.json | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 67771eae50..feb20c9e8a 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -201,11 +201,12 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline): # Set it to default Maya behaviour if it cannot be determined # from instance (but it should be, by the Collector). - default_rs_include_lights = instance.context.data\ - .get('project_settings')\ - .get('maya')\ - .get('RenderSettings')\ - .get('enable_all_lights') + default_rs_include_lights = ( + instance.context.data['project_settings'] + ['maya'] + ['RenderSettings'] + ['enable_all_lights'] + ) rs_include_lights = instance.data.get( "renderSetupIncludeLights", default_rs_include_lights) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 76ef0a7338..cacedb6f7f 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -35,7 +35,7 @@ "RenderSettings": { "apply_render_settings": true, "default_render_image_folder": "renders", - "enable_all_lights": false, + "enable_all_lights": true, "aov_separator": "underscore", "reset_current_frame": false, "arnold_renderer": { From f7fdb10f6bf2fe6e36d7cf03348cb72fd8c06228 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 10 Oct 2022 10:54:06 +0200 Subject: [PATCH 30/55] OP-3939 - updated key for Settings --- .../plugins/publish/preintegrate_thumbnail_representation.py | 2 +- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py index a4ab0443c2..49e69ff34b 100644 --- a/openpype/plugins/publish/preintegrate_thumbnail_representation.py +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -46,7 +46,7 @@ class PreIntegrateThumbnails(pyblish.api.InstancePlugin): self.integrate_profiles, { "hosts": host_name, - "tasks": task.get("name"), + "task_names": task.get("name"), "task_types": task.get("type"), "families": family, "subsets": subset_name, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 9a8d10a4e1..c216d5fd9e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -598,7 +598,7 @@ "type": "task-types-enum" }, { - "key": "tasks", + "key": "task_names", "label": "Task names", "type": "list", "object_type": "text" From 745ac4a2d26508e14f530851f33a75165e705309 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 10 Oct 2022 11:06:41 +0200 Subject: [PATCH 31/55] OP-3939 - updated loop through representations Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../publish/preintegrate_thumbnail_representation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py index 49e69ff34b..2c25d2a2fc 100644 --- a/openpype/plugins/publish/preintegrate_thumbnail_representation.py +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -26,8 +26,12 @@ class PreIntegrateThumbnails(pyblish.api.InstancePlugin): integrate_profiles = {} def process(self, instance): + repres = instance.data.get("representations") + if not repres: + return + thumbnail_repre = None - for repre in instance.data["representations"]: + for repre in repres: if repre["name"] == "thumbnail": thumbnail_repre = repre break From 0c86f321b4bd1365e8453327b4a7e560718642e2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Oct 2022 12:58:47 +0200 Subject: [PATCH 32/55] settings: return back __legacy__ fallback --- openpype/settings/lib.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index 3112400dbf..5eaddf6e6e 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -316,6 +316,22 @@ def _system_settings_backwards_compatible_conversion(studio_overrides): } +def _project_anatomy_backwards_compatible_conversion(project_anatomy): + # Backwards compatibility of node settings in Nuke 3.9.x - 3.10.0 + # - source PR - https://github.com/pypeclub/OpenPype/pull/3143 + value = project_anatomy + for key in ("imageio", "nuke", "nodes", "requiredNodes"): + if key not in value: + return + value = value[key] + + for item in value: + for node in item.get("knobs") or []: + if "type" in node: + break + node["type"] = "__legacy__" + + @require_handler def get_studio_system_settings_overrides(return_version=False): output = _SETTINGS_HANDLER.get_studio_system_settings_overrides( @@ -352,6 +368,7 @@ def get_project_settings_overrides(project_name, return_version=False): @require_handler def get_project_anatomy_overrides(project_name): output = _SETTINGS_HANDLER.get_project_anatomy_overrides(project_name) + _project_anatomy_backwards_compatible_conversion(output) return output From bbf3a8baae3c075133681668dd0c89f7ccdbb6ed Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Oct 2022 12:59:41 +0200 Subject: [PATCH 33/55] nuke: imageio from project settings with backward compatibility switch --- openpype/hosts/nuke/api/lib.py | 8 ++++++++ openpype/settings/defaults/project_settings/nuke.json | 1 + .../schemas/projects_schema/schema_project_nuke.json | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 6297da884c..1aea04d889 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -563,6 +563,14 @@ def get_node_path(path, padding=4): def get_nuke_imageio_settings(): + project_imageio = get_project_settings( + Context.project_name)["nuke"]["imageio"] + + # backward compatibility for project started before 3.10 + # those are still having `__legacy__` knob types + if not project_imageio["enabled"]: + return get_anatomy_settings(Context.project_name)["imageio"]["nuke"] + return get_project_settings(Context.project_name)["nuke"]["imageio"] diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index e0feb06eb6..e5cbacbda7 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -9,6 +9,7 @@ } }, "imageio": { + "enabled": false, "viewer": { "viewerProcess": "sRGB" }, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json index ff341fb919..e23e2b3bec 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -50,9 +50,15 @@ "key": "imageio", "type": "dict", "label": "Color Management (ImageIO)", + "checkbox_key": "enabled", "collapsible": true, "is_group": true, "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, { "key": "viewer", "type": "dict", From be8b2c5faa72f999848214ca2ce1e9e41576ea16 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Oct 2022 13:04:06 +0200 Subject: [PATCH 34/55] nuke: settings imageio separate schema --- .../projects_schema/schema_project_nuke.json | 254 +----------------- .../schemas/schema_nuke_imageio.json | 254 ++++++++++++++++++ 2 files changed, 256 insertions(+), 252 deletions(-) create mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json index e23e2b3bec..154eca254b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_nuke.json @@ -47,258 +47,8 @@ ] }, { - "key": "imageio", - "type": "dict", - "label": "Color Management (ImageIO)", - "checkbox_key": "enabled", - "collapsible": true, - "is_group": true, - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "viewer", - "type": "dict", - "label": "Viewer", - "collapsible": false, - "children": [ - { - "type": "text", - "key": "viewerProcess", - "label": "Viewer Process" - } - ] - }, - { - "key": "baking", - "type": "dict", - "label": "Extract-review baking profile", - "collapsible": false, - "children": [ - { - "type": "text", - "key": "viewerProcess", - "label": "Viewer Process" - } - ] - }, - { - "key": "workfile", - "type": "dict", - "label": "Workfile", - "collapsible": false, - "children": [ - { - "type": "form", - "children": [ - { - "type": "enum", - "key": "colorManagement", - "label": "color management", - "enum_items": [ - { - "Nuke": "Nuke" - }, - { - "OCIO": "OCIO" - } - ] - }, - { - "type": "enum", - "key": "OCIO_config", - "label": "OpenColorIO Config", - "enum_items": [ - { - "nuke-default": "nuke-default" - }, - { - "spi-vfx": "spi-vfx" - }, - { - "spi-anim": "spi-anim" - }, - { - "aces_0.1.1": "aces_0.1.1" - }, - { - "aces_0.7.1": "aces_0.7.1" - }, - { - "aces_1.0.1": "aces_1.0.1" - }, - { - "aces_1.0.3": "aces_1.0.3" - }, - { - "aces_1.1": "aces_1.1" - }, - { - "aces_1.2": "aces_1.2" - }, - { - "custom": "custom" - } - ] - }, - { - "type": "path", - "key": "customOCIOConfigPath", - "label": "Custom OCIO config path", - "multiplatform": true, - "multipath": true - }, - { - "type": "text", - "key": "workingSpaceLUT", - "label": "Working Space" - }, - { - "type": "text", - "key": "monitorLut", - "label": "monitor" - }, - { - "type": "text", - "key": "int8Lut", - "label": "8-bit files" - }, - { - "type": "text", - "key": "int16Lut", - "label": "16-bit files" - }, - { - "type": "text", - "key": "logLut", - "label": "log files" - }, - { - "type": "text", - "key": "floatLut", - "label": "float files" - } - ] - } - ] - }, - { - "key": "nodes", - "type": "dict", - "label": "Nodes", - "collapsible": true, - "children": [ - { - "key": "requiredNodes", - "type": "list", - "label": "Plugin required", - "object_type": { - "type": "dict", - "children": [ - { - "type": "list", - "key": "plugins", - "label": "Used in plugins", - "object_type": { - "type": "text", - "key": "pluginClass" - } - }, - { - "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, - { - "type": "schema_template", - "name": "template_nuke_knob_inputs", - "template_data": [ - { - "label": "Knobs", - "key": "knobs" - } - ] - } - - ] - } - }, - { - "type": "splitter" - }, - { - "type": "list", - "key": "overrideNodes", - "label": "Plugin's node overrides", - "object_type": { - "type": "dict", - "children": [ - { - "type": "list", - "key": "plugins", - "label": "Used in plugins", - "object_type": { - "type": "text", - "key": "pluginClass" - } - }, - { - "type": "text", - "key": "nukeNodeClass", - "label": "Nuke Node Class" - }, - { - "key": "subsets", - "label": "Subsets", - "type": "list", - "object_type": "text" - }, - { - "type": "schema_template", - "name": "template_nuke_knob_inputs", - "template_data": [ - { - "label": "Knobs overrides", - "key": "knobs" - } - ] - } - ] - } - } - ] - }, - { - "key": "regexInputs", - "type": "dict", - "label": "Colorspace on Inputs by regex detection", - "collapsible": true, - "children": [ - { - "type": "list", - "key": "inputs", - "object_type": { - "type": "dict", - "children": [ - { - "type": "text", - "key": "regex", - "label": "Regex" - }, - { - "type": "text", - "key": "colorspace", - "label": "Colorspace" - } - ] - } - } - ] - } - ] + "type": "schema", + "name": "schema_nuke_imageio" }, { "type": "dict", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json new file mode 100644 index 0000000000..52db853ef6 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json @@ -0,0 +1,254 @@ +{ + "key": "imageio", + "type": "dict", + "label": "Color Management (ImageIO)", + "checkbox_key": "enabled", + "collapsible": true, + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "viewer", + "type": "dict", + "label": "Viewer", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, + { + "key": "baking", + "type": "dict", + "label": "Extract-review baking profile", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "viewerProcess", + "label": "Viewer Process" + } + ] + }, + { + "key": "workfile", + "type": "dict", + "label": "Workfile", + "collapsible": false, + "children": [ + { + "type": "form", + "children": [ + { + "type": "enum", + "key": "colorManagement", + "label": "color management", + "enum_items": [ + { + "Nuke": "Nuke" + }, + { + "OCIO": "OCIO" + } + ] + }, + { + "type": "enum", + "key": "OCIO_config", + "label": "OpenColorIO Config", + "enum_items": [ + { + "nuke-default": "nuke-default" + }, + { + "spi-vfx": "spi-vfx" + }, + { + "spi-anim": "spi-anim" + }, + { + "aces_0.1.1": "aces_0.1.1" + }, + { + "aces_0.7.1": "aces_0.7.1" + }, + { + "aces_1.0.1": "aces_1.0.1" + }, + { + "aces_1.0.3": "aces_1.0.3" + }, + { + "aces_1.1": "aces_1.1" + }, + { + "aces_1.2": "aces_1.2" + }, + { + "custom": "custom" + } + ] + }, + { + "type": "path", + "key": "customOCIOConfigPath", + "label": "Custom OCIO config path", + "multiplatform": true, + "multipath": true + }, + { + "type": "text", + "key": "workingSpaceLUT", + "label": "Working Space" + }, + { + "type": "text", + "key": "monitorLut", + "label": "monitor" + }, + { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, + { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, + { + "type": "text", + "key": "logLut", + "label": "log files" + }, + { + "type": "text", + "key": "floatLut", + "label": "float files" + } + ] + } + ] + }, + { + "key": "nodes", + "type": "dict", + "label": "Nodes", + "collapsible": true, + "children": [ + { + "key": "requiredNodes", + "type": "list", + "label": "Plugin required", + "object_type": { + "type": "dict", + "children": [ + { + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass" + } + }, + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Knobs", + "key": "knobs" + } + ] + } + + ] + } + }, + { + "type": "splitter" + }, + { + "type": "list", + "key": "overrideNodes", + "label": "Plugin's node overrides", + "object_type": { + "type": "dict", + "children": [ + { + "type": "list", + "key": "plugins", + "label": "Used in plugins", + "object_type": { + "type": "text", + "key": "pluginClass" + } + }, + { + "type": "text", + "key": "nukeNodeClass", + "label": "Nuke Node Class" + }, + { + "key": "subsets", + "label": "Subsets", + "type": "list", + "object_type": "text" + }, + { + "type": "schema_template", + "name": "template_nuke_knob_inputs", + "template_data": [ + { + "label": "Knobs overrides", + "key": "knobs" + } + ] + } + ] + } + } + ] + }, + { + "key": "regexInputs", + "type": "dict", + "label": "Colorspace on Inputs by regex detection", + "collapsible": true, + "children": [ + { + "type": "list", + "key": "inputs", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "regex", + "label": "Regex" + }, + { + "type": "text", + "key": "colorspace", + "label": "Colorspace" + } + ] + } + } + ] + } + ] +} \ No newline at end of file From a82968a32b8688bd32fcf9a6031deea8e6d8c424 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Oct 2022 13:37:11 +0200 Subject: [PATCH 35/55] nuke: backward compatible knob values fix --- .../nuke/plugins/publish/validate_write_nodes.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py b/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py index 26a563b13b..3e2881f298 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py +++ b/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py @@ -77,11 +77,14 @@ class ValidateNukeWriteNode(pyblish.api.InstancePlugin): # fix type differences if type(node_value) in (int, float): - if isinstance(value, list): - value = color_gui_to_int(value) - else: - value = float(value) - node_value = float(node_value) + try: + if isinstance(value, list): + value = color_gui_to_int(value) + else: + value = float(value) + node_value = float(node_value) + except ValueError: + value = str(value) else: value = str(value) node_value = str(node_value) From 845ad59d6cb6a06ca55a3989bd19a82f12d4ba55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Tue, 11 Oct 2022 14:39:53 +0200 Subject: [PATCH 36/55] Update openpype/hosts/maya/api/lib.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/maya/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 789dec31fa..292b95da84 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -23,7 +23,7 @@ from openpype.client import ( get_last_versions, get_representation_by_name ) -from openpype.api import get_project_settings +from openpype.settings import get_project_settings from openpype.pipeline import ( legacy_io, discover_loader_plugins, From ee8599944c229d6d83e46b9e30f7de100b17f181 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 15:27:22 +0200 Subject: [PATCH 37/55] OP-4218 - added configuration to keep version synched Customer might want to keep all published items to use same version number. This is calculated as max published version of all subsets that are being published. --- .../defaults/project_settings/webpublisher.json | 1 + .../schema_project_webpublisher.json | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/openpype/settings/defaults/project_settings/webpublisher.json b/openpype/settings/defaults/project_settings/webpublisher.json index cba472514e..09c7d3ec94 100644 --- a/openpype/settings/defaults/project_settings/webpublisher.json +++ b/openpype/settings/defaults/project_settings/webpublisher.json @@ -10,6 +10,7 @@ ], "publish": { "CollectPublishedFiles": { + "sync_next_version": false, "task_type_to_family": { "Animation": [ { diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json b/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json index 2ef7a05b21..e93e85ef19 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json @@ -49,6 +49,19 @@ "key": "CollectPublishedFiles", "label": "Collect Published Files", "children": [ + { + "type": "label", + "label": "Select if all versions of published items should be kept same. (As max(published) + 1.)" + }, + { + "type": "boolean", + "key": "sync_next_version", + "label": "Sync next publish version" + }, + { + "type": "label", + "label": "Configure resulting family and tags on representation based on uploaded file and task.
Eg. '.png' is uploaded >> create instance of 'render' family
'Create review' in Tags >> mark representation to create review from." + }, { "type": "dict-modifiable", "collapsible": true, From 9f7e241474aa9ec824c53a1cb0dfa143b6084e5a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 15:30:33 +0200 Subject: [PATCH 38/55] OP-4218 - refactor - moved separator in UI Now it makes more sense, if sequence of .png is published >> create instance of 'render' family, add 'review' tag and additional families. --- .../projects_schema/schema_project_webpublisher.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json b/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json index e93e85ef19..a81a403bcb 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_webpublisher.json @@ -87,6 +87,9 @@ "label": "Extensions", "object_type": "text" }, + { + "type": "separator" + }, { "type": "list", "key": "families", @@ -97,9 +100,6 @@ "type": "schema", "name": "schema_representation_tags" }, - { - "type": "separator" - }, { "type": "text", "key": "result_family", From 06909bc45215203bc256a64b711f17dfeda2325b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 15:31:33 +0200 Subject: [PATCH 39/55] OP-4218 - added variable to control versions Use same version number for all published intances if enabled. --- .../plugins/publish/collect_published_files.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index 278a102f9d..d61ae4b17e 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -46,6 +46,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): # from Settings task_type_to_family = [] + sync_next_version = False # find max version to be published, use for all def process(self, context): batch_dir = context.data["batchDir"] @@ -64,6 +65,9 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): task_type = context.data["taskType"] project_name = context.data["project_name"] variant = context.data["variant"] + + next_versions = [] + instances = [] for task_dir in task_subfolders: task_data = parse_json(os.path.join(task_dir, "manifest.json")) @@ -87,16 +91,15 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): host_name="webpublisher", project_settings=context.data["project_settings"] ) - version = self._get_next_version( + next_versions.append(self._get_next_version( project_name, asset_doc, subset_name - ) + )) instance = context.create_instance(subset_name) instance.data["asset"] = asset_name instance.data["subset"] = subset_name instance.data["family"] = family instance.data["families"] = families - instance.data["version"] = version instance.data["stagingDir"] = tempfile.mkdtemp() instance.data["source"] = "webpublisher" @@ -137,8 +140,16 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): instance.data["handleStart"] = asset_doc["data"]["handleStart"] instance.data["handleEnd"] = asset_doc["data"]["handleEnd"] + instances.append(instance) self.log.info("instance.data:: {}".format(instance.data)) + if not self.sync_version: + return + + max_next_version = max(next_versions) + for inst in instances: + inst.data["version"] = max_next_version + def _get_subset_name(self, family, subset_template, task_name, variant): fill_pairs = { "variant": variant, From 4214aa1403452f89f3d19541b28a54d35e97bfed Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 15:35:41 +0200 Subject: [PATCH 40/55] OP-4218 - fix version logic Added documentation. --- .../plugins/publish/collect_published_files.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index d61ae4b17e..85e0469801 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -91,15 +91,19 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): host_name="webpublisher", project_settings=context.data["project_settings"] ) - next_versions.append(self._get_next_version( + version = self._get_next_version( project_name, asset_doc, subset_name - )) + ) + next_versions.append(version) instance = context.create_instance(subset_name) instance.data["asset"] = asset_name instance.data["subset"] = subset_name + # set configurable result family instance.data["family"] = family + # set configurable additional families instance.data["families"] = families + instance.data["version"] = version instance.data["stagingDir"] = tempfile.mkdtemp() instance.data["source"] = "webpublisher" @@ -146,6 +150,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): if not self.sync_version: return + # overwrite specific version with same version for all max_next_version = max(next_versions) for inst in instances: inst.data["version"] = max_next_version @@ -187,7 +192,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): "ext": ext[1:], "files": files, "stagingDir": task_dir, - "tags": tags + "tags": tags # configurable tags from Settings } self.log.info("sequences repre_data.data:: {}".format(repre_data)) return [repre_data] From b3e9d6a6b66f8b23e8d6fdc035efdbc8a136ef88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 15:57:34 +0200 Subject: [PATCH 41/55] OP-4218 - fix variable --- .../webpublisher/plugins/publish/collect_published_files.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index 85e0469801..9d1c702860 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -147,13 +147,14 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): instances.append(instance) self.log.info("instance.data:: {}".format(instance.data)) - if not self.sync_version: + if not self.sync_next_version: return # overwrite specific version with same version for all max_next_version = max(next_versions) for inst in instances: inst.data["version"] = max_next_version + self.log.debug("overwritten version:: {}".format(max_next_version)) def _get_subset_name(self, family, subset_template, task_name, variant): fill_pairs = { From 994edef5883b5dec83794728ffee8c38bdcfa9fc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 16:04:30 +0200 Subject: [PATCH 42/55] OP-4218 - added docstring --- .../plugins/publish/collect_published_files.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index 9d1c702860..dd4646f356 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -37,6 +37,15 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): This is not applicable for 'studio' processing where host application is called to process uploaded workfile and render frames itself. + + For each task configure what properties should resulting instance have + based on uploaded files: + - uploading sequence of 'png' >> create instance of 'render' family, + by adding 'review' to 'Families' and 'Create review' to Tags it will + produce review. + + There might be difference between single(>>image) and sequence(>>render) + uploaded files. """ # must be really early, context values are only in json file order = pyblish.api.CollectorOrder - 0.490 From f823ecfc05de17587cff0830c0e9b97c3a28d2dd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 11 Oct 2022 16:57:02 +0200 Subject: [PATCH 43/55] Refactor container `schema` value `avalon-core:container-2.0` -> `openpype:container-2.0` - These were the only remainders using the old schema value --- openpype/hosts/houdini/plugins/load/load_image.py | 2 +- openpype/hosts/houdini/plugins/load/load_usd_layer.py | 2 +- openpype/hosts/houdini/plugins/load/load_usd_reference.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/houdini/plugins/load/load_image.py b/openpype/hosts/houdini/plugins/load/load_image.py index 928c2ee734..c78798e58a 100644 --- a/openpype/hosts/houdini/plugins/load/load_image.py +++ b/openpype/hosts/houdini/plugins/load/load_image.py @@ -73,7 +73,7 @@ class ImageLoader(load.LoaderPlugin): # Imprint it manually data = { - "schema": "avalon-core:container-2.0", + "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, "name": node_name, "namespace": namespace, diff --git a/openpype/hosts/houdini/plugins/load/load_usd_layer.py b/openpype/hosts/houdini/plugins/load/load_usd_layer.py index 48580fc3aa..2e5079925b 100644 --- a/openpype/hosts/houdini/plugins/load/load_usd_layer.py +++ b/openpype/hosts/houdini/plugins/load/load_usd_layer.py @@ -43,7 +43,7 @@ class USDSublayerLoader(load.LoaderPlugin): # Imprint it manually data = { - "schema": "avalon-core:container-2.0", + "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, "name": node_name, "namespace": namespace, diff --git a/openpype/hosts/houdini/plugins/load/load_usd_reference.py b/openpype/hosts/houdini/plugins/load/load_usd_reference.py index 6851c77e6d..c4371db39b 100644 --- a/openpype/hosts/houdini/plugins/load/load_usd_reference.py +++ b/openpype/hosts/houdini/plugins/load/load_usd_reference.py @@ -43,7 +43,7 @@ class USDReferenceLoader(load.LoaderPlugin): # Imprint it manually data = { - "schema": "avalon-core:container-2.0", + "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, "name": node_name, "namespace": namespace, From e5f6bca5d540bb809a61cec550fe9be0c83c1143 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Oct 2022 17:32:13 +0200 Subject: [PATCH 44/55] flame: removing redundant validation --- openpype/hosts/flame/hooks/pre_flame_setup.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 8f2edf59a6..f0fdaa86ba 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -44,13 +44,6 @@ class FlamePrelaunch(PreLaunchHook): # get image io project_settings = self.data["project_settings"] - # make sure anatomy settings are having flame key - if not project_settings["flame"].get("imageio"): - raise ApplicationLaunchFailed( - "Project settings are missing `flame/imageio` key. " - "Please make sure to update project settings." - ) - imageio_flame = project_settings["flame"]["imageio"] # get user name and host name From 08442d6b071911e54e2962e9456d67e47069b978 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 11 Oct 2022 17:38:25 +0200 Subject: [PATCH 45/55] use direct import of resources --- openpype/hosts/maya/api/customize.py | 2 +- openpype/hosts/nuke/api/utils.py | 2 +- openpype/tools/launcher/actions.py | 2 +- openpype/tools/launcher/lib.py | 2 +- openpype/tools/launcher/window.py | 2 +- openpype/tools/standalonepublish/app.py | 2 +- openpype/tools/tray/pype_info_widget.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/api/customize.py b/openpype/hosts/maya/api/customize.py index 683e6b24b0..f66858dfb6 100644 --- a/openpype/hosts/maya/api/customize.py +++ b/openpype/hosts/maya/api/customize.py @@ -8,7 +8,7 @@ from functools import partial import maya.cmds as cmds import maya.mel as mel -from openpype.api import resources +from openpype import resources from openpype.tools.utils import host_tools from .lib import get_main_window diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 5b0c607292..6bcb752dd1 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -1,7 +1,7 @@ import os import nuke -from openpype.api import resources +from openpype import resources from .lib import maintained_selection diff --git a/openpype/tools/launcher/actions.py b/openpype/tools/launcher/actions.py index b954110da4..34d06f72cc 100644 --- a/openpype/tools/launcher/actions.py +++ b/openpype/tools/launcher/actions.py @@ -4,7 +4,7 @@ from Qt import QtWidgets, QtGui from openpype import PLUGINS_DIR from openpype import style -from openpype.api import resources +from openpype import resources from openpype.lib import ( Logger, ApplictionExecutableNotFound, diff --git a/openpype/tools/launcher/lib.py b/openpype/tools/launcher/lib.py index c1392b7b8f..68e57c6b92 100644 --- a/openpype/tools/launcher/lib.py +++ b/openpype/tools/launcher/lib.py @@ -1,7 +1,7 @@ import os from Qt import QtGui import qtawesome -from openpype.api import resources +from openpype import resources ICON_CACHE = {} NOT_FOUND = type("NotFound", (object, ), {}) diff --git a/openpype/tools/launcher/window.py b/openpype/tools/launcher/window.py index dab6949613..a9eaa932bb 100644 --- a/openpype/tools/launcher/window.py +++ b/openpype/tools/launcher/window.py @@ -4,7 +4,7 @@ import logging from Qt import QtWidgets, QtCore, QtGui from openpype import style -from openpype.api import resources +from openpype import resources from openpype.pipeline import AvalonMongoDB import qtawesome diff --git a/openpype/tools/standalonepublish/app.py b/openpype/tools/standalonepublish/app.py index 081235c91c..c93c33b2a5 100644 --- a/openpype/tools/standalonepublish/app.py +++ b/openpype/tools/standalonepublish/app.py @@ -13,7 +13,7 @@ from .widgets import ( ) from .widgets.constants import HOST_NAME from openpype import style -from openpype.api import resources +from openpype import resources from openpype.pipeline import AvalonMongoDB from openpype.modules import ModulesManager diff --git a/openpype/tools/tray/pype_info_widget.py b/openpype/tools/tray/pype_info_widget.py index 8414cefec8..232d2024ac 100644 --- a/openpype/tools/tray/pype_info_widget.py +++ b/openpype/tools/tray/pype_info_widget.py @@ -5,7 +5,7 @@ import collections from Qt import QtCore, QtGui, QtWidgets from openpype import style -from openpype.api import resources +from openpype import resources from openpype.settings.lib import get_local_settings from openpype.lib.pype_info import ( get_all_current_info, From bca0c2c8103d1f81d7b2f9cee860f665d04bb3b8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 11 Oct 2022 17:39:04 +0200 Subject: [PATCH 46/55] add preffered qt bindings to Qt.py and qtpy --- openpype/hosts/maya/addon.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/addon.py b/openpype/hosts/maya/addon.py index 7b1f7bf754..16d8ae5cd6 100644 --- a/openpype/hosts/maya/addon.py +++ b/openpype/hosts/maya/addon.py @@ -30,7 +30,11 @@ class MayaAddon(OpenPypeModule, IHostAddon): # Set default values if are not already set via settings defaults = { - "OPENPYPE_LOG_NO_COLORS": "Yes" + "OPENPYPE_LOG_NO_COLORS": "Yes", + # For python module 'qtpy' + "QT_API": "PySide2", + # For python module 'Qt' + "QT_PREFERRED_BINDING": "PySide2" } for key, value in defaults.items(): if not env.get(key): From 3626d7572b2de2fca3f7e6e3fc3f3e2a4fb332f1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Oct 2022 17:47:49 +0200 Subject: [PATCH 47/55] fusion: removing redundant validation --- openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py b/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py index 83cd070924..d1ae5f64fd 100644 --- a/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py +++ b/openpype/hosts/fusion/hooks/pre_fusion_ocio_hook.py @@ -15,12 +15,7 @@ class FusionPreLaunchOCIO(PreLaunchHook): project_settings = self.data["project_settings"] # make sure anatomy settings are having flame key - imageio_fusion = project_settings.get("fusion", {}).get("imageio") - if not imageio_fusion: - raise ApplicationLaunchFailed( - "Project settings are missing `fusion/imageio` key. " - "Please make sure you update your project settings. " - ) + imageio_fusion = project_settings["fusion"]["imageio"] ocio = imageio_fusion.get("ocio") enabled = ocio.get("enabled", False) From 9946ca351c7a57985a2348400080cdbb31c71647 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Oct 2022 18:38:43 +0200 Subject: [PATCH 48/55] Added photoshop and aftereffects into ValidateVersion --- openpype/plugins/publish/validate_version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/validate_version.py b/openpype/plugins/publish/validate_version.py index b94152ef2d..b91633430f 100644 --- a/openpype/plugins/publish/validate_version.py +++ b/openpype/plugins/publish/validate_version.py @@ -10,7 +10,8 @@ class ValidateVersion(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder label = "Validate Version" - hosts = ["nuke", "maya", "houdini", "blender", "standalonepublisher"] + hosts = ["nuke", "maya", "houdini", "blender", "standalonepublisher", + "photoshop", "aftereffects"] optional = False active = True From b527e38eb352be05d2e4231281e5e9d108858eb5 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 12 Oct 2022 04:16:33 +0000 Subject: [PATCH 49/55] [Automated] Bump version --- CHANGELOG.md | 24 +++++++++++------------- openpype/version.py | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 455c7aa900..dca0e7ecef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,38 @@ # Changelog -## [3.14.4-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.14.4-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.3...HEAD) **🚀 Enhancements** - General: Set root environments before DCC launch [\#3947](https://github.com/pypeclub/OpenPype/pull/3947) +- Refactor: changed legacy way to update database for Hero version integrate [\#3941](https://github.com/pypeclub/OpenPype/pull/3941) - Maya: Moved plugin from global to maya [\#3939](https://github.com/pypeclub/OpenPype/pull/3939) +- Fusion: Implement Alembic and FBX mesh loader [\#3927](https://github.com/pypeclub/OpenPype/pull/3927) - Publisher: Instances can be marked as stored [\#3846](https://github.com/pypeclub/OpenPype/pull/3846) **🐛 Bug fixes** +- Maya: Deadline OutputFilePath hack regression for Renderman [\#3950](https://github.com/pypeclub/OpenPype/pull/3950) +- Houdini: Fix validate workfile paths for non-parm file references [\#3948](https://github.com/pypeclub/OpenPype/pull/3948) - Photoshop: missed sync published version of workfile with workfile [\#3946](https://github.com/pypeclub/OpenPype/pull/3946) - Maya: fix regression of Renderman Deadline hack [\#3943](https://github.com/pypeclub/OpenPype/pull/3943) +- Tray: Change order of attribute changes [\#3938](https://github.com/pypeclub/OpenPype/pull/3938) - AttributeDefs: Fix crashing multivalue of files widget [\#3937](https://github.com/pypeclub/OpenPype/pull/3937) +- General: Fix links query on hero version [\#3900](https://github.com/pypeclub/OpenPype/pull/3900) - Publisher: Files Drag n Drop cleanup [\#3888](https://github.com/pypeclub/OpenPype/pull/3888) - Maya: Render settings validation attribute check tweak logging [\#3821](https://github.com/pypeclub/OpenPype/pull/3821) **🔀 Refactored code** +- General: Direct settings imports [\#3934](https://github.com/pypeclub/OpenPype/pull/3934) - General: import 'Logger' from 'openpype.lib' [\#3926](https://github.com/pypeclub/OpenPype/pull/3926) **Merged pull requests:** +- Maya + Yeti: Load Yeti Cache fix frame number recognition [\#3942](https://github.com/pypeclub/OpenPype/pull/3942) +- Fusion: Implement callbacks to Fusion's event system thread [\#3928](https://github.com/pypeclub/OpenPype/pull/3928) - Photoshop: create single frame image in Ftrack as review [\#3908](https://github.com/pypeclub/OpenPype/pull/3908) - Maya: Warn correctly about nodes in render instance with unexpected names [\#3816](https://github.com/pypeclub/OpenPype/pull/3816) @@ -68,6 +77,7 @@ - Unreal: Use new Extractor location [\#3917](https://github.com/pypeclub/OpenPype/pull/3917) - Flame: Use new Extractor location [\#3916](https://github.com/pypeclub/OpenPype/pull/3916) - Houdini: Use new Extractor location [\#3894](https://github.com/pypeclub/OpenPype/pull/3894) +- Harmony: Use new Extractor location [\#3893](https://github.com/pypeclub/OpenPype/pull/3893) - Hiero: Use new Extractor location [\#3851](https://github.com/pypeclub/OpenPype/pull/3851) - Maya: Remove old legacy \(ftrack\) plug-ins that are of no use anymore [\#3819](https://github.com/pypeclub/OpenPype/pull/3819) - Nuke: Use new Extractor location [\#3799](https://github.com/pypeclub/OpenPype/pull/3799) @@ -97,18 +107,6 @@ - Igniter: Fix status handling when version is already installed [\#3804](https://github.com/pypeclub/OpenPype/pull/3804) - Resolve: Addon import is Python 2 compatible [\#3798](https://github.com/pypeclub/OpenPype/pull/3798) - Hiero: retimed clip publishing is working [\#3792](https://github.com/pypeclub/OpenPype/pull/3792) -- nuke: validate write node is not failing due wrong type [\#3780](https://github.com/pypeclub/OpenPype/pull/3780) -- Fix - changed format of version string in pyproject.toml [\#3777](https://github.com/pypeclub/OpenPype/pull/3777) - -**🔀 Refactored code** - -- Photoshop: Use new Extractor location [\#3789](https://github.com/pypeclub/OpenPype/pull/3789) -- Blender: Use new Extractor location [\#3787](https://github.com/pypeclub/OpenPype/pull/3787) -- AfterEffects: Use new Extractor location [\#3784](https://github.com/pypeclub/OpenPype/pull/3784) - -**Merged pull requests:** - -- Standalone Publisher: Ignore empty labels, then still use name like other asset models [\#3779](https://github.com/pypeclub/OpenPype/pull/3779) ## [3.14.1](https://github.com/pypeclub/OpenPype/tree/3.14.1) (2022-08-30) diff --git a/openpype/version.py b/openpype/version.py index 1bd566aa9b..3a0c538daf 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.4-nightly.2" +__version__ = "3.14.4-nightly.3" From de3bdbddab4f811247898c614ca8056ae6689b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Wed, 12 Oct 2022 10:54:51 +0200 Subject: [PATCH 50/55] :recycle: move data classes to new style --- .../deadline/plugins/publish/submit_maya_deadline.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 7fbe134410..479906a5c2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -37,7 +37,7 @@ from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo @attr.s -class MayaPluginInfo: +class MayaPluginInfo(): SceneFile = attr.ib(default=None) # Input OutputFilePath = attr.ib(default=None) # Output directory and filename OutputFilePrefix = attr.ib(default=None) @@ -50,7 +50,7 @@ class MayaPluginInfo: @attr.s -class PythonPluginInfo: +class PythonPluginInfo(): ScriptFile = attr.ib() Version = attr.ib(default="3.6") Arguments = attr.ib(default=None) @@ -58,7 +58,7 @@ class PythonPluginInfo: @attr.s -class VRayPluginInfo: +class VRayPluginInfo(): InputFilename = attr.ib(default=None) # Input SeparateFilesPerFrame = attr.ib(default=None) VRayEngine = attr.ib(default="V-Ray") @@ -69,7 +69,7 @@ class VRayPluginInfo: @attr.s -class ArnoldPluginInfo: +class ArnoldPluginInfo(): ArnoldFile = attr.ib(default=None) From 935c8e0cdd343b0d4c245716b7a31140b6ed755a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 12 Oct 2022 11:22:57 +0200 Subject: [PATCH 51/55] added information about instance passed to 'get_subset_name' and 'get_dynamic_data' --- website/docs/dev_publishing.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/website/docs/dev_publishing.md b/website/docs/dev_publishing.md index f11a2c3047..7a6082a517 100644 --- a/website/docs/dev_publishing.md +++ b/website/docs/dev_publishing.md @@ -198,6 +198,37 @@ class RenderLayerCreator(Creator): - **`get_dynamic_data`** (method) - Can be used to extend data for subset templates which may be required in some cases. +Methods are used before instance creation and on instance subset name update. Update may require to have access to existing instance because dynamic data should be filled from there. Because of that is instance passed to `get_subset_name` and `get_dynamic_data` so the creator can handle that cases. + +This is one example where subset name template may contain `"{layer}"` which is filled during creation because the value is taken from selection. In that case `get_dynamic_data` returns value for `"layer"` -> `"{layer}"` so it can be filled in creation. But when subset name of already existing instance is updated it should return already existing value. Note: Creator must make sure the value is available on instance. + +```python +from openpype.lib import prepare_template_data +from my_host import get_selected_layer + + +class SomeCreator(Creator): + def get_dynamic_data( + self, variant, task_name, asset_doc, project_name, host_name, instance + ): + # Before instance is created return unfilled key + # - the key will be filled during creation + if instance is None: + return {"layer": "{layer}"} + # Take value from existing instance + # - creator must know where to look for the value + return {"layer": instance.data["layer"]} + + def create(self, subset_name, instance_data, pre_create_data): + # Fill the layer name in + layer = get_selected_layer() + layer_name = layer["name"] + layer_fill_data = prepare_template_data({"layer": layer_name}) + subset_name = subset_name.format(**layer_fill_data) + instance_data["layer"] = layer_name + ... +``` + #### *HiddenCreator* Creator which is not showed in UI so artist can't trigger it directly but is available for other creators. This creator is primarily meant for cases when creation should create different types of instances. For example during editorial publishing where input is single edl file but should create 2 or more kind of instances each with different family, attributes and abilities. Arguments for creation were limited to `instance_data` and `source_data`. Data of `instance_data` should follow what is sent to other creators and `source_data` can be used to send custom data defined by main creator. It is expected that `HiddenCreator` has specific main or "parent" creator. From c7d2992b9d6cff3b9e939868639d2410e8b82067 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Oct 2022 11:59:15 +0200 Subject: [PATCH 52/55] OP-3939 - fix wrong method to remove item --- .../plugins/publish/preintegrate_thumbnail_representation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py index 49e69ff34b..3ccaeed147 100644 --- a/openpype/plugins/publish/preintegrate_thumbnail_representation.py +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -62,7 +62,7 @@ class PreIntegrateThumbnails(pyblish.api.InstancePlugin): thumbnail_repre["tags"].append("delete") else: if "delete" in thumbnail_repre["tags"]: - thumbnail_repre["tags"].pop("delete") + thumbnail_repre["tags"].remove("delete") self.log.debug( "Thumbnail repre tags {}".format(thumbnail_repre["tags"])) From e34c2a75789a21ce40babfa4d93c5c72f5af9e32 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 12 Oct 2022 16:11:19 +0200 Subject: [PATCH 53/55] force env changes in maya --- openpype/hosts/maya/addon.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/addon.py b/openpype/hosts/maya/addon.py index 16d8ae5cd6..cdd2bc1667 100644 --- a/openpype/hosts/maya/addon.py +++ b/openpype/hosts/maya/addon.py @@ -28,17 +28,16 @@ class MayaAddon(OpenPypeModule, IHostAddon): env["PYTHONPATH"] = os.pathsep.join(new_python_paths) - # Set default values if are not already set via settings - defaults = { + # Set default environments + envs = { "OPENPYPE_LOG_NO_COLORS": "Yes", # For python module 'qtpy' "QT_API": "PySide2", # For python module 'Qt' "QT_PREFERRED_BINDING": "PySide2" } - for key, value in defaults.items(): - if not env.get(key): - env[key] = value + for key, value in envs.items(): + env[key] = value def get_launch_hook_paths(self, app): if app.host_name != self.host_name: From 0d44dbc6c49ce80ddf9d8b4359dd5bdc2eafd9e8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Oct 2022 17:46:50 +0200 Subject: [PATCH 54/55] OP-3939 - changed label of plugin --- .../plugins/publish/preintegrate_thumbnail_representation.py | 2 +- .../schemas/projects_schema/schemas/schema_global_publish.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py index d38f083876..5d3ea5654f 100644 --- a/openpype/plugins/publish/preintegrate_thumbnail_representation.py +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -19,7 +19,7 @@ from openpype.lib.profiles_filtering import filter_profiles class PreIntegrateThumbnails(pyblish.api.InstancePlugin): """Marks thumbnail representation for integrate to DB or not.""" - label = "Should Integrate Thumbnails" + label = "Override Integrate Thumbnails" order = pyblish.api.IntegratorOrder - 0.1 families = ["review"] diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index c216d5fd9e..773dea1229 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -559,7 +559,7 @@ "type": "dict", "collapsible": true, "key": "PreIntegrateThumbnails", - "label": "Integrate Thumbnail Representations", + "label": "Override Integrate Thumbnail Representations", "is_group": true, "checkbox_key": "enabled", "children": [ From bfbf2a26a9b1a28e91a552cdbcf41cd6b5c24613 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Oct 2022 18:22:57 +0200 Subject: [PATCH 55/55] OP-3939 - changed label of plugin --- .../plugins/publish/preintegrate_thumbnail_representation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/preintegrate_thumbnail_representation.py b/openpype/plugins/publish/preintegrate_thumbnail_representation.py index 5d3ea5654f..f9e23223e6 100644 --- a/openpype/plugins/publish/preintegrate_thumbnail_representation.py +++ b/openpype/plugins/publish/preintegrate_thumbnail_representation.py @@ -19,7 +19,7 @@ from openpype.lib.profiles_filtering import filter_profiles class PreIntegrateThumbnails(pyblish.api.InstancePlugin): """Marks thumbnail representation for integrate to DB or not.""" - label = "Override Integrate Thumbnails" + label = "Override Integrate Thumbnail Representations" order = pyblish.api.IntegratorOrder - 0.1 families = ["review"]