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/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"] diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 60e8aceb75..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 = os.path.normpath(metadata_path) # Generate the payload for Deadline submission payload = { @@ -224,8 +223,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # job so they use the same environment environment = job["Props"].get("Env", {}) environment["PYPE_METADATA_FILE"] = metadata_path - environment["AVALON_PROJECT"] = api.Session.get("AVALON_PROJECT") - + 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/plugins/maya/load/load_yeti_cache.py b/pype/plugins/maya/load/load_yeti_cache.py index a1793cd67d..412c2bd558 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,23 @@ class YetiCacheLoader(api.Loader): def update(self, container, representation): + io.install() namespace = container["namespace"] container_node = container["objectName"] + + 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) with open(settings_fname, "r") as fp: settings = json.load(fp) @@ -147,13 +159,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 +210,12 @@ class YetiCacheLoader(api.Loader): yeti_node = yeti_nodes[0] for attr, value in data["attrs"].items(): + # 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_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) diff --git a/pype/plugins/maya/publish/collect_scene.py b/pype/plugins/maya/publish/collect_scene.py index e6976356e8..b94707fc7f 100644 --- a/pype/plugins/maya/publish/collect_scene.py +++ b/pype/plugins/maya/publish/collect_scene.py @@ -1,9 +1,7 @@ -from maya import cmds - import pyblish.api import avalon.api import os -from pype.maya import lib +from pype.maya import cmds class CollectMayaScene(pyblish.api.ContextPlugin): @@ -44,8 +42,8 @@ class CollectMayaScene(pyblish.api.ContextPlugin): }) data['representations'] = [{ - 'name': 'ma', - 'ext': 'ma', + 'name': ext.lstrip("."), + 'ext': ext.lstrip("."), 'files': file, "stagingDir": folder, }] 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 diff --git a/pype/plugins/maya/publish/extract_quicktime.py b/pype/plugins/maya/publish/extract_playblast.py similarity index 85% rename from pype/plugins/maya/publish/extract_quicktime.py rename to pype/plugins/maya/publish/extract_playblast.py index 29d6b78051..579712018c 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,21 +55,12 @@ 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)), - } + 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) @@ -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) diff --git a/pype/plugins/maya/publish/validate_rig_output_ids.py b/pype/plugins/maya/publish/validate_rig_output_ids.py index aefe883149..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]) + 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) 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"