diff --git a/pype/hooks/premiere/prelaunch.py b/pype/hooks/premiere/prelaunch.py index 118493e9a7f..c0a65c0bf2b 100644 --- a/pype/hooks/premiere/prelaunch.py +++ b/pype/hooks/premiere/prelaunch.py @@ -1,5 +1,6 @@ import os import traceback +import winreg from avalon import api, io, lib from pype.lib import PypeHook from pype.api import Logger, Anatomy @@ -14,6 +15,12 @@ class PremierePrelaunch(PypeHook): 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: @@ -55,6 +62,10 @@ def execute(self, *args, env: dict = None) -> bool: # 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") @@ -69,6 +80,24 @@ def execute(self, *args, env: dict = None) -> bool: 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"] diff --git a/pype/hosts/celaction/cli.py b/pype/hosts/celaction/cli.py index fa55db32007..8cf2bcc7916 100644 --- a/pype/hosts/celaction/cli.py +++ b/pype/hosts/celaction/cli.py @@ -11,7 +11,7 @@ from pype.api import Logger import pype -import pype.celaction +from pype.hosts import celaction log = Logger().get_logger("Celaction_cli_publisher") @@ -49,7 +49,7 @@ def cli(): # parser.add_argument("--programDir", # help=("Directory with celaction program installation")) - pype.celaction.kwargs = parser.parse_args(sys.argv[1:]).__dict__ + celaction.kwargs = parser.parse_args(sys.argv[1:]).__dict__ def _prepare_publish_environments(): diff --git a/pype/hosts/maya/expected_files.py b/pype/hosts/maya/expected_files.py index 3292b867cec..a7204cba93e 100644 --- a/pype/hosts/maya/expected_files.py +++ b/pype/hosts/maya/expected_files.py @@ -536,6 +536,15 @@ def get_renderer_prefix(self): prefix = "{}_".format(prefix) return prefix + def _get_layer_data(self): + """Override to get vray specific extension.""" + layer_data = super(ExpectedFilesVray, self)._get_layer_data() + default_ext = cmds.getAttr("vraySettings.imageFormatStr") + if default_ext == "exr (multichannel)" or default_ext == "exr (deep)": + default_ext = "exr" + layer_data["defaultExt"] = default_ext + return layer_data + def get_files(self): """Get expected files. diff --git a/pype/modules/adobe_communicator/lib/publish.py b/pype/modules/adobe_communicator/lib/publish.py index 6a9faf0403c..b222a1bd592 100644 --- a/pype/modules/adobe_communicator/lib/publish.py +++ b/pype/modules/adobe_communicator/lib/publish.py @@ -18,7 +18,7 @@ def main(env): # Register Host (and it's pyblish plugins) host_name = env["AVALON_APP"] # TODO not sure if use "pype." or "avalon." for host import - host_import_str = f"pype.{host_name}" + host_import_str = f"pype.hosts.{host_name}" try: host_module = importlib.import_module(host_import_str) diff --git a/pype/modules/ftrack/actions/action_delete_asset.py b/pype/modules/ftrack/actions/action_delete_asset.py index 1074efee3b0..27394770e13 100644 --- a/pype/modules/ftrack/actions/action_delete_asset.py +++ b/pype/modules/ftrack/actions/action_delete_asset.py @@ -497,9 +497,8 @@ def launch(self, session, entities, event): for entity in entities: ftrack_id = entity["id"] ftrack_id_name_map[ftrack_id] = entity["name"] - if ftrack_id in ftrack_ids_to_delete: - continue - not_deleted_entities_id.append(ftrack_id) + if ftrack_id not in ftrack_ids_to_delete: + not_deleted_entities_id.append(ftrack_id) mongo_proc_txt = "MongoProcessing: " ftrack_proc_txt = "Ftrack processing: " @@ -534,25 +533,20 @@ def launch(self, session, entities, event): ftrack_proc_txt, ", ".join(ftrack_ids_to_delete) )) - joined_ids_to_delete = ", ".join( - ["\"{}\"".format(id) for id in ftrack_ids_to_delete] + ftrack_ents_to_delete = ( + self._filter_entities_to_delete(ftrack_ids_to_delete, session) ) - ftrack_ents_to_delete = self.session.query( - "select id, link from TypedContext where id in ({})".format( - joined_ids_to_delete - ) - ).all() for entity in ftrack_ents_to_delete: - self.session.delete(entity) + session.delete(entity) try: - self.session.commit() + session.commit() except Exception: ent_path = "/".join( [ent["name"] for ent in entity["link"]] ) msg = "Failed to delete entity" report_messages[msg].append(ent_path) - self.session.rollback() + session.rollback() self.log.warning( "{} <{}>".format(msg, ent_path), exc_info=True @@ -568,7 +562,7 @@ def launch(self, session, entities, event): for name in asset_names_to_delete ]) # Find assets of selected entities with names of checked subsets - assets = self.session.query(( + assets = session.query(( "select id from Asset where" " context_id in ({}) and name in ({})" ).format(joined_not_deleted, joined_asset_names)).all() @@ -578,20 +572,54 @@ def launch(self, session, entities, event): ", ".join([asset["id"] for asset in assets]) )) for asset in assets: - self.session.delete(asset) + session.delete(asset) try: - self.session.commit() + session.commit() except Exception: - self.session.rollback() + session.rollback() msg = "Failed to delete asset" report_messages[msg].append(asset["id"]) self.log.warning( - "{} <{}>".format(asset["id"]), + "Asset: {} <{}>".format(asset["name"], asset["id"]), exc_info=True ) return self.report_handle(report_messages, project_name, event) + def _filter_entities_to_delete(self, ftrack_ids_to_delete, session): + """Filter children entities to avoid CircularDependencyError.""" + joined_ids_to_delete = ", ".join( + ["\"{}\"".format(id) for id in ftrack_ids_to_delete] + ) + to_delete_entities = session.query( + "select id, link from TypedContext where id in ({})".format( + joined_ids_to_delete + ) + ).all() + filtered = to_delete_entities[:] + while True: + changed = False + _filtered = filtered[:] + for entity in filtered: + entity_id = entity["id"] + + for _entity in tuple(_filtered): + if entity_id == _entity["id"]: + continue + + for _link in _entity["link"]: + if entity_id == _link["id"] and _entity in _filtered: + _filtered.remove(_entity) + changed = True + break + + filtered = _filtered + + if not changed: + break + + return filtered + def report_handle(self, report_messages, project_name, event): if not report_messages: return { diff --git a/pype/modules/ftrack/actions/action_djvview.py b/pype/modules/ftrack/actions/action_djvview.py index 9708503ad15..6f667c06044 100644 --- a/pype/modules/ftrack/actions/action_djvview.py +++ b/pype/modules/ftrack/actions/action_djvview.py @@ -1,13 +1,7 @@ import os -import sys -import logging import subprocess from operator import itemgetter -import ftrack_api from pype.modules.ftrack.lib import BaseAction, statics_icon -from pype.api import Logger, config - -log = Logger().get_logger(__name__) class DJVViewAction(BaseAction): @@ -19,20 +13,18 @@ class DJVViewAction(BaseAction): type = 'Application' + allowed_types = [ + "cin", "dpx", "avi", "dv", "gif", "flv", "mkv", "mov", "mpg", "mpeg", + "mp4", "m4v", "mxf", "iff", "z", "ifl", "jpeg", "jpg", "jfif", "lut", + "1dl", "exr", "pic", "png", "ppm", "pnm", "pgm", "pbm", "rla", "rpf", + "sgi", "rgba", "rgb", "bw", "tga", "tiff", "tif", "img" + ] + def __init__(self, session, plugins_presets): '''Expects a ftrack_api.Session instance''' super().__init__(session, plugins_presets) - self.djv_path = None - - self.config_data = config.get_presets()['djv_view']['config'] - self.set_djv_path() - - if self.djv_path is None: - return - self.allowed_types = self.config_data.get( - 'file_ext', ["img", "mov", "exr"] - ) + self.djv_path = self.find_djv_path() def preregister(self): if self.djv_path is None: @@ -53,11 +45,10 @@ def discover(self, session, entities, event): return True return False - def set_djv_path(self): - for path in self.config_data.get("djv_paths", []): + def find_djv_path(self): + for path in (os.environ.get("DJV_PATH") or "").split(os.pathsep): if os.path.exists(path): - self.djv_path = path - break + return path def interface(self, session, entities, event): if event['data'].get('values', {}): @@ -221,43 +212,3 @@ def register(session, plugins_presets={}): """Register hooks.""" DJVViewAction(session, plugins_presets).register() - - -def main(arguments=None): - '''Set up logging and register action.''' - if arguments is None: - arguments = [] - - import argparse - parser = argparse.ArgumentParser() - # Allow setting of logging level from arguments. - loggingLevels = {} - for level in ( - logging.NOTSET, logging.DEBUG, logging.INFO, logging.WARNING, - logging.ERROR, logging.CRITICAL - ): - loggingLevels[logging.getLevelName(level).lower()] = level - - parser.add_argument( - '-v', '--verbosity', - help='Set the logging output verbosity.', - choices=loggingLevels.keys(), - default='info' - ) - namespace = parser.parse_args(arguments) - - # Set up basic logging - logging.basicConfig(level=loggingLevels[namespace.verbosity]) - - session = ftrack_api.Session() - register(session) - - # Wait for events - logging.info( - 'Registered actions and listening for events. Use Ctrl-C to abort.' - ) - session.event_hub.wait() - - -if __name__ == '__main__': - raise SystemExit(main(sys.argv[1:])) diff --git a/pype/modules/ftrack/tray/login_tools.py b/pype/modules/ftrack/tray/login_tools.py index b259f2d2ede..02982294f2a 100644 --- a/pype/modules/ftrack/tray/login_tools.py +++ b/pype/modules/ftrack/tray/login_tools.py @@ -1,16 +1,16 @@ from http.server import BaseHTTPRequestHandler, HTTPServer from urllib import parse -import os import webbrowser import functools -import pype -import inspect from Qt import QtCore +from pype.api import resources class LoginServerHandler(BaseHTTPRequestHandler): '''Login server handler.''' + message_filepath = resources.get_resource("ftrack", "sign_in_message.html") + def __init__(self, login_callback, *args, **kw): '''Initialise handler.''' self.login_callback = login_callback @@ -28,23 +28,21 @@ def do_GET(self): login_credentials = parse.parse_qs(query) api_user = login_credentials['api_user'][0] api_key = login_credentials['api_key'][0] - # get path to resources - path_items = os.path.dirname( - inspect.getfile(pype) - ).split(os.path.sep) - del path_items[-1] - path_items.extend(['res', 'ftrack', 'sign_in_message.html']) - message_filepath = os.path.sep.join(path_items) - message_file = open(message_filepath, 'r') - sign_in_message = message_file.read() - message_file.close() + + with open(self.message_filepath, "r") as message_file: + sign_in_message = message_file.read() + # formatting html code for python - replacement = [('{', '{{'), ('}', '}}'), ('{{}}', '{}')] - for r in (replacement): - sign_in_message = sign_in_message.replace(*r) + replacements = ( + ("{", "{{"), + ("}", "}}"), + ("{{}}", "{}") + ) + for replacement in (replacements): + sign_in_message = sign_in_message.replace(*replacement) message = sign_in_message.format(api_user) else: - message = '

