From e64d00e39c5c1fe64445e87b374d856d230fba91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Jul 2022 03:59:40 +0000 Subject: [PATCH 01/48] Bump terser from 5.10.0 to 5.14.2 in /website Bumps [terser](https://github.com/terser/terser) from 5.10.0 to 5.14.2. - [Release notes](https://github.com/terser/terser/releases) - [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md) - [Commits](https://github.com/terser/terser/commits) --- updated-dependencies: - dependency-name: terser dependency-type: indirect ... Signed-off-by: dependabot[bot] --- website/yarn.lock | 64 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index 04b9dd658b..38812dc6cd 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -1543,15 +1543,37 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@^0.3.0": version "0.3.4" @@ -1561,6 +1583,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@mdx-js/mdx@1.6.22", "@mdx-js/mdx@^1.6.21": version "1.6.22" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" @@ -2140,10 +2170,10 @@ acorn@^6.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^8.0.4, acorn@^8.4.1: - version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== address@^1.0.1, address@^1.1.2: version "1.1.2" @@ -6838,11 +6868,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - sourcemap-codec@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" @@ -7048,12 +7073,13 @@ terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.4: terser "^5.7.2" terser@^5.10.0, terser@^5.7.2: - version "5.10.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" - integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== + version "5.14.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" + integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" commander "^2.20.0" - source-map "~0.7.2" source-map-support "~0.5.20" text-table@^0.2.0: From f2b6e954a1ddb4662835f8d786e34e70100645fc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 14 Sep 2022 00:16:06 +0200 Subject: [PATCH 02/48] Avoid name conflict where `group_name != group_node` due to maya auto renaming new node --- openpype/hosts/maya/plugins/load/load_yeti_cache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_yeti_cache.py b/openpype/hosts/maya/plugins/load/load_yeti_cache.py index 8435ba2493..abc0e6003c 100644 --- a/openpype/hosts/maya/plugins/load/load_yeti_cache.py +++ b/openpype/hosts/maya/plugins/load/load_yeti_cache.py @@ -73,8 +73,8 @@ class YetiCacheLoader(load.LoaderPlugin): c = colors.get(family) if c is not None: - cmds.setAttr(group_name + ".useOutlinerColor", 1) - cmds.setAttr(group_name + ".outlinerColor", + cmds.setAttr(group_node + ".useOutlinerColor", 1) + cmds.setAttr(group_node + ".outlinerColor", (float(c[0])/255), (float(c[1])/255), (float(c[2])/255) From 3d55d4d9554c5b844da7697e7f1efea6f3ffa303 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 31 Oct 2022 19:36:44 +0800 Subject: [PATCH 03/48] Alembic Loader as Arnold Standin --- .../maya/plugins/load/load_abc_to_standin.py | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 openpype/hosts/maya/plugins/load/load_abc_to_standin.py diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py new file mode 100644 index 0000000000..defed4bd73 --- /dev/null +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -0,0 +1,115 @@ +import os +import clique + +from openpype.pipeline import ( + load, + get_representation_path +) +from openpype.settings import get_project_settings + + +class AlembicStandinLoader(load.LoaderPlugin): + """Load Alembic as Arnold Standin""" + + families = ["model", "pointcache"] + representations = ["abc"] + + label = "Import Alembic as Standin" + order = -5 + icon = "code-fork" + color = "orange" + + def load(self, context, name, namespace, options): + + import maya.cmds as cmds + import pymel.core as pm + import mtoa.ui.arnoldmenu + from openpype.hosts.maya.api.pipeline import containerise + from openpype.hosts.maya.api.lib import unique_namespace + + version = context["version"] + version_data = version.get("data", {}) + + self.log.info("version_data: {}\n".format(version_data)) + + frameStart = version_data.get("frameStart", None) + + asset = context["asset"]["name"] + namespace = namespace or unique_namespace( + asset + "_", + prefix="_" if asset[0].isdigit() else "", + suffix="_", + ) + + #Root group + label = "{}:{}".format(namespace, name) + root = pm.group(name=label, empty=True) + + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings["maya"]["load"]["colors"] + + c = colors.get('ass') + if c is not None: + cmds.setAttr(root + ".useOutlinerColor", 1) + cmds.setAttr(root + ".outlinerColor", + c[0], c[1], c[2]) + + transform_name = label + "_ABC" + + standinShape = pm.PyNode(mtoa.ui.arnoldmenu.createStandIn()) + standin = standinShape.getParent() + standin.rename(transform_name) + + pm.parent(standin, root) + + # Set the standin filepath + standinShape.dso.set(self.fname) + if frameStart is not None: + standinShape.useFrameExtension.set(1) + + nodes = [root, standin] + self[:] = nodes + + return containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) + + def update(self, container, representation): + + import pymel.core as pm + + path = get_representation_path(representation) + + # Update the standin + standins = list() + members = pm.sets(container['objectName'], query=True) + for member in members: + shape = member.getShape() + if (shape and shape.type() == "aiStandIn"): + standins.append(shape) + + for standin in standins: + standin.dso.set(path) + standin.useFrameExtension.set(1) + + container = pm.PyNode(container["objectName"]) + container.representation.set(str(representation["_id"])) + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + import maya.cmds as cmds + members = cmds.sets(container['objectName'], query=True) + cmds.lockNode(members, lock=False) + cmds.delete([container['objectName']] + members) + + # Clean up the namespace + try: + cmds.namespace(removeNamespace=container['namespace'], + deleteNamespaceContent=True) + except RuntimeError: + pass From 7f88049d2a38e46fb933cbf23859529d46976915 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 31 Oct 2022 19:56:10 +0800 Subject: [PATCH 04/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index defed4bd73..f39aa56650 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -1,5 +1,4 @@ import os -import clique from openpype.pipeline import ( load, @@ -41,7 +40,7 @@ class AlembicStandinLoader(load.LoaderPlugin): suffix="_", ) - #Root group + # Root group label = "{}:{}".format(namespace, name) root = pm.group(name=label, empty=True) From ac2f268575327c2e82d1cc9ca0a231caf54ea322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Mon, 31 Oct 2022 13:28:50 +0100 Subject: [PATCH 05/48] Feature: Auto download last published workfile as first workfile --- .../hooks/pre_copy_last_published_workfile.py | 124 ++++++++++++++++++ openpype/modules/sync_server/sync_server.py | 9 +- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 openpype/hooks/pre_copy_last_published_workfile.py diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py new file mode 100644 index 0000000000..004f9d25e7 --- /dev/null +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -0,0 +1,124 @@ +import gc +import os +import shutil +from openpype.client.entities import ( + get_last_version_by_subset_id, + get_representations, + get_subsets, +) +from openpype.lib import PreLaunchHook +from openpype.modules.base import ModulesManager +from openpype.pipeline.load.utils import get_representation_path + + +class CopyLastPublishedWorkfile(PreLaunchHook): + """Copy last published workfile as first workfile. + + Prelaunch hook works only if last workfile leads to not existing file. + - That is possible only if it's first version. + """ + + # Before `AddLastWorkfileToLaunchArgs` + order = -1 + app_groups = ["blender", "photoshop", "tvpaint", "aftereffects"] + + def execute(self): + """Check if local workfile doesn't exist, else copy it. + + 1- Check if setting for this feature is enabled + 2- Check if workfile in work area doesn't exist + 3- Check if published workfile exists and is copied locally in publish + + Returns: + None: This is a void method. + """ + # TODO setting + self.log.info("Trying to fetch last published workfile...") + + last_workfile = self.data.get("last_workfile_path") + if os.path.exists(last_workfile): + self.log.debug( + "Last workfile exists. Skipping {} process.".format( + self.__class__.__name__ + ) + ) + return + + project_name = self.data["project_name"] + task_name = self.data["task_name"] + + project_doc = self.data.get("project_doc") + asset_doc = self.data.get("asset_doc") + anatomy = self.data.get("anatomy") + if project_doc and asset_doc: + # Get subset id + subset_id = next( + ( + subset["_id"] + for subset in get_subsets( + project_name, + asset_ids=[asset_doc["_id"]], + fields=["_id", "data.family"], + ) + if subset["data"]["family"] == "workfile" + ), + None, + ) + if not subset_id: + return + + # Get workfile representation + workfile_representation = next( + ( + representation + for representation in get_representations( + project_name, + version_ids=[ + get_last_version_by_subset_id( + project_name, subset_id, fields=["_id"] + )["_id"] + ], + ) + if representation["context"]["task"]["name"] == task_name + ), + None, + ) + + if workfile_representation: # TODO add setting + # Get sync server from Tray, which handles the asynchronous thread instance + sync_server = next( + ( + t["sync_server"] + for t in [ + obj + for obj in gc.get_objects() + if isinstance(obj, ModulesManager) + ] + if t["sync_server"].sync_server_thread + ), + None, + ) + + # Add site and reset timer + active_site = sync_server.get_active_site(project_name) + sync_server.add_site( + project_name, + workfile_representation["_id"], + active_site, + force=True, + ) + sync_server.reset_timer() + + # Wait for the download loop to end + sync_server.sync_server_thread.files_processed.wait() + + # Get paths + published_workfile_path = get_representation_path( + workfile_representation, root=anatomy.roots + ) + local_workfile_dir = os.path.dirname(last_workfile) + + # Copy file and substitute path + self.data["last_workfile_path"] = shutil.copy( + published_workfile_path, local_workfile_dir + ) diff --git a/openpype/modules/sync_server/sync_server.py b/openpype/modules/sync_server/sync_server.py index 8b11055e65..def9e6cfd8 100644 --- a/openpype/modules/sync_server/sync_server.py +++ b/openpype/modules/sync_server/sync_server.py @@ -236,7 +236,11 @@ class SyncServerThread(threading.Thread): """ def __init__(self, module): self.log = Logger.get_logger(self.__class__.__name__) - super(SyncServerThread, self).__init__() + + # Event to trigger files have been processed + self.files_processed = threading.Event() + + super(SyncServerThread, self).__init__(args=(self.files_processed,)) self.module = module self.loop = None self.is_running = False @@ -396,6 +400,8 @@ class SyncServerThread(threading.Thread): representation, site, error) + # Trigger files are processed + self.files_processed.set() duration = time.time() - start_time self.log.debug("One loop took {:.2f}s".format(duration)) @@ -454,6 +460,7 @@ class SyncServerThread(threading.Thread): async def run_timer(self, delay): """Wait for 'delay' seconds to start next loop""" + self.files_processed.clear() await asyncio.sleep(delay) def reset_timer(self): From 576903575e78e20ba0c7401061741b3c4cc50218 Mon Sep 17 00:00:00 2001 From: Felix David Date: Tue, 1 Nov 2022 10:00:26 +0100 Subject: [PATCH 06/48] Project setting --- .../hooks/pre_copy_last_published_workfile.py | 119 ++++++++++++------ .../defaults/project_settings/global.json | 3 +- .../schemas/schema_global_tools.json | 5 + 3 files changed, 88 insertions(+), 39 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 004f9d25e7..312548d2db 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -7,8 +7,10 @@ from openpype.client.entities import ( get_subsets, ) from openpype.lib import PreLaunchHook +from openpype.lib.profiles_filtering import filter_profiles from openpype.modules.base import ModulesManager from openpype.pipeline.load.utils import get_representation_path +from openpype.settings.lib import get_project_settings class CopyLastPublishedWorkfile(PreLaunchHook): @@ -32,9 +34,45 @@ class CopyLastPublishedWorkfile(PreLaunchHook): Returns: None: This is a void method. """ - # TODO setting + project_name = self.data["project_name"] + task_name = self.data["task_name"] + task_type = self.data["task_type"] + host_name = self.application.host_name + + # Check settings has enabled it + project_settings = get_project_settings(project_name) + profiles = project_settings["global"]["tools"]["Workfiles"][ + "last_workfile_on_startup" + ] + filter_data = { + "tasks": task_name, + "task_types": task_type, + "hosts": host_name, + } + last_workfile_settings = filter_profiles(profiles, filter_data) + use_last_published_workfile = last_workfile_settings.get( + "use_last_published_workfile" + ) + if use_last_published_workfile is None: + self.log.info( + ( + "Seems like old version of settings is used." + ' Can\'t access custom templates in host "{}".' + ).format(host_name) + ) + return + elif use_last_published_workfile is False: + self.log.info( + ( + 'Project "{}" has turned off to use last published workfile' + ' as first workfile for host "{}"' + ).format(project_name, host_name) + ) + return + self.log.info("Trying to fetch last published workfile...") + # Check there is no workfile available last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): self.log.debug( @@ -44,9 +82,6 @@ class CopyLastPublishedWorkfile(PreLaunchHook): ) return - project_name = self.data["project_name"] - task_name = self.data["task_name"] - project_doc = self.data.get("project_doc") asset_doc = self.data.get("asset_doc") anatomy = self.data.get("anatomy") @@ -65,6 +100,9 @@ class CopyLastPublishedWorkfile(PreLaunchHook): None, ) if not subset_id: + self.log.debug('No any workfile for asset "{}".').format( + asset_doc["name"] + ) return # Get workfile representation @@ -84,41 +122,46 @@ class CopyLastPublishedWorkfile(PreLaunchHook): None, ) - if workfile_representation: # TODO add setting - # Get sync server from Tray, which handles the asynchronous thread instance - sync_server = next( - ( - t["sync_server"] - for t in [ - obj - for obj in gc.get_objects() - if isinstance(obj, ModulesManager) - ] - if t["sync_server"].sync_server_thread - ), - None, - ) + if not workfile_representation: + self.log.debug( + 'No published workfile for task "{}" and host "{}".' + ).format(task_name, host_name) + return - # Add site and reset timer - active_site = sync_server.get_active_site(project_name) - sync_server.add_site( - project_name, - workfile_representation["_id"], - active_site, - force=True, - ) - sync_server.reset_timer() + # Get sync server from Tray, which handles the asynchronous thread instance + sync_server = next( + ( + t["sync_server"] + for t in [ + obj + for obj in gc.get_objects() + if isinstance(obj, ModulesManager) + ] + if t["sync_server"].sync_server_thread + ), + None, + ) - # Wait for the download loop to end - sync_server.sync_server_thread.files_processed.wait() + # Add site and reset timer + active_site = sync_server.get_active_site(project_name) + sync_server.add_site( + project_name, + workfile_representation["_id"], + active_site, + force=True, + ) + sync_server.reset_timer() - # Get paths - published_workfile_path = get_representation_path( - workfile_representation, root=anatomy.roots - ) - local_workfile_dir = os.path.dirname(last_workfile) + # Wait for the download loop to end + sync_server.sync_server_thread.files_processed.wait() - # Copy file and substitute path - self.data["last_workfile_path"] = shutil.copy( - published_workfile_path, local_workfile_dir - ) + # Get paths + published_workfile_path = get_representation_path( + workfile_representation, root=anatomy.roots + ) + local_workfile_dir = os.path.dirname(last_workfile) + + # Copy file and substitute path + self.data["last_workfile_path"] = shutil.copy( + published_workfile_path, local_workfile_dir + ) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index b128564bc2..5b1c750bf4 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -401,7 +401,8 @@ "hosts": [], "task_types": [], "tasks": [], - "enabled": true + "enabled": true, + "use_last_published_workfile": false } ], "open_workfile_tool_on_startup": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json index ba446135e2..962008d476 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json @@ -149,6 +149,11 @@ "type": "boolean", "key": "enabled", "label": "Enabled" + }, + { + "type": "boolean", + "key": "use_last_published_workfile", + "label": "Use last published workfile" } ] } From 10fb9a141159302d4321d1acb8409fa5f341d7c9 Mon Sep 17 00:00:00 2001 From: Felix David Date: Tue, 1 Nov 2022 10:10:12 +0100 Subject: [PATCH 07/48] docstring --- openpype/hooks/pre_copy_last_published_workfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 312548d2db..b1b2fe2366 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -30,6 +30,7 @@ class CopyLastPublishedWorkfile(PreLaunchHook): 1- Check if setting for this feature is enabled 2- Check if workfile in work area doesn't exist 3- Check if published workfile exists and is copied locally in publish + 4- Substitute copied published workfile as first workfile Returns: None: This is a void method. From 895bfbaae5aaebaf97c30233ae407f12ad52ca7d Mon Sep 17 00:00:00 2001 From: Felix David Date: Tue, 1 Nov 2022 10:14:58 +0100 Subject: [PATCH 08/48] comment length --- openpype/hooks/pre_copy_last_published_workfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index b1b2fe2366..d342151823 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -129,7 +129,8 @@ class CopyLastPublishedWorkfile(PreLaunchHook): ).format(task_name, host_name) return - # Get sync server from Tray, which handles the asynchronous thread instance + # Get sync server from Tray, + # which handles the asynchronous thread instance sync_server = next( ( t["sync_server"] From c49017e6718ee169f052a75689b5624dc36705dc Mon Sep 17 00:00:00 2001 From: Felix David Date: Tue, 1 Nov 2022 10:50:30 +0100 Subject: [PATCH 09/48] lint --- openpype/hooks/pre_copy_last_published_workfile.py | 4 ++-- openpype/modules/sync_server/sync_server.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index d342151823..cf4edeac9b 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -65,8 +65,8 @@ class CopyLastPublishedWorkfile(PreLaunchHook): elif use_last_published_workfile is False: self.log.info( ( - 'Project "{}" has turned off to use last published workfile' - ' as first workfile for host "{}"' + 'Project "{}" has turned off to use last published' + ' workfile as first workfile for host "{}"' ).format(project_name, host_name) ) return diff --git a/openpype/modules/sync_server/sync_server.py b/openpype/modules/sync_server/sync_server.py index def9e6cfd8..353b39c4e1 100644 --- a/openpype/modules/sync_server/sync_server.py +++ b/openpype/modules/sync_server/sync_server.py @@ -239,7 +239,7 @@ class SyncServerThread(threading.Thread): # Event to trigger files have been processed self.files_processed = threading.Event() - + super(SyncServerThread, self).__init__(args=(self.files_processed,)) self.module = module self.loop = None From 02fb9561d7f25a1146d9c59dd2306bce7e166edf Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 1 Nov 2022 20:36:58 +0800 Subject: [PATCH 10/48] Alembic Loader as Arnold Standin --- .../maya/plugins/load/load_abc_to_standin.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index f39aa56650..68aeb24069 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -13,7 +13,7 @@ class AlembicStandinLoader(load.LoaderPlugin): families = ["model", "pointcache"] representations = ["abc"] - label = "Import Alembic as Standin" + label = "Import Alembic as Arnold Standin" order = -5 icon = "code-fork" color = "orange" @@ -21,7 +21,6 @@ class AlembicStandinLoader(load.LoaderPlugin): def load(self, context, name, namespace, options): import maya.cmds as cmds - import pymel.core as pm import mtoa.ui.arnoldmenu from openpype.hosts.maya.api.pipeline import containerise from openpype.hosts.maya.api.lib import unique_namespace @@ -42,7 +41,7 @@ class AlembicStandinLoader(load.LoaderPlugin): # Root group label = "{}:{}".format(namespace, name) - root = pm.group(name=label, empty=True) + root = cmds.group(name=label, empty=True) settings = get_project_settings(os.environ['AVALON_PROJECT']) colors = settings["maya"]["load"]["colors"] @@ -55,16 +54,17 @@ class AlembicStandinLoader(load.LoaderPlugin): transform_name = label + "_ABC" - standinShape = pm.PyNode(mtoa.ui.arnoldmenu.createStandIn()) - standin = standinShape.getParent() - standin.rename(transform_name) + standinShape = cmds.ls(mtoa.ui.arnoldmenu.createStandIn())[0] + standin = cmds.listRelatives(standinShape, parent=True, typ="transform") + standin = cmds.rename(standin, transform_name) + standinShape = cmds.listRelatives(standin, children=True)[0] - pm.parent(standin, root) + cmds.parent(standin, root) # Set the standin filepath - standinShape.dso.set(self.fname) + cmds.setAttr(standinShape + ".dso", self.fname, type="string") if frameStart is not None: - standinShape.useFrameExtension.set(1) + cmds.setAttr(standinShape + ".useFrameExtension", 1) nodes = [root, standin] self[:] = nodes From 6b0d25cb7c6f3c0dd084bbccf0b5f06fe8fe1341 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 1 Nov 2022 20:38:51 +0800 Subject: [PATCH 11/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 68aeb24069..5d6c52eac9 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -55,7 +55,8 @@ class AlembicStandinLoader(load.LoaderPlugin): transform_name = label + "_ABC" standinShape = cmds.ls(mtoa.ui.arnoldmenu.createStandIn())[0] - standin = cmds.listRelatives(standinShape, parent=True, typ="transform") + standin = cmds.listRelatives(standinShape, parent=True, + typ="transform") standin = cmds.rename(standin, transform_name) standinShape = cmds.listRelatives(standin, children=True)[0] From ed96f1d5b33649e8f5f21e5598e4ee56436f63df Mon Sep 17 00:00:00 2001 From: Felix David Date: Thu, 3 Nov 2022 11:41:59 +0100 Subject: [PATCH 12/48] requested cosmetic changes --- .../hooks/pre_copy_last_published_workfile.py | 172 +++++++++--------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index cf4edeac9b..7a835507f7 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -35,6 +35,17 @@ class CopyLastPublishedWorkfile(PreLaunchHook): Returns: None: This is a void method. """ + # Check there is no workfile available + last_workfile = self.data.get("last_workfile_path") + if os.path.exists(last_workfile): + self.log.debug( + "Last workfile exists. Skipping {} process.".format( + self.__class__.__name__ + ) + ) + return + + # Get data project_name = self.data["project_name"] task_name = self.data["task_name"] task_type = self.data["task_type"] @@ -73,97 +84,94 @@ class CopyLastPublishedWorkfile(PreLaunchHook): self.log.info("Trying to fetch last published workfile...") - # Check there is no workfile available - last_workfile = self.data.get("last_workfile_path") - if os.path.exists(last_workfile): - self.log.debug( - "Last workfile exists. Skipping {} process.".format( - self.__class__.__name__ - ) - ) - return - project_doc = self.data.get("project_doc") asset_doc = self.data.get("asset_doc") anatomy = self.data.get("anatomy") - if project_doc and asset_doc: - # Get subset id - subset_id = next( - ( - subset["_id"] - for subset in get_subsets( - project_name, - asset_ids=[asset_doc["_id"]], - fields=["_id", "data.family"], - ) - if subset["data"]["family"] == "workfile" - ), - None, - ) - if not subset_id: - self.log.debug('No any workfile for asset "{}".').format( - asset_doc["name"] - ) - return - # Get workfile representation - workfile_representation = next( - ( - representation - for representation in get_representations( - project_name, - version_ids=[ + # Check it can proceed + if not project_doc and not asset_doc: + return + + # Get subset id + subset_id = next( + ( + subset["_id"] + for subset in get_subsets( + project_name, + asset_ids=[asset_doc["_id"]], + fields=["_id", "data.family"], + ) + if subset["data"]["family"] == "workfile" + ), + None, + ) + if not subset_id: + self.log.debug('No any workfile for asset "{}".').format( + asset_doc["name"] + ) + return + + # Get workfile representation + workfile_representation = next( + ( + representation + for representation in get_representations( + project_name, + version_ids=[ + ( get_last_version_by_subset_id( project_name, subset_id, fields=["_id"] - )["_id"] - ], - ) - if representation["context"]["task"]["name"] == task_name - ), - None, - ) + ) + or {} + ).get("_id") + ], + ) + if representation["context"]["task"]["name"] == task_name + ), + None, + ) - if not workfile_representation: - self.log.debug( - 'No published workfile for task "{}" and host "{}".' - ).format(task_name, host_name) - return + if not workfile_representation: + self.log.debug( + 'No published workfile for task "{}" and host "{}".' + ).format(task_name, host_name) + return - # Get sync server from Tray, - # which handles the asynchronous thread instance - sync_server = next( - ( - t["sync_server"] - for t in [ - obj - for obj in gc.get_objects() - if isinstance(obj, ModulesManager) - ] - if t["sync_server"].sync_server_thread - ), - None, - ) + # Get sync server from Tray, + # which handles the asynchronous thread instance + sync_server = next( + ( + t["sync_server"] + for t in [ + obj + for obj in gc.get_objects() + if isinstance(obj, ModulesManager) + ] + if t["sync_server"].sync_server_thread + ), + None, + ) - # Add site and reset timer - active_site = sync_server.get_active_site(project_name) - sync_server.add_site( - project_name, - workfile_representation["_id"], - active_site, - force=True, - ) - sync_server.reset_timer() + # Add site and reset timer + active_site = sync_server.get_active_site(project_name) + sync_server.add_site( + project_name, + workfile_representation["_id"], + active_site, + force=True, + ) + sync_server.reset_timer() - # Wait for the download loop to end - sync_server.sync_server_thread.files_processed.wait() + # Wait for the download loop to end + sync_server.sync_server_thread.files_processed.wait() - # Get paths - published_workfile_path = get_representation_path( - workfile_representation, root=anatomy.roots - ) - local_workfile_dir = os.path.dirname(last_workfile) + # Get paths + published_workfile_path = get_representation_path( + workfile_representation, root=anatomy.roots + ) + local_workfile_dir = os.path.dirname(last_workfile) - # Copy file and substitute path - self.data["last_workfile_path"] = shutil.copy( - published_workfile_path, local_workfile_dir - ) + # Copy file and substitute path + self.data["last_workfile_path"] = shutil.copy( + published_workfile_path, local_workfile_dir + ) From 82be7ce8d053eefcb430a6b7d26948821ea6ea11 Mon Sep 17 00:00:00 2001 From: Felix David Date: Thu, 3 Nov 2022 18:33:54 +0100 Subject: [PATCH 13/48] Change to REST API using web server --- .../hooks/pre_copy_last_published_workfile.py | 54 ++++++++------- openpype/modules/sync_server/rest_api.py | 68 +++++++++++++++++++ openpype/modules/sync_server/sync_server.py | 12 ++-- .../modules/sync_server/sync_server_module.py | 9 +++ openpype/modules/timers_manager/rest_api.py | 2 +- 5 files changed, 112 insertions(+), 33 deletions(-) create mode 100644 openpype/modules/sync_server/rest_api.py diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 7a835507f7..cefc7e5d40 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -1,14 +1,14 @@ -import gc import os import shutil +from time import sleep from openpype.client.entities import ( get_last_version_by_subset_id, get_representations, get_subsets, ) from openpype.lib import PreLaunchHook +from openpype.lib.local_settings import get_local_site_id from openpype.lib.profiles_filtering import filter_profiles -from openpype.modules.base import ModulesManager from openpype.pipeline.load.utils import get_representation_path from openpype.settings.lib import get_project_settings @@ -137,33 +137,37 @@ class CopyLastPublishedWorkfile(PreLaunchHook): ).format(task_name, host_name) return - # Get sync server from Tray, - # which handles the asynchronous thread instance - sync_server = next( - ( - t["sync_server"] - for t in [ - obj - for obj in gc.get_objects() - if isinstance(obj, ModulesManager) - ] - if t["sync_server"].sync_server_thread - ), - None, - ) + # POST to webserver sites to add to representations + webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") + if not webserver_url: + self.log.warning("Couldn't find webserver url") + return - # Add site and reset timer - active_site = sync_server.get_active_site(project_name) - sync_server.add_site( - project_name, - workfile_representation["_id"], - active_site, - force=True, + entry_point_url = "{}/sync_server".format(webserver_url) + rest_api_url = "{}/add_sites_to_representations".format( + entry_point_url + ) + try: + import requests + except Exception: + self.log.warning( + "Couldn't add sites to representations ('requests' is not available)" + ) + return + + requests.post( + rest_api_url, + json={ + "project_name": project_name, + "sites": [get_local_site_id()], + "representations": [str(workfile_representation["_id"])], + }, ) - sync_server.reset_timer() # Wait for the download loop to end - sync_server.sync_server_thread.files_processed.wait() + rest_api_url = "{}/files_are_processed".format(entry_point_url) + while requests.get(rest_api_url).content: + sleep(5) # Get paths published_workfile_path = get_representation_path( diff --git a/openpype/modules/sync_server/rest_api.py b/openpype/modules/sync_server/rest_api.py new file mode 100644 index 0000000000..b7c5d26d15 --- /dev/null +++ b/openpype/modules/sync_server/rest_api.py @@ -0,0 +1,68 @@ +from aiohttp.web_response import Response +from openpype.lib import Logger + + +class SyncServerModuleRestApi: + """ + REST API endpoint used for calling from hosts when context change + happens in Workfile app. + """ + + def __init__(self, user_module, server_manager): + self._log = None + self.module = user_module + self.server_manager = server_manager + + self.prefix = "/sync_server" + + self.register() + + @property + def log(self): + if self._log is None: + self._log = Logger.get_logger(self.__class__.__name__) + return self._log + + def register(self): + self.server_manager.add_route( + "POST", + self.prefix + "/add_sites_to_representations", + self.add_sites_to_representations, + ) + self.server_manager.add_route( + "GET", + self.prefix + "/files_are_processed", + self.files_are_processed, + ) + + async def add_sites_to_representations(self, request): + # Extract data from request + data = await request.json() + try: + project_name = data["project_name"] + sites = data["sites"] + representations = data["representations"] + except KeyError: + msg = ( + "Payload must contain fields 'project_name," + " 'sites' (list of names) and 'representations' (list of IDs)" + ) + self.log.error(msg) + return Response(status=400, message=msg) + + # Add all sites to each representation + for representation_id in representations: + for site in sites: + self.module.add_site( + project_name, representation_id, site, force=True + ) + + # Force timer to run immediately + self.module.reset_timer() + + return Response(status=200) + + async def files_are_processed(self, _request): + return Response( + body=bytes(self.module.sync_server_thread.files_are_processed) + ) diff --git a/openpype/modules/sync_server/sync_server.py b/openpype/modules/sync_server/sync_server.py index 353b39c4e1..7fd2311c2d 100644 --- a/openpype/modules/sync_server/sync_server.py +++ b/openpype/modules/sync_server/sync_server.py @@ -237,15 +237,13 @@ class SyncServerThread(threading.Thread): def __init__(self, module): self.log = Logger.get_logger(self.__class__.__name__) - # Event to trigger files have been processed - self.files_processed = threading.Event() - - super(SyncServerThread, self).__init__(args=(self.files_processed,)) + super(SyncServerThread, self).__init__() self.module = module self.loop = None self.is_running = False self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=3) self.timer = None + self.files_are_processed = False def run(self): self.is_running = True @@ -400,8 +398,8 @@ class SyncServerThread(threading.Thread): representation, site, error) - # Trigger files are processed - self.files_processed.set() + # Trigger files process finished + self.files_are_processed = False duration = time.time() - start_time self.log.debug("One loop took {:.2f}s".format(duration)) @@ -460,7 +458,6 @@ class SyncServerThread(threading.Thread): async def run_timer(self, delay): """Wait for 'delay' seconds to start next loop""" - self.files_processed.clear() await asyncio.sleep(delay) def reset_timer(self): @@ -469,6 +466,7 @@ class SyncServerThread(threading.Thread): if self.timer: self.timer.cancel() self.timer = None + self.files_are_processed = True def _working_sites(self, project_name): if self.module.is_project_paused(project_name): diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index a478faa9ef..7aaf42006c 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -2089,6 +2089,15 @@ class SyncServerModule(OpenPypeModule, ITrayModule): def cli(self, click_group): click_group.add_command(cli_main) + # Webserver module implementation + def webserver_initialization(self, server_manager): + """Add routes for syncs.""" + if self.tray_initialized: + from .rest_api import SyncServerModuleRestApi + self.rest_api_obj = SyncServerModuleRestApi( + self, server_manager + ) + @click.group(SyncServerModule.name, help="SyncServer module related commands.") def cli_main(): diff --git a/openpype/modules/timers_manager/rest_api.py b/openpype/modules/timers_manager/rest_api.py index 4a2e9e6575..979db9075b 100644 --- a/openpype/modules/timers_manager/rest_api.py +++ b/openpype/modules/timers_manager/rest_api.py @@ -21,7 +21,7 @@ class TimersManagerModuleRestApi: @property def log(self): if self._log is None: - self._log = Logger.get_logger(self.__ckass__.__name__) + self._log = Logger.get_logger(self.__class__.__name__) return self._log def register(self): From bca965cf9cae5eff205edd0e191288731770fa1a Mon Sep 17 00:00:00 2001 From: Felix David Date: Thu, 3 Nov 2022 18:37:13 +0100 Subject: [PATCH 14/48] lint --- openpype/hooks/pre_copy_last_published_workfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index cefc7e5d40..6bec4f7d2c 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -151,7 +151,8 @@ class CopyLastPublishedWorkfile(PreLaunchHook): import requests except Exception: self.log.warning( - "Couldn't add sites to representations ('requests' is not available)" + "Couldn't add sites to representations " + "('requests' is not available)" ) return From e359fb3d8451949ccba65204d69adb39d4a711cf Mon Sep 17 00:00:00 2001 From: Felix David Date: Fri, 4 Nov 2022 10:06:59 +0100 Subject: [PATCH 15/48] legacy compatibility --- openpype/hooks/pre_copy_last_published_workfile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 6bec4f7d2c..f3293fa511 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -99,9 +99,11 @@ class CopyLastPublishedWorkfile(PreLaunchHook): for subset in get_subsets( project_name, asset_ids=[asset_doc["_id"]], - fields=["_id", "data.family"], + fields=["_id", "data.family", "data.families"], ) - if subset["data"]["family"] == "workfile" + if subset["data"].get("family") == "workfile" + # Legacy compatibility + or "workfile" in subset["data"].get("families", {}) ), None, ) From a76ad6035110e917e44b36e799222787bf87fd9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 8 Nov 2022 16:41:19 +0100 Subject: [PATCH 16/48] use 'created_dt' of representation --- openpype/client/entities.py | 28 +++++++++++++++++++ .../hooks/pre_copy_last_published_workfile.py | 19 +++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 43afccf2f1..43c2874f57 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -6,6 +6,7 @@ that has project name as a context (e.g. on 'ProjectEntity'?). + We will need more specific functions doing wery specific queires really fast. """ +from datetime import datetime import re import collections @@ -1367,6 +1368,33 @@ def get_representation_parents(project_name, representation): return parents_by_repre_id[repre_id] +def get_representation_last_created_time_on_site( + representation: dict, site_name: str +) -> datetime: + """Get `created_dt` value for representation on site. + + Args: + representation (dict): Representation to get creation date of + site_name (str): Site from which to get the creation date + + Returns: + datetime: Created time of representation on site + """ + created_time = next( + ( + site.get("created_dt") + for site in representation["files"][0].get("sites", []) + if site["name"] == site_name + ), + None, + ) + if created_time: + return created_time + else: + # Use epoch as 'zero' time + return datetime.utcfromtimestamp(0) + + def get_thumbnail_id_from_source(project_name, src_type, src_id): """Receive thumbnail id from source entity. diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index f3293fa511..4eb66f6f85 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -3,6 +3,8 @@ import shutil from time import sleep from openpype.client.entities import ( get_last_version_by_subset_id, + get_representation_by_id, + get_representation_last_created_time_on_site, get_representations, get_subsets, ) @@ -158,18 +160,29 @@ class CopyLastPublishedWorkfile(PreLaunchHook): ) return + local_site_id = get_local_site_id() requests.post( rest_api_url, json={ "project_name": project_name, - "sites": [get_local_site_id()], + "sites": [local_site_id], "representations": [str(workfile_representation["_id"])], }, ) # Wait for the download loop to end - rest_api_url = "{}/files_are_processed".format(entry_point_url) - while requests.get(rest_api_url).content: + last_created_time = get_representation_last_created_time_on_site( + workfile_representation, local_site_id + ) + while ( + last_created_time + >= get_representation_last_created_time_on_site( + get_representation_by_id( + project_name, workfile_representation["_id"] + ), + local_site_id, + ) + ): sleep(5) # Get paths From 7b1069f708dc9c0d8153fc669a1106bc6d79d030 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 9 Nov 2022 00:32:22 +0800 Subject: [PATCH 17/48] Alembic Loader as Arnold Standin --- .../hosts/maya/plugins/load/load_abc_to_standin.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 5d6c52eac9..94bb974917 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -10,7 +10,7 @@ from openpype.settings import get_project_settings class AlembicStandinLoader(load.LoaderPlugin): """Load Alembic as Arnold Standin""" - families = ["model", "pointcache"] + families = ["animation", "model", "pointcache"] representations = ["abc"] label = "Import Alembic as Arnold Standin" @@ -31,6 +31,7 @@ class AlembicStandinLoader(load.LoaderPlugin): self.log.info("version_data: {}\n".format(version_data)) frameStart = version_data.get("frameStart", None) + frameEnd = version_data.get("frameEnd", None) asset = context["asset"]["name"] namespace = namespace or unique_namespace( @@ -64,7 +65,13 @@ class AlembicStandinLoader(load.LoaderPlugin): # Set the standin filepath cmds.setAttr(standinShape + ".dso", self.fname, type="string") - if frameStart is not None: + cmds.setAttr(standinShape + ".abcFPS", 25) + + if frameStart is None: + cmds.setAttr(standinShape + ".useFrameExtension", 0) + elif frameStart == 1 and frameEnd == 1: + cmds.setAttr(standinShape + ".useFrameExtension", 0) + else: cmds.setAttr(standinShape + ".useFrameExtension", 1) nodes = [root, standin] @@ -93,7 +100,8 @@ class AlembicStandinLoader(load.LoaderPlugin): for standin in standins: standin.dso.set(path) - standin.useFrameExtension.set(1) + standin.useFrameExtension.set(0) + standin.abcFPS.set(25) container = pm.PyNode(container["objectName"]) container.representation.set(str(representation["_id"])) From 3ad8e95ca436c4229ae8f3eadbee5b41c8326a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 8 Nov 2022 18:57:02 +0100 Subject: [PATCH 18/48] add priority to add_site --- openpype/modules/sync_server/rest_api.py | 17 ++++++----------- openpype/modules/sync_server/sync_server.py | 4 ---- .../modules/sync_server/sync_server_module.py | 13 ++++++++++--- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/openpype/modules/sync_server/rest_api.py b/openpype/modules/sync_server/rest_api.py index b7c5d26d15..e92ddc8eee 100644 --- a/openpype/modules/sync_server/rest_api.py +++ b/openpype/modules/sync_server/rest_api.py @@ -1,4 +1,5 @@ from aiohttp.web_response import Response +from openpype.client.entities import get_representation_by_id from openpype.lib import Logger @@ -29,11 +30,6 @@ class SyncServerModuleRestApi: self.prefix + "/add_sites_to_representations", self.add_sites_to_representations, ) - self.server_manager.add_route( - "GET", - self.prefix + "/files_are_processed", - self.files_are_processed, - ) async def add_sites_to_representations(self, request): # Extract data from request @@ -54,15 +50,14 @@ class SyncServerModuleRestApi: for representation_id in representations: for site in sites: self.module.add_site( - project_name, representation_id, site, force=True + project_name, + representation_id, + site, + force=True, + priority=99, ) # Force timer to run immediately self.module.reset_timer() return Response(status=200) - - async def files_are_processed(self, _request): - return Response( - body=bytes(self.module.sync_server_thread.files_are_processed) - ) diff --git a/openpype/modules/sync_server/sync_server.py b/openpype/modules/sync_server/sync_server.py index 7fd2311c2d..d0a40a60ff 100644 --- a/openpype/modules/sync_server/sync_server.py +++ b/openpype/modules/sync_server/sync_server.py @@ -243,7 +243,6 @@ class SyncServerThread(threading.Thread): self.is_running = False self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=3) self.timer = None - self.files_are_processed = False def run(self): self.is_running = True @@ -398,8 +397,6 @@ class SyncServerThread(threading.Thread): representation, site, error) - # Trigger files process finished - self.files_are_processed = False duration = time.time() - start_time self.log.debug("One loop took {:.2f}s".format(duration)) @@ -466,7 +463,6 @@ class SyncServerThread(threading.Thread): if self.timer: self.timer.cancel() self.timer = None - self.files_are_processed = True def _working_sites(self, project_name): if self.module.is_project_paused(project_name): diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 7aaf42006c..788032180e 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -136,7 +136,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): """ Start of Public API """ def add_site(self, project_name, representation_id, site_name=None, - force=False): + force=False, priority=None): """ Adds new site to representation to be synced. @@ -152,6 +152,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): representation_id (string): MongoDB _id value site_name (string): name of configured and active site force (bool): reset site if exists + priority (int): set priority Throws: SiteAlreadyPresentError - if adding already existing site and @@ -167,7 +168,8 @@ class SyncServerModule(OpenPypeModule, ITrayModule): self.reset_site_on_representation(project_name, representation_id, site_name=site_name, - force=force) + force=force, + priority=priority) def remove_site(self, project_name, representation_id, site_name, remove_local_files=False): @@ -1655,7 +1657,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): def reset_site_on_representation(self, project_name, representation_id, side=None, file_id=None, site_name=None, - remove=False, pause=None, force=False): + remove=False, pause=None, force=False, priority=None): """ Reset information about synchronization for particular 'file_id' and provider. @@ -1678,6 +1680,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): remove (bool): if True remove site altogether pause (bool or None): if True - pause, False - unpause force (bool): hard reset - currently only for add_site + priority (int): set priority Raises: SiteAlreadyPresentError - if adding already existing site and @@ -1705,6 +1708,10 @@ class SyncServerModule(OpenPypeModule, ITrayModule): elem = {"name": site_name} + # Add priority + if priority: + elem["priority"] = priority + if file_id: # reset site for particular file self._reset_site_for_file(project_name, representation_id, elem, file_id, site_name) From f19c2b3a7936b5ca0c3742ddc98f1d0fc38555ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 8 Nov 2022 18:58:19 +0100 Subject: [PATCH 19/48] clean --- openpype/modules/sync_server/rest_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/modules/sync_server/rest_api.py b/openpype/modules/sync_server/rest_api.py index e92ddc8eee..0c3b914833 100644 --- a/openpype/modules/sync_server/rest_api.py +++ b/openpype/modules/sync_server/rest_api.py @@ -1,5 +1,4 @@ from aiohttp.web_response import Response -from openpype.client.entities import get_representation_by_id from openpype.lib import Logger From 255d5d8b9b0c34d10b2ac17913e1369d41cc1108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 8 Nov 2022 18:59:03 +0100 Subject: [PATCH 20/48] clean --- openpype/modules/sync_server/sync_server_module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 788032180e..94a97e9f37 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -1657,7 +1657,8 @@ class SyncServerModule(OpenPypeModule, ITrayModule): def reset_site_on_representation(self, project_name, representation_id, side=None, file_id=None, site_name=None, - remove=False, pause=None, force=False, priority=None): + remove=False, pause=None, force=False, + priority=None): """ Reset information about synchronization for particular 'file_id' and provider. From 4408ea9b02a00a0c0ff5adf4b45b2cb1f5168793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 8 Nov 2022 19:02:29 +0100 Subject: [PATCH 21/48] sort fields --- openpype/hooks/pre_copy_last_published_workfile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 4eb66f6f85..acbc9ec1c7 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -178,7 +178,9 @@ class CopyLastPublishedWorkfile(PreLaunchHook): last_created_time >= get_representation_last_created_time_on_site( get_representation_by_id( - project_name, workfile_representation["_id"] + project_name, + workfile_representation["_id"], + fields=["files"], ), local_site_id, ) From bf6af7f7175ff1536b81e6ac8ae3f4c233271a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 8 Nov 2022 19:04:03 +0100 Subject: [PATCH 22/48] clean --- openpype/modules/sync_server/sync_server_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 94a97e9f37..4d848958e8 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -1657,7 +1657,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): def reset_site_on_representation(self, project_name, representation_id, side=None, file_id=None, site_name=None, - remove=False, pause=None, force=False, + remove=False, pause=None, force=False, priority=None): """ Reset information about synchronization for particular 'file_id' From ff565317d0a1abe63671ab5e4b62ce599a000ff7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 9 Nov 2022 08:39:52 +0800 Subject: [PATCH 23/48] Alembic Loader as Arnold Standin --- .../hosts/maya/plugins/load/load_abc_to_standin.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 94bb974917..19e60d33da 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -1,6 +1,7 @@ import os from openpype.pipeline import ( + legacy_io, load, get_representation_path ) @@ -46,6 +47,7 @@ class AlembicStandinLoader(load.LoaderPlugin): settings = get_project_settings(os.environ['AVALON_PROJECT']) colors = settings["maya"]["load"]["colors"] + fps = legacy_io.Session["AVALON_FPS"] c = colors.get('ass') if c is not None: @@ -65,12 +67,14 @@ class AlembicStandinLoader(load.LoaderPlugin): # Set the standin filepath cmds.setAttr(standinShape + ".dso", self.fname, type="string") - cmds.setAttr(standinShape + ".abcFPS", 25) + cmds.setAttr(standinShape + ".abcFPS", float(fps)) if frameStart is None: cmds.setAttr(standinShape + ".useFrameExtension", 0) + elif frameStart == 1 and frameEnd == 1: cmds.setAttr(standinShape + ".useFrameExtension", 0) + else: cmds.setAttr(standinShape + ".useFrameExtension", 1) @@ -89,7 +93,7 @@ class AlembicStandinLoader(load.LoaderPlugin): import pymel.core as pm path = get_representation_path(representation) - + fps = legacy_io.Session["AVALON_FPS"] # Update the standin standins = list() members = pm.sets(container['objectName'], query=True) @@ -101,7 +105,7 @@ class AlembicStandinLoader(load.LoaderPlugin): for standin in standins: standin.dso.set(path) standin.useFrameExtension.set(0) - standin.abcFPS.set(25) + standin.abcFPS.set(float(fps)) container = pm.PyNode(container["objectName"]) container.representation.set(str(representation["_id"])) From 8feedcbc155387958b325e8eccc4c08bfad8a18e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:18:23 +0100 Subject: [PATCH 24/48] fix last version check --- .../hooks/pre_copy_last_published_workfile.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index acbc9ec1c7..96b5ccadb2 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -116,19 +116,19 @@ class CopyLastPublishedWorkfile(PreLaunchHook): return # Get workfile representation + last_version_doc = get_last_version_by_subset_id( + project_name, subset_id, fields=["_id"] + ) + if not last_version_doc: + self.log.debug("Subset does not have any versions") + return + workfile_representation = next( ( representation for representation in get_representations( project_name, - version_ids=[ - ( - get_last_version_by_subset_id( - project_name, subset_id, fields=["_id"] - ) - or {} - ).get("_id") - ], + version_ids=[last_version_doc["_id"]] ) if representation["context"]["task"]["name"] == task_name ), From 50afec52223e415c6118999f7f9ac465ed721d3e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:19:03 +0100 Subject: [PATCH 25/48] replaced 'add_sites_to_representations' with 'reset_timer' in rest api --- openpype/modules/sync_server/rest_api.py | 31 +++--------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/openpype/modules/sync_server/rest_api.py b/openpype/modules/sync_server/rest_api.py index 0c3b914833..51769cd4fb 100644 --- a/openpype/modules/sync_server/rest_api.py +++ b/openpype/modules/sync_server/rest_api.py @@ -26,36 +26,11 @@ class SyncServerModuleRestApi: def register(self): self.server_manager.add_route( "POST", - self.prefix + "/add_sites_to_representations", - self.add_sites_to_representations, + self.prefix + "/reset_timer", + self.reset_timer, ) - async def add_sites_to_representations(self, request): - # Extract data from request - data = await request.json() - try: - project_name = data["project_name"] - sites = data["sites"] - representations = data["representations"] - except KeyError: - msg = ( - "Payload must contain fields 'project_name," - " 'sites' (list of names) and 'representations' (list of IDs)" - ) - self.log.error(msg) - return Response(status=400, message=msg) - - # Add all sites to each representation - for representation_id in representations: - for site in sites: - self.module.add_site( - project_name, - representation_id, - site, - force=True, - priority=99, - ) - + async def reset_timer(self, request): # Force timer to run immediately self.module.reset_timer() From 0ca0173e9b48b8cddc6b42dc4360d7bad8649d0f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:19:15 +0100 Subject: [PATCH 26/48] added ability to rese timer from add_site --- openpype/modules/sync_server/sync_server_module.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 4d848958e8..7dc1e15322 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -136,7 +136,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): """ Start of Public API """ def add_site(self, project_name, representation_id, site_name=None, - force=False, priority=None): + force=False, priority=None, reset_timer=False): """ Adds new site to representation to be synced. @@ -171,6 +171,9 @@ class SyncServerModule(OpenPypeModule, ITrayModule): force=force, priority=priority) + if reset_timer: + self.reset_timer() + def remove_site(self, project_name, representation_id, site_name, remove_local_files=False): """ From a137258b1b2a24c503b9db5e47735c9ac9f293d3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:19:32 +0100 Subject: [PATCH 27/48] 'reset_timer' can reset timer via rest api endpoint --- .../modules/sync_server/sync_server_module.py | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 7dc1e15322..26a6abbbf4 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -916,7 +916,42 @@ class SyncServerModule(OpenPypeModule, ITrayModule): In case of user's involvement (reset site), start that right away. """ - self.sync_server_thread.reset_timer() + + if not self.enabled: + return + + if self.sync_server_thread is None: + self._reset_timer_with_rest_api() + else: + self.sync_server_thread.reset_timer() + + def is_representaion_on_site( + self, project_name, representation_id, site_id + ): + # TODO implement + return False + + def _reset_timer_with_rest_api(self): + # POST to webserver sites to add to representations + webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") + if not webserver_url: + self.log.warning("Couldn't find webserver url") + return + + rest_api_url = "{}/sync_server/reset_timer".format( + webserver_url + ) + + try: + import requests + except Exception: + self.log.warning( + "Couldn't add sites to representations " + "('requests' is not available)" + ) + return + + requests.post(rest_api_url) def get_enabled_projects(self): """Returns list of projects which have SyncServer enabled.""" From 109c52809c1f599a4d93b8dc15d99f4836c03e26 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:20:21 +0100 Subject: [PATCH 28/48] updated prelaunch hook with new abilities of sync server --- .../hooks/pre_copy_last_published_workfile.py | 52 +++++-------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 96b5ccadb2..6fd50a64d6 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -141,49 +141,21 @@ class CopyLastPublishedWorkfile(PreLaunchHook): ).format(task_name, host_name) return - # POST to webserver sites to add to representations - webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") - if not webserver_url: - self.log.warning("Couldn't find webserver url") - return - - entry_point_url = "{}/sync_server".format(webserver_url) - rest_api_url = "{}/add_sites_to_representations".format( - entry_point_url - ) - try: - import requests - except Exception: - self.log.warning( - "Couldn't add sites to representations " - "('requests' is not available)" - ) - return - local_site_id = get_local_site_id() - requests.post( - rest_api_url, - json={ - "project_name": project_name, - "sites": [local_site_id], - "representations": [str(workfile_representation["_id"])], - }, + sync_server = self.modules_manager.get("sync_server") + sync_server.add_site( + project_name, + workfile_representation["_id"], + local_site_id, + force=True, + priority=99, + reset_timer=True ) - # Wait for the download loop to end - last_created_time = get_representation_last_created_time_on_site( - workfile_representation, local_site_id - ) - while ( - last_created_time - >= get_representation_last_created_time_on_site( - get_representation_by_id( - project_name, - workfile_representation["_id"], - fields=["files"], - ), - local_site_id, - ) + while not sync_server.is_representaion_on_site( + project_name, + workfile_representation["_id"], + local_site_id ): sleep(5) From e89051466efa9d12b0700b95711b049ab3b944ca Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:22:56 +0100 Subject: [PATCH 29/48] check if is sync server enabled --- openpype/hooks/pre_copy_last_published_workfile.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 6fd50a64d6..69e3d6efe4 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -37,6 +37,12 @@ class CopyLastPublishedWorkfile(PreLaunchHook): Returns: None: This is a void method. """ + + sync_server = self.modules_manager.get("sync_server") + if not sync_server or not sync_server.enabled: + self.log.deubg("Sync server module is not enabled or available") + return + # Check there is no workfile available last_workfile = self.data.get("last_workfile_path") if os.path.exists(last_workfile): @@ -142,7 +148,6 @@ class CopyLastPublishedWorkfile(PreLaunchHook): return local_site_id = get_local_site_id() - sync_server = self.modules_manager.get("sync_server") sync_server.add_site( project_name, workfile_representation["_id"], From 4b1a19f3bf868813f5f2a1749eda8277cd2882ba Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:36:03 +0100 Subject: [PATCH 30/48] removed 'get_representation_last_created_time_on_site' function --- openpype/client/entities.py | 27 ------------------- .../hooks/pre_copy_last_published_workfile.py | 2 -- 2 files changed, 29 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 43c2874f57..91d4b499b0 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -1368,33 +1368,6 @@ def get_representation_parents(project_name, representation): return parents_by_repre_id[repre_id] -def get_representation_last_created_time_on_site( - representation: dict, site_name: str -) -> datetime: - """Get `created_dt` value for representation on site. - - Args: - representation (dict): Representation to get creation date of - site_name (str): Site from which to get the creation date - - Returns: - datetime: Created time of representation on site - """ - created_time = next( - ( - site.get("created_dt") - for site in representation["files"][0].get("sites", []) - if site["name"] == site_name - ), - None, - ) - if created_time: - return created_time - else: - # Use epoch as 'zero' time - return datetime.utcfromtimestamp(0) - - def get_thumbnail_id_from_source(project_name, src_type, src_id): """Receive thumbnail id from source entity. diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 69e3d6efe4..884b0f54b6 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -3,8 +3,6 @@ import shutil from time import sleep from openpype.client.entities import ( get_last_version_by_subset_id, - get_representation_by_id, - get_representation_last_created_time_on_site, get_representations, get_subsets, ) From 189dc17ce3440f9a45d68c631b19be6ddb017907 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Nov 2022 11:51:00 +0100 Subject: [PATCH 31/48] fix typo --- openpype/hooks/pre_copy_last_published_workfile.py | 2 +- openpype/modules/sync_server/sync_server_module.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 884b0f54b6..0e561334e1 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -155,7 +155,7 @@ class CopyLastPublishedWorkfile(PreLaunchHook): reset_timer=True ) - while not sync_server.is_representaion_on_site( + while not sync_server.is_representation_on_site( project_name, workfile_representation["_id"], local_site_id diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 26a6abbbf4..7228f43f84 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -925,7 +925,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): else: self.sync_server_thread.reset_timer() - def is_representaion_on_site( + def is_representation_on_site( self, project_name, representation_id, site_id ): # TODO implement From 21833283b833327b3b079604e389efe23af7f3c8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 9 Nov 2022 12:27:07 +0100 Subject: [PATCH 32/48] added method to check if representation has all files on site --- .../modules/sync_server/sync_server_module.py | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 7228f43f84..dc20e37a12 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -926,10 +926,27 @@ class SyncServerModule(OpenPypeModule, ITrayModule): self.sync_server_thread.reset_timer() def is_representation_on_site( - self, project_name, representation_id, site_id + self, project_name, representation_id, site_name ): - # TODO implement - return False + """Checks if 'representation_id' has all files avail. on 'site_name'""" + representation = get_representation_by_id(project_name, + representation_id, + fields=["_id", "files"]) + if not representation: + return False + + on_site = False + for file_info in representation.get("files", []): + for site in file_info.get("sites", []): + if site["name"] != site_name: + continue + + if (site.get("progress") or site.get("error") or + not site.get("created_dt")): + return False + on_site = True + + return on_site def _reset_timer_with_rest_api(self): # POST to webserver sites to add to representations From d8c7ff2d15b912f00588771cd7aff8effef7f6df Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 9 Nov 2022 12:48:52 +0100 Subject: [PATCH 33/48] small updates to docstrings --- openpype/modules/sync_server/sync_server_module.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index dc20e37a12..9a2ff97ed6 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -143,7 +143,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): 'project_name' must have synchronization enabled (globally or project only) - Used as a API endpoint from outside applications (Loader etc). + Used as an API endpoint from outside applications (Loader etc). Use 'force' to reset existing site. @@ -153,6 +153,8 @@ class SyncServerModule(OpenPypeModule, ITrayModule): site_name (string): name of configured and active site force (bool): reset site if exists priority (int): set priority + reset_timer (bool): if delay timer should be reset, eg. user mark + some representation to be synced manually Throws: SiteAlreadyPresentError - if adding already existing site and @@ -1601,12 +1603,12 @@ class SyncServerModule(OpenPypeModule, ITrayModule): Args: project_name (string): name of project - force to db connection as each file might come from different collection - new_file_id (string): + new_file_id (string): only present if file synced successfully file (dictionary): info about processed file (pulled from DB) representation (dictionary): parent repr of file (from DB) site (string): label ('gdrive', 'S3') error (string): exception message - progress (float): 0-1 of progress of upload/download + progress (float): 0-0.99 of progress of upload/download priority (int): 0-100 set priority Returns: From f111fc3763a2ae2b43330a08c4bf2064ef646cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 9 Nov 2022 17:09:38 +0100 Subject: [PATCH 34/48] clean --- openpype/client/entities.py | 1 - openpype/modules/sync_server/rest_api.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/client/entities.py b/openpype/client/entities.py index 91d4b499b0..43afccf2f1 100644 --- a/openpype/client/entities.py +++ b/openpype/client/entities.py @@ -6,7 +6,6 @@ that has project name as a context (e.g. on 'ProjectEntity'?). + We will need more specific functions doing wery specific queires really fast. """ -from datetime import datetime import re import collections diff --git a/openpype/modules/sync_server/rest_api.py b/openpype/modules/sync_server/rest_api.py index 51769cd4fb..a7d9dd80b7 100644 --- a/openpype/modules/sync_server/rest_api.py +++ b/openpype/modules/sync_server/rest_api.py @@ -30,8 +30,8 @@ class SyncServerModuleRestApi: self.reset_timer, ) - async def reset_timer(self, request): - # Force timer to run immediately + async def reset_timer(self, _request): + """Force timer to run immediately.""" self.module.reset_timer() return Response(status=200) From 8eb704aeb2703c5809f6b236c7ec8f6b24fd2941 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:09:13 +0800 Subject: [PATCH 35/48] Alembic Loader as Arnold Standin --- .../maya/plugins/load/load_abc_to_standin.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 19e60d33da..a192d9c357 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -28,11 +28,10 @@ class AlembicStandinLoader(load.LoaderPlugin): version = context["version"] version_data = version.get("data", {}) - + family = version["data"]["families"] self.log.info("version_data: {}\n".format(version_data)) - + self.log.info("family: {}\n".format(family)) frameStart = version_data.get("frameStart", None) - frameEnd = version_data.get("frameEnd", None) asset = context["asset"]["name"] namespace = namespace or unique_namespace( @@ -48,12 +47,14 @@ class AlembicStandinLoader(load.LoaderPlugin): settings = get_project_settings(os.environ['AVALON_PROJECT']) colors = settings["maya"]["load"]["colors"] fps = legacy_io.Session["AVALON_FPS"] - - c = colors.get('ass') + c = colors.get(family[0]) if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - c[0], c[1], c[2]) + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255) + ) transform_name = label + "_ABC" @@ -72,7 +73,7 @@ class AlembicStandinLoader(load.LoaderPlugin): if frameStart is None: cmds.setAttr(standinShape + ".useFrameExtension", 0) - elif frameStart == 1 and frameEnd == 1: + elif "model" in family: cmds.setAttr(standinShape + ".useFrameExtension", 0) else: From fe47deca3ce1832f00f336051c27f5a4627964d1 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:10:41 +0800 Subject: [PATCH 36/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index a192d9c357..8ce1aee3ac 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -30,7 +30,6 @@ class AlembicStandinLoader(load.LoaderPlugin): version_data = version.get("data", {}) family = version["data"]["families"] self.log.info("version_data: {}\n".format(version_data)) - self.log.info("family: {}\n".format(family)) frameStart = version_data.get("frameStart", None) asset = context["asset"]["name"] @@ -51,10 +50,10 @@ class AlembicStandinLoader(load.LoaderPlugin): if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) - ) + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255) + ) transform_name = label + "_ABC" From bb924595c88fa0268eb3c6e3615ced6af5d6c755 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:11:56 +0800 Subject: [PATCH 37/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 8ce1aee3ac..d93c85f8a4 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -50,10 +50,10 @@ class AlembicStandinLoader(load.LoaderPlugin): if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) - ) + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255) + ) transform_name = label + "_ABC" From 17c3b1f96ae5fefc1bcec3bc43fcbdc8bf8bb4fc Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:13:04 +0800 Subject: [PATCH 38/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index d93c85f8a4..dafe999d9d 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -50,9 +50,9 @@ class AlembicStandinLoader(load.LoaderPlugin): if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255) ) transform_name = label + "_ABC" From 98244c77b08989959f04293a1b54aa43d3b2c67f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:15:11 +0800 Subject: [PATCH 39/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index dafe999d9d..d93c85f8a4 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -50,9 +50,9 @@ class AlembicStandinLoader(load.LoaderPlugin): if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255) ) transform_name = label + "_ABC" From c5547766074b7999ea880b76705c14fba2828cd3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:16:10 +0800 Subject: [PATCH 40/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index d93c85f8a4..8ce1aee3ac 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -50,10 +50,10 @@ class AlembicStandinLoader(load.LoaderPlugin): if c is not None: cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) - ) + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255) + ) transform_name = label + "_ABC" From d73ac24f59490554c20eb89af7675ae3bbcb0496 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 10 Nov 2022 19:19:11 +0800 Subject: [PATCH 41/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 8ce1aee3ac..9583063c7e 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -30,6 +30,7 @@ class AlembicStandinLoader(load.LoaderPlugin): version_data = version.get("data", {}) family = version["data"]["families"] self.log.info("version_data: {}\n".format(version_data)) + self.log.info("family: {}\n".format(family)) frameStart = version_data.get("frameStart", None) asset = context["asset"]["name"] @@ -48,12 +49,12 @@ class AlembicStandinLoader(load.LoaderPlugin): fps = legacy_io.Session["AVALON_FPS"] c = colors.get(family[0]) if c is not None: + r = (float(c[0]) / 255) + g = (float(c[1]) / 255) + b = (float(c[2]) / 255) cmds.setAttr(root + ".useOutlinerColor", 1) cmds.setAttr(root + ".outlinerColor", - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) - ) + r, g, b) transform_name = label + "_ABC" From 8d467b1a96426646498ffb93f2db31e2ab7344c0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Nov 2022 18:52:59 +0100 Subject: [PATCH 42/48] renamed 'CollectAvalonEntities' to 'CollectContextEntities' --- .../hosts/tvpaint/plugins/publish/collect_instance_frames.py | 2 +- openpype/hosts/tvpaint/plugins/publish/validate_marks.py | 2 +- .../plugins/publish/validate_tvpaint_workfile_data.py | 2 +- ...collect_avalon_entities.py => collect_context_entities.py} | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename openpype/plugins/publish/{collect_avalon_entities.py => collect_context_entities.py} (97%) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py b/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py index f291c363b8..d5b79758ad 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_instance_frames.py @@ -6,7 +6,7 @@ class CollectOutputFrameRange(pyblish.api.ContextPlugin): When instances are collected context does not contain `frameStart` and `frameEnd` keys yet. They are collected in global plugin - `CollectAvalonEntities`. + `CollectContextEntities`. """ label = "Collect output frame range" order = pyblish.api.CollectorOrder diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py index 12d50e17ff..0030b0fd1c 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py @@ -39,7 +39,7 @@ class ValidateMarks(pyblish.api.ContextPlugin): def get_expected_data(context): scene_mark_in = context.data["sceneMarkIn"] - # Data collected in `CollectAvalonEntities` + # Data collected in `CollectContextEntities` frame_end = context.data["frameEnd"] frame_start = context.data["frameStart"] handle_start = context.data["handleStart"] diff --git a/openpype/hosts/webpublisher/plugins/publish/validate_tvpaint_workfile_data.py b/openpype/hosts/webpublisher/plugins/publish/validate_tvpaint_workfile_data.py index a5e4868411..d8b7bb9078 100644 --- a/openpype/hosts/webpublisher/plugins/publish/validate_tvpaint_workfile_data.py +++ b/openpype/hosts/webpublisher/plugins/publish/validate_tvpaint_workfile_data.py @@ -13,7 +13,7 @@ class ValidateWorkfileData(pyblish.api.ContextPlugin): targets = ["tvpaint_worker"] def process(self, context): - # Data collected in `CollectAvalonEntities` + # Data collected in `CollectContextEntities` frame_start = context.data["frameStart"] frame_end = context.data["frameEnd"] handle_start = context.data["handleStart"] diff --git a/openpype/plugins/publish/collect_avalon_entities.py b/openpype/plugins/publish/collect_context_entities.py similarity index 97% rename from openpype/plugins/publish/collect_avalon_entities.py rename to openpype/plugins/publish/collect_context_entities.py index 3b05b6ae98..0a6072a820 100644 --- a/openpype/plugins/publish/collect_avalon_entities.py +++ b/openpype/plugins/publish/collect_context_entities.py @@ -16,11 +16,11 @@ from openpype.client import get_project, get_asset_by_name from openpype.pipeline import legacy_io, KnownPublishError -class CollectAvalonEntities(pyblish.api.ContextPlugin): +class CollectContextEntities(pyblish.api.ContextPlugin): """Collect Anatomy into Context.""" order = pyblish.api.CollectorOrder - 0.1 - label = "Collect Avalon Entities" + label = "Collect Context Entities" def process(self, context): legacy_io.install() From 292e8d45c40d4d3af25196dd8e9c979b9aa2a9f9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Nov 2022 18:53:34 +0100 Subject: [PATCH 43/48] get "asset" and "task" from context --- openpype/plugins/publish/collect_context_entities.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/collect_context_entities.py b/openpype/plugins/publish/collect_context_entities.py index 0a6072a820..31fbeb5dbd 100644 --- a/openpype/plugins/publish/collect_context_entities.py +++ b/openpype/plugins/publish/collect_context_entities.py @@ -3,6 +3,8 @@ Requires: session -> AVALON_ASSET context -> projectName + context -> asset + context -> task Provides: context -> projectEntity - Project document from database. @@ -13,20 +15,19 @@ Provides: import pyblish.api from openpype.client import get_project, get_asset_by_name -from openpype.pipeline import legacy_io, KnownPublishError +from openpype.pipeline import KnownPublishError class CollectContextEntities(pyblish.api.ContextPlugin): - """Collect Anatomy into Context.""" + """Collect entities into Context.""" order = pyblish.api.CollectorOrder - 0.1 label = "Collect Context Entities" def process(self, context): - legacy_io.install() project_name = context.data["projectName"] - asset_name = legacy_io.Session["AVALON_ASSET"] - task_name = legacy_io.Session["AVALON_TASK"] + asset_name = context.data["asset"] + task_name = context.data["task"] project_entity = get_project(project_name) if not project_entity: From 91937c6c287f76a93bbbf13b2aa02deb72d74212 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Nov 2022 19:00:27 +0100 Subject: [PATCH 44/48] get "task" from context in anatomy context data --- openpype/plugins/publish/collect_anatomy_context_data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/collect_anatomy_context_data.py b/openpype/plugins/publish/collect_anatomy_context_data.py index 8433816908..55ce8e06f4 100644 --- a/openpype/plugins/publish/collect_anatomy_context_data.py +++ b/openpype/plugins/publish/collect_anatomy_context_data.py @@ -15,7 +15,6 @@ Provides: import json import pyblish.api -from openpype.pipeline import legacy_io from openpype.pipeline.template_data import get_template_data @@ -53,7 +52,7 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin): asset_entity = context.data.get("assetEntity") task_name = None if asset_entity: - task_name = legacy_io.Session["AVALON_TASK"] + task_name = context.data["task"] anatomy_data = get_template_data( project_entity, asset_entity, task_name, host_name, system_settings From 88bf8840bd7757a19a20a460091800c0fc2741bb Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 14 Nov 2022 17:57:32 +0800 Subject: [PATCH 45/48] Alembic Loader as Arnold Standin --- openpype/hosts/maya/plugins/load/load_abc_to_standin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py index 9583063c7e..605a492e4d 100644 --- a/openpype/hosts/maya/plugins/load/load_abc_to_standin.py +++ b/openpype/hosts/maya/plugins/load/load_abc_to_standin.py @@ -98,6 +98,7 @@ class AlembicStandinLoader(load.LoaderPlugin): # Update the standin standins = list() members = pm.sets(container['objectName'], query=True) + self.log.info("container:{}".format(container)) for member in members: shape = member.getShape() if (shape and shape.type() == "aiStandIn"): @@ -105,8 +106,11 @@ class AlembicStandinLoader(load.LoaderPlugin): for standin in standins: standin.dso.set(path) - standin.useFrameExtension.set(0) standin.abcFPS.set(float(fps)) + if "modelMain" in container['objectName']: + standin.useFrameExtension.set(0) + else: + standin.useFrameExtension.set(1) container = pm.PyNode(container["objectName"]) container.representation.set(str(representation["_id"])) From 20ea1c8212a5d9226d162ce5532ad641ad9a2b73 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Nov 2022 18:27:11 +0100 Subject: [PATCH 46/48] ignore case sensitivity of extension in files widget --- openpype/lib/attribute_definitions.py | 7 +++++++ openpype/tools/attribute_defs/files_widget.py | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index 589a4ef9ab..6baeaec045 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -541,6 +541,13 @@ class FileDefItem(object): return ext return None + @property + def lower_ext(self): + ext = self.ext + if ext is not None: + return ext.lower() + return ext + @property def is_dir(self): if self.is_empty: diff --git a/openpype/tools/attribute_defs/files_widget.py b/openpype/tools/attribute_defs/files_widget.py index 3f1e6a34e1..738e50ba07 100644 --- a/openpype/tools/attribute_defs/files_widget.py +++ b/openpype/tools/attribute_defs/files_widget.py @@ -349,7 +349,7 @@ class FilesModel(QtGui.QStandardItemModel): item.setData(file_item.filenames, FILENAMES_ROLE) item.setData(file_item.directory, DIRPATH_ROLE) item.setData(icon_pixmap, ITEM_ICON_ROLE) - item.setData(file_item.ext, EXT_ROLE) + item.setData(file_item.lower_ext, EXT_ROLE) item.setData(file_item.is_dir, IS_DIR_ROLE) item.setData(file_item.is_sequence, IS_SEQUENCE_ROLE) @@ -463,7 +463,7 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel): for filepath in filepaths: if os.path.isfile(filepath): _, ext = os.path.splitext(filepath) - if ext in self._allowed_extensions: + if ext.lower() in self._allowed_extensions: return True elif self._allow_folders: @@ -475,7 +475,7 @@ class FilesProxyModel(QtCore.QSortFilterProxyModel): for filepath in filepaths: if os.path.isfile(filepath): _, ext = os.path.splitext(filepath) - if ext in self._allowed_extensions: + if ext.lower() in self._allowed_extensions: filtered_paths.append(filepath) elif self._allow_folders: From 088e7507b3144a7deb3cafe4b51286649e446079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 15 Nov 2022 09:06:09 +0100 Subject: [PATCH 47/48] logging format --- .../hooks/pre_copy_last_published_workfile.py | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/openpype/hooks/pre_copy_last_published_workfile.py b/openpype/hooks/pre_copy_last_published_workfile.py index 0e561334e1..44144e5fff 100644 --- a/openpype/hooks/pre_copy_last_published_workfile.py +++ b/openpype/hooks/pre_copy_last_published_workfile.py @@ -75,16 +75,20 @@ class CopyLastPublishedWorkfile(PreLaunchHook): self.log.info( ( "Seems like old version of settings is used." - ' Can\'t access custom templates in host "{}".' - ).format(host_name) + ' Can\'t access custom templates in host "{}".'.format( + host_name + ) + ) ) return elif use_last_published_workfile is False: self.log.info( ( 'Project "{}" has turned off to use last published' - ' workfile as first workfile for host "{}"' - ).format(project_name, host_name) + ' workfile as first workfile for host "{}"'.format( + project_name, host_name + ) + ) ) return @@ -114,8 +118,8 @@ class CopyLastPublishedWorkfile(PreLaunchHook): None, ) if not subset_id: - self.log.debug('No any workfile for asset "{}".').format( - asset_doc["name"] + self.log.debug( + 'No any workfile for asset "{}".'.format(asset_doc["name"]) ) return @@ -131,8 +135,7 @@ class CopyLastPublishedWorkfile(PreLaunchHook): ( representation for representation in get_representations( - project_name, - version_ids=[last_version_doc["_id"]] + project_name, version_ids=[last_version_doc["_id"]] ) if representation["context"]["task"]["name"] == task_name ), @@ -141,8 +144,10 @@ class CopyLastPublishedWorkfile(PreLaunchHook): if not workfile_representation: self.log.debug( - 'No published workfile for task "{}" and host "{}".' - ).format(task_name, host_name) + 'No published workfile for task "{}" and host "{}".'.format( + task_name, host_name + ) + ) return local_site_id = get_local_site_id() @@ -152,13 +157,11 @@ class CopyLastPublishedWorkfile(PreLaunchHook): local_site_id, force=True, priority=99, - reset_timer=True + reset_timer=True, ) while not sync_server.is_representation_on_site( - project_name, - workfile_representation["_id"], - local_site_id + project_name, workfile_representation["_id"], local_site_id ): sleep(5) From 3b9662f97cfb19be8a44b95e16fa50785e22ea21 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Nov 2022 13:26:22 +0100 Subject: [PATCH 48/48] added settings for validate frame range in tray publisher --- .../project_settings/traypublisher.json | 7 +++++ .../schema_project_traypublisher.json | 18 +++++++++++++ .../schemas/template_validate_plugin.json | 26 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 openpype/settings/entities/schemas/projects_schema/schemas/template_validate_plugin.json diff --git a/openpype/settings/defaults/project_settings/traypublisher.json b/openpype/settings/defaults/project_settings/traypublisher.json index 5db2a79772..e99b96b8c4 100644 --- a/openpype/settings/defaults/project_settings/traypublisher.json +++ b/openpype/settings/defaults/project_settings/traypublisher.json @@ -303,5 +303,12 @@ "extensions": [ ".mov" ] + }, + "publish": { + "ValidateFrameRange": { + "enabled": true, + "optional": true, + "active": true + } } } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json b/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json index 7c61aeed50..faa5033d2a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_traypublisher.json @@ -311,6 +311,24 @@ "object_type": "text" } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "children": [ + { + "type": "schema_template", + "name": "template_validate_plugin", + "template_data": [ + { + "key": "ValidateFrameRange", + "label": "Validate frame range" + } + ] + } + ] } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/template_validate_plugin.json b/openpype/settings/entities/schemas/projects_schema/schemas/template_validate_plugin.json new file mode 100644 index 0000000000..b57cad6719 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schemas/template_validate_plugin.json @@ -0,0 +1,26 @@ +[ + { + "type": "dict", + "collapsible": true, + "key": "{key}", + "label": "{label}", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + } + ] + } +]