From d9ea84777c4484e2bcb774f904b173ae08f3e532 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 30 Mar 2020 15:50:36 +0200 Subject: [PATCH 01/13] environment AVALON_PROJECT is sent to publish job an metadata json stars with {root} --- pype/plugins/global/publish/collect_rendered_files.py | 3 +++ pype/plugins/global/publish/submit_publish_job.py | 3 ++- pype/scripts/publish_filesequence.py | 6 ------ 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pype/plugins/global/publish/collect_rendered_files.py b/pype/plugins/global/publish/collect_rendered_files.py index 552fd49f6d..7ae68f8e44 100644 --- a/pype/plugins/global/publish/collect_rendered_files.py +++ b/pype/plugins/global/publish/collect_rendered_files.py @@ -84,6 +84,9 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): session_set = False for path in paths: + path = path.format(**{ + "root": os.environ["PYPE_STUDIO_PROJECTS_PATH"] + }) data = self._load_json(path) if not session_set: self.log.info("Setting session using data from file") diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index dcf19ae32c..c7124d73a5 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -195,7 +195,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): mount_root = os.path.normpath(os.environ["PYPE_STUDIO_PROJECTS_MOUNT"]) network_root = os.environ["PYPE_STUDIO_PROJECTS_PATH"] metadata_path = metadata_path.replace(mount_root, network_root) - metadata_path = os.path.normpath(metadata_path) + metadata_path = metadata_path.replace(network_root, "{root}") # Generate the payload for Deadline submission payload = { @@ -225,6 +225,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): environment = job["Props"].get("Env", {}) environment["PYPE_METADATA_FILE"] = metadata_path + environment["AVALON_PROJECT"] = io.Session["AVALON_PROJECT"] i = 0 for index, key in enumerate(environment): if key.upper() in self.enviro_filter: diff --git a/pype/scripts/publish_filesequence.py b/pype/scripts/publish_filesequence.py index fe795564a5..168011391a 100644 --- a/pype/scripts/publish_filesequence.py +++ b/pype/scripts/publish_filesequence.py @@ -90,12 +90,6 @@ def __main__(): paths = kwargs.paths or [os.environ.get("PYPE_METADATA_FILE")] or [os.getcwd()] # noqa - for path in paths: - data = _load_json(path) - log.info("Setting session using data from file") - os.environ["AVALON_PROJECT"] = data["session"]["AVALON_PROJECT"] - break - args = [ os.path.join(pype_root, pype_command), "publish", From 66f7367524dbb3d749cf6564fbd48fbe00b74a34 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 30 Mar 2020 17:35:16 +0200 Subject: [PATCH 02/13] minor refactor --- ...ract_quicktime.py => extract_playblast.py} | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) rename pype/plugins/maya/publish/{extract_quicktime.py => extract_playblast.py} (87%) diff --git a/pype/plugins/maya/publish/extract_quicktime.py b/pype/plugins/maya/publish/extract_playblast.py similarity index 87% rename from pype/plugins/maya/publish/extract_quicktime.py rename to pype/plugins/maya/publish/extract_playblast.py index 29d6b78051..cc307d6afc 100644 --- a/pype/plugins/maya/publish/extract_quicktime.py +++ b/pype/plugins/maya/publish/extract_playblast.py @@ -3,24 +3,23 @@ import glob import contextlib import clique import capture -# + import pype.maya.lib as lib import pype.api -# -from maya import cmds, mel + +from maya import cmds import pymel.core as pm -# TODO: move codec settings to presets -class ExtractQuicktime(pype.api.Extractor): - """Extract Quicktime from viewport capture. +class ExtractPlayblast(pype.api.Extractor): + """Extract viewport playblast. Takes review camera and creates review Quicktime video based on viewport capture. """ - label = "Quicktime" + label = "Extract Playblast" hosts = ["maya"] families = ["review"] optional = True @@ -29,7 +28,7 @@ class ExtractQuicktime(pype.api.Extractor): self.log.info("Extracting capture..") # get scene fps - fps = mel.eval('currentTimeUnitToFPS()') + fps = instance.data.get("fps") or instance.context.data.get("fps") # if start and end frames cannot be determined, get them # from Maya timeline @@ -39,6 +38,7 @@ class ExtractQuicktime(pype.api.Extractor): start = cmds.playbackOptions(query=True, animationStartTime=True) if end is None: end = cmds.playbackOptions(query=True, animationEndTime=True) + self.log.info("start: {}, end: {}".format(start, end)) # get cameras @@ -47,7 +47,7 @@ class ExtractQuicktime(pype.api.Extractor): try: preset = lib.load_capture_preset(data=capture_preset) - except: + except Exception: preset = {} self.log.info('using viewport preset: {}'.format(preset)) @@ -55,19 +55,10 @@ class ExtractQuicktime(pype.api.Extractor): preset['format'] = "image" # preset['compression'] = "qt" preset['quality'] = 95 - preset['compression'] = "jpg" + preset['compression'] = "png" preset['start_frame'] = start preset['end_frame'] = end preset['camera_options'] = { - "displayGateMask": False, - "displayResolution": False, - "displayFilmGate": False, - "displayFieldChart": False, - "displaySafeAction": False, - "displaySafeTitle": False, - "displayFilmPivot": False, - "displayFilmOrigin": False, - "overscan": 1.0, "depthOfField": cmds.getAttr("{0}.depthOfField".format(camera)), } @@ -90,8 +81,8 @@ class ExtractQuicktime(pype.api.Extractor): filename = preset.get("filename", "%TEMP%") # Force viewer to False in call to capture because we have our own - # viewer opening call to allow a signal to trigger between playblast - # and viewer + # viewer opening call to allow a signal to trigger between + # playblast and viewer preset['viewer'] = False # Remove panel key since it's internal value to capture_gui @@ -112,8 +103,8 @@ class ExtractQuicktime(pype.api.Extractor): instance.data["representations"] = [] representation = { - 'name': 'mov', - 'ext': 'mov', + 'name': 'png', + 'ext': 'png', 'files': collected_frames, "stagingDir": stagingdir, "frameStart": start, @@ -133,7 +124,6 @@ class ExtractQuicktime(pype.api.Extractor): To workaround this we just glob.glob() for any file extensions and assume the latest modified file is the correct file and return it. - """ # Catch cancelled playblast if filepath is None: @@ -164,7 +154,6 @@ class ExtractQuicktime(pype.api.Extractor): return filepath - @contextlib.contextmanager def maintained_time(): ct = cmds.currentTime(query=True) From 3c70a91c3357ae95a169783474568099fe836f17 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 30 Mar 2020 23:25:55 +0200 Subject: [PATCH 03/13] bump version --- pype/__init__.py | 1 - pype/version.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pype/version.py diff --git a/pype/__init__.py b/pype/__init__.py index 5cd9832558..803d3fa60e 100644 --- a/pype/__init__.py +++ b/pype/__init__.py @@ -9,7 +9,6 @@ from pypeapp import config import logging log = logging.getLogger(__name__) -__version__ = "2.6.0" PROJECT_PLUGINS_PATH = os.environ.get("PYPE_PROJECT_PLUGINS") PACKAGE_DIR = os.path.dirname(__file__) diff --git a/pype/version.py b/pype/version.py new file mode 100644 index 0000000000..2614ce9d96 --- /dev/null +++ b/pype/version.py @@ -0,0 +1 @@ +__version__ = "2.7.0" From 12bea639ebd5a0a9dbaed87bad255faeed83ed89 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Apr 2020 17:25:43 +0200 Subject: [PATCH 04/13] preserve mb file type during publish --- pype/plugins/maya/publish/collect_scene.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pype/plugins/maya/publish/collect_scene.py b/pype/plugins/maya/publish/collect_scene.py index 089019f2d3..3f5760fd7b 100644 --- a/pype/plugins/maya/publish/collect_scene.py +++ b/pype/plugins/maya/publish/collect_scene.py @@ -1,5 +1,3 @@ -from maya import cmds - import pyblish.api import avalon.api import os @@ -42,8 +40,8 @@ class CollectMayaScene(pyblish.api.ContextPlugin): }) data['representations'] = [{ - 'name': 'ma', - 'ext': 'ma', + 'name': ext.lstrip("."), + 'ext': ext.lstrip("."), 'files': file, "stagingDir": folder, }] From 17622b046d3e4fd814ad45d4f2e4cdd554d6a39a Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Apr 2020 19:16:54 +0200 Subject: [PATCH 05/13] fixed camera options --- pype/plugins/maya/publish/extract_playblast.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/plugins/maya/publish/extract_playblast.py b/pype/plugins/maya/publish/extract_playblast.py index cc307d6afc..579712018c 100644 --- a/pype/plugins/maya/publish/extract_playblast.py +++ b/pype/plugins/maya/publish/extract_playblast.py @@ -58,9 +58,9 @@ class ExtractPlayblast(pype.api.Extractor): preset['compression'] = "png" preset['start_frame'] = start preset['end_frame'] = end - preset['camera_options'] = { - "depthOfField": cmds.getAttr("{0}.depthOfField".format(camera)), - } + camera_option = preset.get("camera_option", {}) + camera_option["depthOfField"] = cmds.getAttr( + "{0}.depthOfField".format(camera)) stagingdir = self.staging_dir(instance) filename = "{0}".format(instance.name) From 7763b339927784fdb101daad5386347ae29428e1 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 2 Apr 2020 16:27:25 +0200 Subject: [PATCH 06/13] make sure we don't error on missing handles in instance --- pype/plugins/maya/publish/collect_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/collect_review.py b/pype/plugins/maya/publish/collect_review.py index 9b6027b98d..063a854bd1 100644 --- a/pype/plugins/maya/publish/collect_review.py +++ b/pype/plugins/maya/publish/collect_review.py @@ -60,7 +60,7 @@ class CollectReview(pyblish.api.InstancePlugin): data['frameEndHandle'] = instance.data["frameEndHandle"] data["frameStart"] = instance.data["frameStart"] data["frameEnd"] = instance.data["frameEnd"] - data['handles'] = instance.data['handles'] + data['handles'] = instance.data.get('handles', None) data['step'] = instance.data['step'] data['fps'] = instance.data['fps'] cmds.setAttr(str(instance) + '.active', 1) From f81e2e9ca3c5ff119a60e09b51f3027f40f9265f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Thu, 2 Apr 2020 18:10:40 +0200 Subject: [PATCH 07/13] Update validate_rig_output_ids.py --- pype/plugins/maya/publish/validate_rig_output_ids.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/validate_rig_output_ids.py b/pype/plugins/maya/publish/validate_rig_output_ids.py index aefe883149..b6b3735196 100644 --- a/pype/plugins/maya/publish/validate_rig_output_ids.py +++ b/pype/plugins/maya/publish/validate_rig_output_ids.py @@ -38,7 +38,7 @@ class ValidateRigOutputIds(pyblish.api.InstancePlugin): if compute: out_set = next(x for x in instance if x.endswith("out_SET")) instance_nodes = pc.sets(out_set, query=True) - instance_nodes.extend([x.getShape() for x in instance_nodes]) + instance_nodes.extend([x.getShape() for x in instance_nodes if x.getShape()]) scene_nodes = pc.ls(type="transform") + pc.ls(type="mesh") scene_nodes = set(scene_nodes) - set(instance_nodes) From 305ba756ba7ad52e0db200ff3718d4df4eb773b8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Apr 2020 11:32:13 +0200 Subject: [PATCH 08/13] Subprocess in app launch is again stored to variable. Now it is `_popen` and added comment why it is there. --- pype/ftrack/lib/ftrack_app_handler.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pype/ftrack/lib/ftrack_app_handler.py b/pype/ftrack/lib/ftrack_app_handler.py index b5576ae046..56196d15f9 100644 --- a/pype/ftrack/lib/ftrack_app_handler.py +++ b/pype/ftrack/lib/ftrack_app_handler.py @@ -290,7 +290,10 @@ class AppAction(BaseHandler): # Run SW if was found executable if execfile is not None: - avalonlib.launch( + # Store subprocess to varaible. This is due to Blender launch + # bug. Please make sure Blender >=2.81 can be launched before + # remove `_popen` variable. + _popen = avalonlib.launch( executable=execfile, args=[], environment=env ) else: @@ -323,7 +326,7 @@ class AppAction(BaseHandler): 'message': "No executable permission - {}".format( execfile) } - pass + else: self.log.error('Launcher doesn\'t exist - {}'.format( execfile)) @@ -331,10 +334,13 @@ class AppAction(BaseHandler): 'success': False, 'message': "Launcher doesn't exist - {}".format(execfile) } - pass + # Run SW if was found executable if execfile is not None: - avalonlib.launch( + # Store subprocess to varaible. This is due to Blender launch + # bug. Please make sure Blender >=2.81 can be launched before + # remove `_popen` variable. + _popen = avalonlib.launch( '/usr/bin/env', args=['bash', execfile], environment=env ) else: @@ -343,7 +349,6 @@ class AppAction(BaseHandler): 'message': "We didn't found launcher for {0}" .format(self.label) } - pass # Change status of task to In progress presets = config.get_presets()["ftrack"]["ftrack_config"] From 5109ea9a9047db412c2cf199dca9790910d395ac Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 3 Apr 2020 15:32:13 +0200 Subject: [PATCH 09/13] fixed missing import --- pype/plugins/maya/publish/collect_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/collect_scene.py b/pype/plugins/maya/publish/collect_scene.py index bbaaa95b18..b94707fc7f 100644 --- a/pype/plugins/maya/publish/collect_scene.py +++ b/pype/plugins/maya/publish/collect_scene.py @@ -1,7 +1,7 @@ import pyblish.api import avalon.api import os -from pype.maya import lib +from pype.maya import cmds class CollectMayaScene(pyblish.api.ContextPlugin): From f9655b0e2156b8d151436a9c575f56a3c5221aaf Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 3 Apr 2020 19:47:12 +0200 Subject: [PATCH 10/13] fixed handling of fursettings and representation paths --- pype/plugins/maya/load/load_yeti_cache.py | 35 +++++++++++++++---- .../maya/publish/collect_yeti_cache.py | 4 +++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pype/plugins/maya/load/load_yeti_cache.py b/pype/plugins/maya/load/load_yeti_cache.py index a1793cd67d..286b154ca2 100644 --- a/pype/plugins/maya/load/load_yeti_cache.py +++ b/pype/plugins/maya/load/load_yeti_cache.py @@ -6,10 +6,11 @@ from collections import defaultdict from maya import cmds -from avalon import api +from avalon import api, io from avalon.maya import lib as avalon_lib, pipeline from pype.maya import lib from pypeapp import config +from pprint import pprint class YetiCacheLoader(api.Loader): @@ -101,12 +102,26 @@ class YetiCacheLoader(api.Loader): def update(self, container, representation): + io.install() namespace = container["namespace"] container_node = container["objectName"] + # import web_pdb; web_pdb.set_trace() + + fur_settings = io.find_one( + {"parent": representation["parent"], "name": "fursettings"} + ) + + pprint({"parent": representation["parent"], "name": "fursettings"}) + pprint(fur_settings) + assert fur_settings is not None, ( + "cannot find fursettings representation" + ) + + settings_fname = api.get_representation_path(fur_settings) path = api.get_representation_path(representation) # Get all node data - fname, ext = os.path.splitext(path) - settings_fname = "{}.fursettings".format(fname) + # fname, ext = os.path.splitext(path) + # settings_fname = "{}.fursettings".format(fname) with open(settings_fname, "r") as fp: settings = json.load(fp) @@ -147,13 +162,14 @@ class YetiCacheLoader(api.Loader): cmds.delete(to_remove) + # replace frame in filename with %04d + RE_frame = re.compile(r"(\d+)(\.fur)$") + file_name = re.sub(RE_frame, r"%04d\g<2>", os.path.basename(path)) for cb_id, data in meta_data_lookup.items(): # Update cache file name - file_name = data["name"].replace(":", "_") - cache_file_path = "{}.%04d.fur".format(file_name) data["attrs"]["cacheFileName"] = os.path.join( - path, cache_file_path) + os.path.dirname(path), file_name) if cb_id not in scene_lookup: @@ -197,6 +213,13 @@ class YetiCacheLoader(api.Loader): yeti_node = yeti_nodes[0] for attr, value in data["attrs"].items(): + # import web_pdb; web_pdb.set_trace() + # handle empty attribute strings. Those are reported + # as None, so their type is NoneType and this is not + # supported on attributes in Maya. We change it to + # empty string. + if value is None: + value = "" lib.set_attribute(attr, value, yeti_node) cmds.setAttr("{}.representation".format(container_node), diff --git a/pype/plugins/maya/publish/collect_yeti_cache.py b/pype/plugins/maya/publish/collect_yeti_cache.py index 2365162c05..c5300ff0ee 100644 --- a/pype/plugins/maya/publish/collect_yeti_cache.py +++ b/pype/plugins/maya/publish/collect_yeti_cache.py @@ -49,6 +49,10 @@ class CollectYetiCache(pyblish.api.InstancePlugin): attr_data = {} for attr in SETTINGS: current = cmds.getAttr("%s.%s" % (shape, attr)) + # change None to empty string as Maya doesn't support + # NoneType in attributes + if current is None: + current = "" attr_data[attr] = current # Get transform data From 4bd3d0652e9b56ccbdf1c929d0e05c891b3ac666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 3 Apr 2020 19:54:14 +0200 Subject: [PATCH 11/13] shutting up hound --- pype/plugins/maya/publish/validate_rig_output_ids.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pype/plugins/maya/publish/validate_rig_output_ids.py b/pype/plugins/maya/publish/validate_rig_output_ids.py index b6b3735196..89cd37fe64 100644 --- a/pype/plugins/maya/publish/validate_rig_output_ids.py +++ b/pype/plugins/maya/publish/validate_rig_output_ids.py @@ -38,7 +38,8 @@ class ValidateRigOutputIds(pyblish.api.InstancePlugin): if compute: out_set = next(x for x in instance if x.endswith("out_SET")) instance_nodes = pc.sets(out_set, query=True) - instance_nodes.extend([x.getShape() for x in instance_nodes if x.getShape()]) + instance_nodes.extend( + [x.getShape() for x in instance_nodes if x.getShape()]) scene_nodes = pc.ls(type="transform") + pc.ls(type="mesh") scene_nodes = set(scene_nodes) - set(instance_nodes) From 3555a008ee8a38461ffb77fcbce04387cb8d8616 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 6 Apr 2020 13:18:13 +0200 Subject: [PATCH 12/13] revert metadata root replacement --- pype/plugins/global/publish/collect_rendered_files.py | 3 --- pype/plugins/global/publish/submit_publish_job.py | 1 - 2 files changed, 4 deletions(-) diff --git a/pype/plugins/global/publish/collect_rendered_files.py b/pype/plugins/global/publish/collect_rendered_files.py index 9f4c84ec07..8ecf7ba156 100644 --- a/pype/plugins/global/publish/collect_rendered_files.py +++ b/pype/plugins/global/publish/collect_rendered_files.py @@ -84,9 +84,6 @@ class CollectRenderedFiles(pyblish.api.ContextPlugin): session_set = False for path in paths: - path = path.format(**{ - "root": os.environ["PYPE_STUDIO_PROJECTS_PATH"] - }) data = self._load_json(path) if not session_set: self.log.info("Setting session using data from file") diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index cac808b0b0..a6f2d5d79b 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -195,7 +195,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): mount_root = os.path.normpath(os.environ["PYPE_STUDIO_PROJECTS_MOUNT"]) network_root = os.environ["PYPE_STUDIO_PROJECTS_PATH"] metadata_path = metadata_path.replace(mount_root, network_root) - metadata_path = metadata_path.replace(network_root, "{root}") # Generate the payload for Deadline submission payload = { From 05b33a20ce2e9cd905a92665f0f9f2339d1aedc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 8 Apr 2020 11:45:51 +0200 Subject: [PATCH 13/13] code cleanup --- pype/plugins/maya/load/load_yeti_cache.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pype/plugins/maya/load/load_yeti_cache.py b/pype/plugins/maya/load/load_yeti_cache.py index 286b154ca2..412c2bd558 100644 --- a/pype/plugins/maya/load/load_yeti_cache.py +++ b/pype/plugins/maya/load/load_yeti_cache.py @@ -105,7 +105,6 @@ class YetiCacheLoader(api.Loader): io.install() namespace = container["namespace"] container_node = container["objectName"] - # import web_pdb; web_pdb.set_trace() fur_settings = io.find_one( {"parent": representation["parent"], "name": "fursettings"} @@ -120,8 +119,6 @@ class YetiCacheLoader(api.Loader): settings_fname = api.get_representation_path(fur_settings) path = api.get_representation_path(representation) # Get all node data - # fname, ext = os.path.splitext(path) - # settings_fname = "{}.fursettings".format(fname) with open(settings_fname, "r") as fp: settings = json.load(fp) @@ -213,7 +210,6 @@ class YetiCacheLoader(api.Loader): yeti_node = yeti_nodes[0] for attr, value in data["attrs"].items(): - # import web_pdb; web_pdb.set_trace() # handle empty attribute strings. Those are reported # as None, so their type is NoneType and this is not # supported on attributes in Maya. We change it to