Failed to sign in

' + message = "

Failed to sign in

" self.send_response(200) self.end_headers() @@ -74,7 +72,6 @@ def _handle_login(self, api_user, api_key): def run(self): '''Listen for events.''' - # self._server = BaseHTTPServer.HTTPServer( self._server = HTTPServer( ('localhost', 0), functools.partial( diff --git a/pype/modules/rest_api/__init__.py b/pype/modules/rest_api/__init__.py index fbeec00c886..55253bc58b6 100644 --- a/pype/modules/rest_api/__init__.py +++ b/pype/modules/rest_api/__init__.py @@ -2,6 +2,8 @@ from .base_class import RestApi, abort, route, register_statics from .lib import RestMethods, CallbackResult +CLASS_DEFINIION = RestApiServer + def tray_init(tray_widget, main_widget): return RestApiServer() diff --git a/pype/modules/rest_api/rest_api.py b/pype/modules/rest_api/rest_api.py index 5f0969a5a29..cc98b56a3b1 100644 --- a/pype/modules/rest_api/rest_api.py +++ b/pype/modules/rest_api/rest_api.py @@ -6,7 +6,7 @@ from http.server import HTTPServer from .lib import RestApiFactory, Handler from .base_class import route, register_statics -from pype.api import config, Logger +from pype.api import Logger log = Logger().get_logger("RestApiServer") @@ -85,24 +85,22 @@ def get_projects(request_info): Callback may return many types. For more information read docstring of `_handle_callback_result` defined in handler. """ + default_port = 8011 + exclude_ports = [] + def __init__(self): self.qaction = None self.failed_icon = None self._is_running = False - try: - self.presets = config.get_presets()["services"]["rest_api"] - except Exception: - self.presets = {"default_port": 8011, "exclude_ports": []} - log.debug(( - "There are not set presets for RestApiModule." - " Using defaults \"{}\"" - ).format(str(self.presets))) - port = self.find_port() self.rest_api_thread = RestApiThread(self, port) - statics_dir = os.path.sep.join([os.environ["PYPE_MODULE_ROOT"], "res"]) + statics_dir = os.path.join( + os.environ["PYPE_MODULE_ROOT"], + "pype", + "resources" + ) self.register_statics("/res", statics_dir) os.environ["PYPE_STATICS_SERVER"] = "{}/res".format( os.environ["PYPE_REST_API_URL"] @@ -126,8 +124,8 @@ def register_obj(self, obj): RestApiFactory.register_obj(obj) def find_port(self): - start_port = self.presets["default_port"] - exclude_ports = self.presets["exclude_ports"] + start_port = self.default_port + exclude_ports = self.exclude_ports found_port = None # port check takes time so it's lowered to 100 ports for port in range(start_port, start_port+100): diff --git a/pype/modules/timers_manager/__init__.py b/pype/modules/timers_manager/__init__.py index a6c4535f3d6..a8a478d7ae5 100644 --- a/pype/modules/timers_manager/__init__.py +++ b/pype/modules/timers_manager/__init__.py @@ -1,6 +1,8 @@ from .timers_manager import TimersManager from .widget_user_idle import WidgetUserIdle +CLASS_DEFINIION = TimersManager + def tray_init(tray_widget, main_widget): return TimersManager(tray_widget, main_widget) diff --git a/pype/modules/timers_manager/timers_manager.py b/pype/modules/timers_manager/timers_manager.py index 8df7952baf7..82ba1013f02 100644 --- a/pype/modules/timers_manager/timers_manager.py +++ b/pype/modules/timers_manager/timers_manager.py @@ -22,12 +22,20 @@ class TimersManager(metaclass=Singleton): If IdleManager is imported then is able to handle about stop timers when user idles for a long time (set in presets). """ - modules = [] - is_running = False - last_task = None + + # Presetable attributes + # - when timer will stop if idle manager is running (minutes) + full_time = 15 + # - how many minutes before the timer is stopped will popup the message + message_time = 0.5 def __init__(self, tray_widget, main_widget): self.log = Logger().get_logger(self.__class__.__name__) + + self.modules = [] + self.is_running = False + self.last_task = None + self.tray_widget = tray_widget self.main_widget = main_widget @@ -37,20 +45,13 @@ def __init__(self, tray_widget, main_widget): def set_signal_times(self): try: - timer_info = ( - config.get_presets() - .get('services') - .get('timers_manager') - .get('timer') - ) - full_time = int(float(timer_info['full_time'])*60) - message_time = int(float(timer_info['message_time'])*60) + full_time = int(self.full_time * 60) + message_time = int(self.message_time * 60) self.time_show_message = full_time - message_time self.time_stop_timer = full_time return True except Exception: - self.log.warning('Was not able to load presets for TimersManager') - return False + self.log.error("Couldn't set timer signals.", exc_info=True) def add_module(self, module): """ Adds module to context diff --git a/pype/plugins/celaction/publish/collect_celaction_cli_kwargs.py b/pype/plugins/celaction/publish/collect_celaction_cli_kwargs.py index 5042a7b7001..f4a9ec341d6 100644 --- a/pype/plugins/celaction/publish/collect_celaction_cli_kwargs.py +++ b/pype/plugins/celaction/publish/collect_celaction_cli_kwargs.py @@ -1,5 +1,5 @@ import pyblish.api -import pype.celaction +from pype.hosts import celaction class CollectCelactionCliKwargs(pyblish.api.Collector): @@ -9,7 +9,7 @@ class CollectCelactionCliKwargs(pyblish.api.Collector): order = pyblish.api.Collector.order - 0.1 def process(self, context): - kwargs = pype.celaction.kwargs.copy() + kwargs = celaction.kwargs.copy() self.log.info("Storing kwargs: %s" % kwargs) context.set_data("kwargs", kwargs) diff --git a/pype/plugins/celaction/publish/collect_celaction_instances.py b/pype/plugins/celaction/publish/collect_celaction_instances.py index aa2bb5da5d9..431ab722d30 100644 --- a/pype/plugins/celaction/publish/collect_celaction_instances.py +++ b/pype/plugins/celaction/publish/collect_celaction_instances.py @@ -16,6 +16,7 @@ def process(self, context): scene_file = os.path.basename(current_file) version = context.data["version"] asset_entity = context.data["assetEntity"] + project_entity = context.data["projectEntity"] shared_instance_data = { "asset": asset_entity["name"], @@ -24,8 +25,12 @@ def process(self, context): "handleStart": asset_entity["data"]["handleStart"], "handleEnd": asset_entity["data"]["handleEnd"], "fps": asset_entity["data"]["fps"], - "resolutionWidth": asset_entity["data"]["resolutionWidth"], - "resolutionHeight": asset_entity["data"]["resolutionHeight"], + "resolutionWidth": asset_entity["data"].get( + "resolutionWidth", + project_entity["data"]["resolutionWidth"]), + "resolutionHeight": asset_entity["data"].get( + "resolutionHeight", + project_entity["data"]["resolutionHeight"]), "pixelAspect": 1, "step": 1, "version": version diff --git a/pype/plugins/celaction/publish/collect_render_path.py b/pype/plugins/celaction/publish/collect_render_path.py index cddd2643d86..d5fe6c07a50 100644 --- a/pype/plugins/celaction/publish/collect_render_path.py +++ b/pype/plugins/celaction/publish/collect_render_path.py @@ -1,5 +1,6 @@ import os import pyblish.api +import copy class CollectRenderPath(pyblish.api.InstancePlugin): @@ -7,19 +8,21 @@ class CollectRenderPath(pyblish.api.InstancePlugin): label = "Collect Render Path" order = pyblish.api.CollectorOrder + 0.495 + families = ["render.farm"] def process(self, instance): anatomy = instance.context.data["anatomy"] - current_file = instance.context.data["currentFile"] - work_dir = os.path.dirname(current_file) + anatomy_data = copy.deepcopy(instance.data["anatomyData"]) padding = anatomy.templates.get("frame_padding", 4) - render_dir = os.path.join( - work_dir, "render", "celaction" - ) - render_path = os.path.join( - render_dir, - ".".join([instance.data["subset"], f"%0{padding}d", "png"]) - ) + anatomy_data.update({ + "frame": f"%0{padding}d", + "representation": "png" + }) + + anatomy_filled = anatomy.format(anatomy_data) + + render_dir = anatomy_filled["render_tmp"]["folder"] + render_path = anatomy_filled["render_tmp"]["path"] # create dir if it doesnt exists os.makedirs(render_dir, exist_ok=True) diff --git a/pype/plugins/celaction/publish/submit_celaction_deadline.py b/pype/plugins/celaction/publish/submit_celaction_deadline.py index 0bb346f7cf0..c749ec111fc 100644 --- a/pype/plugins/celaction/publish/submit_celaction_deadline.py +++ b/pype/plugins/celaction/publish/submit_celaction_deadline.py @@ -27,6 +27,12 @@ class ExtractCelactionDeadline(pyblish.api.InstancePlugin): deadline_group = "" deadline_chunk_size = 1 + enviro_filter = [ + "FTRACK_API_USER", + "FTRACK_API_KEY", + "FTRACK_SERVER" + ] + def process(self, instance): context = instance.context @@ -155,6 +161,19 @@ def payload_submit(self, plugin = payload["JobInfo"]["Plugin"] self.log.info("using render plugin : {}".format(plugin)) + i = 0 + for key, values in dict(os.environ).items(): + if key.upper() in self.enviro_filter: + payload["JobInfo"].update( + { + "EnvironmentKeyValue%d" + % i: "{key}={value}".format( + key=key, value=values + ) + } + ) + i += 1 + self.log.info("Submitting..") self.log.info(json.dumps(payload, indent=4, sort_keys=True)) diff --git a/pype/plugins/global/load/open_djv.py b/pype/plugins/global/load/open_djv.py index 650936a4dcc..a5003338756 100644 --- a/pype/plugins/global/load/open_djv.py +++ b/pype/plugins/global/load/open_djv.py @@ -1,34 +1,27 @@ import os import subprocess -import json -from pype.api import config from avalon import api -def get_families(): - families = [] - paths = config.get_presets().get("djv_view", {}).get("config", {}).get( - "djv_paths", [] - ) - for path in paths: +def existing_djv_path(): + djv_paths = os.environ.get("DJV_PATH") or "" + for path in djv_paths.split(os.pathsep): if os.path.exists(path): - families.append("*") - break - return families - - -def get_representation(): - return config.get_presets().get("djv_view", {}).get("config", {}).get( - 'file_ext', [] - ) + return path + return None class OpenInDJV(api.Loader): """Open Image Sequence with system default""" - config_data = config.get_presets().get("djv_view", {}).get("config", {}) - families = get_families() - representations = get_representation() + djv_path = existing_djv_path() + families = ["*"] if djv_path else [] + representations = [ + "cin", "dpx", "avi", "dv", "gif", "flv", "mkv", "mov", "mpg", "mpeg", + "mp4", "m4v", "mxf", "iff", "z", "ifl", "jpeg", "jpg", "jfif", "lut", + "1dl", "exr", "pic", "png", "ppm", "pnm", "pgm", "pbm", "rla", "rpf", + "sgi", "rgba", "rgb", "bw", "tga", "tiff", "tif", "img" + ] label = "Open in DJV" order = -10 @@ -36,14 +29,6 @@ class OpenInDJV(api.Loader): color = "orange" def load(self, context, name, namespace, data): - self.djv_path = None - paths = config.get_presets().get("djv_view", {}).get("config", {}).get( - "djv_paths", [] - ) - for path in paths: - if os.path.exists(path): - self.djv_path = path - break directory = os.path.dirname(self.fname) from avalon.vendor import clique diff --git a/pype/plugins/global/publish/extract_review.py b/pype/plugins/global/publish/extract_review.py index 0f152951180..30d1de8328f 100644 --- a/pype/plugins/global/publish/extract_review.py +++ b/pype/plugins/global/publish/extract_review.py @@ -285,6 +285,20 @@ def _ffmpeg_arguments(self, output_def, instance, new_repre, temp_data): # Prepare input and output filepaths self.input_output_paths(new_repre, output_def, temp_data) + # Set output frames len to 1 when ouput is single image + if ( + temp_data["output_ext_is_image"] + and not temp_data["output_is_sequence"] + ): + output_frames_len = 1 + + else: + output_frames_len = ( + temp_data["output_frame_end"] + - temp_data["output_frame_start"] + + 1 + ) + if temp_data["input_is_sequence"]: # Set start frame ffmpeg_input_args.append( @@ -303,31 +317,14 @@ def _ffmpeg_arguments(self, output_def, instance, new_repre, temp_data): ) elif temp_data["without_handles"]: - # TODO use frames ubstead if `-ss`: - # `select="gte(n\,{handle_start}),setpts=PTS-STARTPTS` - # Pros: - # 1.) Python is not good at float operation - # 2.) FPS on instance may not be same as input's start_sec = float(temp_data["handle_start"]) / temp_data["fps"] ffmpeg_input_args.append("-ss {:0.2f}".format(start_sec)) - # Set output frames len to 1 when ouput is single image - if ( - temp_data["output_ext_is_image"] - and not temp_data["output_is_sequence"] - ): - output_frames_len = 1 - - else: - output_frames_len = ( - temp_data["output_frame_end"] - - temp_data["output_frame_start"] - + 1 - ) + duration_sec = float(output_frames_len / temp_data["fps"]) + ffmpeg_output_args.append("-t {:0.2f}".format(duration_sec)) - # NOTE used `-frames` instead of `-t` - should work the same way - # NOTE this also replaced `-shortest` argument - ffmpeg_output_args.append("-frames {}".format(output_frames_len)) + # Use shortest input + ffmpeg_output_args.append("-shortest") # Add video/image input path ffmpeg_input_args.append( diff --git a/pype/plugins/global/publish/submit_publish_job.py b/pype/plugins/global/publish/submit_publish_job.py index 82de2ec099a..7a73e921e2c 100644 --- a/pype/plugins/global/publish/submit_publish_job.py +++ b/pype/plugins/global/publish/submit_publish_job.py @@ -166,8 +166,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "FTRACK_SERVER", "PYPE_METADATA_FILE", "AVALON_PROJECT", - "PYPE_LOG_NO_COLORS", - "PYPE_PYTHON_EXE" + "PYPE_LOG_NO_COLORS" ] # custom deadline atributes @@ -191,6 +190,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # list of family names to transfer to new family if present families_transfer = ["render3d", "render2d", "ftrack", "slate"] + plugin_python_version = "3.7" def _submit_deadline_post_job(self, instance, job): """Submit publish job to Deadline. @@ -202,9 +202,7 @@ def _submit_deadline_post_job(self, instance, job): """ data = instance.data.copy() subset = data["subset"] - job_name = "{batch} - {subset} [publish image sequence]".format( - batch=job["Props"]["Name"], subset=subset - ) + job_name = "Publish - {subset}".format(subset=subset) output_dir = instance.data["outputDir"] # Convert output dir to `{root}/rest/of/path/...` with Anatomy @@ -240,7 +238,7 @@ def _submit_deadline_post_job(self, instance, job): "OutputDirectory0": output_dir }, "PluginInfo": { - "Version": "3.6", + "Version": self.plugin_python_version, "ScriptFile": _get_script(), "Arguments": "", "SingleFrameOnly": "True", diff --git a/pype/plugins/maya/publish/submit_maya_deadline.py b/pype/plugins/maya/publish/submit_maya_deadline.py index 8750d88b90b..d81d43749ca 100644 --- a/pype/plugins/maya/publish/submit_maya_deadline.py +++ b/pype/plugins/maya/publish/submit_maya_deadline.py @@ -59,7 +59,7 @@ } -def get_renderer_variables(renderlayer=None): +def get_renderer_variables(renderlayer, root): """Retrieve the extension which has been set in the VRay settings. Will return None if the current renderer is not VRay @@ -68,6 +68,7 @@ def get_renderer_variables(renderlayer=None): Args: renderlayer (str): the node name of the renderlayer. + root (str): base path to render Returns: dict @@ -87,6 +88,7 @@ def get_renderer_variables(renderlayer=None): filename_0 = filename_0.replace('_', '_beauty') prefix_attr = "defaultRenderGlobals.imageFilePrefix" if renderer == "vray": + renderlayer = renderlayer.split("_")[-1] # Maya's renderSettings function does not return V-Ray file extension # so we get the extension from vraySettings extension = cmds.getAttr("vraySettings.imageFormatStr") @@ -101,6 +103,16 @@ def get_renderer_variables(renderlayer=None): extension = "exr" prefix_attr = "vraySettings.fileNamePrefix" + filename_prefix = cmds.getAttr(prefix_attr) + # we need to determine path for vray as maya `renderSettings` query + # does not work for vray. + scene = cmds.file(query=True, sceneName=True) + scene, _ = os.path.splitext(os.path.basename(scene)) + filename_0 = filename_prefix.replace('', scene) + filename_0 = filename_0.replace('', renderlayer) + filename_0 = "{}.{}.{}".format( + filename_0, "#" * int(padding), extension) + filename_0 = os.path.normpath(os.path.join(root, filename_0)) elif renderer == "renderman": prefix_attr = "rmanGlobals.imageFileFormat" elif renderer == "redshift": @@ -236,7 +248,7 @@ def process(self, instance): jobname = "%s - %s" % (filename, instance.name) # Get the variables depending on the renderer - render_variables = get_renderer_variables(renderlayer) + render_variables = get_renderer_variables(renderlayer, dirname) filename_0 = render_variables["filename_0"] if self.use_published: new_scene = os.path.splitext(filename)[0] @@ -267,6 +279,11 @@ def process(self, instance): payload_data["workspace"] = workspace payload_data["dirname"] = dirname + self.log.info("--- Submission data:") + for k, v in payload_data.items(): + self.log.info("- {}: {}".format(k, v)) + self.log.info("-" * 20) + frame_pattern = payload_skeleton["JobInfo"]["Frames"] payload_skeleton["JobInfo"]["Frames"] = frame_pattern.format( start=int(self._instance.data["frameStartHandle"]), @@ -295,9 +312,7 @@ def process(self, instance): dependencies = instance.context.data["fileDependencies"] dependencies.append(filepath) for dependency in dependencies: - self.log.info(dependency) key = "AssetDependency" + str(dependencies.index(dependency)) - self.log.info(key) payload_skeleton["JobInfo"][key] = dependency # Handle environments ----------------------------------------------- @@ -311,8 +326,7 @@ def process(self, instance): "AVALON_TASK", "PYPE_USERNAME", "PYPE_DEV", - "PYPE_LOG_NO_COLORS", - "PYPE_SETUP_PATH" + "PYPE_LOG_NO_COLORS" ] environment = dict({key: os.environ[key] for key in keys @@ -388,7 +402,7 @@ def process(self, instance): raise Exception(response.text) # Store output dir for unified publisher (filesequence) - instance.data["outputDir"] = os.path.dirname(filename_0) + instance.data["outputDir"] = os.path.dirname(output_filename_0) instance.data["deadlineSubmissionJob"] = response.json() def _get_maya_payload(self, data): @@ -423,6 +437,13 @@ def _get_maya_payload(self, data): def _get_vray_export_payload(self, data): payload = copy.deepcopy(payload_skeleton) + vray_settings = cmds.ls(type="VRaySettingsNode") + node = vray_settings[0] + template = cmds.getAttr("{}.vrscene_filename".format(node)) + scene, _ = os.path.splitext(data["filename"]) + first_file = self.format_vray_output_filename(scene, template) + first_file = "{}/{}".format(data["workspace"], first_file) + output = os.path.dirname(first_file) job_info_ext = { # Job name, as seen in Monitor "Name": "Export {} [{}-{}]".format( @@ -444,7 +465,8 @@ def _get_vray_export_payload(self, data): "UsingRenderLayers": True, "UseLegacyRenderLayers": True, "RenderLayer": data["renderlayer"], - "ProjectPath": data["workspace"] + "ProjectPath": data["workspace"], + "OutputFilePath": output } payload["JobInfo"].update(job_info_ext) @@ -545,6 +567,8 @@ def _get_vray_render_payload(self, data): "Width": self._instance.data["resolutionWidth"], "Height": self._instance.data["resolutionHeight"], + "OutputFilePath": payload["JobInfo"]["OutputDirectory0"], + "OutputFileName": payload["JobInfo"]["OutputFilename0"] } payload["JobInfo"].update(job_info_ext) @@ -669,11 +693,13 @@ def smart_replace(string, key_values): # Ensure filename has no extension file_name, _ = os.path.splitext(filename) + layer = self._instance.data['setMembers'] + # Reformat without tokens output_path = smart_replace( template, {"": file_name, - "": self._instance.data['setMembers']}) + "": layer}) if dir: return output_path.replace("\\", "/") diff --git a/pype/plugins/maya/publish/validate_rendersettings.py b/pype/plugins/maya/publish/validate_rendersettings.py index 16484affad6..297ead5ebb4 100644 --- a/pype/plugins/maya/publish/validate_rendersettings.py +++ b/pype/plugins/maya/publish/validate_rendersettings.py @@ -56,7 +56,7 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin): 'arnold': 'maya///_', 'redshift': 'maya///', - 'vray': 'maya///', + 'vray': 'maya///', 'renderman': '_..' } @@ -77,7 +77,7 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin): R_SCENE_TOKEN = re.compile(r'%s|', re.IGNORECASE) DEFAULT_PADDING = 4 - VRAY_PREFIX = "maya///" + VRAY_PREFIX = "maya///" DEFAULT_PREFIX = "maya///_" def process(self, instance): diff --git a/pype/plugins/maya/publish/validate_vray_translator_settings.py b/pype/plugins/maya/publish/validate_vray_translator_settings.py index 493febf49aa..592f24e36f6 100644 --- a/pype/plugins/maya/publish/validate_vray_translator_settings.py +++ b/pype/plugins/maya/publish/validate_vray_translator_settings.py @@ -46,7 +46,7 @@ def get_invalid(cls, context): invalid = True vrscene_filename = cmds.getAttr("{}.vrscene_filename".format(node)) - if vrscene_filename != "vrayscene//_/": + if vrscene_filename != "vrayscene///": cls.log.error("Template for file name is wrong") invalid = True @@ -65,5 +65,5 @@ def repair(cls, context): cmds.setAttr("{}.vrscene_on".format(node), True) cmds.setAttr("{}.misc_eachFrameInFile".format(node), True) cmds.setAttr("{}.vrscene_filename".format(node), - "vrayscene//_/", + "vrayscene///", type="string") diff --git a/pype/plugins/nuke/publish/submit_nuke_deadline.py b/pype/plugins/nuke/publish/submit_nuke_deadline.py index 3731cd25f0d..2b8efb4640c 100644 --- a/pype/plugins/nuke/publish/submit_nuke_deadline.py +++ b/pype/plugins/nuke/publish/submit_nuke_deadline.py @@ -120,7 +120,7 @@ def payload_submit(self, chunk_size = self.deadline_chunk_size priority = instance.data.get("deadlinePriority") - if priority != 50: + if not priority: priority = self.deadline_priority payload = { diff --git a/pype/plugins/premiere/publish/collect_frameranges.py b/pype/plugins/premiere/publish/collect_frameranges.py index ffcc1023b53..075f84e8e3d 100644 --- a/pype/plugins/premiere/publish/collect_frameranges.py +++ b/pype/plugins/premiere/publish/collect_frameranges.py @@ -11,7 +11,7 @@ class CollectFrameranges(pyblish.api.InstancePlugin): """ label = "Collect Clip Frameranges" - order = pyblish.api.CollectorOrder + order = pyblish.api.CollectorOrder - 0.01 families = ['clip'] def process(self, instance): diff --git a/pype/plugins/premiere/publish/collect_instance_representations.py b/pype/plugins/premiere/publish/collect_instance_representations.py index f53c60ad642..b62b47c4735 100644 --- a/pype/plugins/premiere/publish/collect_instance_representations.py +++ b/pype/plugins/premiere/publish/collect_instance_representations.py @@ -12,7 +12,7 @@ class CollectClipRepresentations(pyblish.api.InstancePlugin): """ label = "Collect Clip Representations" - order = pyblish.api.CollectorOrder + order = pyblish.api.CollectorOrder + 0.1 families = ['clip'] def process(self, instance): diff --git a/pype/plugins/premiere/publish/validate_auto_sync_off.py b/pype/plugins/premiere/publish/validate_auto_sync_off.py index b6429cfa05f..1f3f0b58a5a 100644 --- a/pype/plugins/premiere/publish/validate_auto_sync_off.py +++ b/pype/plugins/premiere/publish/validate_auto_sync_off.py @@ -37,13 +37,7 @@ def get_invalid(context): query = 'Project where full_name is "{}"'.format(project_name) project = session.query(query).one() - invalid = None - - if project.get('custom_attributes', {}).get( - 'avalon_auto_sync', False): - invalid = project - - return invalid + return project @classmethod def repair(cls, context): @@ -55,4 +49,4 @@ def repair(cls, context): except Exception: tp, value, tb = sys.exc_info() session.rollback() - six.reraise(tp, value, tb) + raise diff --git a/res/app_icons/Aport.png b/pype/resources/app_icons/Aport.png similarity index 100% rename from res/app_icons/Aport.png rename to pype/resources/app_icons/Aport.png diff --git a/res/app_icons/blender.png b/pype/resources/app_icons/blender.png similarity index 100% rename from res/app_icons/blender.png rename to pype/resources/app_icons/blender.png diff --git a/res/app_icons/celaction_local.png b/pype/resources/app_icons/celaction_local.png similarity index 100% rename from res/app_icons/celaction_local.png rename to pype/resources/app_icons/celaction_local.png diff --git a/res/app_icons/celaction_remotel.png b/pype/resources/app_icons/celaction_remotel.png similarity index 100% rename from res/app_icons/celaction_remotel.png rename to pype/resources/app_icons/celaction_remotel.png diff --git a/res/app_icons/clockify-white.png b/pype/resources/app_icons/clockify-white.png similarity index 100% rename from res/app_icons/clockify-white.png rename to pype/resources/app_icons/clockify-white.png diff --git a/res/app_icons/clockify.png b/pype/resources/app_icons/clockify.png similarity index 100% rename from res/app_icons/clockify.png rename to pype/resources/app_icons/clockify.png diff --git a/res/app_icons/djvView.png b/pype/resources/app_icons/djvView.png similarity index 100% rename from res/app_icons/djvView.png rename to pype/resources/app_icons/djvView.png diff --git a/res/app_icons/harmony.png b/pype/resources/app_icons/harmony.png similarity index 100% rename from res/app_icons/harmony.png rename to pype/resources/app_icons/harmony.png diff --git a/res/app_icons/houdini.png b/pype/resources/app_icons/houdini.png similarity index 100% rename from res/app_icons/houdini.png rename to pype/resources/app_icons/houdini.png diff --git a/res/app_icons/maya.png b/pype/resources/app_icons/maya.png similarity index 100% rename from res/app_icons/maya.png rename to pype/resources/app_icons/maya.png diff --git a/res/app_icons/nuke.png b/pype/resources/app_icons/nuke.png similarity index 100% rename from res/app_icons/nuke.png rename to pype/resources/app_icons/nuke.png diff --git a/res/app_icons/nukex.png b/pype/resources/app_icons/nukex.png similarity index 100% rename from res/app_icons/nukex.png rename to pype/resources/app_icons/nukex.png diff --git a/res/app_icons/photoshop.png b/pype/resources/app_icons/photoshop.png similarity index 100% rename from res/app_icons/photoshop.png rename to pype/resources/app_icons/photoshop.png diff --git a/res/app_icons/premiere.png b/pype/resources/app_icons/premiere.png similarity index 100% rename from res/app_icons/premiere.png rename to pype/resources/app_icons/premiere.png diff --git a/res/app_icons/python.png b/pype/resources/app_icons/python.png similarity index 100% rename from res/app_icons/python.png rename to pype/resources/app_icons/python.png diff --git a/res/app_icons/resolve.png b/pype/resources/app_icons/resolve.png similarity index 100% rename from res/app_icons/resolve.png rename to pype/resources/app_icons/resolve.png diff --git a/res/app_icons/storyboardpro.png b/pype/resources/app_icons/storyboardpro.png similarity index 100% rename from res/app_icons/storyboardpro.png rename to pype/resources/app_icons/storyboardpro.png diff --git a/res/app_icons/ue4.png b/pype/resources/app_icons/ue4.png similarity index 100% rename from res/app_icons/ue4.png rename to pype/resources/app_icons/ue4.png diff --git a/res/ftrack/action_icons/ActionAskWhereIRun.svg b/pype/resources/ftrack/action_icons/ActionAskWhereIRun.svg similarity index 100% rename from res/ftrack/action_icons/ActionAskWhereIRun.svg rename to pype/resources/ftrack/action_icons/ActionAskWhereIRun.svg diff --git a/res/ftrack/action_icons/AssetsRemover.svg b/pype/resources/ftrack/action_icons/AssetsRemover.svg similarity index 100% rename from res/ftrack/action_icons/AssetsRemover.svg rename to pype/resources/ftrack/action_icons/AssetsRemover.svg diff --git a/res/ftrack/action_icons/ComponentOpen.svg b/pype/resources/ftrack/action_icons/ComponentOpen.svg similarity index 100% rename from res/ftrack/action_icons/ComponentOpen.svg rename to pype/resources/ftrack/action_icons/ComponentOpen.svg diff --git a/res/ftrack/action_icons/CreateFolders.svg b/pype/resources/ftrack/action_icons/CreateFolders.svg similarity index 100% rename from res/ftrack/action_icons/CreateFolders.svg rename to pype/resources/ftrack/action_icons/CreateFolders.svg diff --git a/res/ftrack/action_icons/CreateProjectFolders.svg b/pype/resources/ftrack/action_icons/CreateProjectFolders.svg similarity index 100% rename from res/ftrack/action_icons/CreateProjectFolders.svg rename to pype/resources/ftrack/action_icons/CreateProjectFolders.svg diff --git a/res/ftrack/action_icons/DeleteAsset.svg b/pype/resources/ftrack/action_icons/DeleteAsset.svg similarity index 100% rename from res/ftrack/action_icons/DeleteAsset.svg rename to pype/resources/ftrack/action_icons/DeleteAsset.svg diff --git a/res/ftrack/action_icons/Delivery.svg b/pype/resources/ftrack/action_icons/Delivery.svg similarity index 100% rename from res/ftrack/action_icons/Delivery.svg rename to pype/resources/ftrack/action_icons/Delivery.svg diff --git a/res/ftrack/action_icons/MultipleNotes.svg b/pype/resources/ftrack/action_icons/MultipleNotes.svg similarity index 100% rename from res/ftrack/action_icons/MultipleNotes.svg rename to pype/resources/ftrack/action_icons/MultipleNotes.svg diff --git a/res/ftrack/action_icons/PrepareProject.svg b/pype/resources/ftrack/action_icons/PrepareProject.svg similarity index 100% rename from res/ftrack/action_icons/PrepareProject.svg rename to pype/resources/ftrack/action_icons/PrepareProject.svg diff --git a/res/ftrack/action_icons/PypeAdmin.svg b/pype/resources/ftrack/action_icons/PypeAdmin.svg similarity index 100% rename from res/ftrack/action_icons/PypeAdmin.svg rename to pype/resources/ftrack/action_icons/PypeAdmin.svg diff --git a/res/ftrack/action_icons/PypeDoctor.svg b/pype/resources/ftrack/action_icons/PypeDoctor.svg similarity index 100% rename from res/ftrack/action_icons/PypeDoctor.svg rename to pype/resources/ftrack/action_icons/PypeDoctor.svg diff --git a/res/ftrack/action_icons/PypeUpdate.svg b/pype/resources/ftrack/action_icons/PypeUpdate.svg similarity index 100% rename from res/ftrack/action_icons/PypeUpdate.svg rename to pype/resources/ftrack/action_icons/PypeUpdate.svg diff --git a/res/ftrack/action_icons/RV.png b/pype/resources/ftrack/action_icons/RV.png similarity index 100% rename from res/ftrack/action_icons/RV.png rename to pype/resources/ftrack/action_icons/RV.png diff --git a/res/ftrack/action_icons/SeedProject.svg b/pype/resources/ftrack/action_icons/SeedProject.svg similarity index 100% rename from res/ftrack/action_icons/SeedProject.svg rename to pype/resources/ftrack/action_icons/SeedProject.svg diff --git a/res/ftrack/action_icons/SyncHierarchicalAttrs.svg b/pype/resources/ftrack/action_icons/SyncHierarchicalAttrs.svg similarity index 100% rename from res/ftrack/action_icons/SyncHierarchicalAttrs.svg rename to pype/resources/ftrack/action_icons/SyncHierarchicalAttrs.svg diff --git a/res/ftrack/action_icons/SyncToAvalon.svg b/pype/resources/ftrack/action_icons/SyncToAvalon.svg similarity index 100% rename from res/ftrack/action_icons/SyncToAvalon.svg rename to pype/resources/ftrack/action_icons/SyncToAvalon.svg diff --git a/res/ftrack/action_icons/TestAction.svg b/pype/resources/ftrack/action_icons/TestAction.svg similarity index 100% rename from res/ftrack/action_icons/TestAction.svg rename to pype/resources/ftrack/action_icons/TestAction.svg diff --git a/res/ftrack/action_icons/Thumbnail.svg b/pype/resources/ftrack/action_icons/Thumbnail.svg similarity index 100% rename from res/ftrack/action_icons/Thumbnail.svg rename to pype/resources/ftrack/action_icons/Thumbnail.svg diff --git a/res/ftrack/sign_in_message.html b/pype/resources/ftrack/sign_in_message.html similarity index 100% rename from res/ftrack/sign_in_message.html rename to pype/resources/ftrack/sign_in_message.html diff --git a/pype/tools/tray/pype_tray.py b/pype/tools/tray/pype_tray.py index 677c329ad2f..5b1185fa717 100644 --- a/pype/tools/tray/pype_tray.py +++ b/pype/tools/tray/pype_tray.py @@ -30,12 +30,15 @@ def __init__(self, tray_widget, main_window): os.path.join(CURRENT_DIR, "modules_imports.json") ) presets = config.get_presets(first_run=True) + menu_items = presets["tray"]["menu_items"] try: - self.modules_usage = presets["tray"]["menu_items"]["item_usage"] + self.modules_usage = menu_items["item_usage"] except Exception: self.modules_usage = {} self.log.critical("Couldn't find modules usage data.") + self.module_attributes = menu_items.get("attributes") or {} + self.icon_run = QtGui.QIcon( resources.get_resource("icons", "circle_green.png") ) @@ -71,12 +74,20 @@ def process_presets(self): if item_usage is None: item_usage = self.modules_usage.get(import_path, True) - if item_usage: - items.append(item) - else: + if not item_usage: if not title: title = import_path self.log.info("{} - Module ignored".format(title)) + continue + + _attributes = self.module_attributes.get(title) + if _attributes is None: + _attributes = self.module_attributes.get(import_path) + + if _attributes: + item["attributes"] = _attributes + + items.append(item) if items: self.process_items(items, self.tray_widget.menu) @@ -153,11 +164,29 @@ def add_module(self, item, parent_menu): import_path = item.get('import_path', None) title = item.get('title', import_path) fromlist = item.get('fromlist', []) + attributes = item.get("attributes", {}) try: module = __import__( "{}".format(import_path), fromlist=fromlist ) + klass = getattr(module, "CLASS_DEFINIION", None) + if not klass and attributes: + self.log.error(( + "There are defined attributes for module \"{}\" but" + "module does not have defined \"CLASS_DEFINIION\"." + ).format(import_path)) + + elif klass and attributes: + for key, value in attributes.items(): + if hasattr(klass, key): + setattr(klass, key, value) + else: + self.log.error(( + "Module \"{}\" does not have attribute \"{}\"." + " Check your settings please." + ).format(import_path, key)) + obj = module.tray_init(self.tray_widget, self.main_window) name = obj.__class__.__name__ if hasattr(obj, 'tray_menu'):