From 8b1a74547a893a8b8a4388a6cf35b5808a07acc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:01:55 +0100 Subject: [PATCH 01/71] frames:v argument is used only if output is sequence --- pype/plugins/global/publish/extract_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index d08748209d..75d7682dd4 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -387,7 +387,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ffmpeg_output_args.append("-t {:0.2f}".format(duration_sec)) # Set frame range of output when input or output is sequence - elif temp_data["input_is_sequence"] or temp_data["output_is_sequence"]: + elif temp_data["output_is_sequence"]: ffmpeg_output_args.append("-frames:v {}".format(output_frames_len)) # Add video/image input path From 86a2256a8f0dfd4ef14afdfeabe639453355fe6c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:03:57 +0100 Subject: [PATCH 02/71] add duration of input in seconds is is sequence and output is video --- pype/plugins/global/publish/extract_review.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 75d7682dd4..2340253066 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -348,6 +348,8 @@ class ExtractReview(pyblish.api.InstancePlugin): + 1 ) + duration_seconds = float(output_frames_len / temp_data["fps"]) + if temp_data["input_is_sequence"]: # Set start frame of input sequence (just frame in filename) # - definition of input filepath @@ -375,21 +377,30 @@ class ExtractReview(pyblish.api.InstancePlugin): # Change output's duration and start point if should not contain # handles + start_sec = 0 if temp_data["without_handles"] and temp_data["handles_are_set"]: # Set start time without handles # - check if handle_start is bigger than 0 to avoid zero division if temp_data["handle_start"] > 0: start_sec = float(temp_data["handle_start"]) / temp_data["fps"] - ffmpeg_input_args.append("-ss {:0.2f}".format(start_sec)) + ffmpeg_input_args.append("-ss {:0.10f}".format(start_sec)) # Set output duration inn seconds - duration_sec = float(output_frames_len / temp_data["fps"]) - ffmpeg_output_args.append("-t {:0.2f}".format(duration_sec)) + ffmpeg_output_args.append("-t {:0.10}".format(duration_seconds)) # Set frame range of output when input or output is sequence elif temp_data["output_is_sequence"]: ffmpeg_output_args.append("-frames:v {}".format(output_frames_len)) + # Add duration of an input sequence if output is video + if ( + temp_data["input_is_sequence"] + and not temp_data["output_is_sequence"] + ): + ffmpeg_input_args.append("-to {:0.10f}".format( + duration_seconds + start_sec + )) + # Add video/image input path ffmpeg_input_args.append( "-i \"{}\"".format(temp_data["full_input_path"]) From e7ed093bfaefe641ed2b76e55e23a70e8626c940 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:04:17 +0100 Subject: [PATCH 03/71] set audio duration to full length of output --- pype/plugins/global/publish/extract_review.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 2340253066..9bdc2103b1 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -412,7 +412,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # Add audio arguments if there are any. Skipped when output are images. if not temp_data["output_ext_is_image"] and temp_data["with_audio"]: audio_in_args, audio_filters, audio_out_args = self.audio_args( - instance, temp_data + instance, temp_data, duration_seconds ) ffmpeg_input_args.extend(audio_in_args) ffmpeg_audio_filters.extend(audio_filters) @@ -627,7 +627,7 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("Input path {}".format(full_input_path)) self.log.debug("Output path {}".format(full_output_path)) - def audio_args(self, instance, temp_data): + def audio_args(self, instance, temp_data, duration_seconds): """Prepares FFMpeg arguments for audio inputs.""" audio_in_args = [] audio_filters = [] @@ -650,11 +650,19 @@ class ExtractReview(pyblish.api.InstancePlugin): audio_in_args.append( "-ss {}".format(offset_seconds) ) + elif offset_seconds < 0: audio_in_args.append( "-itsoffset {}".format(abs(offset_seconds)) ) + # Audio duration is offset from `-ss` + audio_duration = duration_seconds + offset_seconds + + # Set audio duration + audio_in_args.append("-to {:0.10f}".format(audio_duration)) + + # Add audio input path audio_in_args.append("-i \"{}\"".format(audio["filename"])) # NOTE: These were changed from input to output arguments. From ef2b8ac1320cd42c8529d3e6714c703f3c53b62c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 14:04:26 +0100 Subject: [PATCH 04/71] shortest is not needed anymore --- pype/plugins/global/publish/extract_review.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 9bdc2103b1..aa8d8accb5 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -406,9 +406,6 @@ class ExtractReview(pyblish.api.InstancePlugin): "-i \"{}\"".format(temp_data["full_input_path"]) ) - # Use shortest input - ffmpeg_output_args.append("-shortest") - # Add audio arguments if there are any. Skipped when output are images. if not temp_data["output_ext_is_image"] and temp_data["with_audio"]: audio_in_args, audio_filters, audio_out_args = self.audio_args( From 8fb95d5c52054740cc0b050ebd8673a1ff1fd8e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 15:52:40 +0100 Subject: [PATCH 05/71] collector collect context from workfile metadata and update it (except project name) --- .../tvpaint/publish/collect_workfile_data.py | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/pype/plugins/tvpaint/publish/collect_workfile_data.py b/pype/plugins/tvpaint/publish/collect_workfile_data.py index 31fd97ced4..e26e45a1e4 100644 --- a/pype/plugins/tvpaint/publish/collect_workfile_data.py +++ b/pype/plugins/tvpaint/publish/collect_workfile_data.py @@ -1,6 +1,8 @@ +import os import json import pyblish.api +import avalon.api from avalon.tvpaint import pipeline, lib @@ -10,26 +12,58 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): hosts = ["tvpaint"] def process(self, context): + # Collect and store current context to have reference + current_context = { + "project": avalon.api.Session["AVALON_PROJECT"], + "asset": avalon.api.Session["AVALON_ASSET"], + "task": avalon.api.Session["AVALON_TASK"] + } + context.data["previous_context"] = current_context + self.log.debug("Current context is: {}".format(current_context)) + + # Collect context from workfile metadata + self.log.info("Collecting workfile context") + workfile_context = pipeline.get_current_workfile_context() + if workfile_context: + # Change current context with context from workfile + key_map = (("AVALON_ASSET", "asset"), ("AVALON_TASK", "task")) + for env_key, key in key_map: + avalon.api.Session[env_key] = workfile_context[key] + os.environ[env_key] = workfile_context[key] + else: + # Handle older workfiles or workfiles without metadata + self.log.warning( + "Workfile does not contain information about context." + " Using current Session context." + ) + workfile_context = current_context.copy() + + context.data["workfile_context"] = workfile_context + self.log.info("Context changed to: {}".format(workfile_context)) + + # Collect instances self.log.info("Collecting instance data from workfile") instance_data = pipeline.list_instances() + context.data["workfileInstances"] = instance_data self.log.debug( "Instance data:\"{}".format(json.dumps(instance_data, indent=4)) ) - context.data["workfileInstances"] = instance_data + # Collect information about layers self.log.info("Collecting layers data from workfile") layers_data = lib.layers_data() + context.data["layersData"] = layers_data self.log.debug( "Layers data:\"{}".format(json.dumps(layers_data, indent=4)) ) - context.data["layersData"] = layers_data + # Collect information about groups self.log.info("Collecting groups data from workfile") group_data = lib.groups_data() + context.data["groupsData"] = group_data self.log.debug( "Group data:\"{}".format(json.dumps(group_data, indent=4)) ) - context.data["groupsData"] = group_data self.log.info("Collecting scene data from workfile") workfile_info_parts = lib.execute_george("tv_projectinfo").split(" ") From 1591cab85f2fc5d832ca3e2145f99b8dbbbdbcda Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 15:52:52 +0100 Subject: [PATCH 06/71] added validator for project name check --- .../publish/validate_workfile_project_name.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 pype/plugins/tvpaint/publish/validate_workfile_project_name.py diff --git a/pype/plugins/tvpaint/publish/validate_workfile_project_name.py b/pype/plugins/tvpaint/publish/validate_workfile_project_name.py new file mode 100644 index 0000000000..7c1032fcad --- /dev/null +++ b/pype/plugins/tvpaint/publish/validate_workfile_project_name.py @@ -0,0 +1,37 @@ +import os +import pyblish.api + + +class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): + """Validate project name stored in workfile metadata. + + It is not possible to publish from different project than is set in + environment variable "AVALON_PROJECT". + """ + + label = "Validate Workfile Project Name" + order = pyblish.api.ValidatorOrder + + def process(self, context): + workfile_context = context.data["workfile_context"] + workfile_project_name = workfile_context["project"] + env_project_name = os.environ["AVALON_PROJECT"] + if workfile_project_name == env_project_name: + self.log.info(( + "Both workfile project and environment project are same. {}" + ).format(env_project_name)) + return + + # Raise an error + raise AssertionError(( + # Short message + "Workfile from different Project ({})." + # Description what's wrong + " It is not possible to publish when TVPaint was launched in" + "context of different project. Current context project is \"{}\"." + " Launch TVPaint in context of project \"{}\" and then publish." + ).format( + workfile_project_name, + env_project_name, + workfile_project_name, + )) From a0ffcfccaae5d417fe943af493991db60183a857 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 15:59:33 +0100 Subject: [PATCH 07/71] formatting change --- pype/plugins/tvpaint/publish/collect_workfile_data.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/plugins/tvpaint/publish/collect_workfile_data.py b/pype/plugins/tvpaint/publish/collect_workfile_data.py index e26e45a1e4..cf6113740c 100644 --- a/pype/plugins/tvpaint/publish/collect_workfile_data.py +++ b/pype/plugins/tvpaint/publish/collect_workfile_data.py @@ -26,7 +26,10 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): workfile_context = pipeline.get_current_workfile_context() if workfile_context: # Change current context with context from workfile - key_map = (("AVALON_ASSET", "asset"), ("AVALON_TASK", "task")) + key_map = ( + ("AVALON_ASSET", "asset"), + ("AVALON_TASK", "task") + ) for env_key, key in key_map: avalon.api.Session[env_key] = workfile_context[key] os.environ[env_key] = workfile_context[key] From 095883c0b0e3af917da931000ee66cd0d246b77b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 1 Dec 2020 16:55:57 +0100 Subject: [PATCH 08/71] make sure current project is selected project --- pype/plugins/tvpaint/publish/collect_workfile_data.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/plugins/tvpaint/publish/collect_workfile_data.py b/pype/plugins/tvpaint/publish/collect_workfile_data.py index cf6113740c..c6179b76cf 100644 --- a/pype/plugins/tvpaint/publish/collect_workfile_data.py +++ b/pype/plugins/tvpaint/publish/collect_workfile_data.py @@ -12,6 +12,9 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): hosts = ["tvpaint"] def process(self, context): + current_project_id = lib.execute_george("tv_projectcurrentid") + lib.execute_george("tv_projectselect {}".format(current_project_id)) + # Collect and store current context to have reference current_context = { "project": avalon.api.Session["AVALON_PROJECT"], From 0dacc5ca0e2f634f26184a4e8fe8d1ba43506004 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 1 Dec 2020 21:55:09 +0100 Subject: [PATCH 09/71] fix extract template and palettes thumbnails on mac --- pype/hosts/harmony/js/PypeHarmony.js | 6 +++--- pype/plugins/harmony/publish/extract_palette.py | 7 ++++++- pype/plugins/harmony/publish/extract_template.py | 6 +++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pype/hosts/harmony/js/PypeHarmony.js b/pype/hosts/harmony/js/PypeHarmony.js index 57137d98c6..504bcc9ba2 100644 --- a/pype/hosts/harmony/js/PypeHarmony.js +++ b/pype/hosts/harmony/js/PypeHarmony.js @@ -183,11 +183,11 @@ PypeHarmony.color = function(rgba) { /** * get all dependencies for given node. * @function - * @param {string} node node path. + * @param {string} _node node path. * @return {array} List of dependent nodes. */ -PypeHarmony.getDependencies = function(node) { - var target_node = node; +PypeHarmony.getDependencies = function(_node) { + var target_node = _node; var numInput = node.numberOfInputPorts(target_node); var dependencies = []; for (var i = 0 ; i < numInput; i++) { diff --git a/pype/plugins/harmony/publish/extract_palette.py b/pype/plugins/harmony/publish/extract_palette.py index 0996079dea..029a4f0f11 100644 --- a/pype/plugins/harmony/publish/extract_palette.py +++ b/pype/plugins/harmony/publish/extract_palette.py @@ -38,7 +38,7 @@ class ExtractPalette(pype.api.Extractor): os.path.basename(palette_file) .split(".plt")[0] + "_swatches.png" ) - self.log.info(f"Temporary humbnail path {tmp_thumb_path}") + self.log.info(f"Temporary thumbnail path {tmp_thumb_path}") palette_version = str(instance.data.get("version")).zfill(3) @@ -52,6 +52,11 @@ class ExtractPalette(pype.api.Extractor): palette_version, palette_file, tmp_thumb_path) + except OSError as e: + # FIXME: this happens on Mac where PIL cannot access fonts + # for some reason. + self.log.warning("Thumbnail generation failed") + self.log.warning(e) except ValueError: self.log.error("Unsupported palette type for thumbnail.") diff --git a/pype/plugins/harmony/publish/extract_template.py b/pype/plugins/harmony/publish/extract_template.py index 06c4c1efcf..b8437c85ea 100644 --- a/pype/plugins/harmony/publish/extract_template.py +++ b/pype/plugins/harmony/publish/extract_template.py @@ -31,7 +31,11 @@ class ExtractTemplate(pype.api.Extractor): for backdrop in self.get_backdrops(dependency): backdrops[backdrop["title"]["text"]] = backdrop unique_backdrops = [backdrops[x] for x in set(backdrops.keys())] - + if not unique_backdrops: + self.log.error(("No backdrops detected for template. " + "Please move template instance node onto " + "some backdrop and try again.")) + raise AssertionError("No backdrop detected") # Get non-connected nodes within backdrops. all_nodes = instance.context.data.get("allNodes") for node in [x for x in all_nodes if x not in dependencies]: From be2434b01e11a0bcf1a02df0cc5f9bfc519f7ec0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 2 Dec 2020 12:33:11 +0100 Subject: [PATCH 10/71] loader do not use "objectName" key to discover right container --- pype/plugins/tvpaint/load/load_reference_image.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pype/plugins/tvpaint/load/load_reference_image.py b/pype/plugins/tvpaint/load/load_reference_image.py index b3cefee4c3..e0e8885d57 100644 --- a/pype/plugins/tvpaint/load/load_reference_image.py +++ b/pype/plugins/tvpaint/load/load_reference_image.py @@ -104,6 +104,7 @@ class LoadImage(pipeline.Loader): def _remove_layers(self, layer_ids, layers=None): if not layer_ids: + self.log.warning("Got empty layer ids list.") return if layers is None: @@ -117,6 +118,7 @@ class LoadImage(pipeline.Loader): layer_ids_to_remove.append(layer_id) if not layer_ids_to_remove: + self.log.warning("No layers to delete.") return george_script_lines = [] @@ -128,12 +130,14 @@ class LoadImage(pipeline.Loader): def remove(self, container): layer_ids = self.layer_ids_from_container(container) + self.log.warning("Layers to delete {}".format(layer_ids)) self._remove_layers(layer_ids) current_containers = pipeline.ls() pop_idx = None for idx, cur_con in enumerate(current_containers): - if cur_con["objectName"] == container["objectName"]: + cur_con_layer_ids = self.layer_ids_from_container(cur_con) + if cur_con_layer_ids == layer_ids: pop_idx = idx break From 9576c46c2166893d41484778abb89c382f43af43 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 2 Dec 2020 12:57:11 +0100 Subject: [PATCH 11/71] bump version --- pype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/version.py b/pype/version.py index e199aa5550..abe7e03a96 100644 --- a/pype/version.py +++ b/pype/version.py @@ -1 +1 @@ -__version__ = "2.14.1" +__version__ = "2.14.2" From 3449779fdea8b392c9d278b5fd720200c53c8515 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:10:45 +0100 Subject: [PATCH 12/71] AE - added background loader Reworked structure of json metadata --- .../stubs/aftereffects_server_stub.py | 239 ++++++++++++++---- .../aftereffects/load/load_background.py | 96 +++++++ pype/plugins/aftereffects/load/load_file.py | 15 +- pype/plugins/lib.py | 43 +++- 4 files changed, 325 insertions(+), 68 deletions(-) create mode 100644 pype/plugins/aftereffects/load/load_background.py diff --git a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py index 0b8c54e884..d4c1cf4512 100644 --- a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py +++ b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py @@ -4,13 +4,27 @@ from pype.modules.websocket_server import WebSocketServer Used anywhere solution is calling client methods. """ import json -from collections import namedtuple - +import attr import logging log = logging.getLogger(__name__) +@attr.s +class AEItem(object): + """ + Object denoting Item in AE. Each item is created in AE by any Loader, + but contains same fields, which are being used in later processing. + """ + # metadata + id = attr.ib() # id created by AE, could be used for querying + name = attr.ib() # name of item + item_type = attr.ib(default=None) # item type (footage, folder, comp) + # all imported elements, single for + # regular image, array for Backgrounds + members = attr.ib(factory=list) + + class AfterEffectsServerStub(): """ Stub for calling function on client (Photoshop js) side. @@ -34,22 +48,14 @@ class AfterEffectsServerStub(): ('AfterEffects.open', path=path) ) - def read(self, layer, layers_meta=None): - """ - Parses layer metadata from Label field of active document - Args: - layer: - res(string): - json representation + Returns: + res(string): - json representation """ if not res: return [] @@ -358,9 +480,14 @@ class AfterEffectsServerStub(): return [] ret = [] - # convert to namedtuple to use dot donation - if isinstance(layers_data, dict): # TODO refactore + # convert to AEItem to use dot donation + if isinstance(layers_data, dict): layers_data = [layers_data] for d in layers_data: - ret.append(namedtuple('Layer', d.keys())(*d.values())) + # currently implemented and expected fields + item = AEItem(d.get('id'), + d.get('name'), + d.get('type'), + d.get('members')) + ret.append(item) return ret diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py new file mode 100644 index 0000000000..9e7162761f --- /dev/null +++ b/pype/plugins/aftereffects/load/load_background.py @@ -0,0 +1,96 @@ +import re + +from avalon import api, aftereffects + +from pype.plugins.lib import get_background_layers, get_unique_layer_name + +stub = aftereffects.stub() + + +class BackgroundLoader(api.Loader): + """ + Load images from Background family + Creates for each background separate folder with all imported images + from background json AND automatically created composition with layers, + each layer for separate image. + + For each load container is created and stored in project (.aep) + metadata + """ + families = ["background"] + representations = ["json"] + + def load(self, context, name=None, namespace=None, data=None): + items = stub.get_items(comps=True) + existing_items = [layer.name for layer in items] + + comp_name = get_unique_layer_name(existing_items, + "{}_{}".format(context["asset"]["name"], name)) + + layers = get_background_layers(self.fname) + comp = stub.import_background(None, comp_name, layers) + + if not comp: + self.log.warning( + "Import background failed.") + self.log.warning("Check host app for alert error.") + return + + self[:] = [comp] + namespace = namespace or comp_name + + return aftereffects.containerise( + name, + namespace, + comp, + context, + self.__class__.__name__ + ) + + def update(self, container, representation): + """ Switch asset or change version """ + context = representation.get("context", {}) + + # without iterator number (_001, 002...) + namespace_from_container = re.sub(r'_\d{3}$', '', + container["namespace"]) + comp_name = "{}_{}".format(context["asset"], context["subset"]) + + # switching assets + if namespace_from_container != comp_name: + items = stub.get_items(comps=True) + existing_items = [layer.name for layer in items] + comp_name = get_unique_layer_name(existing_items, + "{}_{}".format(context["asset"], context["subset"])) + else: # switching version - keep same name + comp_name = container["namespace"] + + path = api.get_representation_path(representation) + + layers = get_background_layers(path) + comp = stub.reload_background(container["members"][1], + comp_name, + layers) + + # update container + container["representation"] = str(representation["_id"]) + container["name"] = context["subset"] + container["namespace"] = comp_name + container["members"] = comp.members + + stub.imprint(comp, container) + + def remove(self, container): + """ + Removes element from scene: deletes layer + removes from file + metadata. + Args: + container (dict): container to be removed - used to get layer_id + """ + print("!!!! container:: {}".format(container)) + layer = container.pop("layer") + stub.imprint(layer, {}) + stub.delete_item(layer.id) + + def switch(self, container, representation): + self.update(container, representation) \ No newline at end of file diff --git a/pype/plugins/aftereffects/load/load_file.py b/pype/plugins/aftereffects/load/load_file.py index 0705dbd776..551c8f4de7 100644 --- a/pype/plugins/aftereffects/load/load_file.py +++ b/pype/plugins/aftereffects/load/load_file.py @@ -21,9 +21,10 @@ class FileLoader(api.Loader): representations = ["*"] def load(self, context, name=None, namespace=None, data=None): - comp_name = lib.get_unique_layer_name(stub.get_items(comps=True), - context["asset"]["name"], - name) + layers = stub.get_items(comps=True, folders=True, footages=True) + existing_layers = [layer.name for layer in layers] + comp_name = lib.get_unique_layer_name(existing_layers, + "{}_{}".format(context["asset"]["name"], name)) import_options = {} @@ -77,9 +78,11 @@ class FileLoader(api.Loader): layer_name = "{}_{}".format(context["asset"], context["subset"]) # switching assets if namespace_from_container != layer_name: - layer_name = lib.get_unique_layer_name(stub.get_items(comps=True), - context["asset"], - context["subset"]) + layers = stub.get_items(comps=True) + existing_layers = [layer.name for layer in layers] + layer_name = lib.get_unique_layer_name(existing_layers, + "{}_{}".format(context["asset"], + context["subset"])) else: # switching version - keep same name layer_name = container["namespace"] path = api.get_representation_path(representation) diff --git a/pype/plugins/lib.py b/pype/plugins/lib.py index d7d90af165..c0a4934439 100644 --- a/pype/plugins/lib.py +++ b/pype/plugins/lib.py @@ -1,22 +1,22 @@ import re +import json +import os -def get_unique_layer_name(layers, asset_name, subset_name): +def get_unique_layer_name(layers, name): """ Gets all layer names and if 'name' is present in them, increases suffix by 1 (eg. creates unique layer name - for Loader) Args: - layers (list): of namedtuples, expects 'name' field present - asset_name (string): in format asset_subset (Hero) - subset_name (string): (LOD) + layers (list): of strings, names only + name (string): checked value Returns: (string): name_00X (without version) """ - name = "{}_{}".format(asset_name, subset_name) names = {} for layer in layers: - layer_name = re.sub(r'_\d{3}$', '', layer.name) + layer_name = re.sub(r'_\d{3}$', '', layer) if layer_name in names.keys(): names[layer_name] = names[layer_name] + 1 else: @@ -24,3 +24,34 @@ def get_unique_layer_name(layers, asset_name, subset_name): occurrences = names.get(name, 0) return "{}_{:0>3d}".format(name, occurrences + 1) + + +def get_background_layers(file_url): + """ + Pulls file name from background json file, enrich with folder url for + AE to be able import files. + + Order is important, follows order in json. + + Args: + file_url (str): abs url of background json + + Returns: + (list): of abs paths to images + """ + with open(file_url) as json_file: + data = json.load(json_file) + + layers = list() + bg_folder = os.path.dirname(file_url) + for child in data['children']: + if child.get("filename"): + layers.append(os.path.join(bg_folder, child.get("filename")). + replace("\\", "/")) + else: + for layer in child['children']: + if layer.get("filename"): + layers.append(os.path.join(bg_folder, + layer.get("filename")). + replace("\\", "/")) + return layers From 96c567c9de8144c143b2b5ae7ef91ca8e813c74a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:11:28 +0100 Subject: [PATCH 13/71] AE - fix throw exc when no Render Queue --- pype/plugins/aftereffects/publish/collect_render.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pype/plugins/aftereffects/publish/collect_render.py b/pype/plugins/aftereffects/publish/collect_render.py index fbe392d52b..2dd447d03e 100644 --- a/pype/plugins/aftereffects/publish/collect_render.py +++ b/pype/plugins/aftereffects/publish/collect_render.py @@ -110,7 +110,10 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): # pull file name from Render Queue Output module render_q = aftereffects.stub().get_render_info() + if not render_q: + raise ValueError("No file extension set in Render Queue") _, ext = os.path.splitext(os.path.basename(render_q.file_name)) + base_dir = self._get_output_dir(render_instance) expected_files = [] if "#" not in render_q.file_name: # single frame (mov)W From e92c842c8af4439d46ac211781aa876fa553c8c8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:13:03 +0100 Subject: [PATCH 14/71] Fix - same frameStart and frameEnd didnt work --- pype/lib/abstract_collect_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/abstract_collect_render.py b/pype/lib/abstract_collect_render.py index 6bcef1ba90..19e7c37dba 100644 --- a/pype/lib/abstract_collect_render.py +++ b/pype/lib/abstract_collect_render.py @@ -73,14 +73,14 @@ class RenderInstance(object): @frameStart.validator def check_frame_start(self, _, value): """Validate if frame start is not larger then end.""" - if value >= self.frameEnd: + if value > self.frameEnd: raise ValueError("frameStart must be smaller " "or equal then frameEnd") @frameEnd.validator def check_frame_end(self, _, value): """Validate if frame end is not less then start.""" - if value <= self.frameStart: + if value < self.frameStart: raise ValueError("frameEnd must be smaller " "or equal then frameStart") From 19be39858742df181b60ac3cfdb9c882239998eb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Dec 2020 21:34:15 +0100 Subject: [PATCH 15/71] Hound --- pype/plugins/aftereffects/load/load_background.py | 8 +++++--- pype/plugins/aftereffects/load/load_file.py | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py index 9e7162761f..14f75c2850 100644 --- a/pype/plugins/aftereffects/load/load_background.py +++ b/pype/plugins/aftereffects/load/load_background.py @@ -24,7 +24,8 @@ class BackgroundLoader(api.Loader): items = stub.get_items(comps=True) existing_items = [layer.name for layer in items] - comp_name = get_unique_layer_name(existing_items, + comp_name = get_unique_layer_name( + existing_items, "{}_{}".format(context["asset"]["name"], name)) layers = get_background_layers(self.fname) @@ -60,7 +61,8 @@ class BackgroundLoader(api.Loader): if namespace_from_container != comp_name: items = stub.get_items(comps=True) existing_items = [layer.name for layer in items] - comp_name = get_unique_layer_name(existing_items, + comp_name = get_unique_layer_name( + existing_items, "{}_{}".format(context["asset"], context["subset"])) else: # switching version - keep same name comp_name = container["namespace"] @@ -93,4 +95,4 @@ class BackgroundLoader(api.Loader): stub.delete_item(layer.id) def switch(self, container, representation): - self.update(container, representation) \ No newline at end of file + self.update(container, representation) diff --git a/pype/plugins/aftereffects/load/load_file.py b/pype/plugins/aftereffects/load/load_file.py index 551c8f4de7..2e07e933f7 100644 --- a/pype/plugins/aftereffects/load/load_file.py +++ b/pype/plugins/aftereffects/load/load_file.py @@ -23,8 +23,8 @@ class FileLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): layers = stub.get_items(comps=True, folders=True, footages=True) existing_layers = [layer.name for layer in layers] - comp_name = lib.get_unique_layer_name(existing_layers, - "{}_{}".format(context["asset"]["name"], name)) + comp_name = lib.get_unique_layer_name( + existing_layers, "{}_{}".format(context["asset"]["name"], name)) import_options = {} @@ -80,9 +80,9 @@ class FileLoader(api.Loader): if namespace_from_container != layer_name: layers = stub.get_items(comps=True) existing_layers = [layer.name for layer in layers] - layer_name = lib.get_unique_layer_name(existing_layers, - "{}_{}".format(context["asset"], - context["subset"])) + layer_name = lib.get_unique_layer_name( + existing_layers, + "{}_{}".format(context["asset"], context["subset"])) else: # switching version - keep same name layer_name = container["namespace"] path = api.get_representation_path(representation) From ecaf3ce879aede686948bd141396e48f7603ac01 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 09:24:34 +0100 Subject: [PATCH 16/71] start cleaning up colours --- pype/tools/settings/settings/style/style.css | 98 +++++++++++--------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index 38fcd14448..7215aa0f3e 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -1,13 +1,19 @@ +/* :root { + --border-color-: #464b54; +} + */ + + QWidget { color: #bfccd6; - background-color: #293742; + background-color: #282C34; font-size: 12px; border-radius: 0px; } QMenu { border: 1px solid #555555; - background-color: #1d272f; + background-color: #21252B; } QMenu::item { @@ -26,24 +32,24 @@ QCheckBox::indicator {} QCheckBox::indicator:focus {} QLineEdit, QSpinBox, QDoubleSpinBox, QPlainTextEdit, QTextEdit { - border: 1px solid #aaaaaa; + border: 1px solid #464b54; border-radius: 3px; - background-color: #1d272f; + background-color: #21252B; } QLineEdit:disabled, QSpinBox:disabled, QDoubleSpinBox:disabled, QPlainTextEdit:disabled, QTextEdit:disabled, QPushButton:disabled { - background-color: #4e6474; + background-color: #464b54; } QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus, QPlainTextEdit:focus, QTextEdit:focus { - border: 1px solid #ffffff; + border: 1px solid #839caf; } QComboBox { - border: 1px solid #aaaaaa; + border: 1px solid #464b54; border-radius: 3px; padding: 2px 2px 4px 4px; - background: #1d272f; + background: #21252B; } QComboBox QAbstractItemView::item { @@ -56,25 +62,25 @@ QToolButton { QLabel { background: transparent; - color: #7390a5; + color: #73787b; } -QLabel:hover {color: #839caf;} +QLabel:hover {color: #949ca0;} -QLabel[state="studio"] {color: #bfccd6;} +QLabel[state="studio"] {color: #73C990;} QLabel[state="studio"]:hover {color: #ffffff;} -QLabel[state="modified"] {color: #137cbd;} -QLabel[state="modified"]:hover {color: #1798e8;} -QLabel[state="overriden-modified"] {color: #137cbd;} -QLabel[state="overriden-modified"]:hover {color: #1798e8;} +QLabel[state="modified"] {color: #189aea;} +QLabel[state="modified"]:hover {color: #46b1f3;} +QLabel[state="overriden-modified"] {color: #189aea;} +QLabel[state="overriden-modified"]:hover {color: #46b1f3;} QLabel[state="overriden"] {color: #ff8c1a;} QLabel[state="overriden"]:hover {color: #ffa64d;} QLabel[state="invalid"] {color: #ad2e2e;} QLabel[state="invalid"]:hover {color: #ad2e2e;} -QWidget[input-state="studio"] {border-color: #bfccd6;} -QWidget[input-state="modified"] {border-color: #137cbd;} -QWidget[input-state="overriden-modified"] {border-color: #137cbd;} +QWidget[input-state="studio"] {border-color: #858a94;} +QWidget[input-state="modified"] {border-color: #189aea;} +QWidget[input-state="overriden-modified"] {border-color: #189aea;} QWidget[input-state="overriden"] {border-color: #ff8c1a;} QWidget[input-state="invalid"] {border-color: #ad2e2e;} @@ -84,7 +90,9 @@ QPushButton { padding: 5px; } QPushButton:hover { - background-color: #31424e; + background-color: #333840; + border: 1px solid #fff; + color: #fff; } QPushButton[btn-type="tool-item"] { border: 1px solid #bfccd6; @@ -92,8 +100,8 @@ QPushButton[btn-type="tool-item"] { } QPushButton[btn-type="tool-item"]:hover { - border-color: #137cbd; - color: #137cbd; + border-color: #189aea; + color: #46b1f3; background-color: transparent; } @@ -103,16 +111,16 @@ QPushButton[btn-type="tool-item-icon"] { } QPushButton[btn-type="expand-toggle"] { - background: #1d272f; + background: #21252B; } #GroupWidget { - border-bottom: 1px solid #1d272f; + border-bottom: 1px solid #21252B; } #ProjectListWidget QListView { - border: 1px solid #aaaaaa; - background: #1d272f; + border: 1px solid #464b54; + background: #21252B; } #ProjectListWidget QLabel { background: transparent; @@ -123,8 +131,8 @@ QPushButton[btn-type="expand-toggle"] { font-size: 12px; } -#DictKey[state="studio"] {border-color: #bfccd6;} -#DictKey[state="modified"] {border-color: #137cbd;} +#DictKey[state="studio"] {border-color: #464b54;} +#DictKey[state="modified"] {border-color: #189aea;} #DictKey[state="overriden"] {border-color: #00f;} #DictKey[state="overriden-modified"] {border-color: #0f0;} #DictKey[state="invalid"] {border-color: #ad2e2e;} @@ -141,9 +149,9 @@ QPushButton[btn-type="expand-toggle"] { } #SideLineWidget { - background-color: #31424e; + background-color: #333942; border-style: solid; - border-color: #3b4f5e; + border-color: #4e5254; border-left-width: 3px; border-bottom-width: 0px; border-right-width: 0px; @@ -151,14 +159,14 @@ QPushButton[btn-type="expand-toggle"] { } #SideLineWidget:hover { - border-color: #58768d; + border-color: #7d8386; } -#SideLineWidget[state="child-studio"] {border-color: #455c6e;} -#SideLineWidget[state="child-studio"]:hover {border-color: #62839d;} +#SideLineWidget[state="child-studio"] {border-color: #56a06f;} +#SideLineWidget[state="child-studio"]:hover {border-color: #73C990;} #SideLineWidget[state="child-modified"] {border-color: #106aa2;} -#SideLineWidget[state="child-modified"]:hover {border-color: #137cbd;} +#SideLineWidget[state="child-modified"]:hover {border-color: #189aea;} #SideLineWidget[state="child-invalid"] {border-color: #ad2e2e;} #SideLineWidget[state="child-invalid"]:hover {border-color: #c93636;} @@ -167,7 +175,7 @@ QPushButton[btn-type="expand-toggle"] { #SideLineWidget[state="child-overriden"]:hover {border-color: #ff8c1a;} #SideLineWidget[state="child-overriden-modified"] {border-color: #106aa2;} -#SideLineWidget[state="child-overriden-modified"]:hover {border-color: #137cbd;} +#SideLineWidget[state="child-overriden-modified"]:hover {border-color: #189aea;} #MainWidget { background: #141a1f; @@ -177,12 +185,12 @@ QPushButton[btn-type="expand-toggle"] { background: transparent; } #DictAsWidgetBody[show_borders="1"] { - border: 2px solid #cccccc; + border: 1px solid #4e5254; border-radius: 5px; } #SplitterItem { - background-color: #1d272f; + background-color: #21252B; } QTabWidget::pane { @@ -200,18 +208,18 @@ QTabBar::tab { } QTabBar::tab:selected { - background: #293742; + background: #282C34; border-color: #9B9B9B; border-bottom-color: #C2C7CB; } QTabBar::tab:!selected { margin-top: 2px; - background: #1d272f; + background: #21252B; } QTabBar::tab:!selected:hover { - background: #3b4f5e; + background: #333840; } @@ -231,13 +239,13 @@ QTabBar::tab:only-one { QScrollBar:horizontal { height: 15px; margin: 3px 15px 3px 15px; - border: 1px transparent #1d272f; + border: 1px transparent #21252B; border-radius: 4px; - background-color: #1d272f; + background-color: #21252B; } QScrollBar::handle:horizontal { - background-color: #61839e; + background-color: #4B5362; min-width: 5px; border-radius: 4px; } @@ -285,15 +293,15 @@ QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { } QScrollBar:vertical { - background-color: #1d272f; + background-color: #21252B; width: 15px; margin: 15px 3px 15px 3px; - border: 1px transparent #1d272f; + border: 1px transparent #21252B; border-radius: 4px; } QScrollBar::handle:vertical { - background-color: #61839e; + background-color: #4B5362; min-height: 5px; border-radius: 4px; } From c4b50b9637affbbbd715963a17f9f1a3f1aafc89 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 09:29:18 +0100 Subject: [PATCH 17/71] lighter labels --- pype/tools/settings/settings/style/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/tools/settings/settings/style/style.css b/pype/tools/settings/settings/style/style.css index 7215aa0f3e..82313d5cfa 100644 --- a/pype/tools/settings/settings/style/style.css +++ b/pype/tools/settings/settings/style/style.css @@ -62,9 +62,9 @@ QToolButton { QLabel { background: transparent; - color: #73787b; + color: #969b9e; } -QLabel:hover {color: #949ca0;} +QLabel:hover {color: #b8c1c5;} QLabel[state="studio"] {color: #73C990;} QLabel[state="studio"]:hover {color: #ffffff;} From df76d931a1b0a9076074d9a0d25ce2c2975bdb53 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Dec 2020 10:57:53 +0100 Subject: [PATCH 18/71] AE - fix publish after background load --- pype/lib/__init__.py | 9 ++- pype/lib/plugin_tools.py | 56 ++++++++++++++++++ .../stubs/aftereffects_server_stub.py | 13 ++++- .../aftereffects/create/create_render.py | 2 +- .../aftereffects/load/load_background.py | 3 +- pype/plugins/aftereffects/load/load_file.py | 2 +- .../aftereffects/publish/collect_render.py | 6 +- pype/plugins/lib.py | 57 ------------------- 8 files changed, 84 insertions(+), 64 deletions(-) delete mode 100644 pype/plugins/lib.py diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 1ade97cd0e..78fd69da98 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -25,7 +25,12 @@ from .applications import ( _subprocess ) -from .plugin_tools import filter_pyblish_plugins, source_hash +from .plugin_tools import ( + filter_pyblish_plugins, + source_hash, + get_unique_layer_name, + get_background_layers +) from .path_tools import ( version_up, @@ -57,6 +62,8 @@ __all__ = [ "ApplicationAction", "filter_pyblish_plugins", + "get_unique_layer_name", + "get_background_layers", "version_up", "get_version_from_path", diff --git a/pype/lib/plugin_tools.py b/pype/lib/plugin_tools.py index 0b6ace807e..589aaa58f6 100644 --- a/pype/lib/plugin_tools.py +++ b/pype/lib/plugin_tools.py @@ -3,6 +3,8 @@ import os import inspect import logging +import re +import json from ..api import config @@ -78,3 +80,57 @@ def source_hash(filepath, *args): time = str(os.path.getmtime(filepath)) size = str(os.path.getsize(filepath)) return "|".join([file_name, time, size] + list(args)).replace(".", ",") + + +def get_unique_layer_name(layers, name): + """ + Gets all layer names and if 'name' is present in them, increases + suffix by 1 (eg. creates unique layer name - for Loader) + Args: + layers (list): of strings, names only + name (string): checked value + + Returns: + (string): name_00X (without version) + """ + names = {} + for layer in layers: + layer_name = re.sub(r'_\d{3}$', '', layer) + if layer_name in names.keys(): + names[layer_name] = names[layer_name] + 1 + else: + names[layer_name] = 1 + occurrences = names.get(name, 0) + + return "{}_{:0>3d}".format(name, occurrences + 1) + + +def get_background_layers(file_url): + """ + Pulls file name from background json file, enrich with folder url for + AE to be able import files. + + Order is important, follows order in json. + + Args: + file_url (str): abs url of background json + + Returns: + (list): of abs paths to images + """ + with open(file_url) as json_file: + data = json.load(json_file) + + layers = list() + bg_folder = os.path.dirname(file_url) + for child in data['children']: + if child.get("filename"): + layers.append(os.path.join(bg_folder, child.get("filename")). + replace("\\", "/")) + else: + for layer in child['children']: + if layer.get("filename"): + layers.append(os.path.join(bg_folder, + layer.get("filename")). + replace("\\", "/")) + return layers \ No newline at end of file diff --git a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py index d4c1cf4512..9449d0b378 100644 --- a/pype/modules/websocket_server/stubs/aftereffects_server_stub.py +++ b/pype/modules/websocket_server/stubs/aftereffects_server_stub.py @@ -23,6 +23,10 @@ class AEItem(object): # all imported elements, single for # regular image, array for Backgrounds members = attr.ib(factory=list) + workAreaStart = attr.ib(default=None) + workAreaDuration = attr.ib(default=None) + frameRate = attr.ib(default=None) + file_name = attr.ib(default=None) class AfterEffectsServerStub(): @@ -66,7 +70,7 @@ class AfterEffectsServerStub(): metadata = json.loads(res) except json.decoder.JSONDecodeError: raise ValueError("Unparsable metadata {}".format(res)) - return metadata or {} + return metadata or [] def read(self, item, layers_meta=None): """ @@ -488,6 +492,11 @@ class AfterEffectsServerStub(): item = AEItem(d.get('id'), d.get('name'), d.get('type'), - d.get('members')) + d.get('members'), + d.get('workAreaStart'), + d.get('workAreaDuration'), + d.get('frameRate'), + d.get('file_name')) + ret.append(item) return ret diff --git a/pype/plugins/aftereffects/create/create_render.py b/pype/plugins/aftereffects/create/create_render.py index 1944cf9937..6d876e349d 100644 --- a/pype/plugins/aftereffects/create/create_render.py +++ b/pype/plugins/aftereffects/create/create_render.py @@ -35,7 +35,7 @@ class CreateRender(api.Creator): if self.name.lower() == item.name.lower(): self._show_msg(txt) return False - + self.data["members"] = [item.id] stub.imprint(item, self.data) stub.set_label_color(item.id, 14) # Cyan options 0 - 16 stub.rename_item(item, self.data["subset"]) diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py index 14f75c2850..fc23d5098e 100644 --- a/pype/plugins/aftereffects/load/load_background.py +++ b/pype/plugins/aftereffects/load/load_background.py @@ -2,7 +2,7 @@ import re from avalon import api, aftereffects -from pype.plugins.lib import get_background_layers, get_unique_layer_name +from pype.lib import get_background_layers, get_unique_layer_name stub = aftereffects.stub() @@ -51,6 +51,7 @@ class BackgroundLoader(api.Loader): def update(self, container, representation): """ Switch asset or change version """ context = representation.get("context", {}) + layer = container.pop("layer") # without iterator number (_001, 002...) namespace_from_container = re.sub(r'_\d{3}$', '', diff --git a/pype/plugins/aftereffects/load/load_file.py b/pype/plugins/aftereffects/load/load_file.py index 2e07e933f7..ba11856678 100644 --- a/pype/plugins/aftereffects/load/load_file.py +++ b/pype/plugins/aftereffects/load/load_file.py @@ -1,5 +1,5 @@ from avalon import api, aftereffects -from pype.plugins import lib +from pype import lib import re stub = aftereffects.stub() diff --git a/pype/plugins/aftereffects/publish/collect_render.py b/pype/plugins/aftereffects/publish/collect_render.py index 2dd447d03e..7f7d5a52bc 100644 --- a/pype/plugins/aftereffects/publish/collect_render.py +++ b/pype/plugins/aftereffects/publish/collect_render.py @@ -33,12 +33,16 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender): compositions = aftereffects.stub().get_items(True) compositions_by_id = {item.id: item for item in compositions} - for item_id, inst in aftereffects.stub().get_metadata().items(): + for inst in aftereffects.stub().get_metadata(): schema = inst.get('schema') # loaded asset container skip it if schema and 'container' in schema: continue + if not inst["members"]: + raise ValueError("Couldn't find id, unable to publish. " + + "Please recreate instance.") + item_id = inst["members"][0] work_area_info = aftereffects.stub().get_work_area(int(item_id)) frameStart = work_area_info.workAreaStart diff --git a/pype/plugins/lib.py b/pype/plugins/lib.py deleted file mode 100644 index c0a4934439..0000000000 --- a/pype/plugins/lib.py +++ /dev/null @@ -1,57 +0,0 @@ -import re -import json -import os - - -def get_unique_layer_name(layers, name): - """ - Gets all layer names and if 'name' is present in them, increases - suffix by 1 (eg. creates unique layer name - for Loader) - Args: - layers (list): of strings, names only - name (string): checked value - - Returns: - (string): name_00X (without version) - """ - names = {} - for layer in layers: - layer_name = re.sub(r'_\d{3}$', '', layer) - if layer_name in names.keys(): - names[layer_name] = names[layer_name] + 1 - else: - names[layer_name] = 1 - occurrences = names.get(name, 0) - - return "{}_{:0>3d}".format(name, occurrences + 1) - - -def get_background_layers(file_url): - """ - Pulls file name from background json file, enrich with folder url for - AE to be able import files. - - Order is important, follows order in json. - - Args: - file_url (str): abs url of background json - - Returns: - (list): of abs paths to images - """ - with open(file_url) as json_file: - data = json.load(json_file) - - layers = list() - bg_folder = os.path.dirname(file_url) - for child in data['children']: - if child.get("filename"): - layers.append(os.path.join(bg_folder, child.get("filename")). - replace("\\", "/")) - else: - for layer in child['children']: - if layer.get("filename"): - layers.append(os.path.join(bg_folder, - layer.get("filename")). - replace("\\", "/")) - return layers From 4d742f50e21f3de4274a1b298188b0455495193d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Dec 2020 11:40:27 +0100 Subject: [PATCH 19/71] Hound --- pype/lib/plugin_tools.py | 2 +- pype/plugins/aftereffects/load/load_background.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/plugin_tools.py b/pype/lib/plugin_tools.py index 589aaa58f6..f5eb354ca3 100644 --- a/pype/lib/plugin_tools.py +++ b/pype/lib/plugin_tools.py @@ -133,4 +133,4 @@ def get_background_layers(file_url): layers.append(os.path.join(bg_folder, layer.get("filename")). replace("\\", "/")) - return layers \ No newline at end of file + return layers diff --git a/pype/plugins/aftereffects/load/load_background.py b/pype/plugins/aftereffects/load/load_background.py index fc23d5098e..879734e4f9 100644 --- a/pype/plugins/aftereffects/load/load_background.py +++ b/pype/plugins/aftereffects/load/load_background.py @@ -51,7 +51,7 @@ class BackgroundLoader(api.Loader): def update(self, container, representation): """ Switch asset or change version """ context = representation.get("context", {}) - layer = container.pop("layer") + _ = container.pop("layer") # without iterator number (_001, 002...) namespace_from_container = re.sub(r'_\d{3}$', '', From 8e39835e4ab845ca523a15f9066edf098089ea84 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 14:42:31 +0100 Subject: [PATCH 20/71] add full path to acre module --- igniter/tools.py | 7 ++++++- pype.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/igniter/tools.py b/igniter/tools.py index 6cbda996fc..ea130b29d7 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -97,7 +97,12 @@ def load_environments(sections: list = None) -> dict: try: import acre except ImportError: - sys.path.append("repos/acre") + acre_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "repos", + "acre" + ) + sys.path.append(acre_dir) import acre from pype import settings diff --git a/pype.py b/pype.py index 954249b2b2..c0b91303ae 100644 --- a/pype.py +++ b/pype.py @@ -49,7 +49,12 @@ from igniter.tools import load_environments try: import acre except ImportError: - sys.path.append("repos/acre") + acre_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "repos", + "acre" + ) + sys.path.append(acre_dir) import acre from igniter import BootstrapRepos From d16287869467eca10605d84abad69b3ad6501d84 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 14:57:11 +0100 Subject: [PATCH 21/71] remove state property from Projects label --- pype/tools/settings/settings/widgets/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/tools/settings/settings/widgets/base.py b/pype/tools/settings/settings/widgets/base.py index 404ef8fed7..4ff7ec26e7 100644 --- a/pype/tools/settings/settings/widgets/base.py +++ b/pype/tools/settings/settings/widgets/base.py @@ -412,7 +412,6 @@ class ProjectListWidget(QtWidgets.QWidget): self.setObjectName("ProjectListWidget") label_widget = QtWidgets.QLabel("Projects") - label_widget.setProperty("state", "studio") project_list = ProjectListView(self) project_list.setModel(QtGui.QStandardItemModel()) From 852823dcf9c85a5a0a34461444d38cbf9ba294e4 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 15:40:26 +0100 Subject: [PATCH 22/71] convert deadline from ENV var to settings --- pype/lib/abstract_submit_deadline.py | 8 +++++-- .../publish/submit_celaction_deadline.py | 13 +++++++---- .../plugins/fusion/publish/submit_deadline.py | 12 ++++++---- .../global/publish/collect_settings.py | 13 +++++++++++ .../global/publish/submit_publish_job.py | 11 ++++++---- pype/plugins/maya/create/create_render.py | 8 +++++-- .../maya/publish/submit_maya_deadline.py | 22 +++++++++---------- .../publish/validate_deadline_connection.py | 15 ++++++------- .../nuke/publish/submit_nuke_deadline.py | 14 +++++++----- .../defaults/project_settings/maya.json | 7 ++++-- .../project_settings/standalonepublisher.json | 2 +- .../defaults/system_settings/modules.json | 4 ++-- .../schemas/schema_global_publish.json | 4 ++-- .../system_schema/schema_modules.json | 6 ++--- 14 files changed, 88 insertions(+), 51 deletions(-) create mode 100644 pype/plugins/global/publish/collect_settings.py diff --git a/pype/lib/abstract_submit_deadline.py b/pype/lib/abstract_submit_deadline.py index 170e4908b7..9862f534f1 100644 --- a/pype/lib/abstract_submit_deadline.py +++ b/pype/lib/abstract_submit_deadline.py @@ -373,8 +373,12 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): """Plugin entry point.""" self._instance = instance context = instance.context - self._deadline_url = os.environ.get( - "DEADLINE_REST_URL", "http://localhost:8082") + self._deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] + ) assert self._deadline_url, "Requires DEADLINE_REST_URL" file_path = None diff --git a/pype/plugins/celaction/publish/submit_celaction_deadline.py b/pype/plugins/celaction/publish/submit_celaction_deadline.py index 30e7175a60..fd958d11a3 100644 --- a/pype/plugins/celaction/publish/submit_celaction_deadline.py +++ b/pype/plugins/celaction/publish/submit_celaction_deadline.py @@ -11,7 +11,7 @@ class ExtractCelactionDeadline(pyblish.api.InstancePlugin): """Submit CelAction2D scene to Deadline Renders are submitted to a Deadline Web Service as - supplied via the environment variable DEADLINE_REST_URL + supplied via settings key "DEADLINE_REST_URL". """ @@ -37,10 +37,15 @@ class ExtractCelactionDeadline(pyblish.api.InstancePlugin): instance.data["toBeRenderedOn"] = "deadline" context = instance.context - DEADLINE_REST_URL = os.environ.get("DEADLINE_REST_URL") - assert DEADLINE_REST_URL, "Requires DEADLINE_REST_URL" + deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] + ) + assert deadline_url, "Requires DEADLINE_REST_URL" - self.deadline_url = "{}/api/jobs".format(DEADLINE_REST_URL) + self.deadline_url = "{}/api/jobs".format(deadline_url) self._comment = context.data.get("comment", "") self._deadline_user = context.data.get( "deadlineUser", getpass.getuser()) diff --git a/pype/plugins/fusion/publish/submit_deadline.py b/pype/plugins/fusion/publish/submit_deadline.py index ed3fb06586..050e558d2e 100644 --- a/pype/plugins/fusion/publish/submit_deadline.py +++ b/pype/plugins/fusion/publish/submit_deadline.py @@ -12,7 +12,7 @@ class FusionSubmitDeadline(pyblish.api.InstancePlugin): """Submit current Comp to Deadline Renders are submitted to a Deadline Web Service as - supplied via the environment variable DEADLINE_REST_URL + supplied via settings key "DEADLINE_REST_URL". """ @@ -32,9 +32,13 @@ class FusionSubmitDeadline(pyblish.api.InstancePlugin): from avalon.fusion.lib import get_frame_path - DEADLINE_REST_URL = api.Session.get("DEADLINE_REST_URL", - "http://localhost:8082") - assert DEADLINE_REST_URL, "Requires DEADLINE_REST_URL" + deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] + ) + assert deadline_url, "Requires DEADLINE_REST_URL" # Collect all saver instances in context that are to be rendered saver_instances = [] diff --git a/pype/plugins/global/publish/collect_settings.py b/pype/plugins/global/publish/collect_settings.py new file mode 100644 index 0000000000..8531e530ac --- /dev/null +++ b/pype/plugins/global/publish/collect_settings.py @@ -0,0 +1,13 @@ +from pyblish import api +from pype.api import get_current_project_settings, get_system_settings + + +class CollectSettings(api.ContextPlugin): + """Collect Settings and store in the context.""" + + order = api.CollectorOrder - 0.491 + label = "Collect Settings" + + def process(self, context): + context.data["project_settings"] = get_current_project_settings() + context.data["system_settings"] = get_system_settings() diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index b8bf240c06..c90837dfed 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -305,7 +305,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): self.log.info("Submitting Deadline job ...") - url = "{}/api/jobs".format(self.DEADLINE_REST_URL) + url = "{}/api/jobs".format(self.deadline_url) response = requests.post(url, json=payload, timeout=10) if not response.ok: raise Exception(response.text) @@ -924,10 +924,13 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): } if submission_type == "deadline": - self.DEADLINE_REST_URL = os.environ.get( - "DEADLINE_REST_URL", "http://localhost:8082" + self.deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] ) - assert self.DEADLINE_REST_URL, "Requires DEADLINE_REST_URL" + assert self.deadline_url, "Requires DEADLINE_REST_URL" self._submit_deadline_post_job(instance, render_job, instances) diff --git a/pype/plugins/maya/create/create_render.py b/pype/plugins/maya/create/create_render.py index fa0e269126..a3700e2f60 100644 --- a/pype/plugins/maya/create/create_render.py +++ b/pype/plugins/maya/create/create_render.py @@ -9,6 +9,7 @@ from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup from pype.hosts.maya import lib +from pype.api import get_system_settings import avalon.maya @@ -124,8 +125,11 @@ class CreateRender(avalon.maya.Creator): # get pools pools = [] - deadline_url = os.environ.get("DEADLINE_REST_URL", None) - muster_url = os.environ.get("MUSTER_REST_URL", None) + system_settings = get_system_settings()["modules"] + + deadline_url = system_settings["deadline"]["DEADLINE_REST_URL"] + muster_url = system_settings["muster"]["MUSTER_REST_URL"] + if deadline_url and muster_url: self.log.error( "Both Deadline and Muster are enabled. " "Cannot support both." diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 0ae19cbb81..e1cfe5591c 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -238,11 +238,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): """Submit available render layers to Deadline. Renders are submitted to a Deadline Web Service as - supplied via the environment variable ``DEADLINE_REST_URL``. - - Note: - If Deadline configuration is not detected, this plugin will - be disabled. + supplied via settings key "DEADLINE_REST_URL". Attributes: use_published (bool): Use published scene to render instead of the @@ -254,11 +250,6 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): order = pyblish.api.IntegratorOrder + 0.1 hosts = ["maya"] families = ["renderlayer"] - if not os.environ.get("DEADLINE_REST_URL"): - optional = False - active = False - else: - optional = True use_published = True tile_assembler_plugin = "PypeTileAssembler" @@ -267,9 +258,16 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): def process(self, instance): """Plugin entry point.""" instance.data["toBeRenderedOn"] = "deadline" + context = instance.context + self._instance = instance - self._deadline_url = os.environ.get( - "DEADLINE_REST_URL", "http://localhost:8082") + self._deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] + ) + assert self._deadline_url, "Requires DEADLINE_REST_URL" context = instance.context diff --git a/pype/plugins/maya/publish/validate_deadline_connection.py b/pype/plugins/maya/publish/validate_deadline_connection.py index f9c11620ba..0733c3badf 100644 --- a/pype/plugins/maya/publish/validate_deadline_connection.py +++ b/pype/plugins/maya/publish/validate_deadline_connection.py @@ -12,8 +12,6 @@ class ValidateDeadlineConnection(pyblish.api.ContextPlugin): order = pyblish.api.ValidatorOrder hosts = ["maya"] families = ["renderlayer"] - if not os.environ.get("DEADLINE_REST_URL"): - active = False def process(self, context): @@ -21,14 +19,15 @@ class ValidateDeadlineConnection(pyblish.api.ContextPlugin): if not contextplugin_should_run(self, context): return - try: - DEADLINE_REST_URL = os.environ["DEADLINE_REST_URL"] - except KeyError: - self.log.error("Deadline REST API url not found.") - raise ValueError("Deadline REST API url not found.") + deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] + ) # Check response - response = self._requests_get(DEADLINE_REST_URL) + response = self._requests_get(deadline_url) assert response.ok, "Response must be ok" assert response.text.startswith("Deadline Web Service "), ( "Web service did not respond with 'Deadline Web Service'" diff --git a/pype/plugins/nuke/publish/submit_nuke_deadline.py b/pype/plugins/nuke/publish/submit_nuke_deadline.py index 2c7d468d3a..5e5efb6e62 100644 --- a/pype/plugins/nuke/publish/submit_nuke_deadline.py +++ b/pype/plugins/nuke/publish/submit_nuke_deadline.py @@ -12,7 +12,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): """Submit write to Deadline Renders are submitted to a Deadline Web Service as - supplied via the environment variable DEADLINE_REST_URL + supplied via settings key "DEADLINE_REST_URL". """ @@ -34,11 +34,15 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin): node = instance[0] context = instance.context - DEADLINE_REST_URL = os.environ.get("DEADLINE_REST_URL", - "http://localhost:8082") - assert DEADLINE_REST_URL, "Requires DEADLINE_REST_URL" + deadline_url = ( + context.data["system_settings"] + ["modules"] + ["deadline"] + ["DEADLINE_REST_URL"] + ) + assert deadline_url, "Requires DEADLINE_REST_URL" - self.deadline_url = "{}/api/jobs".format(DEADLINE_REST_URL) + self.deadline_url = "{}/api/jobs".format(deadline_url) self._comment = context.data.get("comment", "") self._ver = re.search(r"\d+\.\d+", context.data.get("hostVersion")) self._deadline_user = context.data.get( diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index 9193ea2b52..10e3db2dd7 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -109,7 +109,7 @@ }, "publish": { "CollectMayaRender": { - "sync_workfile_version": true + "sync_workfile_version": false }, "ValidateCameraAttributes": { "enabled": true, @@ -134,6 +134,9 @@ "ValidateMeshHasOverlappingUVs": { "enabled": false }, + "ValidateAttributes": { + "enabled": false + }, "ExtractCameraAlembic": { "enabled": true, "optional": true, @@ -316,4 +319,4 @@ "ValidateNoAnimation": false } } -} +} \ No newline at end of file diff --git a/pype/settings/defaults/project_settings/standalonepublisher.json b/pype/settings/defaults/project_settings/standalonepublisher.json index b8015f2832..67b4826692 100644 --- a/pype/settings/defaults/project_settings/standalonepublisher.json +++ b/pype/settings/defaults/project_settings/standalonepublisher.json @@ -123,4 +123,4 @@ "help": "Script exported from matchmoving application" } } -} +} \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index a36a3b75cf..74268c9254 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -169,11 +169,11 @@ "enabled": false, "workspace_name": "studio name" }, - "Deadline": { + "deadline": { "enabled": true, "DEADLINE_REST_URL": "http://localhost:8082" }, - "Muster": { + "muster": { "enabled": false, "MUSTER_REST_URL": "http://127.0.0.1:9890", "templates_mapping": { diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json index 86c6f2963e..e9e3e26c60 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_publish.json @@ -344,12 +344,12 @@ "label": "Deadline Group" }, { - "type": "text", + "type": "number", "key": "deadline_chunk_size", "label": "Deadline Chunk Size" }, { - "type": "text", + "type": "number", "key": "deadline_priority", "label": "Deadline Priotity" }, diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json index 31eaab2ede..62aaafc27b 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/schema_modules.json @@ -100,7 +100,7 @@ ] }, { "type": "dict", - "key": "Deadline", + "key": "deadline", "label": "Deadline", "collapsable": true, "checkbox_key": "enabled", @@ -115,7 +115,7 @@ }] }, { "type": "dict", - "key": "Muster", + "key": "muster", "label": "Muster", "collapsable": true, "checkbox_key": "enabled", @@ -126,7 +126,7 @@ }, { "type": "text", "key": "MUSTER_REST_URL", - "label": "Muster Resl URL" + "label": "Muster Rest URL" }, { "type": "dict-modifiable", "object_type": { From 7c08d8cd92c0fd74329d8717ca4ecf090755f2ef Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 15:41:42 +0100 Subject: [PATCH 23/71] tweak deadline settings and remove collect_presets plugin --- .../plugins/global/publish/collect_presets.py | 24 ------------------- .../defaults/project_settings/global.json | 4 ++-- 2 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 pype/plugins/global/publish/collect_presets.py diff --git a/pype/plugins/global/publish/collect_presets.py b/pype/plugins/global/publish/collect_presets.py deleted file mode 100644 index 95fb4dbfad..0000000000 --- a/pype/plugins/global/publish/collect_presets.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Requires: - config_data -> colorspace.default - config_data -> dataflow.default - -Provides: - context -> presets -""" - -from pyblish import api -from pype.api import get_current_project_settings - - -class CollectPresets(api.ContextPlugin): - """Collect Presets.""" - - order = api.CollectorOrder - 0.491 - label = "Collect Presets" - - def process(self, context): - project_settings = get_current_project_settings() - context.data["presets"] = project_settings - - return diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json index 5f76f2d0f6..6661729e3d 100644 --- a/pype/settings/defaults/project_settings/global.json +++ b/pype/settings/defaults/project_settings/global.json @@ -94,8 +94,8 @@ "deadline_department": "", "deadline_pool": "", "deadline_group": "", - "deadline_chunk_size": "", - "deadline_priority": "", + "deadline_chunk_size": 1, + "deadline_priority": 50, "aov_filter": { "maya": [ ".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*" From 318e8d60ea23d0e2b7cc1e5ffecf566001ad2d64 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 15:42:09 +0100 Subject: [PATCH 24/71] disable attribute validator in maya by default --- .../schemas/schema_maya_publish.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json index 33d0a06d2c..1cd526adb9 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json @@ -123,6 +123,19 @@ "label": "Enabled" }] }, + { + "type": "dict", + "collapsable": true, + "key": "ValidateAttributes", + "label": "ValidateAttributes", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, { "type": "splitter" }, From 8b7b62c044350d94e9620ed3633d9868de8cdd60 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 16:40:59 +0100 Subject: [PATCH 25/71] add acre to sys path in igniter as pype.py has invalid relative path to repos --- igniter/tools.py | 33 +++++++++++++++++++++++---------- pype.py | 15 ++++----------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/igniter/tools.py b/igniter/tools.py index ea130b29d7..a06c444e62 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -81,6 +81,26 @@ def validate_path_string(path: str) -> (bool, str): return False, "Not implemented yet" +def add_acre_to_sys_path(): + """Add full path of acre module to sys.path on ignitation.""" + try: + # Skip if is possible to import + import acre + + except ImportError: + # Full path to acred repository related to current file + acre_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "repos", + "acre" + ) + # Add path to sys.path + sys.path.append(acre_dir) + + # Validate that acre can be imported + import acre + + def load_environments(sections: list = None) -> dict: """Load environments from Pype. @@ -94,16 +114,9 @@ def load_environments(sections: list = None) -> dict: dict of str: loaded and processed environments. """ - try: - import acre - except ImportError: - acre_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), - "repos", - "acre" - ) - sys.path.append(acre_dir) - import acre + add_acre_to_sys_path() + import acre + from pype import settings all_env = settings.get_environments() diff --git a/pype.py b/pype.py index c0b91303ae..28177dbdd4 100644 --- a/pype.py +++ b/pype.py @@ -44,20 +44,13 @@ import re import sys import traceback -from igniter.tools import load_environments +from igniter.tools import load_environments, add_acre_to_sys_path -try: - import acre -except ImportError: - acre_dir = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "repos", - "acre" - ) - sys.path.append(acre_dir) - import acre from igniter import BootstrapRepos +add_acre_to_sys_path() +import acre + def set_environments() -> None: """Set loaded environments. From 69b74856122f32b16d9336e972a7ec4904f9f7df Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 17:01:09 +0100 Subject: [PATCH 26/71] use `postlaunch_hook` instead of `prelaunch_hook` and use their classes names instead of their "representation" string --- pype/lib/applications.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index bee51d0570..70b33d6edf 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -707,7 +707,7 @@ class ApplicationLaunchContext: # Execute prelaunch hooks for prelaunch_hook in self.prelaunch_hooks: self.log.debug("Executing prelaunch hook: {}".format( - str(prelaunch_hook) + str(prelaunch_hook.__class__.__name__) )) prelaunch_hook.execute() @@ -726,7 +726,7 @@ class ApplicationLaunchContext: # Process post launch hooks for postlaunch_hook in self.postlaunch_hooks: self.log.debug("Executing postlaunch hook: {}".format( - str(prelaunch_hook) + str(postlaunch_hook.__class__.__name__) )) # TODO how to handle errors? From 4a300e0b087fb40e044a99f3a0d0d5c3835f0a19 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 18:08:12 +0100 Subject: [PATCH 27/71] maya uses settings from context instead of presets --- pype/plugins/maya/create/create_render.py | 2 +- .../plugins/maya/create/create_rendersetup.py | 2 +- .../maya/publish/extract_camera_mayaScene.py | 7 +- .../maya/publish/extract_maya_scene_raw.py | 6 +- pype/plugins/maya/publish/extract_model.py | 6 +- .../plugins/maya/publish/extract_playblast.py | 4 +- pype/plugins/maya/publish/extract_rig.py | 6 +- .../plugins/maya/publish/extract_thumbnail.py | 5 +- pype/plugins/maya/publish/extract_yeti_rig.py | 6 +- .../maya/publish/submit_maya_muster.py | 2 +- .../maya/publish/validate_attributes.py | 14 +- .../publish/validate_muster_connection.py | 2 +- .../defaults/project_settings/maya.json | 3 +- .../schemas/schema_maya_capture.json | 1058 +++++++++-------- .../schemas/schema_maya_publish.json | 8 +- 15 files changed, 617 insertions(+), 514 deletions(-) diff --git a/pype/plugins/maya/create/create_render.py b/pype/plugins/maya/create/create_render.py index a3700e2f60..bdd237a54e 100644 --- a/pype/plugins/maya/create/create_render.py +++ b/pype/plugins/maya/create/create_render.py @@ -202,7 +202,7 @@ class CreateRender(avalon.maya.Creator): """Load Muster credentials. Load Muster credentials from file and set ``MUSTER_USER``, - ``MUSTER_PASSWORD``, ``MUSTER_REST_URL`` is loaded from presets. + ``MUSTER_PASSWORD``, ``MUSTER_REST_URL`` is loaded from settings. Raises: RuntimeError: If loaded credentials are invalid. diff --git a/pype/plugins/maya/create/create_rendersetup.py b/pype/plugins/maya/create/create_rendersetup.py index 969c085ea6..40e3731bf0 100644 --- a/pype/plugins/maya/create/create_rendersetup.py +++ b/pype/plugins/maya/create/create_rendersetup.py @@ -15,7 +15,7 @@ class CreateRenderSetup(avalon.maya.Creator): super(CreateRenderSetup, self).__init__(*args, **kwargs) # here we can pre-create renderSetup layers, possibly utlizing - # presets for it. + # settings for it. # _____ # / __\__ diff --git a/pype/plugins/maya/publish/extract_camera_mayaScene.py b/pype/plugins/maya/publish/extract_camera_mayaScene.py index 0443357ba9..a1854b1db0 100644 --- a/pype/plugins/maya/publish/extract_camera_mayaScene.py +++ b/pype/plugins/maya/publish/extract_camera_mayaScene.py @@ -102,10 +102,11 @@ class ExtractCameraMayaScene(pype.api.Extractor): def process(self, instance): """Plugin entry point.""" # get settings - ext_mapping = (instance.context.data["presets"]["maya"] - .get("ext_mapping")) # noqa: E501 + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) if ext_mapping: - self.log.info("Looking in presets for scene type ...") + self.log.info("Looking in settings for scene type ...") # use extension mapping for first family found for family in self.families: try: diff --git a/pype/plugins/maya/publish/extract_maya_scene_raw.py b/pype/plugins/maya/publish/extract_maya_scene_raw.py index 0e256bc69f..8df9f8e715 100644 --- a/pype/plugins/maya/publish/extract_maya_scene_raw.py +++ b/pype/plugins/maya/publish/extract_maya_scene_raw.py @@ -24,9 +24,11 @@ class ExtractMayaSceneRaw(pype.api.Extractor): def process(self, instance): """Plugin entry point.""" - ext_mapping = instance.context.data["presets"]["maya"].get("ext_mapping") # noqa: E501 + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) if ext_mapping: - self.log.info("Looking in presets for scene type ...") + self.log.info("Looking in settings for scene type ...") # use extension mapping for first family found for family in self.families: try: diff --git a/pype/plugins/maya/publish/extract_model.py b/pype/plugins/maya/publish/extract_model.py index d77e65f989..8276d1a006 100644 --- a/pype/plugins/maya/publish/extract_model.py +++ b/pype/plugins/maya/publish/extract_model.py @@ -31,9 +31,11 @@ class ExtractModel(pype.api.Extractor): def process(self, instance): """Plugin entry point.""" - ext_mapping = instance.context.data["presets"]["maya"].get("ext_mapping") # noqa: E501 + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) if ext_mapping: - self.log.info("Looking in presets for scene type ...") + self.log.info("Looking in settings for scene type ...") # use extension mapping for first family found for family in self.families: try: diff --git a/pype/plugins/maya/publish/extract_playblast.py b/pype/plugins/maya/publish/extract_playblast.py index 647d1f4503..82795ab9df 100644 --- a/pype/plugins/maya/publish/extract_playblast.py +++ b/pype/plugins/maya/publish/extract_playblast.py @@ -43,7 +43,9 @@ class ExtractPlayblast(pype.api.Extractor): # get cameras camera = instance.data['review_camera'] - capture_preset = instance.context.data['presets']['maya']['capture'] + capture_preset = ( + instance.context.data['project_settings']['maya']['capture'] + ) try: preset = lib.load_capture_preset(data=capture_preset) diff --git a/pype/plugins/maya/publish/extract_rig.py b/pype/plugins/maya/publish/extract_rig.py index 8ebeae4184..fd7fc051aa 100644 --- a/pype/plugins/maya/publish/extract_rig.py +++ b/pype/plugins/maya/publish/extract_rig.py @@ -18,9 +18,11 @@ class ExtractRig(pype.api.Extractor): def process(self, instance): """Plugin entry point.""" - ext_mapping = instance.context.data["presets"]["maya"].get("ext_mapping") # noqa: E501 + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) if ext_mapping: - self.log.info("Looking in presets for scene type ...") + self.log.info("Looking in settings for scene type ...") # use extension mapping for first family found for family in self.families: try: diff --git a/pype/plugins/maya/publish/extract_thumbnail.py b/pype/plugins/maya/publish/extract_thumbnail.py index 524fc1e17c..6956341ffd 100644 --- a/pype/plugins/maya/publish/extract_thumbnail.py +++ b/pype/plugins/maya/publish/extract_thumbnail.py @@ -33,7 +33,10 @@ class ExtractThumbnail(pype.api.Extractor): camera = instance.data['review_camera'] capture_preset = "" - capture_preset = instance.context.data['presets']['maya']['capture'] + capture_preset = ( + instance.context.data["project_settings"]['maya']['capture'] + ) + try: preset = lib.load_capture_preset(data=capture_preset) except: diff --git a/pype/plugins/maya/publish/extract_yeti_rig.py b/pype/plugins/maya/publish/extract_yeti_rig.py index d48a956b88..71d4cf6198 100644 --- a/pype/plugins/maya/publish/extract_yeti_rig.py +++ b/pype/plugins/maya/publish/extract_yeti_rig.py @@ -101,9 +101,11 @@ class ExtractYetiRig(pype.api.Extractor): def process(self, instance): """Plugin entry point.""" - ext_mapping = instance.context.data["presets"]["maya"].get("ext_mapping") # noqa: E501 + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) if ext_mapping: - self.log.info("Looking in presets for scene type ...") + self.log.info("Looking in settings for scene type ...") # use extension mapping for first family found for family in self.families: try: diff --git a/pype/plugins/maya/publish/submit_maya_muster.py b/pype/plugins/maya/publish/submit_maya_muster.py index 9c67b45721..37bb3ad4c8 100644 --- a/pype/plugins/maya/publish/submit_maya_muster.py +++ b/pype/plugins/maya/publish/submit_maya_muster.py @@ -153,7 +153,7 @@ class MayaSubmitMuster(pyblish.api.InstancePlugin): def _load_credentials(self): """ Load Muster credentials from file and set `MUSTER_USER`, - `MUSTER_PASSWORD`, `MUSTER_REST_URL` is loaded from presets. + `MUSTER_PASSWORD`, `MUSTER_REST_URL` is loaded from settings. .. todo:: diff --git a/pype/plugins/maya/publish/validate_attributes.py b/pype/plugins/maya/publish/validate_attributes.py index a77fbe5e93..97e63a475b 100644 --- a/pype/plugins/maya/publish/validate_attributes.py +++ b/pype/plugins/maya/publish/validate_attributes.py @@ -22,9 +22,12 @@ class ValidateAttributes(pyblish.api.ContextPlugin): actions = [pype.api.RepairContextAction] optional = True + attributes = None + def process(self, context): # Check for preset existence. - if not context.data["presets"]["maya"].get("attributes"): + + if not self.attributes: return invalid = self.get_invalid(context, compute=True) @@ -43,7 +46,6 @@ class ValidateAttributes(pyblish.api.ContextPlugin): @classmethod def get_invalid_attributes(cls, context): - presets = context.data["presets"]["maya"]["attributes"] invalid_attributes = [] for instance in context: # Filter publisable instances. @@ -53,23 +55,23 @@ class ValidateAttributes(pyblish.api.ContextPlugin): # Filter families. families = [instance.data["family"]] families += instance.data.get("families", []) - families = list(set(families) & set(presets.keys())) + families = list(set(families) & set(self.attributes.keys())) if not families: continue # Get all attributes to validate. attributes = {} for family in families: - for preset in presets[family]: + for preset in self.attributes[family]: [node_name, attribute_name] = preset.split(".") try: attributes[node_name].update( - {attribute_name: presets[family][preset]} + {attribute_name: self.attributes[family][preset]} ) except KeyError: attributes.update({ node_name: { - attribute_name: presets[family][preset] + attribute_name: self.attributes[family][preset] } }) diff --git a/pype/plugins/maya/publish/validate_muster_connection.py b/pype/plugins/maya/publish/validate_muster_connection.py index d125542cda..35acdb326f 100644 --- a/pype/plugins/maya/publish/validate_muster_connection.py +++ b/pype/plugins/maya/publish/validate_muster_connection.py @@ -58,7 +58,7 @@ class ValidateMusterConnection(pyblish.api.ContextPlugin): def _load_credentials(self): """ Load Muster credentials from file and set `MUSTER_USER`, - `MUSTER_PASSWORD`, `MUSTER_REST_URL` is loaded from presets. + `MUSTER_PASSWORD`, `MUSTER_REST_URL` is loaded from settings. .. todo:: diff --git a/pype/settings/defaults/project_settings/maya.json b/pype/settings/defaults/project_settings/maya.json index 10e3db2dd7..c779d495c4 100644 --- a/pype/settings/defaults/project_settings/maya.json +++ b/pype/settings/defaults/project_settings/maya.json @@ -1,5 +1,5 @@ { - "maya_capture": { + "capture": { "Codec": { "compression": "jpg", "format": "image", @@ -107,6 +107,7 @@ "overscan": 1.0 } }, + "ext_mapping": {}, "publish": { "CollectMayaRender": { "sync_workfile_version": false diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json index 314fdc7514..8c2c6c1884 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_capture.json @@ -1,506 +1,586 @@ { - "type": "dict", - "collapsable": true, - "key": "maya_capture", - "label": "Maya Playblast settings", - "is_file": true, + "type": "collapsible-wrap", + "label": "Collapsible Wrapper without key", "children": [ - { + { "type": "dict", - "key": "Codec", + "collapsable": true, + "key": "capture", + "label": "Maya Playblast settings", + "is_file": true, "children": [ - { - "type": "label", - "label": "Codec" - }, - { - "type": "text", - "key": "compression", - "label": "Compression type" - }, { - "type": "text", - "key": "format", - "label": "Data format" - }, { - "type": "number", - "key": "quality", - "label": "Quality", - "decimal": 0, - "minimum": 0, - "maximum": 100 - }, + { + "type": "dict", + "key": "Codec", + "children": [ + { + "type": "label", + "label": "Codec" + }, + { + "type": "text", + "key": "compression", + "label": "Compression type" + }, + { + "type": "text", + "key": "format", + "label": "Data format" + }, + { + "type": "number", + "key": "quality", + "label": "Quality", + "decimal": 0, + "minimum": 0, + "maximum": 100 + }, - { - "type": "splitter" - } - ] - }, { - "type": "dict", - "key": "Display Options", - "children": [ - { - "type": "label", - "label": "Display Options" - }, - { - "type": "list-strict", - "key": "background", - "label": "Background Color: ", - "object_types": [ - { - "label": "Red", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, { - "label": "Green", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, { - "label": "Blue", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - } - ] - }, { + { + "type": "splitter" + } + ] + }, + { + "type": "dict", + "key": "Display Options", + "children": [ + { + "type": "label", + "label": "Display Options" + }, + { + "type": "list-strict", + "key": "background", + "label": "Background Color: ", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }] + }, + { "type": "list-strict", "key": "backgroundBottom", "label": "Background Bottom: ", "object_types": [ - { - "label": "Red", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, { - "label": "Green", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, { - "label": "Blue", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - } - ] - }, { - "type": "list-strict", - "key": "backgroundTop", - "label": "Background Top: ", - "object_types": [ - { - "label": "Red", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, { - "label": "Green", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - }, { - "label": "Blue", - "type": "number", - "minimum": 0, - "maximum": 1, - "decimal": 3 - } - ] - }, { - "type": "boolean", - "key": "override_display", - "label": "Override display options" - } - ] - }, - { - "type": "splitter" - } , - { - "type": "dict", - "key": "Generic", - "children": [ - { - "type": "label", - "label": "Generic" - }, - { - "type": "boolean", - "key": "isolate_view", - "label": " Isolate view" - },{ - "type": "boolean", - "key": "off_screen", - "label": " Off Screen" - } - ] - },{ - "type": "dict", - "key": "IO", - "children": [ - { - "type": "label", - "label": "IO" - }, - { - "type": "text", - "key": "name", - "label": "Name" - },{ - "type": "boolean", - "key": "open_finished", - "label": "Open finished" - },{ - "type": "boolean", - "key": "raw_frame_numbers", - "label": "Raw frame numbers" - },{ - "type": "list", - "key": "recent_playblasts", - "label": "Recent Playblasts", - "object_type": "text" - },{ - "type": "boolean", - "key": "save_file", - "label": "Save file" - } - ] - },{ - "type": "dict", - "key": "PanZoom", - "children": [ - { - "type": "boolean", - "key": "pan_zoom", - "label": " Pan Zoom" - } - ] - }, - { - "type": "splitter" - },{ - "type": "dict", - "key": "Renderer", - "children": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }] + }, + { + "type": "list-strict", + "key": "backgroundTop", + "label": "Background Top: ", + "object_types": [ + { + "label": "Red", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Green", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }, + { + "label": "Blue", + "type": "number", + "minimum": 0, + "maximum": 1, + "decimal": 3 + }] + }, + { + "type": "boolean", + "key": "override_display", + "label": "Override display options" + }] + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "Generic", + "children": [ + { + "type": "label", + "label": "Generic" + }, + { + "type": "boolean", + "key": "isolate_view", + "label": " Isolate view" + }, + { + "type": "boolean", + "key": "off_screen", + "label": " Off Screen" + }] + }, + { + "type": "dict", + "key": "IO", + "children": [ + { + "type": "label", + "label": "IO" + }, + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "boolean", + "key": "open_finished", + "label": "Open finished" + }, + { + "type": "boolean", + "key": "raw_frame_numbers", + "label": "Raw frame numbers" + }, + { + "type": "list", + "key": "recent_playblasts", + "label": "Recent Playblasts", + "object_type": "text" + }, + { + "type": "boolean", + "key": "save_file", + "label": "Save file" + }] + }, + { + "type": "dict", + "key": "PanZoom", + "children": [ + { + "type": "boolean", + "key": "pan_zoom", + "label": " Pan Zoom" + }] + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "Renderer", + "children": [ - { - "type": "label", - "label": "Renderer" - }, - { - "type": "text", - "key": "rendererName", - "label": " Renderer name" - } - ] - },{ - "type": "dict", - "key": "Resolution", - "children": [ + { + "type": "label", + "label": "Renderer" + }, + { + "type": "text", + "key": "rendererName", + "label": " Renderer name" + } + ] + }, + { + "type": "dict", + "key": "Resolution", + "children": [ - { - "type": "splitter" - }, - { - "type": "label", - "label": "Resolution" - }, - { - "type": "number", - "key": "width", - "label": " Width", - "decimal": 0, - "minimum": 0, - "maximum": 99999 - },{ - "type": "number", - "key": "height", - "label": "Height", - "decimal": 0, - "minimum": 0, - "maximum": 99999 - },{ - "type": "number", - "key": "percent", - "label": "percent", - "decimal": 1, - "minimum": 0, - "maximum": 200 - },{ - "type": "text", - "key": "mode", - "label": "Mode" - } - ] - }, - { - "type": "splitter" + { + "type": "splitter" + }, + { + "type": "label", + "label": "Resolution" + }, + { + "type": "number", + "key": "width", + "label": " Width", + "decimal": 0, + "minimum": 0, + "maximum": 99999 + }, + { + "type": "number", + "key": "height", + "label": "Height", + "decimal": 0, + "minimum": 0, + "maximum": 99999 + }, + { + "type": "number", + "key": "percent", + "label": "percent", + "decimal": 1, + "minimum": 0, + "maximum": 200 + }, + { + "type": "text", + "key": "mode", + "label": "Mode" + } + ] + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "Time Range", + "children": [ + { + "type": "label", + "label": "Time Range" + }, + { + "type": "number", + "key": "start_frame", + "label": " Start frame", + "decimal": 0, + "minimum": 0, + "maximum": 999999 + }, + { + "type": "number", + "key": "end_frame", + "label": "End frame", + "decimal": 0, + "minimum": 0, + "maximum": 999999 + }, + { + "type": "text", + "key": "frame", + "label": "Frame" + }, + { + "type": "text", + "key": "time", + "label": "Time" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "Viewport Options", + "label": "Viewport Options", + "children": [ + { + "type": "boolean", + "key": "cameras", + "label": "cameras" + }, + { + "type": "boolean", + "key": "clipGhosts", + "label": "clipGhosts" + }, + { + "type": "boolean", + "key": "controlVertices", + "label": "controlVertices" + }, + { + "type": "boolean", + "key": "deformers", + "label": "deformers" + }, + { + "type": "boolean", + "key": "dimensions", + "label": "dimensions" + }, + { + "type": "number", + "key": "displayLights", + "label": "displayLights", + "decimal": 0, + "minimum": 0, + "maximum": 10 + }, + { + "type": "boolean", + "key": "dynamicConstraints", + "label": "dynamicConstraints" + }, + { + "type": "boolean", + "key": "dynamics", + "label": "dynamics" + }, + { + "type": "boolean", + "key": "fluids", + "label": "fluids" + }, + { + "type": "boolean", + "key": "follicles", + "label": "follicles" + }, + { + "type": "boolean", + "key": "gpuCacheDisplayFilter", + "label": "gpuCacheDisplayFilter" + }, + { + "type": "boolean", + "key": "greasePencils", + "label": "greasePencils" + }, + { + "type": "boolean", + "key": "grid", + "label": "grid" + }, + { + "type": "boolean", + "key": "hairSystems", + "label": "hairSystems" + }, + { + "type": "boolean", + "key": "handles", + "label": "handles" + }, + { + "type": "boolean", + "key": "high_quality", + "label": "high_quality" + }, + { + "type": "boolean", + "key": "hud", + "label": "hud" + }, + { + "type": "boolean", + "key": "hulls", + "label": "hulls" + }, + { + "type": "boolean", + "key": "ikHandles", + "label": "ikHandles" + }, + { + "type": "boolean", + "key": "imagePlane", + "label": "imagePlane" + }, + { + "type": "boolean", + "key": "joints", + "label": "joints" + }, + { + "type": "boolean", + "key": "lights", + "label": "lights" + }, + { + "type": "boolean", + "key": "locators", + "label": "locators" + }, + { + "type": "boolean", + "key": "manipulators", + "label": "manipulators" + }, + { + "type": "boolean", + "key": "motionTrails", + "label": "motionTrails" + }, + { + "type": "boolean", + "key": "nCloths", + "label": "nCloths" + }, + { + "type": "boolean", + "key": "nParticles", + "label": "nParticles" + }, + { + "type": "boolean", + "key": "nRigids", + "label": "nRigids" + }, + { + "type": "boolean", + "key": "nurbsCurves", + "label": "nurbsCurves" + }, + { + "type": "boolean", + "key": "nurbsSurfaces", + "label": "nurbsSurfaces" + }, + { + "type": "boolean", + "key": "override_viewport_options", + "label": "override_viewport_options" + }, + { + "type": "boolean", + "key": "particleInstancers", + "label": "particleInstancers" + }, + { + "type": "boolean", + "key": "pivots", + "label": "pivots" + }, + { + "type": "boolean", + "key": "planes", + "label": "planes" + }, + { + "type": "boolean", + "key": "pluginShapes", + "label": "pluginShapes" + }, + { + "type": "boolean", + "key": "polymeshes", + "label": "polymeshes" + }, + { + "type": "boolean", + "key": "shadows", + "label": "shadows" + }, + { + "type": "boolean", + "key": "strokes", + "label": "strokes" + }, + { + "type": "boolean", + "key": "subdivSurfaces", + "label": "subdivSurfaces" + }, + { + "type": "boolean", + "key": "textures", + "label": "textures" + }, + { + "type": "boolean", + "key": "twoSidedLighting", + "label": "twoSidedLighting" + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "Camera Options", + "label": "Camera Options", + "children": [ + { + "type": "boolean", + "key": "displayGateMask", + "label": "displayGateMask" + }, + { + "type": "boolean", + "key": "displayResolution", + "label": "displayResolution" + }, + { + "type": "boolean", + "key": "displayFilmGate", + "label": "displayFilmGate" + }, + { + "type": "boolean", + "key": "displayFieldChart", + "label": "displayFieldChart" + }, + { + "type": "boolean", + "key": "displaySafeAction", + "label": "displaySafeAction" + }, + { + "type": "boolean", + "key": "displaySafeTitle", + "label": "displaySafeTitle" + }, + { + "type": "boolean", + "key": "displayFilmPivot", + "label": "displayFilmPivot" + }, + { + "type": "boolean", + "key": "displayFilmOrigin", + "label": "displayFilmOrigin" + }, + { + "type": "number", + "key": "overscan", + "label": "overscan", + "decimal": 1, + "minimum": 0, + "maximum": 10 + }] + }] }, { - "type": "dict", - "key": "Time Range", - "children": [ - { - "type": "label", - "label": "Time Range" - }, - { - "type": "number", - "key": "start_frame", - "label": " Start frame", - "decimal": 0, - "minimum": 0, - "maximum": 999999 - },{ - "type": "number", - "key": "end_frame", - "label": "End frame", - "decimal": 0, - "minimum": 0, - "maximum": 999999 - },{ - "type": "text", - "key": "frame", - "label": "Frame" - },{ - "type": "text", - "key": "time", - "label": "Time" + "type": "dict-modifiable", + "key": "ext_mapping", + "label": "Extension Mapping", + "object_type":{ + "type": "text" } - ] - },{ - "type": "dict", - "collapsable": true, - "key": "Viewport Options", - "label": "Viewport Options", - "children": [ - { - "type": "boolean", - "key": "cameras", - "label": "cameras" - },{ - "type": "boolean", - "key": "clipGhosts", - "label": "clipGhosts" - },{ - "type": "boolean", - "key": "controlVertices", - "label": "controlVertices" - },{ - "type": "boolean", - "key": "deformers", - "label": "deformers" - },{ - "type": "boolean", - "key": "dimensions", - "label": "dimensions" - },{ - "type": "number", - "key": "displayLights", - "label": "displayLights", - "decimal": 0, - "minimum": 0, - "maximum": 10 - },{ - "type": "boolean", - "key": "dynamicConstraints", - "label": "dynamicConstraints" - },{ - "type": "boolean", - "key": "dynamics", - "label": "dynamics" - },{ - "type": "boolean", - "key": "fluids", - "label": "fluids" - },{ - "type": "boolean", - "key": "follicles", - "label": "follicles" - },{ - "type": "boolean", - "key": "gpuCacheDisplayFilter", - "label": "gpuCacheDisplayFilter" - },{ - "type": "boolean", - "key": "greasePencils", - "label": "greasePencils" - },{ - "type": "boolean", - "key": "grid", - "label": "grid" - },{ - "type": "boolean", - "key": "hairSystems", - "label": "hairSystems" - },{ - "type": "boolean", - "key": "handles", - "label": "handles" - },{ - "type": "boolean", - "key": "high_quality", - "label": "high_quality" - },{ - "type": "boolean", - "key": "hud", - "label": "hud" - },{ - "type": "boolean", - "key": "hulls", - "label": "hulls" - },{ - "type": "boolean", - "key": "ikHandles", - "label": "ikHandles" - },{ - "type": "boolean", - "key": "imagePlane", - "label": "imagePlane" - },{ - "type": "boolean", - "key": "joints", - "label": "joints" - },{ - "type": "boolean", - "key": "lights", - "label": "lights" - },{ - "type": "boolean", - "key": "locators", - "label": "locators" - },{ - "type": "boolean", - "key": "manipulators", - "label": "manipulators" - },{ - "type": "boolean", - "key": "motionTrails", - "label": "motionTrails" - },{ - "type": "boolean", - "key": "nCloths", - "label": "nCloths" - },{ - "type": "boolean", - "key": "nParticles", - "label": "nParticles" - },{ - "type": "boolean", - "key": "nRigids", - "label": "nRigids" - },{ - "type": "boolean", - "key": "nurbsCurves", - "label": "nurbsCurves" - },{ - "type": "boolean", - "key": "nurbsSurfaces", - "label": "nurbsSurfaces" - },{ - "type": "boolean", - "key": "override_viewport_options", - "label": "override_viewport_options" - },{ - "type": "boolean", - "key": "particleInstancers", - "label": "particleInstancers" - },{ - "type": "boolean", - "key": "pivots", - "label": "pivots" - },{ - "type": "boolean", - "key": "planes", - "label": "planes" - },{ - "type": "boolean", - "key": "pluginShapes", - "label": "pluginShapes" - },{ - "type": "boolean", - "key": "polymeshes", - "label": "polymeshes" - },{ - "type": "boolean", - "key": "shadows", - "label": "shadows" - },{ - "type": "boolean", - "key": "strokes", - "label": "strokes" - },{ - "type": "boolean", - "key": "subdivSurfaces", - "label": "subdivSurfaces" - },{ - "type": "boolean", - "key": "textures", - "label": "textures" - },{ - "type": "boolean", - "key": "twoSidedLighting", - "label": "twoSidedLighting" - } - ] - },{ - "type": "dict", - "collapsable": true, - "key": "Camera Options", - "label": "Camera Options", - "children": [ - { - "type": "boolean", - "key": "displayGateMask", - "label": "displayGateMask" - },{ - "type": "boolean", - "key": "displayResolution", - "label": "displayResolution" - },{ - "type": "boolean", - "key": "displayFilmGate", - "label": "displayFilmGate" - },{ - "type": "boolean", - "key": "displayFieldChart", - "label": "displayFieldChart" - },{ - "type": "boolean", - "key": "displaySafeAction", - "label": "displaySafeAction" - },{ - "type": "boolean", - "key": "displaySafeTitle", - "label": "displaySafeTitle" - },{ - "type": "boolean", - "key": "displayFilmPivot", - "label": "displayFilmPivot" - },{ - "type": "boolean", - "key": "displayFilmOrigin", - "label": "displayFilmOrigin" - },{ - "type": "number", - "key": "overscan", - "label": "overscan", - "decimal": 1, - "minimum": 0, - "maximum": 10 - } - ] } ] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json index 1cd526adb9..e5d66045d4 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_maya_publish.json @@ -134,7 +134,13 @@ "type": "boolean", "key": "enabled", "label": "Enabled" - }] + }, + { + "type": "raw-json", + "key": "attributes", + "label": "Attributes" + } + ] }, { "type": "splitter" From 34c598835ff49b17741d627b6bb0f9d23a2765f8 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 18:09:20 +0100 Subject: [PATCH 28/71] validate custom attributes from settings and creator tweak --- .../ftrack/publish/validate_custom_ftrack_attributes.py | 8 ++++++-- pype/settings/defaults/project_settings/global.json | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py b/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py index 4bddcd2e03..ed3fdc75ac 100644 --- a/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py +++ b/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py @@ -61,8 +61,12 @@ class ValidateFtrackAttributes(pyblish.api.InstancePlugin): "Missing FTrack Task entity in context") host = pyblish.api.current_host() - to_check = context.data["presets"].get( - host, {}).get("ftrack_custom_attributes") + to_check = ( + context.data["project_settings"] + ["ftrack"] + .get(host, {}) + .get("ftrack_custom_attributes") + ) if not to_check: self.log.warning("ftrack_attributes preset not found") return diff --git a/pype/settings/defaults/project_settings/global.json b/pype/settings/defaults/project_settings/global.json index 6661729e3d..da56fd34e7 100644 --- a/pype/settings/defaults/project_settings/global.json +++ b/pype/settings/defaults/project_settings/global.json @@ -111,7 +111,7 @@ } }, "tools": { - "Creator": { + "creator": { "families_smart_select": { "Render": [ "light", @@ -179,4 +179,4 @@ } } } -} \ No newline at end of file +} From 1a8406618c111071e33f02395307be4a4c444636 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 18:09:49 +0100 Subject: [PATCH 29/71] schema for ftrack validate attributes and creator --- .../schema_project_ftrack.json | 513 ++++++++++-------- .../schemas/schema_global_tools.json | 2 +- 2 files changed, 282 insertions(+), 233 deletions(-) diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json index f54c1232a6..ea01400e94 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_ftrack.json @@ -6,261 +6,310 @@ "checkbox_key": "enabled", "is_file": true, "children": [ - { - "type": "splitter" - }, - { - "type": "label", - "label": "Additional Ftrack paths" - }, - { - "type": "list", - "key": "ftrack_actions_path", - "label": "Action paths", - "object_type": "text" - }, - { - "type": "list", - "key": "ftrack_events_path", - "label": "Event paths", - "object_type": "text" - }, - { - "type": "splitter" - }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Additional Ftrack paths" + }, + { + "type": "list", + "key": "ftrack_actions_path", + "label": "Action paths", + "object_type": "text" + }, + { + "type": "list", + "key": "ftrack_events_path", + "label": "Event paths", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "dict", + "key": "events", + "label": "Server Events", + "children": [ { "type": "dict", - "key": "events", - "label": "Server Events", + "key": "sync_to_avalon", + "label": "Sync to avalon", + "checkbox_key": "enabled", "children": [ - { - "type": "dict", - "key": "sync_to_avalon", - "label": "Sync to avalon", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "label", - "label": "Allow name and hierarchy change only if following statuses are on all children tasks" - }, - { - "type": "list", - "key": "statuses_name_change", - "label": "Statuses", - "object_type": { - "type": "text", - "multiline": false - } - } - ] - }, - { - "type": "dict", - "key": "push_frame_values_to_task", - "label": "Sync Hierarchical and Entity Attributes", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "list", - "key": "interest_entity_types", - "label": "Entity types of interest", - "object_type": { - "type": "text", - "multiline": false - } - }, { - "type": "list", - "key": "interest_attributess", - "label": "Attributes to sync", - "object_type": { - "type": "text", - "multiline": false - } - }] - }, - { - "type": "dict", - "key": "thumbnail_updates", - "label": "Update Hierarchy thumbnails", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - },{ - "type": "label", - "label": "Push thumbnail from version, up through multiple hierarchy levels." - },{ - "type": "number", - "key": "levels", - "label": "Levels" - }] - }, - { - "type": "dict", - "key": "user_assignment", - "label": "Run script on user assignments", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }] - }, + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Allow name and hierarchy change only if following statuses are on all children tasks" + }, + { + "type": "list", + "key": "statuses_name_change", + "label": "Statuses", + "object_type": { - "type": "dict", - "key": "status_update", - "label": "Update status on task action", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "mapping", - "type": "dict-modifiable", - "object_type": { - "type": "list", - "object_type": "text" - } - }] - }, - { - "type": "dict", - "key": "status_task_to_parent", - "label": "Sync status from Task to Parent", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "parent_status_match_all_task_statuses", - "type": "dict-modifiable", - "label": "Change parent if all tasks match", - "object_type": { - "type": "list", - "object_type": "text" - } - }, - { - "key": "parent_status_by_task_status", - "type": "dict-modifiable", - "label": "Change parent status if a single task matches", - "object_type": { - "type": "list", - "object_type": "text" - } - }] - }, - { - "type": "dict", - "key": "status_task_to_version", - "label": "Sync status from Task to Version", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "dict-modifiable", - "key": "mapping", - "object_type": { - "type": "list", - "object_type": "text" - } - }] - }, - { - "type": "dict", - "key": "status_version_to_task", - "label": "Sync status from Version to Task", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, { - "type": "dict-modifiable", - "key": "mapping", - "object_type": { - "type": "list", - "object_type": "text" - } - }] - }, - { - "type": "dict", - "key": "first_version_status", - "label": "Set status on first created version", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - },{ - "type": "text", - "key": "status", - "label": "Status" - } - ] - }, - { - "type": "dict", - "key": "next_task_update", - "label": "Update status on next task", - "checkbox_key": "enabled", - "children": [{ - "type": "boolean", - "key": "enabled", - "label": "Enabled" - },{ - "type": "dict-modifiable", - "key": "mapping", - "object_type": { - "type": "text" - } - }] + "type": "text", + "multiline": false } - ] + }] }, { "type": "dict", - "collapsable": true, - "key": "publish", - "label": "Publish plugins", - "is_file": true, - "children": [{ + "key": "push_frame_values_to_task", + "label": "Sync Hierarchical and Entity Attributes", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "interest_entity_types", + "label": "Entity types of interest", + "object_type": + { + "type": "text", + "multiline": false + } + }, + { + "type": "list", + "key": "interest_attributess", + "label": "Attributes to sync", + "object_type": + { + "type": "text", + "multiline": false + } + }] + }, + { + "type": "dict", + "key": "thumbnail_updates", + "label": "Update Hierarchy thumbnails", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Push thumbnail from version, up through multiple hierarchy levels." + }, + { + "type": "number", + "key": "levels", + "label": "Levels" + }] + }, + { + "type": "dict", + "key": "user_assignment", + "label": "Run script on user assignments", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }] + }, + { + "type": "dict", + "key": "status_update", + "label": "Update status on task action", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "mapping", + "type": "dict-modifiable", + "object_type": + { + "type": "list", + "object_type": "text" + } + }] + }, + { + "type": "dict", + "key": "status_task_to_parent", + "label": "Sync status from Task to Parent", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "parent_status_match_all_task_statuses", + "type": "dict-modifiable", + "label": "Change parent if all tasks match", + "object_type": + { + "type": "list", + "object_type": "text" + } + }, + { + "key": "parent_status_by_task_status", + "type": "dict-modifiable", + "label": "Change parent status if a single task matches", + "object_type": + { + "type": "list", + "object_type": "text" + } + }] + }, + { + "type": "dict", + "key": "status_task_to_version", + "label": "Sync status from Task to Version", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict-modifiable", + "key": "mapping", + "object_type": + { + "type": "list", + "object_type": "text" + } + }] + }, + { + "type": "dict", + "key": "status_version_to_task", + "label": "Sync status from Version to Task", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict-modifiable", + "key": "mapping", + "object_type": + { + "type": "list", + "object_type": "text" + } + }] + }, + { + "type": "dict", + "key": "first_version_status", + "label": "Set status on first created version", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "status", + "label": "Status" + }] + }, + { + "type": "dict", + "key": "next_task_update", + "label": "Update status on next task", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict-modifiable", + "key": "mapping", + "object_type": + { + "type": "text" + } + }] + }] + }, + { + "type": "dict", + "collapsable": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { "type": "dict", "collapsable": true, "checkbox_key": "enabled", "key": "IntegrateFtrackNote", "label": "IntegrateFtrackNote", "is_group": true, - "children": [{ + "children": [ + { "type": "boolean", "key": "enabled", "label": "Enabled" - }, { + }, + { "type": "text", "key": "note_with_intent_template", "label": "Note with intent template" - }, { + }, + { "type": "list", "object_type": "text", "key": "note_labels", "label": "Note labels" }] - }] - } - ] + }, + + { + "type": "dict", + "collapsable": true, + "checkbox_key": "enabled", + "key": "ValidateFtrackAttributes", + "label": "ValidateFtrackAttributes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "raw-json", + "key": "ftrack_custom_attributes", + "label": "Custom attributes to validate" + }] + } + + ] + }] } diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json index 529794fd28..21b953d64b 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schemas/schema_global_tools.json @@ -7,7 +7,7 @@ { "type": "dict", "collapsable": true, - "key": "Creator", + "key": "creator", "label": "Creator", "children": [ { From cdfdb2292b021f9daebd90ff583b54aec162ffce Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 18:12:52 +0100 Subject: [PATCH 30/71] default for validate ftrack attributes --- pype/settings/defaults/project_settings/ftrack.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/settings/defaults/project_settings/ftrack.json b/pype/settings/defaults/project_settings/ftrack.json index 023b85cb3b..5481574ef8 100644 --- a/pype/settings/defaults/project_settings/ftrack.json +++ b/pype/settings/defaults/project_settings/ftrack.json @@ -93,6 +93,10 @@ "enabled": true, "note_with_intent_template": "", "note_labels": [] + }, + "ValidateFtrackAttributes": { + "enabled": false, + "ftrack_custom_attributes": {} } } } \ No newline at end of file From e215e8c4bcd3f45361d1bada64642b91470cf964 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 18:25:27 +0100 Subject: [PATCH 31/71] fix missing local variable --- pype/lib/applications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/lib/applications.py b/pype/lib/applications.py index 70b33d6edf..4970579108 100644 --- a/pype/lib/applications.py +++ b/pype/lib/applications.py @@ -245,11 +245,11 @@ class ApplicationTool: class ApplicationExecutable: def __init__(self, executable): default_launch_args = [] + executable_path = None if isinstance(executable, str): executable_path = executable elif isinstance(executable, list): - executable_path = None for arg in executable: if arg: if executable_path is None: From 37f6aa89621083f9303a9b500813ee0610746fd7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 18:25:59 +0100 Subject: [PATCH 32/71] CollectAnatomyContextData plugin is using `ApplicationManager` to get app name for templates --- .../global/publish/collect_anatomy_context_data.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pype/plugins/global/publish/collect_anatomy_context_data.py b/pype/plugins/global/publish/collect_anatomy_context_data.py index e1e6c12ee9..07e58d8cb7 100644 --- a/pype/plugins/global/publish/collect_anatomy_context_data.py +++ b/pype/plugins/global/publish/collect_anatomy_context_data.py @@ -15,6 +15,7 @@ Provides: import os import json +from pype.lib import ApplicationManager from avalon import api, lib import pyblish.api @@ -64,12 +65,12 @@ class CollectAnatomyContextData(pyblish.api.ContextPlugin): "username": context.data["user"] } - avalon_app_name = os.environ.get("AVALON_APP_NAME") - if avalon_app_name: - application_def = lib.get_application(avalon_app_name) - app_dir = application_def.get("application_dir") - if app_dir: - context_data["app"] = app_dir + app_manager = ApplicationManager() + app_name = os.environ.get("AVALON_APP_NAME") + if app_name: + app = app_manager.applications.get(app_name) + if app: + context_data["app"] = app.host_name datetime_data = context.data.get("datetimeData") or {} context_data.update(datetime_data) From fc63fcfc5c2d42dae56fb3b6ddddf0216e29d042 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 18:28:08 +0100 Subject: [PATCH 33/71] remove legacy extract burnin --- pype/plugins/global/publish/extract_burnin.py | 228 +----------------- 1 file changed, 6 insertions(+), 222 deletions(-) diff --git a/pype/plugins/global/publish/extract_burnin.py b/pype/plugins/global/publish/extract_burnin.py index 5be5060590..501162b6a6 100644 --- a/pype/plugins/global/publish/extract_burnin.py +++ b/pype/plugins/global/publish/extract_burnin.py @@ -68,8 +68,6 @@ class ExtractBurnin(pype.api.Extractor): if "representations" not in instance.data: raise RuntimeError("Burnin needs already created mov to work on.") - if self.use_legacy_code(instance): - return self.legacy_process(instance) self.main_process(instance) # Remove any representations tagged for deletion. @@ -79,12 +77,6 @@ class ExtractBurnin(pype.api.Extractor): self.log.debug("Removing representation: {}".format(repre)) instance.data["representations"].remove(repre) - def use_legacy_code(self, instance): - presets = instance.context.data.get("presets") - if presets is None and self.profiles is None: - return True - return "burnins" in (presets.get("tools") or {}) - def main_process(self, instance): # TODO get these data from context host_name = os.environ["AVALON_APP"] @@ -700,7 +692,7 @@ class ExtractBurnin(pype.api.Extractor): return filtered_burnin_defs def families_filter_validation(self, families, output_families_filter): - """Determines if entered families intersect with families filters. + """Determine if entered families intersect with families filters. All family values are lowered to avoid unexpected results. """ @@ -747,7 +739,7 @@ class ExtractBurnin(pype.api.Extractor): return regexes def validate_value_by_regexes(self, value, in_list): - """Validates in any regexe from list match entered value. + """Validate in any regexe from list match entered value. Args: in_list (list): List with regexes. @@ -770,14 +762,14 @@ class ExtractBurnin(pype.api.Extractor): return output def main_family_from_instance(self, instance): - """Returns main family of entered instance.""" + """Return main family of entered instance.""" family = instance.data.get("family") if not family: family = instance.data["families"][0] return family def families_from_instance(self, instance): - """Returns all families of entered instance.""" + """Return all families of entered instance.""" families = [] family = instance.data.get("family") if family: @@ -789,7 +781,7 @@ class ExtractBurnin(pype.api.Extractor): return families def burnin_script_path(self): - """Returns path to python script for burnin processing.""" + """Return path to python script for burnin processing.""" # TODO maybe convert to Plugin's attribute # Get script path. module_path = os.environ["PYPE_MODULE_ROOT"] @@ -813,7 +805,7 @@ class ExtractBurnin(pype.api.Extractor): return scriptpath def python_executable_path(self): - """Returns path to Python 3 executable.""" + """Return path to Python 3 executable.""" # TODO maybe convert to Plugin's attribute # Get executable. executable = os.getenv("PYPE_PYTHON_EXE") @@ -825,211 +817,3 @@ class ExtractBurnin(pype.api.Extractor): self.log.debug("executable: {}".format(executable)) return executable - - def legacy_process(self, instance): - self.log.warning("Legacy burnin presets are used.") - - context_data = instance.context.data - - version = instance.data.get( - 'version', instance.context.data.get('version')) - frame_start = int(instance.data.get("frameStart") or 0) - frame_end = int(instance.data.get("frameEnd") or 1) - handle_start = instance.data.get("handleStart", - context_data.get("handleStart")) - handle_end = instance.data.get("handleEnd", - context_data.get("handleEnd")) - - frame_start_handle = frame_start - handle_start - frame_end_handle = frame_end + handle_end - duration = frame_end_handle - frame_start_handle + 1 - - prep_data = copy.deepcopy(instance.data["anatomyData"]) - - if "slate.farm" in instance.data["families"]: - frame_start_handle += 1 - duration -= 1 - - prep_data.update({ - "frame_start": frame_start_handle, - "frame_end": frame_end_handle, - "duration": duration, - "version": int(version), - "comment": instance.context.data.get("comment", "") - }) - - intent_label = instance.context.data.get("intent") - if intent_label and isinstance(intent_label, dict): - intent_label = intent_label.get("label") - - if intent_label: - prep_data["intent"] = intent_label - - # get anatomy project - anatomy = instance.context.data['anatomy'] - - self.log.debug("__ prep_data: {}".format(prep_data)) - for i, repre in enumerate(instance.data["representations"]): - self.log.debug("__ i: `{}`, repre: `{}`".format(i, repre)) - - if instance.data.get("multipartExr") is True: - # ffmpeg doesn't support multipart exrs - continue - - if "burnin" not in repre.get("tags", []): - continue - - is_sequence = "sequence" in repre.get("tags", []) - - # no handles switch from profile tags - no_handles = "no-handles" in repre.get("tags", []) - - stagingdir = repre["stagingDir"] - filename = "{0}".format(repre["files"]) - - if is_sequence: - filename = repre["sequence_file"] - - name = "_burnin" - ext = os.path.splitext(filename)[1] - movieFileBurnin = filename.replace(ext, "") + name + ext - - if is_sequence: - fn_splt = filename.split(".") - movieFileBurnin = ".".join( - ((fn_splt[0] + name), fn_splt[-2], fn_splt[-1])) - - self.log.debug("__ movieFileBurnin: `{}`".format(movieFileBurnin)) - - full_movie_path = os.path.join( - os.path.normpath(stagingdir), filename) - full_burnin_path = os.path.join( - os.path.normpath(stagingdir), movieFileBurnin) - - self.log.debug("__ full_movie_path: {}".format(full_movie_path)) - self.log.debug("__ full_burnin_path: {}".format(full_burnin_path)) - - # create copy of prep_data for anatomy formatting - _prep_data = copy.deepcopy(prep_data) - _prep_data["representation"] = repre["name"] - filled_anatomy = anatomy.format_all(_prep_data) - _prep_data["anatomy"] = filled_anatomy.get_solved() - - # copy frame range variables - frame_start_cp = frame_start_handle - frame_end_cp = frame_end_handle - duration_cp = duration - - if no_handles: - frame_start_cp = frame_start - frame_end_cp = frame_end - duration_cp = frame_end_cp - frame_start_cp + 1 - _prep_data.update({ - "frame_start": frame_start_cp, - "frame_end": frame_end_cp, - "duration": duration_cp, - }) - - # dealing with slates - slate_frame_start = frame_start_cp - slate_frame_end = frame_end_cp - slate_duration = duration_cp - - # exception for slate workflow - if "slate" in instance.data["families"]: - if "slate-frame" in repre.get("tags", []): - slate_frame_start = frame_start_cp - 1 - slate_frame_end = frame_end_cp - slate_duration = duration_cp + 1 - - self.log.debug("__1 slate_frame_start: {}".format( - slate_frame_start)) - - _prep_data.update({ - "slate_frame_start": slate_frame_start, - "slate_frame_end": slate_frame_end, - "slate_duration": slate_duration - }) - - burnin_data = { - "input": full_movie_path.replace("\\", "/"), - "codec": repre.get("codec", []), - "output": full_burnin_path.replace("\\", "/"), - "burnin_data": _prep_data - } - - self.log.debug("__ burnin_data2: {}".format(burnin_data)) - - json_data = json.dumps(burnin_data) - - # Get script path. - module_path = os.environ['PYPE_MODULE_ROOT'] - - # There can be multiple paths in PYPE_MODULE_ROOT, in which case - # we just take first one. - if os.pathsep in module_path: - module_path = module_path.split(os.pathsep)[0] - - scriptpath = os.path.normpath( - os.path.join( - module_path, - "pype", - "scripts", - "otio_burnin.py" - ) - ) - - self.log.debug("__ scriptpath: {}".format(scriptpath)) - - # Get executable. - executable = os.getenv("PYPE_PYTHON_EXE") - - # There can be multiple paths in PYPE_PYTHON_EXE, in which case - # we just take first one. - if os.pathsep in executable: - executable = executable.split(os.pathsep)[0] - - self.log.debug("__ EXE: {}".format(executable)) - - args = [executable, scriptpath, json_data] - self.log.debug("Executing: {}".format(args)) - output = pype.api.subprocess(args, shell=True, logger=self.log) - self.log.debug("Output: {}".format(output)) - - repre_update = { - "files": movieFileBurnin, - "name": repre["name"], - "tags": [x for x in repre["tags"] if x != "delete"] - } - - if is_sequence: - burnin_seq_files = list() - for frame_index in range(_prep_data["duration"] + 1): - if frame_index == 0: - continue - burnin_seq_files.append(movieFileBurnin % frame_index) - repre_update.update({ - "files": burnin_seq_files - }) - - instance.data["representations"][i].update(repre_update) - - # removing the source mov file - if is_sequence: - for frame_index in range(_prep_data["duration"] + 1): - if frame_index == 0: - continue - rm_file = full_movie_path % frame_index - os.remove(rm_file) - self.log.debug("Removed: `{}`".format(rm_file)) - else: - os.remove(full_movie_path) - self.log.debug("Removed: `{}`".format(full_movie_path)) - - # Remove any representations tagged for deletion. - for repre in instance.data["representations"]: - if "delete" in repre.get("tags", []): - self.log.debug("Removing representation: {}".format(repre)) - instance.data["representations"].remove(repre) - - self.log.debug(instance.data["representations"]) From c1133a9b6d242ce7245b4f54641dc7386c246033 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Dec 2020 18:50:40 +0100 Subject: [PATCH 34/71] Fix - json metadata and rendered files should be in same folder --- .../publish/submit_aftereffects_deadline.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py index 9414bdd39d..5e5c00dec1 100644 --- a/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py +++ b/pype/plugins/aftereffects/publish/submit_aftereffects_deadline.py @@ -105,3 +105,13 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline deadline_plugin_info.Output = render_path.replace("\\", "/") return attr.asdict(deadline_plugin_info) + + def from_published_scene(self): + """ Do not overwrite expected files. + + Use published is set to True, so rendering will be triggered + from published scene (in 'publish' folder). Default implementation + of abstract class renames expected (eg. rendered) files accordingly + which is not needed here. + """ + return super().from_published_scene(False) From 45a6988fd80d075b74a6cdf9f7c257ab3abd6e7f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 19:14:24 +0100 Subject: [PATCH 35/71] added default envent handler paths to ftrack lib --- pype/modules/ftrack/lib/__init__.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/lib/__init__.py b/pype/modules/ftrack/lib/__init__.py index 3890eacf90..cfa00df060 100644 --- a/pype/modules/ftrack/lib/__init__.py +++ b/pype/modules/ftrack/lib/__init__.py @@ -1,9 +1,20 @@ +import os from . import avalon_sync from . import credentials from .ftrack_base_handler import BaseHandler from .ftrack_event_handler import BaseEvent from .ftrack_action_handler import BaseAction, ServerAction, statics_icon +FTRACK_MODULE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SERVER_HANDLERS_DIR = os.path.join( + FTRACK_MODULE_DIR, + "events" +) +USER_HANDLERS_DIR = os.path.join( + FTRACK_MODULE_DIR, + "actions" +) + __all__ = ( "avalon_sync", "credentials", @@ -11,5 +22,8 @@ __all__ = ( "BaseEvent", "BaseAction", "ServerAction", - "statics_icon" + "statics_icon", + "FTRACK_MODULE_DIR", + "SERVER_HANDLERS_DIR", + "USER_HANDLERS_DIR" ) From f096c78f47554b353d4fcf3dd27d0fb349756d2b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 19:28:09 +0100 Subject: [PATCH 36/71] ftrack server does not load event handlers from environemtns but expect to be passed inside --- .../ftrack/ftrack_server/ftrack_server.py | 55 ++++++++++++------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/ftrack_server.py b/pype/modules/ftrack/ftrack_server/ftrack_server.py index ec39bdc50f..f2b3193fd3 100644 --- a/pype/modules/ftrack/ftrack_server/ftrack_server.py +++ b/pype/modules/ftrack/ftrack_server/ftrack_server.py @@ -32,7 +32,7 @@ PYTHONPATH # Path to ftrack_api and paths to all modules used in actions class FtrackServer: - def __init__(self, server_type='action'): + def __init__(self, handler_paths=[], server_type='action'): """ - 'type' is by default set to 'action' - Runs Action server - enter 'event' for Event server @@ -47,14 +47,17 @@ class FtrackServer: ftrack_log = logging.getLogger("ftrack_api") ftrack_log.setLevel(logging.WARNING) - env_key = "FTRACK_ACTIONS_PATH" - if server_type.lower() == 'event': - env_key = "FTRACK_EVENTS_PATH" + self.stopped = True + self.is_running = False + + self.handler_paths = handler_paths self.server_type = server_type - self.env_key = env_key + + self.session_type = None def stop_session(self): + self.stopped = True if self.session.event_hub.connected is True: self.session.event_hub.disconnect() self.session.close() @@ -107,10 +110,6 @@ class FtrackServer: " in registered paths: \"{}\"" ).format("| ".join(paths))) - # Load presets for setting plugins - key = "user" - if self.server_type.lower() == "event": - key = "server" # TODO replace with settings or get rid of passing the dictionary plugins_presets = {} @@ -132,25 +131,41 @@ class FtrackServer: ) log.warning(msg, exc_info=True) + def set_handler_paths(self, paths): + self.handler_paths = paths + if self.is_running: + self.stop_session() + self.run_server() + + elif not self.stopped: + self.run_server() + def run_server(self, session=None, load_files=True): - if not session: + self.stopped = False + self.is_running = True + if session: + self.session_type = type(session) + elif self.session_type: + session = self.session_type(auto_connect_event_hub=True) + else: session = ftrack_api.Session(auto_connect_event_hub=True) self.session = session - if load_files: - paths_str = os.environ.get(self.env_key) - if paths_str is None: - log.error(( - "Env var \"{}\" is not set, \"{}\" server won\'t launch" - ).format(self.env_key, self.server_type)) + if not self.handler_paths: + log.warning(( + "Paths to event handlers are not set." + " Ftrack server won't launch." + )) + self.is_running = False return - paths = paths_str.split(os.pathsep) - self.set_files(paths) + self.set_files(self.handler_paths) - log.info(60*"*") - log.info('Registration of actions/events has finished!') + msg = "Registration of event handlers has finished!" + log.info(len(msg) * "*") + log.info(msg) # keep event_hub on session running self.session.event_hub.wait() + self.is_running = False From 715ee9117e6c5a513c0bb22d0a71510f3ae5abf3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 19:28:35 +0100 Subject: [PATCH 37/71] added functions to get event handler paths for server and user server --- pype/modules/ftrack/ftrack_server/lib.py | 78 ++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index 79b708b17a..1978d4529e 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -19,8 +19,13 @@ import ftrack_api._centralized_storage_scenario import ftrack_api.event from ftrack_api.logging import LazyLogMessage as L +from pype.modules.ftrack.lib import ( + SERVER_HANDLERS_DIR, + USER_HANDLERS_DIR +) from pype.api import ( Logger, + get_system_settings, get_default_components, decompose_url, compose_url @@ -33,6 +38,79 @@ TOPIC_STATUS_SERVER = "pype.event.server.status" TOPIC_STATUS_SERVER_RESULT = "pype.event.server.status.result" +log = Logger().get_logger("ftrack_server.lib") + + +def clockify_event_path(): + api_key = os.environ.get("CLOCKIFY_API_KEY") + if not api_key: + log.warning("Clockify API key is not set.") + return + + workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") + if not workspace_name: + log.warning("Clockify Workspace is not set.") + return + + from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH + + return CLOCKIFY_FTRACK_SERVER_PATH + + +def get_server_event_handler_paths(): + paths = [] + # Add pype's default dir + paths.append(SERVER_HANDLERS_DIR) + # Add additional paths from settings + paths.extend( + get_system_settings() + ["modules"] + ["Ftrack"] + ["ftrack_events_path"] + ) + try: + clockify_path = clockify_event_path() + if clockify_path: + paths.append(clockify_path) + except Exception: + log.warning("Clockify paths function failed.", exc_info=True) + + # Filter only existing paths + _paths = [] + for path in paths: + if os.path.exists(path): + _paths.append(path) + else: + log.warning(( + "Registered event handler path is not accessible: {}" + ).format(path)) + return _paths + + +def get_user_event_handler_paths(): + paths = [] + # Add pype's default dir + paths.append(USER_HANDLERS_DIR) + # Add additional paths from settings + paths.extend( + get_system_settings() + ["modules"] + ["Ftrack"] + ["ftrack_actions_path"] + ) + + # Filter only existing paths + _paths = [] + for path in paths: + if os.path.exists(path): + _paths.append(path) + else: + log.warning(( + "Registered event handler path is not accessible: {}" + ).format(path)) + return _paths + + def get_ftrack_event_mongo_info(): database_name = ( os.environ.get("FTRACK_EVENTS_MONGO_DB") or "pype" From 18bd9ac334eba38c749b7decdaf11be0c4146356 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 19:29:21 +0100 Subject: [PATCH 38/71] status and storer processes just change arg to kwarg --- pype/modules/ftrack/ftrack_server/sub_event_status.py | 2 +- pype/modules/ftrack/ftrack_server/sub_event_storer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/sub_event_status.py b/pype/modules/ftrack/ftrack_server/sub_event_status.py index 00a6687de3..e478c1a1e8 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_status.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_status.py @@ -368,7 +368,7 @@ def main(args): ObjectFactory.session = session session.event_hub.heartbeat_callbacks.append(heartbeat) register(session) - server = FtrackServer("event") + server = FtrackServer(server_type="event") log.debug("Launched Ftrack Event statuser") server.run_server(session, load_files=False) diff --git a/pype/modules/ftrack/ftrack_server/sub_event_storer.py b/pype/modules/ftrack/ftrack_server/sub_event_storer.py index 2f4395c8db..938448c8a7 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_storer.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_storer.py @@ -193,7 +193,7 @@ def main(args): ) SessionFactory.session = session register(session) - server = FtrackServer("event") + server = FtrackServer(server_type="event") log.debug("Launched Ftrack Event storer") server.run_server(session, load_files=False) From 5b6e2254a2c50cd37339c3a2f2fed6f9a20684ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 4 Dec 2020 19:29:50 +0100 Subject: [PATCH 39/71] processor, user process and legacy event server are loading event handler paths from ftrac server lib --- .../ftrack_server/sub_event_processor.py | 40 ++++++------------- .../ftrack/ftrack_server/sub_legacy_server.py | 6 ++- .../ftrack/ftrack_server/sub_user_server.py | 6 ++- 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/sub_event_processor.py b/pype/modules/ftrack/ftrack_server/sub_event_processor.py index c719c8fd08..536ac3faf2 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_processor.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_processor.py @@ -6,11 +6,16 @@ import datetime from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( - SocketSession, ProcessEventHub, TOPIC_STATUS_SERVER + SocketSession, + ProcessEventHub, + TOPIC_STATUS_SERVER, + get_server_event_handler_paths ) -import ftrack_api + from pype.api import Logger +import ftrack_api + log = Logger().get_logger("Event processor") subprocess_started = datetime.datetime.now() @@ -55,26 +60,6 @@ def register(session): ) -def clockify_module_registration(): - api_key = os.environ.get("CLOCKIFY_API_KEY") - if not api_key: - log.warning("Clockify API key is not set.") - return - - workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") - if not workspace_name: - log.warning("Clockify Workspace is not set.") - return - - from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH - - current = os.environ.get("FTRACK_EVENTS_PATH") or "" - if current: - current += os.pathsep - os.environ["FTRACK_EVENTS_PATH"] = current + CLOCKIFY_FTRACK_SERVER_PATH - return True - - def main(args): port = int(args[-1]) # Create a TCP/IP socket @@ -86,11 +71,8 @@ def main(args): sock.connect(server_address) sock.sendall(b"CreatedProcess") - try: - clockify_module_registration() - except Exception: - log.info("Clockify registration failed.", exc_info=True) + returncode = 0 try: session = SocketSession( auto_connect_event_hub=True, sock=sock, Eventhub=ProcessEventHub @@ -98,17 +80,19 @@ def main(args): register(session) SessionFactory.session = session - server = FtrackServer("event") + event_handler_paths = get_server_event_handler_paths() + server = FtrackServer(event_handler_paths, "event") log.debug("Launched Ftrack Event processor") server.run_server(session) except Exception: + returncode = 1 log.error("Event server crashed. See traceback below", exc_info=True) finally: log.debug("First closing socket") sock.close() - return 1 + return returncode if __name__ == "__main__": diff --git a/pype/modules/ftrack/ftrack_server/sub_legacy_server.py b/pype/modules/ftrack/ftrack_server/sub_legacy_server.py index c37727a784..bd5962d04a 100644 --- a/pype/modules/ftrack/ftrack_server/sub_legacy_server.py +++ b/pype/modules/ftrack/ftrack_server/sub_legacy_server.py @@ -7,6 +7,9 @@ import threading from ftrack_server import FtrackServer import ftrack_api from pype.api import Logger +from pype.modules.ftrack.ftrack_server.lib import ( + get_server_event_handler_paths +) log = Logger().get_logger("Event Server Legacy") @@ -62,7 +65,8 @@ class TimerChecker(threading.Thread): def main(args): check_thread = None try: - server = FtrackServer("event") + event_handler_paths = get_server_event_handler_paths() + server = FtrackServer(event_handler_paths, "event") session = ftrack_api.Session(auto_connect_event_hub=True) check_thread = TimerChecker(server, session) diff --git a/pype/modules/ftrack/ftrack_server/sub_user_server.py b/pype/modules/ftrack/ftrack_server/sub_user_server.py index f4b81922eb..6ba832125b 100644 --- a/pype/modules/ftrack/ftrack_server/sub_user_server.py +++ b/pype/modules/ftrack/ftrack_server/sub_user_server.py @@ -7,7 +7,8 @@ import traceback from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, - SocketBaseEventHub + SocketBaseEventHub, + get_user_event_handler_paths ) from pype.api import Logger @@ -33,7 +34,8 @@ def main(args): session = SocketSession( auto_connect_event_hub=True, sock=sock, Eventhub=SocketBaseEventHub ) - server = FtrackServer("action") + event_handler_paths = get_user_event_handler_paths() + server = FtrackServer(event_handler_paths, "action") log.debug("Launched User Ftrack Server") server.run_server(session=session) except Exception: From 5fcca6fa46d6390b1133ab9c0d772536a29315b3 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 23:51:39 +0100 Subject: [PATCH 40/71] nuke validateKnobs from settings --- pype/settings/defaults/project_settings/nuke.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/settings/defaults/project_settings/nuke.json b/pype/settings/defaults/project_settings/nuke.json index 873f249769..61001914d2 100644 --- a/pype/settings/defaults/project_settings/nuke.json +++ b/pype/settings/defaults/project_settings/nuke.json @@ -35,7 +35,7 @@ ] } }, - "ValidateNukeWriteKnobs": { + "ValidateKnobs": { "enabled": true, "knobs": { "render": { @@ -87,4 +87,4 @@ ] }, "filters": {} -} \ No newline at end of file +} From c8889b712b638313219cb13c2bf223b8a7605d66 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 23:52:18 +0100 Subject: [PATCH 41/71] nuke validateKnobs from settings --- pype/plugins/nuke/publish/validate_knobs.py | 26 ++++--------------- .../projects_schema/schema_project_nuke.json | 4 +-- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/pype/plugins/nuke/publish/validate_knobs.py b/pype/plugins/nuke/publish/validate_knobs.py index 22f0d344c9..34c82f4b36 100644 --- a/pype/plugins/nuke/publish/validate_knobs.py +++ b/pype/plugins/nuke/publish/validate_knobs.py @@ -9,8 +9,7 @@ class ValidateKnobs(pyblish.api.ContextPlugin): Knobs to validate and their values comes from the - Example for presets in config: - "presets/plugins/nuke/publish.json" preset, which needs this structure: + Controled by plugin settings that require json in following structure: "ValidateKnobs": { "enabled": true, "knobs": { @@ -28,20 +27,6 @@ class ValidateKnobs(pyblish.api.ContextPlugin): optional = True def process(self, context): - nuke_presets = context.data["presets"].get("nuke") - - if not nuke_presets: - return - - publish_presets = nuke_presets.get("publish") - - if not publish_presets: - return - - plugin_preset = publish_presets.get("ValidateKnobs") - - if not plugin_preset: - return invalid = self.get_invalid(context, compute=True) if invalid: @@ -60,8 +45,7 @@ class ValidateKnobs(pyblish.api.ContextPlugin): @classmethod def get_invalid_knobs(cls, context): invalid_knobs = [] - publish_presets = context.data["presets"]["nuke"]["publish"] - knobs_preset = publish_presets["ValidateKnobs"]["knobs"] + for instance in context: # Filter publisable instances. if not instance.data["publish"]: @@ -70,15 +54,15 @@ class ValidateKnobs(pyblish.api.ContextPlugin): # Filter families. families = [instance.data["family"]] families += instance.data.get("families", []) - families = list(set(families) & set(knobs_preset.keys())) + families = list(set(families) & set(self.knobs.keys())) if not families: continue # Get all knobs to validate. knobs = {} for family in families: - for preset in knobs_preset[family]: - knobs.update({preset: knobs_preset[family][preset]}) + for preset in self.knobs[family]: + knobs.update({preset: self.knobs[family][preset]}) # Get invalid knobs. nodes = [] diff --git a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json index 3870fbe8bd..ef0541da58 100644 --- a/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json +++ b/pype/tools/settings/settings/gui_schemas/projects_schema/schema_project_nuke.json @@ -57,8 +57,8 @@ "type": "dict", "collapsable": true, "checkbox_key": "enabled", - "key": "ValidateNukeWriteKnobs", - "label": "ValidateNukeWriteKnobs", + "key": "ValidateKnobs", + "label": "ValidateKnobs", "is_group": true, "children": [{ "type": "boolean", From f9a0a72b3ab98a2a7c2b1a0033fb37902955fecb Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Dec 2020 23:52:47 +0100 Subject: [PATCH 42/71] validate ftrack attributes from settings --- .../ftrack/publish/validate_custom_ftrack_attributes.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py b/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py index ed3fdc75ac..b141a8bcb7 100644 --- a/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py +++ b/pype/plugins/ftrack/publish/validate_custom_ftrack_attributes.py @@ -61,12 +61,8 @@ class ValidateFtrackAttributes(pyblish.api.InstancePlugin): "Missing FTrack Task entity in context") host = pyblish.api.current_host() - to_check = ( - context.data["project_settings"] - ["ftrack"] - .get(host, {}) - .get("ftrack_custom_attributes") - ) + to_check = self.ftrack_custom_attributes.get(host, {})) + if not to_check: self.log.warning("ftrack_attributes preset not found") return From 38449bc5467300bc3cf5f4e584374311b82b5c29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 11:34:06 +0100 Subject: [PATCH 43/71] implemented prelaunch hook that add shell executable before launched executable which may be required for few applications --- pype/hooks/global/pre_with_windows_shell.py | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 pype/hooks/global/pre_with_windows_shell.py diff --git a/pype/hooks/global/pre_with_windows_shell.py b/pype/hooks/global/pre_with_windows_shell.py new file mode 100644 index 0000000000..cbf2405563 --- /dev/null +++ b/pype/hooks/global/pre_with_windows_shell.py @@ -0,0 +1,25 @@ +import os +from pype.lib import PreLaunchHook + + +class LaunchWithWindowsShell(PreLaunchHook): + """Add shell command before executable. + + Some hosts have issues when are launched directly from python in that case + it is possible to prepend shell executable which will trigger process + instead. + """ + + order = 10 + app_groups = ["nuke", "nukex", "hiero", "nukestudio"] + platforms = ["windows"] + + def execute(self): + # Get comspec which is cmd.exe in most cases. + comspec = os.environ.get("COMSPEC", "cmd.exe") + # Add comspec to arguments list and add "/k" + title = self.application.full_label + new_args = [comspec, "/c"] + new_args.extend(self.launch_context.launch_args) + # Replace launch args with new one + self.launch_context.launch_args = new_args From 73c6b8ee0272a393ca93e69b64106ed294ac712d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 11:52:21 +0100 Subject: [PATCH 44/71] removed unused variable --- pype/hooks/global/pre_with_windows_shell.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pype/hooks/global/pre_with_windows_shell.py b/pype/hooks/global/pre_with_windows_shell.py index cbf2405563..918c0d63fd 100644 --- a/pype/hooks/global/pre_with_windows_shell.py +++ b/pype/hooks/global/pre_with_windows_shell.py @@ -18,7 +18,6 @@ class LaunchWithWindowsShell(PreLaunchHook): # Get comspec which is cmd.exe in most cases. comspec = os.environ.get("COMSPEC", "cmd.exe") # Add comspec to arguments list and add "/k" - title = self.application.full_label new_args = [comspec, "/c"] new_args.extend(self.launch_context.launch_args) # Replace launch args with new one From f1ddbd17bcd3134ed99dc155946f0b15aa065e77 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 11:53:24 +0100 Subject: [PATCH 45/71] removed launch hooks with same code and move it to global --- .../hooks/global/pre_add_last_workfile_arg.py | 26 +++++++++++++++++++ pype/hooks/hiero/pre_launch_args.py | 15 ----------- pype/hooks/maya/pre_launch_args.py | 16 ------------ pype/hooks/nukestudio/pre_launch_args.py | 15 ----------- pype/hooks/nukex/pre_launch_args.py | 15 ----------- 5 files changed, 26 insertions(+), 61 deletions(-) create mode 100644 pype/hooks/global/pre_add_last_workfile_arg.py delete mode 100644 pype/hooks/hiero/pre_launch_args.py delete mode 100644 pype/hooks/maya/pre_launch_args.py delete mode 100644 pype/hooks/nukestudio/pre_launch_args.py delete mode 100644 pype/hooks/nukex/pre_launch_args.py diff --git a/pype/hooks/global/pre_add_last_workfile_arg.py b/pype/hooks/global/pre_add_last_workfile_arg.py new file mode 100644 index 0000000000..5c70cc2ac8 --- /dev/null +++ b/pype/hooks/global/pre_add_last_workfile_arg.py @@ -0,0 +1,26 @@ +import os +from pype.lib import PreLaunchHook + + +class AddLastWorkfileToLaunchArgs(PreLaunchHook): + order = 0 + app_groups = ["maya", "nuke", "nukex", "hiero", "nukestudio"] + + def execute(self): + """Prepare suprocess launch arguments for Nuke.""" + + if not self.data.get("start_last_workfile"): + self.log.info("It is set to not start last workfile on start.") + return + + last_workfile = self.data.get("last_workfile_path") + if not last_workfile: + self.log.warning("Last workfile was not collected.") + return + + if not os.path.exists(last_workfile): + self.log.info("Current context does not have any workfile yet.") + return + + # Add path to workfile to arguments + self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/hiero/pre_launch_args.py b/pype/hooks/hiero/pre_launch_args.py deleted file mode 100644 index 6f5d0c0b00..0000000000 --- a/pype/hooks/hiero/pre_launch_args.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from pype.lib import PreLaunchHook - - -class HieroLaunchArguments(PreLaunchHook): - order = 0 - app_groups = ["hiero"] - - def execute(self): - """Prepare suprocess launch arguments for Hiero.""" - # Add path to workfile to arguments - if self.data.get("start_last_workfile"): - last_workfile = self.data.get("last_workfile_path") - if os.path.exists(last_workfile): - self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/maya/pre_launch_args.py b/pype/hooks/maya/pre_launch_args.py deleted file mode 100644 index 26b935ea01..0000000000 --- a/pype/hooks/maya/pre_launch_args.py +++ /dev/null @@ -1,16 +0,0 @@ -import os -from pype.lib import PreLaunchHook - - -class MayaLaunchArguments(PreLaunchHook): - """Add path to last workfile to launch arguments.""" - order = 0 - app_groups = ["maya"] - - def execute(self): - """Prepare suprocess launch arguments for Maya.""" - # Add path to workfile to arguments - if self.data.get("start_last_workfile"): - last_workfile = self.data.get("last_workfile_path") - if os.path.exists(last_workfile): - self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/nukestudio/pre_launch_args.py b/pype/hooks/nukestudio/pre_launch_args.py deleted file mode 100644 index 6056441042..0000000000 --- a/pype/hooks/nukestudio/pre_launch_args.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from pype.lib import PreLaunchHook - - -class NukeStudioLaunchArguments(PreLaunchHook): - order = 0 - app_groups = ["nukestudio"] - - def execute(self): - """Prepare suprocess launch arguments for NukeStudio.""" - # Add path to workfile to arguments - if self.data.get("start_last_workfile"): - last_workfile = self.data.get("last_workfile_path") - if os.path.exists(last_workfile): - self.launch_context.launch_args.append(last_workfile) diff --git a/pype/hooks/nukex/pre_launch_args.py b/pype/hooks/nukex/pre_launch_args.py deleted file mode 100644 index 979bfcce0b..0000000000 --- a/pype/hooks/nukex/pre_launch_args.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from pype.lib import PreLaunchHook - - -class NukeXLaunchArguments(PreLaunchHook): - order = 0 - app_groups = ["nukex"] - - def execute(self): - """Prepare suprocess launch arguments for NukeX.""" - # Add path to workfile to arguments - if self.data.get("start_last_workfile"): - last_workfile = self.data.get("last_workfile_path") - if os.path.exists(last_workfile): - self.launch_context.launch_args.append(last_workfile) From 6512d9478dc0c09a7cdf2cc6a23f5b86a584b4a1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 11:55:42 +0100 Subject: [PATCH 46/71] modify docstring --- pype/hooks/global/pre_add_last_workfile_arg.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pype/hooks/global/pre_add_last_workfile_arg.py b/pype/hooks/global/pre_add_last_workfile_arg.py index 5c70cc2ac8..e19a884eff 100644 --- a/pype/hooks/global/pre_add_last_workfile_arg.py +++ b/pype/hooks/global/pre_add_last_workfile_arg.py @@ -3,12 +3,15 @@ from pype.lib import PreLaunchHook class AddLastWorkfileToLaunchArgs(PreLaunchHook): + """Add last workfile path to launch arguments. + + This is not possible to do for all applications the same way. + """ + order = 0 app_groups = ["maya", "nuke", "nukex", "hiero", "nukestudio"] def execute(self): - """Prepare suprocess launch arguments for Nuke.""" - if not self.data.get("start_last_workfile"): self.log.info("It is set to not start last workfile on start.") return From 78116b6be6dbd4e2174d3da76ac0e3e4941b1db0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 12:09:38 +0100 Subject: [PATCH 47/71] removed testing --- pype/modules/ftrack/ftrack_server/ftrack_server.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/ftrack_server.py b/pype/modules/ftrack/ftrack_server/ftrack_server.py index f2b3193fd3..b03ed51cef 100644 --- a/pype/modules/ftrack/ftrack_server/ftrack_server.py +++ b/pype/modules/ftrack/ftrack_server/ftrack_server.py @@ -54,8 +54,6 @@ class FtrackServer: self.server_type = server_type - self.session_type = None - def stop_session(self): self.stopped = True if self.session.event_hub.connected is True: @@ -143,11 +141,7 @@ class FtrackServer: def run_server(self, session=None, load_files=True): self.stopped = False self.is_running = True - if session: - self.session_type = type(session) - elif self.session_type: - session = self.session_type(auto_connect_event_hub=True) - else: + if not session: session = ftrack_api.Session(auto_connect_event_hub=True) self.session = session From 5c235f1416bb7273eb5db23935be40e891cc67ea Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 12:19:57 +0100 Subject: [PATCH 48/71] removed FTRACK_ACTIONS_PATH and FTRACK_EVENTS_PATH environments --- pype/settings/defaults/system_settings/modules.json | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 74268c9254..3c44d15b76 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -135,18 +135,10 @@ "environment": { "__environment_keys__": { "ftrack": [ - "FTRACK_ACTIONS_PATH", - "FTRACK_EVENTS_PATH", "PYBLISHPLUGINPATH", "PYTHONPATH" ] }, - "FTRACK_ACTIONS_PATH": [ - "{PYPE_MODULE_ROOT}/pype/modules/ftrack/actions" - ], - "FTRACK_EVENTS_PATH": [ - "{PYPE_MODULE_ROOT}/pype/modules/ftrack/events" - ], "PYBLISHPLUGINPATH": [ "{PYPE_MODULE_ROOT}/pype/plugins/ftrack/publish" ], @@ -202,4 +194,4 @@ "Idle Manager": { "enabled": true } -} +} \ No newline at end of file From c3bc7382f0a0b01906b244065b92282c622e738c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 12:22:26 +0100 Subject: [PATCH 49/71] replaced traceback print with logger warning --- pype/modules/ftrack/ftrack_server/sub_user_server.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/sub_user_server.py b/pype/modules/ftrack/ftrack_server/sub_user_server.py index 6ba832125b..d899e93a8f 100644 --- a/pype/modules/ftrack/ftrack_server/sub_user_server.py +++ b/pype/modules/ftrack/ftrack_server/sub_user_server.py @@ -2,8 +2,6 @@ import sys import signal import socket -import traceback - from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, @@ -36,10 +34,11 @@ def main(args): ) event_handler_paths = get_user_event_handler_paths() server = FtrackServer(event_handler_paths, "action") - log.debug("Launched User Ftrack Server") + log.debug("Launching User Ftrack Server") server.run_server(session=session) + except Exception: - traceback.print_exception(*sys.exc_info()) + log.warning("Ftrack session server failed.", exc_info=True) finally: log.debug("Closing socket") From 76e42476d49f844f6bd7e88eb3a59e00e0e5c09f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 12:40:17 +0100 Subject: [PATCH 50/71] modified login dialog --- pype/modules/ftrack/tray/login_dialog.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pype/modules/ftrack/tray/login_dialog.py b/pype/modules/ftrack/tray/login_dialog.py index 94ad29e478..6c7373e337 100644 --- a/pype/modules/ftrack/tray/login_dialog.py +++ b/pype/modules/ftrack/tray/login_dialog.py @@ -126,20 +126,26 @@ class CredentialsDialog(QtWidgets.QDialog): self.setLayout(main) + def show(self, *args, **kwargs): + super(CredentialsDialog, self).show(*args, **kwargs) + self.fill_ftrack_url() + def fill_ftrack_url(self): url = os.getenv("FTRACK_SERVER") + if url == self.ftsite_input.text(): + return + checked_url = self.check_url(url) + self.ftsite_input.setText(checked_url or "") - if checked_url is None: - checked_url = "" - self.btn_login.setEnabled(False) - self.btn_ftrack_login.setEnabled(False) + enabled = bool(checked_url) - self.api_input.setEnabled(False) - self.user_input.setEnabled(False) - self.ftsite_input.setEnabled(False) + self.btn_login.setEnabled(enabled) + self.btn_ftrack_login.setEnabled(enabled) - self.ftsite_input.setText(checked_url) + self.api_input.setEnabled(enabled) + self.user_input.setEnabled(enabled) + self.ftsite_input.setEnabled(enabled) def set_advanced_mode(self, is_advanced): self._in_advance_mode = is_advanced From ab2477b26ef78047f82d882b0d7685b94f661fc1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 12:42:05 +0100 Subject: [PATCH 51/71] ftrack url is loaded on module init --- pype/modules/ftrack/tray/ftrack_module.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/tray/ftrack_module.py b/pype/modules/ftrack/tray/ftrack_module.py index 0b011c5b33..36ce1eec9f 100644 --- a/pype/modules/ftrack/tray/ftrack_module.py +++ b/pype/modules/ftrack/tray/ftrack_module.py @@ -10,7 +10,7 @@ from ..ftrack_server import socket_thread from ..lib import credentials from . import login_dialog -from pype.api import Logger, resources +from pype.api import Logger, resources, get_system_settings log = Logger().get_logger("FtrackModule", "ftrack") @@ -29,6 +29,8 @@ class FtrackModule: self.bool_action_thread_running = False self.bool_timer_event = False + self.load_ftrack_url() + self.widget_login = login_dialog.CredentialsDialog() self.widget_login.login_changed.connect(self.on_login_change) self.widget_login.logout_signal.connect(self.on_logout) @@ -292,6 +294,15 @@ class FtrackModule: def tray_exit(self): self.stop_action_server() + def load_ftrack_url(self): + ftrack_url = ( + get_system_settings() + ["modules"] + ["Ftrack"] + ["ftrack_server"] + ) + os.environ["FTRACK_SERVER"] = ftrack_url + # Definition of visibility of each menu actions def set_menu_visibility(self): self.tray_server_menu.menuAction().setVisible(self.bool_logged) From 59cef39c82c82419a0c2c83370949c73b0ed7b97 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 13:04:20 +0100 Subject: [PATCH 52/71] added settings to ftrack module lib for loading settings --- pype/modules/ftrack/lib/__init__.py | 42 +++++++------ pype/modules/ftrack/lib/settings.py | 98 +++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 pype/modules/ftrack/lib/settings.py diff --git a/pype/modules/ftrack/lib/__init__.py b/pype/modules/ftrack/lib/__init__.py index cfa00df060..cad1a0343c 100644 --- a/pype/modules/ftrack/lib/__init__.py +++ b/pype/modules/ftrack/lib/__init__.py @@ -1,29 +1,35 @@ -import os +from . settings import ( + FTRACK_MODULE_DIR, + SERVER_HANDLERS_DIR, + USER_HANDLERS_DIR, + get_ftrack_url_from_settings, + get_server_event_handler_paths, + get_user_event_handler_paths +) from . import avalon_sync from . import credentials from .ftrack_base_handler import BaseHandler from .ftrack_event_handler import BaseEvent from .ftrack_action_handler import BaseAction, ServerAction, statics_icon -FTRACK_MODULE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -SERVER_HANDLERS_DIR = os.path.join( - FTRACK_MODULE_DIR, - "events" -) -USER_HANDLERS_DIR = os.path.join( - FTRACK_MODULE_DIR, - "actions" -) __all__ = ( - "avalon_sync", - "credentials", - "BaseHandler", - "BaseEvent", - "BaseAction", - "ServerAction", - "statics_icon", "FTRACK_MODULE_DIR", "SERVER_HANDLERS_DIR", - "USER_HANDLERS_DIR" + "USER_HANDLERS_DIR", + "get_ftrack_url_from_settings", + "get_server_event_handler_paths", + "get_user_event_handler_paths", + + "avalon_sync", + + "credentials", + + "BaseHandler", + + "BaseEvent", + + "BaseAction", + "ServerAction", + "statics_icon" ) diff --git a/pype/modules/ftrack/lib/settings.py b/pype/modules/ftrack/lib/settings.py new file mode 100644 index 0000000000..bf30b8d4f8 --- /dev/null +++ b/pype/modules/ftrack/lib/settings.py @@ -0,0 +1,98 @@ +import os +from pype.api import ( + Logger, + get_system_settings +) + + +log = Logger().get_logger("ftrack_server.lib") + +FTRACK_MODULE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SERVER_HANDLERS_DIR = os.path.join(FTRACK_MODULE_DIR, "events") +USER_HANDLERS_DIR = os.path.join(FTRACK_MODULE_DIR, "actions") + + +def get_ftrack_url_from_settings(): + ftrack_url = ( + get_system_settings() + ["modules"] + ["Ftrack"] + ["ftrack_server"] + ) + return ftrack_url + + +def get_server_event_handler_paths(): + paths = [] + # Environment variable overrides settings + if "FTRACK_EVENTS_PATH" in os.environ: + env_paths = os.environ.get("FTRACK_EVENTS_PATH") + paths.extend(env_paths.split(os.pathsep)) + return paths + + # Add pype's default dir + paths.append(SERVER_HANDLERS_DIR) + # Add additional paths from settings + paths.extend( + get_system_settings() + ["modules"] + ["Ftrack"] + ["ftrack_events_path"] + ) + try: + clockify_path = clockify_event_path() + if clockify_path: + paths.append(clockify_path) + except Exception: + log.warning("Clockify paths function failed.", exc_info=True) + + # Filter only existing paths + _paths = [] + for path in paths: + if os.path.exists(path): + _paths.append(path) + else: + log.warning(( + "Registered event handler path is not accessible: {}" + ).format(path)) + return _paths + + +def get_user_event_handler_paths(): + paths = [] + # Add pype's default dir + paths.append(USER_HANDLERS_DIR) + # Add additional paths from settings + paths.extend( + get_system_settings() + ["modules"] + ["Ftrack"] + ["ftrack_actions_path"] + ) + + # Filter only existing paths + _paths = [] + for path in paths: + if os.path.exists(path): + _paths.append(path) + else: + log.warning(( + "Registered event handler path is not accessible: {}" + ).format(path)) + return _paths + + +def clockify_event_path(): + api_key = os.environ.get("CLOCKIFY_API_KEY") + if not api_key: + log.warning("Clockify API key is not set.") + return + + workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") + if not workspace_name: + log.warning("Clockify Workspace is not set.") + return + + from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH + + return CLOCKIFY_FTRACK_SERVER_PATH From 79da22562263d8ce4cb47bf0f27c164280947dcb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 13:04:58 +0100 Subject: [PATCH 53/71] fixed imports in ftrack subprocesses --- pype/modules/ftrack/ftrack_server/lib.py | 70 ------------------- .../ftrack_server/sub_event_processor.py | 4 +- .../ftrack/ftrack_server/sub_event_status.py | 6 +- .../ftrack/ftrack_server/sub_user_server.py | 4 +- 4 files changed, 8 insertions(+), 76 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index 1978d4529e..0a65af843b 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -41,76 +41,6 @@ TOPIC_STATUS_SERVER_RESULT = "pype.event.server.status.result" log = Logger().get_logger("ftrack_server.lib") -def clockify_event_path(): - api_key = os.environ.get("CLOCKIFY_API_KEY") - if not api_key: - log.warning("Clockify API key is not set.") - return - - workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") - if not workspace_name: - log.warning("Clockify Workspace is not set.") - return - - from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH - - return CLOCKIFY_FTRACK_SERVER_PATH - - -def get_server_event_handler_paths(): - paths = [] - # Add pype's default dir - paths.append(SERVER_HANDLERS_DIR) - # Add additional paths from settings - paths.extend( - get_system_settings() - ["modules"] - ["Ftrack"] - ["ftrack_events_path"] - ) - try: - clockify_path = clockify_event_path() - if clockify_path: - paths.append(clockify_path) - except Exception: - log.warning("Clockify paths function failed.", exc_info=True) - - # Filter only existing paths - _paths = [] - for path in paths: - if os.path.exists(path): - _paths.append(path) - else: - log.warning(( - "Registered event handler path is not accessible: {}" - ).format(path)) - return _paths - - -def get_user_event_handler_paths(): - paths = [] - # Add pype's default dir - paths.append(USER_HANDLERS_DIR) - # Add additional paths from settings - paths.extend( - get_system_settings() - ["modules"] - ["Ftrack"] - ["ftrack_actions_path"] - ) - - # Filter only existing paths - _paths = [] - for path in paths: - if os.path.exists(path): - _paths.append(path) - else: - log.warning(( - "Registered event handler path is not accessible: {}" - ).format(path)) - return _paths - - def get_ftrack_event_mongo_info(): database_name = ( os.environ.get("FTRACK_EVENTS_MONGO_DB") or "pype" diff --git a/pype/modules/ftrack/ftrack_server/sub_event_processor.py b/pype/modules/ftrack/ftrack_server/sub_event_processor.py index 536ac3faf2..6cf0ccf63b 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_processor.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_processor.py @@ -8,9 +8,9 @@ from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, ProcessEventHub, - TOPIC_STATUS_SERVER, - get_server_event_handler_paths + TOPIC_STATUS_SERVER ) +from pype.modules.ftrack.lib import get_server_event_handler_paths from pype.api import Logger diff --git a/pype/modules/ftrack/ftrack_server/sub_event_status.py b/pype/modules/ftrack/ftrack_server/sub_event_status.py index e478c1a1e8..a398b019eb 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_status.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_status.py @@ -9,8 +9,10 @@ import datetime import ftrack_api from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( - SocketSession, StatusEventHub, - TOPIC_STATUS_SERVER, TOPIC_STATUS_SERVER_RESULT + SocketSession, + StatusEventHub, + TOPIC_STATUS_SERVER, + TOPIC_STATUS_SERVER_RESULT ) from pype.api import Logger diff --git a/pype/modules/ftrack/ftrack_server/sub_user_server.py b/pype/modules/ftrack/ftrack_server/sub_user_server.py index d899e93a8f..2686c74c2e 100644 --- a/pype/modules/ftrack/ftrack_server/sub_user_server.py +++ b/pype/modules/ftrack/ftrack_server/sub_user_server.py @@ -5,9 +5,9 @@ import socket from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, - SocketBaseEventHub, - get_user_event_handler_paths + SocketBaseEventHub ) +from pype.modules.ftrack.lib import get_user_event_handler_paths from pype.api import Logger From db6427e07ed8eaff01e7b1842b1da23ab096ef67 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 13:06:00 +0100 Subject: [PATCH 54/71] processing of event paths is skipped in event_server_cli --- .../ftrack/ftrack_server/event_server_cli.py | 41 ++++--------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/event_server_cli.py b/pype/modules/ftrack/ftrack_server/event_server_cli.py index bf51c37290..2f582059ff 100644 --- a/pype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/pype/modules/ftrack/ftrack_server/event_server_cli.py @@ -14,9 +14,13 @@ import uuid import ftrack_api import pymongo -from pype.modules.ftrack.lib import credentials +from pype.modules.ftrack.lib import ( + credentials, + get_ftrack_url_from_settings +) from pype.modules.ftrack.ftrack_server.lib import ( - check_ftrack_url, get_ftrack_event_mongo_info + check_ftrack_url, + get_ftrack_event_mongo_info ) import socket_thread @@ -87,25 +91,6 @@ def validate_credentials(url, user, api): return True -def process_event_paths(event_paths): - print('DEBUG: Processing event paths: {}.'.format(str(event_paths))) - return_paths = [] - not_found = [] - if not event_paths: - return return_paths, not_found - - if isinstance(event_paths, str): - event_paths = event_paths.split(os.pathsep) - - for path in event_paths: - if os.path.exists(path): - return_paths.append(path) - else: - not_found.append(path) - - return os.pathsep.join(return_paths), not_found - - def legacy_server(ftrack_url): # Current file file_path = os.path.dirname(os.path.realpath(__file__)) @@ -568,6 +553,7 @@ def main(argv): os.environ["CLOCKIFY_API_KEY"] = kwargs.clockifyapikey legacy = kwargs.legacy + # Check url regex and accessibility ftrack_url = check_ftrack_url(ftrack_url) if not ftrack_url: @@ -579,19 +565,6 @@ def main(argv): print('Exiting! < Please enter valid credentials >') return 1 - # Process events path - event_paths, not_found = process_event_paths(event_paths) - if not_found: - print( - 'WARNING: These paths were not found: {}'.format(str(not_found)) - ) - if not event_paths: - if not_found: - print('ERROR: Any of entered paths is valid or can be accesible.') - else: - print('ERROR: Paths to events are not set. Exiting.') - return 1 - if kwargs.storecred: credentials.save_credentials(username, api_key, ftrack_url) From 979c58c18f8fd6b1e326fdc777e7d2aadc915c5d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 13:06:41 +0100 Subject: [PATCH 55/71] FTRACK_EVENTS_PATH env is set only if any paths were added via argument --- pype/modules/ftrack/ftrack_server/event_server_cli.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/ftrack_server/event_server_cli.py b/pype/modules/ftrack/ftrack_server/event_server_cli.py index 2f582059ff..3f8ab49405 100644 --- a/pype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/pype/modules/ftrack/ftrack_server/event_server_cli.py @@ -572,7 +572,10 @@ def main(argv): os.environ["FTRACK_SERVER"] = ftrack_url os.environ["FTRACK_API_USER"] = username os.environ["FTRACK_API_KEY"] = api_key - os.environ["FTRACK_EVENTS_PATH"] = event_paths + if event_paths: + if isinstance(event_paths, (list, tuple)): + event_paths = os.pathsep.join(event_paths) + os.environ["FTRACK_EVENTS_PATH"] = event_paths if legacy: return legacy_server(ftrack_url) From f421a604047986e4fa021e4631da63ba67cac0bb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 13:07:15 +0100 Subject: [PATCH 56/71] ftrack url is taken from settings if not set via arguments --- .../modules/ftrack/ftrack_server/event_server_cli.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/event_server_cli.py b/pype/modules/ftrack/ftrack_server/event_server_cli.py index 3f8ab49405..dbf2a2dc10 100644 --- a/pype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/pype/modules/ftrack/ftrack_server/event_server_cli.py @@ -522,16 +522,20 @@ def main(argv): "environment: $CLOCKIFY_WORKSPACE)" ) ) - ftrack_url = os.environ.get('FTRACK_SERVER') - username = os.environ.get('FTRACK_API_USER') - api_key = os.environ.get('FTRACK_API_KEY') - event_paths = os.environ.get('FTRACK_EVENTS_PATH') + ftrack_url = os.environ.get("FTRACK_SERVER") + username = os.environ.get("FTRACK_API_USER") + api_key = os.environ.get("FTRACK_API_KEY") kwargs, args = parser.parse_known_args(argv) if kwargs.ftrackurl: ftrack_url = kwargs.ftrackurl + # Load Ftrack url from settings if not set + if not ftrack_url: + ftrack_url = get_ftrack_url_from_settings() + + event_paths = None if kwargs.ftrackeventpaths: event_paths = kwargs.ftrackeventpaths From d7a0252c9f541969c6a2f56dc4ba123f4d36867c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 14:24:03 +0100 Subject: [PATCH 57/71] moved get_ftrack_event_mongo_info to ftrack.lib --- pype/modules/ftrack/ftrack_server/lib.py | 36 +------------- .../ftrack/ftrack_server/sub_event_storer.py | 8 ++-- pype/modules/ftrack/lib/settings.py | 47 ++++++++++++------- 3 files changed, 36 insertions(+), 55 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/lib.py b/pype/modules/ftrack/ftrack_server/lib.py index 0a65af843b..436e15f497 100644 --- a/pype/modules/ftrack/ftrack_server/lib.py +++ b/pype/modules/ftrack/ftrack_server/lib.py @@ -19,47 +19,15 @@ import ftrack_api._centralized_storage_scenario import ftrack_api.event from ftrack_api.logging import LazyLogMessage as L -from pype.modules.ftrack.lib import ( - SERVER_HANDLERS_DIR, - USER_HANDLERS_DIR -) -from pype.api import ( - Logger, - get_system_settings, - get_default_components, - decompose_url, - compose_url -) +from pype.modules.ftrack.lib import get_ftrack_event_mongo_info from .custom_db_connector import CustomDbConnector - +from pype.api import Logger TOPIC_STATUS_SERVER = "pype.event.server.status" TOPIC_STATUS_SERVER_RESULT = "pype.event.server.status.result" -log = Logger().get_logger("ftrack_server.lib") - - -def get_ftrack_event_mongo_info(): - database_name = ( - os.environ.get("FTRACK_EVENTS_MONGO_DB") or "pype" - ) - collection_name = ( - os.environ.get("FTRACK_EVENTS_MONGO_COL") or "ftrack_events" - ) - - mongo_url = os.environ.get("FTRACK_EVENTS_MONGO_URL") - if mongo_url is not None: - components = decompose_url(mongo_url) - else: - components = get_default_components() - - uri = compose_url(**components) - - return uri, components["port"], database_name, collection_name - - def check_ftrack_url(url, log_errors=True): """Checks if Ftrack server is responding""" if not url: diff --git a/pype/modules/ftrack/ftrack_server/sub_event_storer.py b/pype/modules/ftrack/ftrack_server/sub_event_storer.py index 938448c8a7..3523e5701f 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_storer.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_storer.py @@ -8,10 +8,12 @@ import pymongo import ftrack_api from ftrack_server import FtrackServer from pype.modules.ftrack.ftrack_server.lib import ( - SocketSession, StorerEventHub, - get_ftrack_event_mongo_info, - TOPIC_STATUS_SERVER, TOPIC_STATUS_SERVER_RESULT + SocketSession, + StorerEventHub, + TOPIC_STATUS_SERVER, + TOPIC_STATUS_SERVER_RESULT ) +from pype.modules.ftrack.lib import get_ftrack_event_mongo_info from pype.modules.ftrack.ftrack_server.custom_db_connector import ( CustomDbConnector ) diff --git a/pype/modules/ftrack/lib/settings.py b/pype/modules/ftrack/lib/settings.py index bf30b8d4f8..e8cff482db 100644 --- a/pype/modules/ftrack/lib/settings.py +++ b/pype/modules/ftrack/lib/settings.py @@ -1,25 +1,25 @@ import os from pype.api import ( Logger, - get_system_settings + get_system_settings, + get_default_components, + decompose_url, + compose_url ) - -log = Logger().get_logger("ftrack_server.lib") +log = Logger().get_logger(__name__) FTRACK_MODULE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SERVER_HANDLERS_DIR = os.path.join(FTRACK_MODULE_DIR, "events") USER_HANDLERS_DIR = os.path.join(FTRACK_MODULE_DIR, "actions") +def get_ftrack_settings(): + return get_system_settings()["modules"]["Ftrack"] + + def get_ftrack_url_from_settings(): - ftrack_url = ( - get_system_settings() - ["modules"] - ["Ftrack"] - ["ftrack_server"] - ) - return ftrack_url + return get_ftrack_settings()["ftrack_server"] def get_server_event_handler_paths(): @@ -34,10 +34,7 @@ def get_server_event_handler_paths(): paths.append(SERVER_HANDLERS_DIR) # Add additional paths from settings paths.extend( - get_system_settings() - ["modules"] - ["Ftrack"] - ["ftrack_events_path"] + get_ftrack_settings()["ftrack_events_path"] ) try: clockify_path = clockify_event_path() @@ -64,10 +61,7 @@ def get_user_event_handler_paths(): paths.append(USER_HANDLERS_DIR) # Add additional paths from settings paths.extend( - get_system_settings() - ["modules"] - ["Ftrack"] - ["ftrack_actions_path"] + get_ftrack_settings()["ftrack_actions_path"] ) # Filter only existing paths @@ -96,3 +90,20 @@ def clockify_event_path(): from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH return CLOCKIFY_FTRACK_SERVER_PATH + + +def get_ftrack_event_mongo_info(): + ftrack_settings = get_ftrack_settings() + database_name = ftrack_settings["mongo_database_name"] + collection_name = ftrack_settings["mongo_collection_name"] + + # TODO add possibility to set in settings and use PYPE_MONGO_URL if not set + mongo_url = os.environ.get("FTRACK_EVENTS_MONGO_URL") + if mongo_url is not None: + components = decompose_url(mongo_url) + else: + components = get_default_components() + + uri = compose_url(**components) + + return uri, components["port"], database_name, collection_name From 3b911a39748a252d464c62b7244fbe99c4d85c06 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 14:24:20 +0100 Subject: [PATCH 58/71] FTRACK_EVENTS_MONGO_DB and FTRACK_EVENTS_MONGO_COL renamed in settings --- pype/settings/defaults/system_settings/modules.json | 4 ++-- .../system_schema/module_settings/schema_ftrack.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 3c44d15b76..835b05b23c 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -43,8 +43,8 @@ "ftrack_server": "https://pype.ftrackapp.com", "ftrack_actions_path": [], "ftrack_events_path": [], - "FTRACK_EVENTS_MONGO_DB": "pype", - "FTRACK_EVENTS_MONGO_COL": "ftrack_events", + "mongo_database_name": "pype", + "mongo_collection_name": "ftrack_events", "intent": { "items": { "-": "-", diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index 58cd81f544..b8c41c8856 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -43,12 +43,12 @@ }, { "type": "text", - "key": "FTRACK_EVENTS_MONGO_DB", + "key": "mongo_database_name", "label": "Event Mongo DB" }, { "type": "text", - "key": "FTRACK_EVENTS_MONGO_COL", + "key": "mongo_collection_name", "label": "Events Mongo Collection" }, { From c6609d01318d9dadeb5cd803cc24f02e75af2b02 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 14:27:04 +0100 Subject: [PATCH 59/71] add get_ftrack_event_mongo_info to lib's __init__ --- pype/modules/ftrack/lib/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/lib/__init__.py b/pype/modules/ftrack/lib/__init__.py index cad1a0343c..bb79faf0cb 100644 --- a/pype/modules/ftrack/lib/__init__.py +++ b/pype/modules/ftrack/lib/__init__.py @@ -4,7 +4,8 @@ from . settings import ( USER_HANDLERS_DIR, get_ftrack_url_from_settings, get_server_event_handler_paths, - get_user_event_handler_paths + get_user_event_handler_paths, + get_ftrack_event_mongo_info ) from . import avalon_sync from . import credentials @@ -20,6 +21,7 @@ __all__ = ( "get_ftrack_url_from_settings", "get_server_event_handler_paths", "get_user_event_handler_paths", + "get_ftrack_event_mongo_info", "avalon_sync", From 58bdc2bcf9af771b0893e0ce210ad897ddbbebf1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 14:51:11 +0100 Subject: [PATCH 60/71] fix hound --- pype/modules/ftrack/ftrack_server/ftrack_server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/ftrack_server/ftrack_server.py b/pype/modules/ftrack/ftrack_server/ftrack_server.py index b03ed51cef..af48bfadc8 100644 --- a/pype/modules/ftrack/ftrack_server/ftrack_server.py +++ b/pype/modules/ftrack/ftrack_server/ftrack_server.py @@ -32,7 +32,7 @@ PYTHONPATH # Path to ftrack_api and paths to all modules used in actions class FtrackServer: - def __init__(self, handler_paths=[], server_type='action'): + def __init__(self, handler_paths=None, server_type='action'): """ - 'type' is by default set to 'action' - Runs Action server - enter 'event' for Event server @@ -50,7 +50,7 @@ class FtrackServer: self.stopped = True self.is_running = False - self.handler_paths = handler_paths + self.handler_paths = handler_paths or [] self.server_type = server_type From acacd2cfee979e79147f2a89c04d0950b8a1c0f5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 15:17:25 +0100 Subject: [PATCH 61/71] removed Ftrack environments from settings and moved ftrack's `PYBLISHPLUGINPATH` to global --- .../defaults/system_settings/general.json | 8 ++++++-- .../defaults/system_settings/modules.json | 15 --------------- .../module_settings/schema_ftrack.json | 9 --------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/pype/settings/defaults/system_settings/general.json b/pype/settings/defaults/system_settings/general.json index 7d6894446e..d610d6eba3 100644 --- a/pype/settings/defaults/system_settings/general.json +++ b/pype/settings/defaults/system_settings/general.json @@ -22,7 +22,8 @@ "PYPE_PROJECT_CONFIGS", "PYPE_PYTHON_EXE", "PYPE_OCIO_CONFIG", - "PYBLISH_GUI" + "PYBLISH_GUI", + "PYBLISHPLUGINPATH" ] }, "PYPE_APP_ROOT": "{PYPE_SETUP_PATH}/pypeapp", @@ -50,6 +51,9 @@ "darwin": "{VIRTUAL_ENV}/bin/python" }, "PYPE_OCIO_CONFIG": "{STUDIO_SOFT}/OpenColorIO-Configs", - "PYBLISH_GUI": "pyblish_pype" + "PYBLISH_GUI": "pyblish_pype", + "PYBLISHPLUGINPATH": [ + "{PYPE_MODULE_ROOT}/pype/plugins/ftrack/publish" + ] } } \ No newline at end of file diff --git a/pype/settings/defaults/system_settings/modules.json b/pype/settings/defaults/system_settings/modules.json index 835b05b23c..93c099a43e 100644 --- a/pype/settings/defaults/system_settings/modules.json +++ b/pype/settings/defaults/system_settings/modules.json @@ -131,21 +131,6 @@ "read_security_role": [] } } - }, - "environment": { - "__environment_keys__": { - "ftrack": [ - "PYBLISHPLUGINPATH", - "PYTHONPATH" - ] - }, - "PYBLISHPLUGINPATH": [ - "{PYPE_MODULE_ROOT}/pype/plugins/ftrack/publish" - ], - "PYTHONPATH": [ - "{PYPE_MODULE_ROOT}/pype/vendor", - "{PYTHONPATH}" - ] } }, "Rest Api": { diff --git a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json index b8c41c8856..5459379bcb 100644 --- a/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json +++ b/pype/tools/settings/settings/gui_schemas/system_schema/module_settings/schema_ftrack.json @@ -151,14 +151,5 @@ }] } }] - }, - { - "type": "splitter" - }, - { - "key": "environment", - "label": "Environment", - "type": "raw-json", - "env_group_key": "ftrack" }] } From b77cf7f4f34617c98bb3706cb0baaf3058cfe787 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 15:39:51 +0100 Subject: [PATCH 62/71] dont use pypeapp import --- pype/modules/logging/tray/gui/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/logging/tray/gui/models.py b/pype/modules/logging/tray/gui/models.py index ae2666f501..3591f3dde2 100644 --- a/pype/modules/logging/tray/gui/models.py +++ b/pype/modules/logging/tray/gui/models.py @@ -1,7 +1,7 @@ import collections from Qt import QtCore, QtGui from pype.api import Logger -from pypeapp.lib.log import _bootstrap_mongo_log, LOG_COLLECTION_NAME +from pype.lib.log import _bootstrap_mongo_log, LOG_COLLECTION_NAME log = Logger().get_logger("LogModel", "LoggingModule") From f94d7ac37a2bf16090b0c3710bf79224dea4aae1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 15:47:30 +0100 Subject: [PATCH 63/71] fix merge changes --- pype/lib/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 72158d3203..29ed375fe1 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -55,7 +55,9 @@ from .applications import ( from .plugin_tools import ( filter_pyblish_plugins, - source_hash + source_hash, + get_unique_layer_name, + get_background_layers ) from .user_settings import ( @@ -104,6 +106,7 @@ __all__ = [ "PostLaunchHook", "filter_pyblish_plugins", + "source_hash", "get_unique_layer_name", "get_background_layers", From d92c89197f7e425133e377ffbfb7bebc1070ad9a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 15:52:36 +0100 Subject: [PATCH 64/71] fix temp fix of ftrack server environment --- pype/modules/ftrack/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/ftrack/__init__.py b/pype/modules/ftrack/__init__.py index c223b6bbc8..1c0e1450f6 100644 --- a/pype/modules/ftrack/__init__.py +++ b/pype/modules/ftrack/__init__.py @@ -4,11 +4,11 @@ from . import ftrack_server from .ftrack_server import FtrackServer, check_ftrack_url from .lib import BaseHandler, BaseEvent, BaseAction, ServerAction -from pype.api import system_settings +from pype.api import get_system_settings # TODO: set in ftrack module os.environ["FTRACK_SERVER"] = ( - system_settings()["global"]["modules"]["Ftrack"]["ftrack_server"] + get_system_settings()["global"]["modules"]["Ftrack"]["ftrack_server"] ) __all__ = ( "ftrack_server", From c8e12a965bc9048dd45f13873d1c14e9300e19e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 15:55:56 +0100 Subject: [PATCH 65/71] ftrack server settings loading skipped "global" key --- pype/modules/ftrack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/ftrack/__init__.py b/pype/modules/ftrack/__init__.py index 1c0e1450f6..cd3bee216f 100644 --- a/pype/modules/ftrack/__init__.py +++ b/pype/modules/ftrack/__init__.py @@ -8,7 +8,7 @@ from pype.api import get_system_settings # TODO: set in ftrack module os.environ["FTRACK_SERVER"] = ( - get_system_settings()["global"]["modules"]["Ftrack"]["ftrack_server"] + get_system_settings()["modules"]["Ftrack"]["ftrack_server"] ) __all__ = ( "ftrack_server", From 4bac1556c020d1170a148eb34c1a341dc41726e7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 16:33:20 +0100 Subject: [PATCH 66/71] minor fixes --- .../ftrack/actions/action_prepare_project.py | 2 +- .../defaults/environments/global.json | 32 ------------------- 2 files changed, 1 insertion(+), 33 deletions(-) delete mode 100644 pype/settings/defaults/environments/global.json diff --git a/pype/modules/ftrack/actions/action_prepare_project.py b/pype/modules/ftrack/actions/action_prepare_project.py index b3a2a20151..3f79401430 100644 --- a/pype/modules/ftrack/actions/action_prepare_project.py +++ b/pype/modules/ftrack/actions/action_prepare_project.py @@ -2,7 +2,7 @@ import os import json from pype.modules.ftrack.lib import BaseAction, statics_icon -from pype.api import config, Anatomy, project_overrides_dir_path +from pype.api import config, Anatomy from pype.modules.ftrack.lib.avalon_sync import get_pype_attr diff --git a/pype/settings/defaults/environments/global.json b/pype/settings/defaults/environments/global.json deleted file mode 100644 index f3cc9867c3..0000000000 --- a/pype/settings/defaults/environments/global.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "PYPE_STUDIO_NAME": "Studio Name", - "PYPE_STUDIO_CODE": "stu", - "PYPE_APP_ROOT": "{PYPE_SETUP_PATH}/pypeapp", - "PYPE_MODULE_ROOT": "{PYPE_SETUP_PATH}/repos/pype", - "PYPE_PROJECT_PLUGINS": "", - "STUDIO_SOFT": "{PYPE_SETUP_ROOT}/soft", - "FFMPEG_PATH": { - "windows": "{VIRTUAL_ENV}/localized/ffmpeg_exec/windows/bin;{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/windows/bin", - "darwin": "{VIRTUAL_ENV}/localized/ffmpeg_exec/darwin/bin:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/darwin/bin", - "linux": "{VIRTUAL_ENV}/localized/ffmpeg_exec/linux:{PYPE_SETUP_PATH}/vendor/bin/ffmpeg_exec/linux" - }, - "PATH": [ - "{PYPE_CONFIG}/launchers", - "{PYPE_APP_ROOT}", - "{FFMPEG_PATH}", - "{PATH}" - ], - "PYPE_OCIO_CONFIG": "{STUDIO_SOFT}/OpenColorIO-Configs", - "PYTHONPATH": { - "windows": "{VIRTUAL_ENV}/Lib/site-packages;{PYPE_MODULE_ROOT}/pype/tools;{PYTHONPATH}", - "linux": "{VIRTUAL_ENV}/lib/python{PYTHON_VERSION}/site-packages:{PYPE_MODULE_ROOT}/pype/tools:{PYTHONPATH}", - "darwin": "{VIRTUAL_ENV}/lib/python{PYTHON_VERSION}/site-packages:{PYPE_MODULE_ROOT}/pype/tools:{PYTHONPATH}" - }, - "PYPE_PROJECT_CONFIGS": "{PYPE_SETUP_PATH}/../studio-project-configs", - "PYPE_PYTHON_EXE": { - "windows": "{VIRTUAL_ENV}/Scripts/python.exe", - "linux": "{VIRTUAL_ENV}/Scripts/python", - "darwin": "{VIRTUAL_ENV}/bin/python" - }, - "PYBLISH_GUI": "pyblish_pype" -} From e694a08e5ec8b4371915e43194e5aebf1627920b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 16:45:59 +0100 Subject: [PATCH 67/71] replaced PYPE_SETUP_PATH with PYPE_MODULE_ROOT in applications environments --- .../defaults/system_settings/applications.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pype/settings/defaults/system_settings/applications.json b/pype/settings/defaults/system_settings/applications.json index 208a8dc5e5..79d39c94f9 100644 --- a/pype/settings/defaults/system_settings/applications.json +++ b/pype/settings/defaults/system_settings/applications.json @@ -18,8 +18,8 @@ ] }, "PYTHONPATH": [ - "{PYPE_SETUP_PATH}/repos/avalon-core/setup/maya", - "{PYPE_SETUP_PATH}/repos/maya-look-assigner", + "{PYPE_MODULE_ROOT}/repos/avalon-core/setup/maya", + "{PYPE_MODULE_ROOT}/repos/maya-look-assigner", "{PYTHON_ENV}/python2/Lib/site-packages", "{PYTHONPATH}" ], @@ -140,8 +140,8 @@ ] }, "PYTHONPATH": [ - "{PYPE_SETUP_PATH}/repos/avalon-core/setup/maya", - "{PYPE_SETUP_PATH}/repos/maya-look-assigner", + "{PYPE_MODULE_ROOT}/repos/avalon-core/setup/maya", + "{PYPE_MODULE_ROOT}/repos/maya-look-assigner", "{PYTHON_ENV}/python2/Lib/site-packages", "{PYTHONPATH}" ], @@ -241,7 +241,7 @@ ] }, "NUKE_PATH": [ - "{PYPE_SETUP_PATH}/repos/avalon-core/setup/nuke/nuke_path", + "{PYPE_MODULE_ROOT}/repos/avalon-core/setup/nuke/nuke_path", "{PYPE_MODULE_ROOT}/setup/nuke/nuke_path", "{PYPE_STUDIO_PLUGINS}/nuke" ], @@ -364,7 +364,7 @@ ] }, "NUKE_PATH": [ - "{PYPE_SETUP_PATH}/repos/avalon-core/setup/nuke/nuke_path", + "{PYPE_MODULE_ROOT}/repos/avalon-core/setup/nuke/nuke_path", "{PYPE_MODULE_ROOT}/setup/nuke/nuke_path", "{PYPE_STUDIO_PLUGINS}/nuke" ], @@ -917,9 +917,9 @@ "CREATE_NEW_CONSOLE" ] }, - "BLENDER_USER_SCRIPTS": "{PYPE_SETUP_PATH}/repos/avalon-core/setup/blender", + "BLENDER_USER_SCRIPTS": "{PYPE_MODULE_ROOT}/repos/avalon-core/setup/blender", "PYTHONPATH": [ - "{PYPE_SETUP_PATH}/repos/avalon-core/setup/blender", + "{PYPE_MODULE_ROOT}/repos/avalon-core/setup/blender", "{PYTHONPATH}" ], "CREATE_NEW_CONSOLE": "yes" @@ -1277,7 +1277,7 @@ "QT_PREFERRED_BINDING" ] }, - "AVALON_UNREAL_PLUGIN": "{PYPE_SETUP_PATH}/repos/avalon-unreal-integration", + "AVALON_UNREAL_PLUGIN": "{PYPE_MODULE_ROOT}/repos/avalon-unreal-integration", "PYPE_LOG_NO_COLORS": "True", "QT_PREFERRED_BINDING": "PySide" }, From ab3091d634075730d5a10546694cbeb4957abb9b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 17:12:07 +0100 Subject: [PATCH 68/71] removed previous hooks implementation --- pype/lib/__init__.py | 2 -- pype/lib/hooks.py | 71 -------------------------------------------- 2 files changed, 73 deletions(-) delete mode 100644 pype/lib/hooks.py diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index 533d797982..c08fc84513 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -22,8 +22,6 @@ from .avalon_context import ( BuildWorkfile ) -from .hooks import PypeHook, execute_hook - from .applications import ( ApplicationLaunchFailed, ApplictionExecutableNotFound, diff --git a/pype/lib/hooks.py b/pype/lib/hooks.py deleted file mode 100644 index bb5406572e..0000000000 --- a/pype/lib/hooks.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -"""Package containing code for handling hooks.""" -import os -import sys -import types -import logging -from abc import ABCMeta, abstractmethod - -import six - - -log = logging.getLogger(__name__) - - -@six.add_metaclass(ABCMeta) -class PypeHook: - """Abstract class from all hooks should inherit.""" - - def __init__(self): - """Constructor.""" - pass - - @abstractmethod - def execute(self, *args, **kwargs): - """Abstract execute method.""" - pass - - -def execute_hook(hook, *args, **kwargs): - """Execute hook with arguments. - - This will load hook file, instantiate class and call - :meth:`PypeHook.execute` method on it. Hook must be in a form:: - - $PYPE_SETUP_PATH/repos/pype/path/to/hook.py/HookClass - - This will load `hook.py`, instantiate HookClass and then execute_hook - `execute(*args, **kwargs)` - - Args: - hook (str): path to hook class. - - """ - class_name = hook.split("/")[-1] - - abspath = os.path.join(os.getenv('PYPE_SETUP_PATH'), - 'repos', 'pype', *hook.split("/")[:-1]) - - mod_name, mod_ext = os.path.splitext(os.path.basename(abspath)) - - if not mod_ext == ".py": - return False - - module = types.ModuleType(mod_name) - module.__file__ = abspath - - try: - with open(abspath, errors='ignore') as f: - six.exec_(f.read(), module.__dict__) - - sys.modules[abspath] = module - - except Exception as exp: - log.exception("loading hook failed: {}".format(exp), - exc_info=True) - return False - - obj = getattr(module, class_name) - hook_obj = obj() - ret_val = hook_obj.execute(*args, **kwargs) - return ret_val From 4690d905d90e912154bd0a935d9b7131f60bd3cf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 7 Dec 2020 17:14:20 +0100 Subject: [PATCH 69/71] removed converted hooks --- pype/hooks/celaction/prelaunch.py | 208 -------------------------- pype/hooks/fusion/prelaunch.py | 61 -------- pype/hooks/photoshop/prelaunch.py | 23 --- pype/hooks/premiere/prelaunch.py | 161 -------------------- pype/hooks/resolve/prelaunch.py | 69 --------- pype/hooks/tvpaint/prelaunch.py | 136 ----------------- pype/hooks/unreal/unreal_prelaunch.py | 83 ---------- 7 files changed, 741 deletions(-) delete mode 100644 pype/hooks/celaction/prelaunch.py delete mode 100644 pype/hooks/fusion/prelaunch.py delete mode 100644 pype/hooks/photoshop/prelaunch.py delete mode 100644 pype/hooks/premiere/prelaunch.py delete mode 100644 pype/hooks/resolve/prelaunch.py delete mode 100644 pype/hooks/tvpaint/prelaunch.py delete mode 100644 pype/hooks/unreal/unreal_prelaunch.py diff --git a/pype/hooks/celaction/prelaunch.py b/pype/hooks/celaction/prelaunch.py deleted file mode 100644 index c8541a9bc3..0000000000 --- a/pype/hooks/celaction/prelaunch.py +++ /dev/null @@ -1,208 +0,0 @@ -import logging -import os -import winreg -import shutil -from pype.lib import PypeHook -from pype.api import ( - Anatomy, - Logger, - get_last_version_from_path -) - -from avalon import io, api, lib - -log = logging.getLogger(__name__) - - -class CelactionPrelaunchHook(PypeHook): - """ - This hook will check if current workfile path has Unreal - project inside. IF not, it initialize it and finally it pass - path to the project by environment variable to Unreal launcher - shell script. - """ - workfile_ext = "scn" - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - if not env: - env = os.environ - - # initialize - self._S = api.Session - - # get publish version of celaction - app = "celaction_publish" - - # get context variables - project = self._S["AVALON_PROJECT"] = env["AVALON_PROJECT"] - asset = self._S["AVALON_ASSET"] = env["AVALON_ASSET"] - task = self._S["AVALON_TASK"] = env["AVALON_TASK"] - workdir = self._S["AVALON_WORKDIR"] = env["AVALON_WORKDIR"] - - # get workfile path - anatomy_filled = self.get_anatomy_filled() - workfile = anatomy_filled["work"]["file"] - version = anatomy_filled["version"] - - # create workdir if doesn't exist - os.makedirs(workdir, exist_ok=True) - self.log.info(f"Work dir is: `{workdir}`") - - # get last version of workfile - workfile_last = env.get("AVALON_LAST_WORKFILE") - self.log.debug(f"_ workfile_last: `{workfile_last}`") - - if workfile_last: - workfile = workfile_last - - workfile_path = os.path.join(workdir, workfile) - - # copy workfile from template if doesnt exist any on path - if not os.path.isfile(workfile_path): - # try to get path from environment or use default - # from `pype.celation` dir - template_path = env.get("CELACTION_TEMPLATE") or os.path.join( - env.get("PYPE_MODULE_ROOT"), - "pype/hosts/celaction/celaction_template_scene.scn" - ) - self.log.info( - f"Creating workfile from template: `{template_path}`") - shutil.copy2( - os.path.normpath(template_path), - os.path.normpath(workfile_path) - ) - - self.log.info(f"Workfile to open: `{workfile_path}`") - - # adding compulsory environment var for openting file - env["PYPE_CELACTION_PROJECT_FILE"] = workfile_path - - # setting output parameters - path = r"Software\CelAction\CelAction2D\User Settings" - winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) - hKey = winreg.OpenKey( - winreg.HKEY_CURRENT_USER, - "Software\\CelAction\\CelAction2D\\User Settings", 0, - winreg.KEY_ALL_ACCESS) - - # TODO: change to root path and pyblish standalone to premiere way - pype_root_path = os.getenv("PYPE_SETUP_PATH") - path = os.path.join(pype_root_path, - "pype.bat") - - winreg.SetValueEx(hKey, "SubmitAppTitle", 0, winreg.REG_SZ, path) - - parameters = [ - "launch", - f"--app {app}", - f"--project {project}", - f"--asset {asset}", - f"--task {task}", - "--currentFile \\\"\"*SCENE*\"\\\"", - "--chunk 10", - "--frameStart *START*", - "--frameEnd *END*", - "--resolutionWidth *X*", - "--resolutionHeight *Y*", - # "--programDir \"'*PROGPATH*'\"" - ] - winreg.SetValueEx(hKey, "SubmitParametersTitle", 0, winreg.REG_SZ, - " ".join(parameters)) - - # setting resolution parameters - path = r"Software\CelAction\CelAction2D\User Settings\Dialogs" - path += r"\SubmitOutput" - winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) - hKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, - winreg.KEY_ALL_ACCESS) - winreg.SetValueEx(hKey, "SaveScene", 0, winreg.REG_DWORD, 1) - winreg.SetValueEx(hKey, "CustomX", 0, winreg.REG_DWORD, 1920) - winreg.SetValueEx(hKey, "CustomY", 0, winreg.REG_DWORD, 1080) - - # making sure message dialogs don't appear when overwriting - path = r"Software\CelAction\CelAction2D\User Settings\Messages" - path += r"\OverwriteScene" - winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) - hKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, - winreg.KEY_ALL_ACCESS) - winreg.SetValueEx(hKey, "Result", 0, winreg.REG_DWORD, 6) - winreg.SetValueEx(hKey, "Valid", 0, winreg.REG_DWORD, 1) - - path = r"Software\CelAction\CelAction2D\User Settings\Messages" - path += r"\SceneSaved" - winreg.CreateKey(winreg.HKEY_CURRENT_USER, path) - hKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, - winreg.KEY_ALL_ACCESS) - winreg.SetValueEx(hKey, "Result", 0, winreg.REG_DWORD, 1) - winreg.SetValueEx(hKey, "Valid", 0, winreg.REG_DWORD, 1) - - return True - - def get_anatomy_filled(self): - root_path = api.registered_root() - project_name = self._S["AVALON_PROJECT"] - asset_name = self._S["AVALON_ASSET"] - - io.install() - project_entity = io.find_one({ - "type": "project", - "name": project_name - }) - assert project_entity, ( - "Project '{0}' was not found." - ).format(project_name) - log.debug("Collected Project \"{}\"".format(project_entity)) - - asset_entity = io.find_one({ - "type": "asset", - "name": asset_name, - "parent": project_entity["_id"] - }) - assert asset_entity, ( - "No asset found by the name '{0}' in project '{1}'" - ).format(asset_name, project_name) - - project_name = project_entity["name"] - - log.info( - "Anatomy object collected for project \"{}\".".format(project_name) - ) - - hierarchy_items = asset_entity["data"]["parents"] - hierarchy = "" - if hierarchy_items: - hierarchy = os.path.join(*hierarchy_items) - - template_data = { - "root": root_path, - "project": { - "name": project_name, - "code": project_entity["data"].get("code") - }, - "asset": asset_entity["name"], - "hierarchy": hierarchy.replace("\\", "/"), - "task": self._S["AVALON_TASK"], - "ext": self.workfile_ext, - "version": 1, - "username": os.getenv("PYPE_USERNAME", "").strip() - } - - avalon_app_name = os.environ.get("AVALON_APP_NAME") - if avalon_app_name: - application_def = lib.get_application(avalon_app_name) - app_dir = application_def.get("application_dir") - if app_dir: - template_data["app"] = app_dir - - anatomy = Anatomy(project_name) - anatomy_filled = anatomy.format_all(template_data).get_solved() - - return anatomy_filled diff --git a/pype/hooks/fusion/prelaunch.py b/pype/hooks/fusion/prelaunch.py deleted file mode 100644 index 69e91eda05..0000000000 --- a/pype/hooks/fusion/prelaunch.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import traceback -import importlib -from pype.lib import PypeHook -from pypeapp import Logger -from pype.hosts.fusion import utils - - -class FusionPrelaunch(PypeHook): - """ - This hook will check if current workfile path has Fusion - project inside. - """ - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - - if not env: - env = os.environ - - # making sure pyton 3.6 is installed at provided path - py36_dir = os.path.normpath(env.get("PYTHON36", "")) - assert os.path.isdir(py36_dir), ( - "Python 3.6 is not installed at the provided folder path. Either " - "make sure the `environments\resolve.json` is having correctly " - "set `PYTHON36` or make sure Python 3.6 is installed " - f"in given path. \nPYTHON36E: `{py36_dir}`" - ) - self.log.info(f"Path to Fusion Python folder: `{py36_dir}`...") - env["PYTHON36"] = py36_dir - - # setting utility scripts dir for scripts syncing - us_dir = os.path.normpath(env.get("FUSION_UTILITY_SCRIPTS_DIR", "")) - assert os.path.isdir(us_dir), ( - "Fusion utility script dir does not exists. Either make sure " - "the `environments\fusion.json` is having correctly set " - "`FUSION_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" - f"FUSION_UTILITY_SCRIPTS_DIR: `{us_dir}`" - ) - - try: - __import__("avalon.fusion") - __import__("pyblish") - - except ImportError as e: - print(traceback.format_exc()) - print("pyblish: Could not load integration: %s " % e) - - else: - # Resolve Setup integration - importlib.reload(utils) - utils.setup(env) - - return True diff --git a/pype/hooks/photoshop/prelaunch.py b/pype/hooks/photoshop/prelaunch.py deleted file mode 100644 index 4f00e4cd83..0000000000 --- a/pype/hooks/photoshop/prelaunch.py +++ /dev/null @@ -1,23 +0,0 @@ -import pype.lib -from pype.api import Logger - - -class PhotoshopPrelaunch(pype.lib.PypeHook): - """This hook will check for the existence of PyWin - - PyWin is a requirement for the Photoshop integration. - """ - project_code = None - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - output = pype.lib._subprocess(["pip", "install", "pywin32==227"]) - self.log.info(output) - return True diff --git a/pype/hooks/premiere/prelaunch.py b/pype/hooks/premiere/prelaunch.py deleted file mode 100644 index c0a65c0bf2..0000000000 --- a/pype/hooks/premiere/prelaunch.py +++ /dev/null @@ -1,161 +0,0 @@ -import os -import traceback -import winreg -from avalon import api, io, lib -from pype.lib import PypeHook -from pype.api import Logger, Anatomy -from pype.hosts.premiere import lib as prlib - - -class PremierePrelaunch(PypeHook): - """ - This hook will check if current workfile path has Adobe Premiere - project inside. IF not, it initialize it and finally it pass - path to the project by environment variable to Premiere launcher - shell script. - """ - project_code = None - reg_string_value = [{ - "path": r"Software\Adobe\CSXS.9", - "name": "PlayerDebugMode", - "type": winreg.REG_SZ, - "value": "1" - }] - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - - if not env: - env = os.environ - - # initialize - self._S = api.Session - - # get context variables - self._S["AVALON_PROJECT"] = env["AVALON_PROJECT"] - self._S["AVALON_ASSET"] = env["AVALON_ASSET"] - task = self._S["AVALON_TASK"] = env["AVALON_TASK"] - - # get workfile path - anatomy_filled = self.get_anatomy_filled() - - # if anatomy template should have different root for particular task - # just add for example > work[conforming]: - workfile_search_key = f"work[{task.lower()}]" - workfile_key = anatomy_filled.get( - workfile_search_key, - anatomy_filled.get("work") - ) - workdir = env["AVALON_WORKDIR"] = workfile_key["folder"] - - # create workdir if doesn't exist - os.makedirs(workdir, exist_ok=True) - self.log.info(f"Work dir is: `{workdir}`") - - # adding project code to env - env["AVALON_PROJECT_CODE"] = self.project_code - - # add keys to registry - self.modify_registry() - - # start avalon - try: - __import__("pype.hosts.premiere") - __import__("pyblish") - - except ImportError as e: - print(traceback.format_exc()) - print("pyblish: Could not load integration: %s " % e) - - else: - # Premiere Setup integration - prlib.setup(env) - - return True - - def modify_registry(self): - # adding key to registry - for key in self.reg_string_value: - winreg.CreateKey(winreg.HKEY_CURRENT_USER, key["path"]) - rg_key = winreg.OpenKey( - key=winreg.HKEY_CURRENT_USER, - sub_key=key["path"], - reserved=0, - access=winreg.KEY_ALL_ACCESS) - - winreg.SetValueEx( - rg_key, - key["name"], - 0, - key["type"], - key["value"] - ) - - def get_anatomy_filled(self): - root_path = api.registered_root() - project_name = self._S["AVALON_PROJECT"] - asset_name = self._S["AVALON_ASSET"] - - io.install() - project_entity = io.find_one({ - "type": "project", - "name": project_name - }) - assert project_entity, ( - "Project '{0}' was not found." - ).format(project_name) - self.log.debug("Collected Project \"{}\"".format(project_entity)) - - asset_entity = io.find_one({ - "type": "asset", - "name": asset_name, - "parent": project_entity["_id"] - }) - assert asset_entity, ( - "No asset found by the name '{0}' in project '{1}'" - ).format(asset_name, project_name) - - project_name = project_entity["name"] - self.project_code = project_entity["data"].get("code") - - self.log.info( - "Anatomy object collected for project \"{}\".".format(project_name) - ) - - hierarchy_items = asset_entity["data"]["parents"] - hierarchy = "" - if hierarchy_items: - hierarchy = os.path.join(*hierarchy_items) - - template_data = { - "root": root_path, - "project": { - "name": project_name, - "code": self.project_code - }, - "asset": asset_entity["name"], - "hierarchy": hierarchy.replace("\\", "/"), - "task": self._S["AVALON_TASK"], - "ext": "ppro", - "version": 1, - "username": os.getenv("PYPE_USERNAME", "").strip() - } - - avalon_app_name = os.environ.get("AVALON_APP_NAME") - if avalon_app_name: - application_def = lib.get_application(avalon_app_name) - app_dir = application_def.get("application_dir") - if app_dir: - template_data["app"] = app_dir - - anatomy = Anatomy(project_name) - anatomy_filled = anatomy.format_all(template_data).get_solved() - - return anatomy_filled diff --git a/pype/hooks/resolve/prelaunch.py b/pype/hooks/resolve/prelaunch.py deleted file mode 100644 index a122b87868..0000000000 --- a/pype/hooks/resolve/prelaunch.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import traceback -import importlib -from pype.lib import PypeHook -from pypeapp import Logger -from pype.hosts.resolve import utils - - -class ResolvePrelaunch(PypeHook): - """ - This hook will check if current workfile path has Resolve - project inside. IF not, it initialize it and finally it pass - path to the project by environment variable to Premiere launcher - shell script. - """ - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - - if not env: - env = os.environ - - # making sure pyton 3.6 is installed at provided path - py36_dir = os.path.normpath(env.get("PYTHON36_RESOLVE", "")) - assert os.path.isdir(py36_dir), ( - "Python 3.6 is not installed at the provided folder path. Either " - "make sure the `environments\resolve.json` is having correctly " - "set `PYTHON36_RESOLVE` or make sure Python 3.6 is installed " - f"in given path. \nPYTHON36_RESOLVE: `{py36_dir}`" - ) - self.log.info(f"Path to Resolve Python folder: `{py36_dir}`...") - env["PYTHON36_RESOLVE"] = py36_dir - - # setting utility scripts dir for scripts syncing - us_dir = os.path.normpath(env.get("RESOLVE_UTILITY_SCRIPTS_DIR", "")) - assert os.path.isdir(us_dir), ( - "Resolve utility script dir does not exists. Either make sure " - "the `environments\resolve.json` is having correctly set " - "`RESOLVE_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" - f"RESOLVE_UTILITY_SCRIPTS_DIR: `{us_dir}`" - ) - self.log.debug(f"-- us_dir: `{us_dir}`") - - # correctly format path for pre python script - pre_py_sc = os.path.normpath(env.get("PRE_PYTHON_SCRIPT", "")) - env["PRE_PYTHON_SCRIPT"] = pre_py_sc - self.log.debug(f"-- pre_py_sc: `{pre_py_sc}`...") - try: - __import__("pype.hosts.resolve") - __import__("pyblish") - - except ImportError as e: - print(traceback.format_exc()) - print("pyblish: Could not load integration: %s " % e) - - else: - # Resolve Setup integration - importlib.reload(utils) - self.log.debug(f"-- utils.__file__: `{utils.__file__}`") - utils.setup(env) - - return True diff --git a/pype/hooks/tvpaint/prelaunch.py b/pype/hooks/tvpaint/prelaunch.py deleted file mode 100644 index 0b3899f555..0000000000 --- a/pype/hooks/tvpaint/prelaunch.py +++ /dev/null @@ -1,136 +0,0 @@ -import os -import shutil -import platform -import pype.lib -from pype.api import Anatomy, Logger -import getpass -import avalon.api - - -class TvpaintPrelaunchHook(pype.lib.PypeHook): - """ - Workfile preparation hook - """ - host_name = "tvpaint" - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def install_pywin(self): - if platform.system().lower() != "windows": - return - - try: - from win32com.shell import shell - except Exception: - output = pype.lib._subprocess(["pip", "install", "pywin32==227"]) - self.log.info(output) - - def execute(self, *args, env: dict = None) -> bool: - if not env: - env = os.environ - - self.install_pywin() - - # get context variables - project_name = env["AVALON_PROJECT"] - asset_name = env["AVALON_ASSET"] - task_name = env["AVALON_TASK"] - workdir = env["AVALON_WORKDIR"] - extension = avalon.api.HOST_WORKFILE_EXTENSIONS[self.host_name][0] - - # get workfile path - workfile_path = self.get_anatomy_filled( - workdir, project_name, asset_name, task_name) - - # create workdir if doesn't exist - os.makedirs(workdir, exist_ok=True) - self.log.info(f"Work dir is: `{workdir}`") - - # get last version of workfile - workfile_last = env.get("AVALON_LAST_WORKFILE") - self.log.debug(f"_ workfile_last: `{workfile_last}`") - - if workfile_last: - workfile = workfile_last - workfile_path = os.path.join(workdir, workfile) - - # copy workfile from template if doesnt exist any on path - if not os.path.isfile(workfile_path): - # try to get path from environment or use default - # from `pype.hosts.tvpaint` dir - template_path = env.get("TVPAINT_TEMPLATE") or os.path.join( - env.get("PYPE_MODULE_ROOT"), - "pype/hosts/tvpaint/template.tvpp" - ) - - # try to get template from project config folder - proj_config_path = os.path.join( - env["PYPE_PROJECT_CONFIGS"], project_name) - if os.path.exists(proj_config_path): - - template_file = None - for f in os.listdir(proj_config_path): - if extension in os.path.splitext(f): - template_file = f - - if template_file: - template_path = os.path.join( - proj_config_path, template_file) - self.log.info( - f"Creating workfile from template: `{template_path}`") - - # copy template to new destinantion - shutil.copy2( - os.path.normpath(template_path), - os.path.normpath(workfile_path) - ) - - self.log.info(f"Workfile to open: `{workfile_path}`") - - # adding compulsory environment var for openting file - env["PYPE_TVPAINT_PROJECT_FILE"] = workfile_path - - return True - - def get_anatomy_filled(self, workdir, project_name, asset_name, task_name): - dbcon = avalon.api.AvalonMongoDB() - dbcon.install() - dbcon.Session["AVALON_PROJECT"] = project_name - project_document = dbcon.find_one({"type": "project"}) - asset_document = dbcon.find_one({ - "type": "asset", - "name": asset_name - }) - dbcon.uninstall() - - asset_doc_parents = asset_document["data"].get("parents") - hierarchy = "/".join(asset_doc_parents) - - data = { - "project": { - "name": project_document["name"], - "code": project_document["data"].get("code") - }, - "task": task_name, - "asset": asset_name, - "app": self.host_name, - "hierarchy": hierarchy - } - anatomy = Anatomy(project_name) - extensions = avalon.api.HOST_WORKFILE_EXTENSIONS[self.host_name] - file_template = anatomy.templates["work"]["file"] - data.update({ - "version": 1, - "user": os.environ.get("PYPE_USERNAME") or getpass.getuser(), - "ext": extensions[0] - }) - - return avalon.api.last_workfile( - workdir, file_template, data, extensions, True - ) diff --git a/pype/hooks/unreal/unreal_prelaunch.py b/pype/hooks/unreal/unreal_prelaunch.py deleted file mode 100644 index 9f0cc45b96..0000000000 --- a/pype/hooks/unreal/unreal_prelaunch.py +++ /dev/null @@ -1,83 +0,0 @@ -import logging -import os - -from pype.lib import PypeHook -from pype.hosts.unreal import lib as unreal_lib -from pype.api import Logger - -log = logging.getLogger(__name__) - - -class UnrealPrelaunch(PypeHook): - """ - This hook will check if current workfile path has Unreal - project inside. IF not, it initialize it and finally it pass - path to the project by environment variable to Unreal launcher - shell script. - """ - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - if not env: - env = os.environ - asset = env["AVALON_ASSET"] - task = env["AVALON_TASK"] - workdir = env["AVALON_WORKDIR"] - engine_version = env["AVALON_APP_NAME"].split("_")[-1] - project_name = f"{asset}_{task}" - - # Unreal is sensitive about project names longer then 20 chars - if len(project_name) > 20: - self.log.warning((f"Project name exceed 20 characters " - f"({project_name})!")) - - # Unreal doesn't accept non alphabet characters at the start - # of the project name. This is because project name is then used - # in various places inside c++ code and there variable names cannot - # start with non-alpha. We append 'P' before project name to solve it. - # 😱 - if not project_name[:1].isalpha(): - self.log.warning(f"Project name doesn't start with alphabet " - f"character ({project_name}). Appending 'P'") - project_name = f"P{project_name}" - - project_path = os.path.join(workdir, project_name) - - self.log.info((f"{self.signature} requested UE4 version: " - f"[ {engine_version} ]")) - - detected = unreal_lib.get_engine_versions() - detected_str = ', '.join(detected.keys()) or 'none' - self.log.info((f"{self.signature} detected UE4 versions: " - f"[ {detected_str} ]")) - del(detected_str) - engine_version = ".".join(engine_version.split(".")[:2]) - if engine_version not in detected.keys(): - self.log.error((f"{self.signature} requested version not " - f"detected [ {engine_version} ]")) - return False - - os.makedirs(project_path, exist_ok=True) - - project_file = os.path.join(project_path, f"{project_name}.uproject") - engine_path = detected[engine_version] - if not os.path.isfile(project_file): - self.log.info((f"{self.signature} creating unreal " - f"project [ {project_name} ]")) - if env.get("AVALON_UNREAL_PLUGIN"): - os.environ["AVALON_UNREAL_PLUGIN"] = env.get("AVALON_UNREAL_PLUGIN") # noqa: E501 - unreal_lib.create_unreal_project(project_name, - engine_version, - project_path, - engine_path=engine_path) - - env["PYPE_UNREAL_PROJECT_FILE"] = project_file - env["AVALON_CURRENT_UNREAL_ENGINE"] = engine_path - return True From cf82186326af5b4eb7f9b0487658bc9774a7c1a1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 8 Dec 2020 09:53:03 +0100 Subject: [PATCH 70/71] remove hooks that were added during merge --- pype/hooks/fusion/prelaunch.py | 61 ----------------------------- pype/hooks/resolve/prelaunch.py | 69 --------------------------------- 2 files changed, 130 deletions(-) delete mode 100644 pype/hooks/fusion/prelaunch.py delete mode 100644 pype/hooks/resolve/prelaunch.py diff --git a/pype/hooks/fusion/prelaunch.py b/pype/hooks/fusion/prelaunch.py deleted file mode 100644 index 5c9535c11f..0000000000 --- a/pype/hooks/fusion/prelaunch.py +++ /dev/null @@ -1,61 +0,0 @@ -import os -import traceback -import importlib -from pype.lib import PypeHook -from pype.api import Logger -from pype.hosts.fusion import utils - - -class FusionPrelaunch(PypeHook): - """ - This hook will check if current workfile path has Fusion - project inside. - """ - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - - if not env: - env = os.environ - - # making sure pyton 3.6 is installed at provided path - py36_dir = os.path.normpath(env.get("PYTHON36", "")) - assert os.path.isdir(py36_dir), ( - "Python 3.6 is not installed at the provided folder path. Either " - "make sure the `environments\resolve.json` is having correctly " - "set `PYTHON36` or make sure Python 3.6 is installed " - f"in given path. \nPYTHON36E: `{py36_dir}`" - ) - self.log.info(f"Path to Fusion Python folder: `{py36_dir}`...") - env["PYTHON36"] = py36_dir - - # setting utility scripts dir for scripts syncing - us_dir = os.path.normpath(env.get("FUSION_UTILITY_SCRIPTS_DIR", "")) - assert os.path.isdir(us_dir), ( - "Fusion utility script dir does not exists. Either make sure " - "the `environments\fusion.json` is having correctly set " - "`FUSION_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" - f"FUSION_UTILITY_SCRIPTS_DIR: `{us_dir}`" - ) - - try: - __import__("avalon.fusion") - __import__("pyblish") - - except ImportError as e: - print(traceback.format_exc()) - print("pyblish: Could not load integration: %s " % e) - - else: - # Resolve Setup integration - importlib.reload(utils) - utils.setup(env) - - return True diff --git a/pype/hooks/resolve/prelaunch.py b/pype/hooks/resolve/prelaunch.py deleted file mode 100644 index 50cfee7a1c..0000000000 --- a/pype/hooks/resolve/prelaunch.py +++ /dev/null @@ -1,69 +0,0 @@ -import os -import traceback -import importlib -from pype.lib import PypeHook -from pype.api import Logger -from pype.hosts.resolve import utils - - -class ResolvePrelaunch(PypeHook): - """ - This hook will check if current workfile path has Resolve - project inside. IF not, it initialize it and finally it pass - path to the project by environment variable to Premiere launcher - shell script. - """ - - def __init__(self, logger=None): - if not logger: - self.log = Logger().get_logger(self.__class__.__name__) - else: - self.log = logger - - self.signature = "( {} )".format(self.__class__.__name__) - - def execute(self, *args, env: dict = None) -> bool: - - if not env: - env = os.environ - - # making sure pyton 3.6 is installed at provided path - py36_dir = os.path.normpath(env.get("PYTHON36_RESOLVE", "")) - assert os.path.isdir(py36_dir), ( - "Python 3.6 is not installed at the provided folder path. Either " - "make sure the `environments\resolve.json` is having correctly " - "set `PYTHON36_RESOLVE` or make sure Python 3.6 is installed " - f"in given path. \nPYTHON36_RESOLVE: `{py36_dir}`" - ) - self.log.info(f"Path to Resolve Python folder: `{py36_dir}`...") - env["PYTHON36_RESOLVE"] = py36_dir - - # setting utility scripts dir for scripts syncing - us_dir = os.path.normpath(env.get("RESOLVE_UTILITY_SCRIPTS_DIR", "")) - assert os.path.isdir(us_dir), ( - "Resolve utility script dir does not exists. Either make sure " - "the `environments\resolve.json` is having correctly set " - "`RESOLVE_UTILITY_SCRIPTS_DIR` or reinstall DaVinci Resolve. \n" - f"RESOLVE_UTILITY_SCRIPTS_DIR: `{us_dir}`" - ) - self.log.debug(f"-- us_dir: `{us_dir}`") - - # correctly format path for pre python script - pre_py_sc = os.path.normpath(env.get("PRE_PYTHON_SCRIPT", "")) - env["PRE_PYTHON_SCRIPT"] = pre_py_sc - self.log.debug(f"-- pre_py_sc: `{pre_py_sc}`...") - try: - __import__("pype.hosts.resolve") - __import__("pyblish") - - except ImportError as e: - print(traceback.format_exc()) - print("pyblish: Could not load integration: %s " % e) - - else: - # Resolve Setup integration - importlib.reload(utils) - self.log.debug(f"-- utils.__file__: `{utils.__file__}`") - utils.setup(env) - - return True From c33ba0c0453eceab87c082608b2cf7147f77d4ec Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 9 Dec 2020 09:35:11 +0100 Subject: [PATCH 71/71] Updated submodule repos/avalon-core --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index e55c3d10a6..6b53d81387 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit e55c3d10a63cb4d660fcb9eb11f7448b86b15443 +Subproject commit 6b53d813877b283ff534d8f2e998213866e016d6