From 63083c76523740b4dc6b2aba2d473b296490e158 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:32:25 +0200 Subject: [PATCH] removed royalrender addon --- .../client/ayon_royalrender/__init__.py | 9 - .../client/ayon_royalrender/addon.py | 33 -- .../client/ayon_royalrender/api.py | 181 --------- .../client/ayon_royalrender/lib.py | 360 ------------------ .../publish/collect_rr_path_from_instance.py | 52 --- .../publish/collect_sequences_from_job.py | 228 ----------- .../publish/create_maya_royalrender_job.py | 43 --- .../publish/create_nuke_royalrender_job.py | 72 ---- .../publish/create_publish_royalrender_job.py | 244 ------------ .../publish/submit_jobs_to_royalrender.py | 91 ----- .../client/ayon_royalrender/rr_job.py | 305 --------------- .../client/ayon_royalrender/rr_root/README.md | 5 - .../perjob/m50__openpype_publish_render.py | 205 ---------- .../render_apps/_config/E01__OpenPype.png | Bin 2075 -> 0 bytes .../_config/E01__OpenPype__PublishJob.cfg | 71 ---- .../_config/E01__OpenPype___global.inc | 2 - .../render_apps/_install_paths/OpenPype.cfg | 12 - .../_prepost_scripts/OpenPypeEnvironment.cfg | 11 - .../PreOpenPypeInjectEnvironments.py | 4 - .../client/ayon_royalrender/version.py | 3 - server_addon/royalrender/package.py | 10 - server_addon/royalrender/server/__init__.py | 13 - server_addon/royalrender/server/settings.py | 75 ---- 23 files changed, 2029 deletions(-) delete mode 100644 server_addon/royalrender/client/ayon_royalrender/__init__.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/addon.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/api.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/lib.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_rr_path_from_instance.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_sequences_from_job.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_maya_royalrender_job.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_nuke_royalrender_job.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_publish_royalrender_job.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/plugins/publish/submit_jobs_to_royalrender.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_job.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/README.md delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype.png delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype__PublishJob.cfg delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype___global.inc delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_install_paths/OpenPype.cfg delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/OpenPypeEnvironment.cfg delete mode 100644 server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/PreOpenPypeInjectEnvironments.py delete mode 100644 server_addon/royalrender/client/ayon_royalrender/version.py delete mode 100644 server_addon/royalrender/package.py delete mode 100644 server_addon/royalrender/server/__init__.py delete mode 100644 server_addon/royalrender/server/settings.py diff --git a/server_addon/royalrender/client/ayon_royalrender/__init__.py b/server_addon/royalrender/client/ayon_royalrender/__init__.py deleted file mode 100644 index 8bf207e7db..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .version import __version__ -from .addon import RoyalRenderAddon - - -__all__ = ( - "__version__", - - "RoyalRenderAddon", -) diff --git a/server_addon/royalrender/client/ayon_royalrender/addon.py b/server_addon/royalrender/client/ayon_royalrender/addon.py deleted file mode 100644 index 1ea6954e3f..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/addon.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -"""Module providing support for Royal Render.""" -import os - -from ayon_core.addon import AYONAddon, IPluginPaths - -from .version import __version__ - - -class RoyalRenderAddon(AYONAddon, IPluginPaths): - """Class providing basic Royal Render implementation logic.""" - name = "royalrender" - version = __version__ - - def initialize(self, studio_settings): - # type: (dict) -> None - self.enabled = False - addon_settings = studio_settings.get(self.name) - if addon_settings: - self.enabled = addon_settings["enabled"] - - @staticmethod - def get_plugin_paths(): - # type: () -> dict - """Royal Render plugin paths. - - Returns: - dict: Dictionary of plugin paths for RR. - """ - current_dir = os.path.dirname(os.path.abspath(__file__)) - return { - "publish": [os.path.join(current_dir, "plugins", "publish")] - } diff --git a/server_addon/royalrender/client/ayon_royalrender/api.py b/server_addon/royalrender/client/ayon_royalrender/api.py deleted file mode 100644 index ef715811c5..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/api.py +++ /dev/null @@ -1,181 +0,0 @@ -# -*- coding: utf-8 -*- -"""Wrapper around Royal Render API.""" -import os -import sys - -from ayon_core.lib import Logger, run_subprocess, AYONSettingsRegistry -from ayon_core.lib.vendor_bin_utils import find_tool_in_custom_paths - -from .rr_job import SubmitFile -from .rr_job import RRJob, SubmitterParameter # noqa F401 - - -class Api: - - _settings = None - RR_SUBMIT_CONSOLE = 1 - RR_SUBMIT_API = 2 - - def __init__(self, rr_path=None): - self.log = Logger.get_logger("RoyalRender") - self._rr_path = rr_path - os.environ["RR_ROOT"] = rr_path - - @staticmethod - def get_rr_bin_path(rr_root, tool_name=None): - # type: (str, str) -> str - """Get path to RR bin folder. - - Args: - tool_name (str): Name of RR executable you want. - rr_root (str): Custom RR root if needed. - - Returns: - str: Path to the tool based on current platform. - - """ - is_64bit_python = sys.maxsize > 2 ** 32 - - rr_bin_parts = [rr_root, "bin"] - if sys.platform.lower() == "win32": - rr_bin_parts.append("win") - - if sys.platform.lower() == "darwin": - rr_bin_parts.append("mac") - - if sys.platform.lower().startswith("linux"): - rr_bin_parts.append("lx") - - rr_bin_path = os.sep.join(rr_bin_parts) - - paths_to_check = [] - # if we use 64bit python, append 64bit specific path first - if is_64bit_python: - if not tool_name: - return rr_bin_path + "64" - paths_to_check.append(rr_bin_path + "64") - - # otherwise use 32bit - if not tool_name: - return rr_bin_path - paths_to_check.append(rr_bin_path) - - return find_tool_in_custom_paths(paths_to_check, tool_name) - - def _initialize_module_path(self): - # type: () -> None - """Set RR modules for Python.""" - # default for linux - rr_bin = self.get_rr_bin_path(self._rr_path) - rr_module_path = os.path.join(rr_bin, "lx64/lib") - - if sys.platform.lower() == "win32": - rr_module_path = rr_bin - rr_module_path = rr_module_path.replace( - "/", os.path.sep - ) - - if sys.platform.lower() == "darwin": - rr_module_path = os.path.join(rr_bin, "lib/python/27") - - sys.path.append(os.path.join(self._rr_path, rr_module_path)) - - @staticmethod - def create_submission(jobs, submitter_attributes): - # type: (list[RRJob], list[SubmitterParameter]) -> SubmitFile - """Create jobs submission file. - - Args: - jobs (list): List of :class:`RRJob` - submitter_attributes (list): List of submitter attributes - :class:`SubmitterParameter` for whole submission batch. - - Returns: - str: XML data of job submission files. - - """ - return SubmitFile(SubmitterParameters=submitter_attributes, Jobs=jobs) - - def submit_file(self, file, mode=RR_SUBMIT_CONSOLE): - # type: (SubmitFile, int) -> None - if mode == self.RR_SUBMIT_CONSOLE: - self._submit_using_console(file) - return - - # RR v7 supports only Python 2.7, so we bail out in fear - # until there is support for Python 3 😰 - raise NotImplementedError( - "Submission via RoyalRender API is not supported yet") - # self._submit_using_api(file) - - def _submit_using_console(self, job_file): - # type: (SubmitFile) -> None - rr_start_local = self.get_rr_bin_path( - self._rr_path, "rrStartLocal") - - self.log.info("rr_console: {}".format(rr_start_local)) - - args = [rr_start_local, "rrSubmitterconsole", job_file] - self.log.info("Executing: {}".format(" ".join(args))) - env = os.environ - env["RR_ROOT"] = self._rr_path - run_subprocess(args, logger=self.log, env=env) - - def _submit_using_api(self, file): - # type: (SubmitFile) -> None - """Use RR API to submit jobs. - - Args: - file (SubmitFile): Submit jobs definition. - - Throws: - RoyalRenderException: When something fails. - - """ - self._initialize_module_path() - import libpyRR2 as rrLib # noqa - from rrJob import getClass_JobBasics # noqa - import libpyRR2 as _RenderAppBasic # noqa - - tcp = rrLib._rrTCP("") # noqa - rr_server = tcp.getRRServer() - - if len(rr_server) == 0: - self.log.info("Got RR IP address {}".format(rr_server)) - - # TODO: Port is hardcoded in RR? If not, move it to Settings - if not tcp.setServer(rr_server, 7773): - self.log.error( - "Can not set RR server: {}".format(tcp.errorMessage())) - raise RoyalRenderException(tcp.errorMessage()) - - # TODO: This need UI and better handling of username/password. - # We can't store password in keychain as it is pulled multiple - # times and users on linux must enter keychain password every time. - # Probably best way until we setup our own user management would be - # to encrypt password and save it to json locally. Not bulletproof - # but at least it is not stored in plaintext. - reg = AYONSettingsRegistry("rr_settings") - try: - rr_user = reg.get_item("rr_username") - rr_password = reg.get_item("rr_password") - except ValueError: - # user has no rr credentials set - pass - else: - # login to RR - tcp.setLogin(rr_user, rr_password) - - job = getClass_JobBasics() - renderer = _RenderAppBasic() - - # iterate over SubmitFile, set _JobBasic (job) and renderer - # and feed it to jobSubmitNew() - # not implemented yet - job.renderer = renderer - tcp.jobSubmitNew(job) - - -class RoyalRenderException(Exception): - """Exception used in various error states coming from RR.""" - pass diff --git a/server_addon/royalrender/client/ayon_royalrender/lib.py b/server_addon/royalrender/client/ayon_royalrender/lib.py deleted file mode 100644 index ec67cfa29e..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/lib.py +++ /dev/null @@ -1,360 +0,0 @@ -# -*- coding: utf-8 -*- -"""Submitting render job to RoyalRender.""" -import os -import json -import re -import tempfile -import uuid -from datetime import datetime - -import pyblish.api - -from ayon_core.lib import ( - BoolDef, - NumberDef, - is_running_from_build, - is_in_tests, -) -from ayon_core.lib.execute import run_ayon_launcher_process -from ayon_royalrender.api import Api as rrApi -from ayon_royalrender.rr_job import ( - CustomAttribute, - RRJob, - RREnvList, - get_rr_platform, -) -from ayon_core.pipeline import AYONPyblishPluginMixin -from ayon_core.pipeline.publish import KnownPublishError -from ayon_core.pipeline.publish.lib import get_published_workfile_instance - - -class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin, - AYONPyblishPluginMixin): - """Creates separate rendering job for Royal Render""" - label = "Create Nuke Render job in RR" - order = pyblish.api.IntegratorOrder + 0.1 - hosts = ["nuke"] - families = ["render", "prerender"] - targets = ["local"] - optional = True - - priority = 50 - chunk_size = 1 - concurrent_tasks = 1 - use_gpu = True - use_published = True - - @classmethod - def get_attribute_defs(cls): - return [ - NumberDef( - "priority", - label="Priority", - default=cls.priority, - decimals=0 - ), - NumberDef( - "chunk", - label="Frames Per Task", - default=cls.chunk_size, - decimals=0, - minimum=1, - maximum=1000 - ), - NumberDef( - "concurrency", - label="Concurrency", - default=cls.concurrent_tasks, - decimals=0, - minimum=1, - maximum=10 - ), - BoolDef( - "use_gpu", - default=cls.use_gpu, - label="Use GPU" - ), - BoolDef( - "suspend_publish", - default=False, - label="Suspend publish" - ), - BoolDef( - "use_published", - default=cls.use_published, - label="Use published workfile" - ) - ] - - def __init__(self, *args, **kwargs): - self._rr_root = None - self.scene_path = None - self.job = None - self.submission_parameters = None - self.rr_api = None - - def process(self, instance): - if not instance.data.get("farm"): - self.log.info("Skipping local instance.") - return - - instance.data["attributeValues"] = self.get_attr_values_from_data( - instance.data) - - # add suspend_publish attributeValue to instance data - instance.data["suspend_publish"] = instance.data["attributeValues"][ - "suspend_publish"] - - context = instance.context - - self._rr_root = instance.data.get("rr_root") - if not self._rr_root: - raise KnownPublishError( - ("Missing RoyalRender root. " - "You need to configure RoyalRender module.")) - - self.rr_api = rrApi(self._rr_root) - - self.scene_path = context.data["currentFile"] - if self.use_published: - published_workfile = get_published_workfile_instance(context) - - # fallback if nothing was set - if published_workfile is None: - self.log.warning("Falling back to workfile") - file_path = context.data["currentFile"] - else: - workfile_repre = published_workfile.data["representations"][0] - file_path = workfile_repre["published_path"] - - self.scene_path = file_path - self.log.info( - "Using published scene for render {}".format(self.scene_path) - ) - - if not instance.data.get("expectedFiles"): - instance.data["expectedFiles"] = [] - - if not instance.data.get("rrJobs"): - instance.data["rrJobs"] = [] - - def get_job(self, instance, script_path, render_path, node_name): - """Get RR job based on current instance. - - Args: - script_path (str): Path to Nuke script. - render_path (str): Output path. - node_name (str): Name of the render node. - - Returns: - RRJob: RoyalRender Job instance. - - """ - start_frame = int(instance.data["frameStartHandle"]) - end_frame = int(instance.data["frameEndHandle"]) - - batch_name = os.path.basename(script_path) - jobname = "%s - %s" % (batch_name, instance.name) - if is_in_tests(): - batch_name += datetime.now().strftime("%d%m%Y%H%M%S") - - render_dir = os.path.normpath(os.path.dirname(render_path)) - output_filename_0 = self.pad_file_name(render_path, str(start_frame)) - file_name, file_ext = os.path.splitext( - os.path.basename(output_filename_0)) - - custom_attributes = [] - if is_running_from_build(): - custom_attributes = [ - CustomAttribute( - name="OpenPypeVersion", - value=os.environ.get("OPENPYPE_VERSION")) - ] - - # this will append expected files to instance as needed. - expected_files = self.expected_files( - instance, render_path, start_frame, end_frame) - instance.data["expectedFiles"].extend(expected_files) - - job = RRJob( - Software="", - Renderer="", - SeqStart=int(start_frame), - SeqEnd=int(end_frame), - SeqStep=int(instance.data.get("byFrameStep", 1)), - SeqFileOffset=0, - Version=0, - SceneName=script_path, - IsActive=True, - ImageDir=render_dir.replace("\\", "/"), - ImageFilename=file_name, - ImageExtension=file_ext, - ImagePreNumberLetter="", - ImageSingleOutputFile=False, - SceneOS=get_rr_platform(), - Layer=node_name, - SceneDatabaseDir=script_path, - CustomSHotName=jobname, - CompanyProjectName=instance.context.data["projectName"], - ImageWidth=instance.data["resolutionWidth"], - ImageHeight=instance.data["resolutionHeight"], - CustomAttributes=custom_attributes - ) - - return job - - def update_job_with_host_specific(self, instance, job): - """Host specific mapping for RRJob""" - raise NotImplementedError - - def expected_files(self, instance, path, start_frame, end_frame): - """Get expected files. - - This function generate expected files from provided - path and start/end frames. - - It was taken from Deadline module, but this should be - probably handled better in collector to support more - flexible scenarios. - - Args: - instance (Instance) - path (str): Output path. - start_frame (int): Start frame. - end_frame (int): End frame. - - Returns: - list: List of expected files. - - """ - dir_name = os.path.dirname(path) - file = os.path.basename(path) - - expected_files = [] - - if "#" in file: - pparts = file.split("#") - padding = "%0{}d".format(len(pparts) - 1) - file = pparts[0] + padding + pparts[-1] - - if "%" not in file: - expected_files.append(path) - return expected_files - - if instance.data.get("slate"): - start_frame -= 1 - - expected_files.extend( - os.path.join(dir_name, (file % i)).replace("\\", "/") - for i in range(start_frame, (end_frame + 1)) - ) - return expected_files - - def pad_file_name(self, path, first_frame): - """Return output file path with #### for padding. - - RR requires the path to be formatted with # in place of numbers. - For example `/path/to/render.####.png` - - Args: - path (str): path to rendered image - first_frame (str): from representation to cleany replace with # - padding - - Returns: - str - - """ - self.log.debug("pad_file_name path: `{}`".format(path)) - if "%" in path: - search_results = re.search(r"(%0)(\d)(d.)", path).groups() - self.log.debug("_ search_results: `{}`".format(search_results)) - return int(search_results[1]) - if "#" in path: - self.log.debug("already padded: `{}`".format(path)) - return path - - if first_frame: - padding = len(first_frame) - path = path.replace(first_frame, "#" * padding) - - return path - - def inject_environment(self, instance, job): - # type: (pyblish.api.Instance, RRJob) -> RRJob - """Inject environment variables for RR submission. - - This function mimics the behaviour of the Deadline - integration. It is just temporary solution until proper - runtime environment injection is implemented in RR. - - Args: - instance (pyblish.api.Instance): Publishing instance - job (RRJob): RRJob instance to be injected. - - Returns: - RRJob: Injected RRJob instance. - - Throws: - RuntimeError: If any of the required env vars is missing. - - """ - - temp_file_name = "{}_{}.json".format( - datetime.utcnow().strftime('%Y%m%d%H%M%S%f'), - str(uuid.uuid1()) - ) - - export_url = os.path.join(tempfile.gettempdir(), temp_file_name) - print(">>> Temporary path: {}".format(export_url)) - - anatomy_data = instance.context.data["anatomyData"] - addons_manager = instance.context.data["ayonAddonsManager"] - applications_addon = addons_manager.get_enabled_addon("applications") - - folder_key = "folder" - if applications_addon is None: - # Use 'asset' when applications addon command is not used - folder_key = "asset" - - add_kwargs = { - "project": anatomy_data["project"]["name"], - folder_key: instance.context.data["folderPath"], - "task": anatomy_data["task"]["name"], - "app": instance.context.data.get("appName"), - "envgroup": "farm" - } - - if not all(add_kwargs.values()): - raise RuntimeError(( - "Missing required env vars: AYON_PROJECT_NAME, AYON_FOLDER_PATH," - " AYON_TASK_NAME, AYON_APP_NAME" - )) - - args = ["--headless"] - # Use applications addon to extract environments - # NOTE this is for backwards compatibility, the global command - # will be removed in future and only applications addon command - # should be used. - if applications_addon is not None: - args.extend(["addon", "applications"]) - - args.extend([ - "extractenvironments", - export_url - ]) - - if os.getenv('IS_TEST'): - args.append("--automatic-tests") - - for key, value in add_kwargs.items(): - args.extend([f"--{key}", value]) - self.log.debug("Executing: {}".format(" ".join(args))) - run_ayon_launcher_process(*args, logger=self.log) - - self.log.debug("Loading file ...") - with open(export_url) as fp: - contents = json.load(fp) - - job.rrEnvList = RREnvList(contents).serialize() - return job diff --git a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_rr_path_from_instance.py b/server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_rr_path_from_instance.py deleted file mode 100644 index e3c3ddc74b..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_rr_path_from_instance.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Requires: - instance.context.data["project_settings"] -Provides: - instance.data["rr_root"] (str) - root folder of RoyalRender server -""" -import os.path - -import pyblish.api -from ayon_royalrender.rr_job import get_rr_platform - - -class CollectRRPathFromInstance(pyblish.api.InstancePlugin): - """Collect RR Path from instance. - - All RoyalRender server roots are set in `Studio Settings`, each project - uses only key pointing to that part to limit typos inside of Project - settings. - Eventually could be possible to add dropdown with these keys to the - Creators to allow artists to select which RR server they would like to use. - """ - - order = pyblish.api.CollectorOrder - label = "Collect Royal Render path name from the Instance" - families = ["render", "prerender", "renderlayer"] - - def process(self, instance): - instance.data["rr_root"] = self._collect_root(instance) - self.log.info( - "Using '{}' for submission.".format(instance.data["rr_root"])) - - def _collect_root(self, instance): - # type: (pyblish.api.Instance) -> str - """Get Royal Render pat name from render instance. - If artist should be able to select specific RR server it must be added - to creator. It is not there yet. - """ - rr_settings = instance.context.data["project_settings"]["royalrender"] - rr_paths = rr_settings["rr_paths"] - selected_keys = rr_settings["selected_rr_paths"] - - platform = get_rr_platform() - key_to_path = { - item["name"]: item["value"][platform] - for item in rr_paths - } - - for selected_key in selected_keys: - rr_root = key_to_path[selected_key] - if os.path.exists(rr_root): - return rr_root diff --git a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_sequences_from_job.py b/server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_sequences_from_job.py deleted file mode 100644 index 5e0ea0e258..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/collect_sequences_from_job.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- -"""Collect sequences from Royal Render Job.""" -import os -import re -import copy -import json -from pprint import pformat - -import pyblish.api - - -def collect(root, - regex=None, - exclude_regex=None, - frame_start=None, - frame_end=None): - """Collect sequence collections in root""" - - import clique - - files = [] - for filename in os.listdir(root): - - # Must have extension - ext = os.path.splitext(filename)[1] - if not ext: - continue - - # Only files - if not os.path.isfile(os.path.join(root, filename)): - continue - - # Include and exclude regex - if regex and not re.search(regex, filename): - continue - if exclude_regex and re.search(exclude_regex, filename): - continue - - files.append(filename) - - # Match collections - # Support filenames like: projectX_shot01_0010.tiff with this regex - pattern = r"(?P(?P0*)\d+)\.\D+\d?$" - collections, remainder = clique.assemble(files, - patterns=[pattern], - minimum_items=1) - - # Ignore any remainders - if remainder: - print("Skipping remainder {}".format(remainder)) - - # Exclude any frames outside start and end frame. - for collection in collections: - for index in list(collection.indexes): - if frame_start is not None and index < frame_start: - collection.indexes.discard(index) - continue - if frame_end is not None and index > frame_end: - collection.indexes.discard(index) - continue - - # Keep only collections that have at least a single frame - collections = [c for c in collections if c.indexes] - - return collections - - -class CollectSequencesFromJob(pyblish.api.ContextPlugin): - """Gather file sequences from job directory. - - When "AYON_PUBLISH_DATA" environment variable is set these paths - (folders or .json files) are parsed for image sequences. Otherwise, the - current working directory is searched for file sequences. - - """ - order = pyblish.api.CollectorOrder - targets = ["rr_control"] - label = "Collect Rendered Frames" - settings_category = "royalrender" - review = True - - def process(self, context): - - self.review = ( - context.data - ["project_settings"] - ["royalrender"] - ["publish"] - ["CollectSequencesFromJob"] - ["review"] - ) - - publish_data_paths = ( - os.environ.get("AYON_PUBLISH_DATA") - or os.environ.get("OPENPYPE_PUBLISH_DATA") - ) - if publish_data_paths: - self.log.debug(publish_data_paths) - paths = publish_data_paths.split(os.pathsep) - self.log.info("Collecting paths: {}".format(paths)) - else: - cwd = context.get("workspaceDir", os.getcwd()) - paths = [cwd] - - for path in paths: - - self.log.info("Loading: {}".format(path)) - - if path.endswith(".json"): - # Search using .json configuration - with open(path, "r") as f: - try: - data = json.load(f) - except Exception as exc: - self.log.error("Error loading json: " - "{} - Exception: {}".format(path, exc)) - raise - - cwd = os.path.dirname(path) - root_override = data.get("root") - if root_override: - if os.path.isabs(root_override): - root = root_override - else: - root = os.path.join(cwd, root_override) - else: - root = cwd - - metadata = data.get("metadata") - if metadata: - session = metadata.get("session") - if session: - self.log.info("setting session using metadata") - os.environ.update(session) - - else: - # Search in directory - data = {} - root = path - - self.log.info("Collecting: {}".format(root)) - regex = data.get("regex") - if regex: - self.log.info("Using regex: {}".format(regex)) - - collections = collect(root=root, - regex=regex, - exclude_regex=data.get("exclude_regex"), - frame_start=data.get("frameStart"), - frame_end=data.get("frameEnd")) - - self.log.info("Found collections: {}".format(collections)) - - if data.get("productName") and len(collections) > 1: - self.log.error("Forced produce can only work with a single " - "found sequence") - raise RuntimeError("Invalid sequence") - - fps = data.get("fps", 25) - - # Get family from the data - families = data.get("families", ["render"]) - if "render" not in families: - families.append("render") - if "ftrack" not in families: - families.append("ftrack") - if "review" not in families and self.review: - self.log.info("attaching review") - families.append("review") - - for collection in collections: - instance = context.create_instance(str(collection)) - self.log.info("Collection: %s" % list(collection)) - - # Ensure each instance gets a unique reference to the data - data = copy.deepcopy(data) - - # If no product provided, get it from collection's head - product_name = ( - data.get("productName", collection.head.rstrip("_. ")) - ) - - # If no start or end frame provided, get it from collection - indices = list(collection.indexes) - start = data.get("frameStart", indices[0]) - end = data.get("frameEnd", indices[-1]) - - ext = list(collection)[0].split('.')[-1] - - instance.data.update({ - "name": str(collection), - "productType": families[0], - "family": families[0], - "families": list(families), - "productName": product_name, - "folderPath": data.get( - "folderPath", context.data["folderPath"] - ), - "stagingDir": root, - "frameStart": start, - "frameEnd": end, - "fps": fps, - "source": data.get('source', '') - }) - instance.append(collection) - instance.context.data['fps'] = fps - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': ext, - 'ext': '{}'.format(ext), - 'files': list(collection), - "frameStart": start, - "frameEnd": end, - "stagingDir": root, - "anatomy_template": "render", - "fps": fps, - "tags": ['review'] - } - instance.data["representations"].append(representation) - - if data.get('user'): - context.data["user"] = data['user'] - - self.log.debug("Collected instance:\n" - "{}".format(pformat(instance.data))) diff --git a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_maya_royalrender_job.py b/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_maya_royalrender_job.py deleted file mode 100644 index 4e52e8e24b..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_maya_royalrender_job.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -"""Submitting render job to RoyalRender.""" -import os - -from maya.OpenMaya import MGlobal # noqa: F401 - -from ayon_royalrender import lib -from ayon_core.pipeline.farm.tools import iter_expected_files - - -class CreateMayaRoyalRenderJob(lib.BaseCreateRoyalRenderJob): - label = "Create Maya Render job in RR" - hosts = ["maya"] - families = ["renderlayer"] - - def update_job_with_host_specific(self, instance, job): - job.Software = "Maya" - job.Version = "{0:.2f}".format(MGlobal.apiVersion() / 10000) - if instance.data.get("cameras"): - job.Camera = instance.data["cameras"][0].replace("'", '"') - workspace = instance.context.data["workspaceDir"] - job.SceneDatabaseDir = workspace - - return job - - def process(self, instance): - """Plugin entry point.""" - super(CreateMayaRoyalRenderJob, self).process(instance) - - expected_files = instance.data["expectedFiles"] - first_file_path = next(iter_expected_files(expected_files)) - output_dir = os.path.dirname(first_file_path) - instance.data["outputDir"] = output_dir - - layer = instance.data["setMembers"] # type: str - layer_name = layer.removeprefix("rs_") - - job = self.get_job(instance, self.scene_path, first_file_path, - layer_name) - job = self.update_job_with_host_specific(instance, job) - job = self.inject_environment(instance, job) - - instance.data["rrJobs"].append(job) diff --git a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_nuke_royalrender_job.py b/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_nuke_royalrender_job.py deleted file mode 100644 index 442485d211..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_nuke_royalrender_job.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -"""Submitting render job to RoyalRender.""" -import re - -from ayon_royalrender import lib - - -class CreateNukeRoyalRenderJob(lib.BaseCreateRoyalRenderJob): - """Creates separate rendering job for Royal Render""" - label = "Create Nuke Render job in RR" - hosts = ["nuke"] - families = ["render", "prerender"] - - def process(self, instance): - super(CreateNukeRoyalRenderJob, self).process(instance) - - # redefinition of families - if "render" in instance.data["productType"]: - instance.data["productType"] = "write" - instance.data["family"] = "write" - instance.data["families"].insert(0, "render2d") - elif "prerender" in instance.data["productType"]: - instance.data["productType"] = "write" - instance.data["family"] = "write" - instance.data["families"].insert(0, "prerender") - - jobs = self.create_jobs(instance) - for job in jobs: - job = self.update_job_with_host_specific(instance, job) - job = self.inject_environment(instance, job) - - instance.data["rrJobs"].append(job) - - def update_job_with_host_specific(self, instance, job): - nuke_version = re.search( - r"\d+\.\d+", instance.context.data.get("hostVersion")) - - job.Software = "Nuke" - job.Version = nuke_version.group() - - return job - - def create_jobs(self, instance): - """Nuke creates multiple RR jobs - for baking etc.""" - # get output path - render_path = instance.data['path'] - script_path = self.scene_path - node = instance.data["transientData"]["node"] - - # main job - jobs = [ - self.get_job( - instance, - script_path, - render_path, - node.name() - ) - ] - - for baking_script in instance.data.get("bakingNukeScripts", []): - render_path = baking_script["bakeRenderPath"] - script_path = baking_script["bakeScriptPath"] - exe_node_name = baking_script["bakeWriteNodeName"] - - jobs.append(self.get_job( - instance, - script_path, - render_path, - exe_node_name - )) - - return jobs diff --git a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_publish_royalrender_job.py b/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_publish_royalrender_job.py deleted file mode 100644 index 0bc092ecd8..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/create_publish_royalrender_job.py +++ /dev/null @@ -1,244 +0,0 @@ -# -*- coding: utf-8 -*- -"""Create publishing job on RoyalRender.""" -import os -import attr -import json - -import pyblish.api - -from ayon_royalrender.rr_job import ( - RRJob, - RREnvList, - get_rr_platform -) -from ayon_core.pipeline.publish import KnownPublishError -from ayon_core.pipeline.farm.pyblish_functions import ( - create_skeleton_instance, - create_instances_for_aov, - attach_instances_to_product, - prepare_representations, - create_metadata_path -) -from ayon_core.pipeline import publish - - -class CreatePublishRoyalRenderJob(pyblish.api.InstancePlugin, - publish.ColormanagedPyblishPluginMixin): - """Creates job which publishes rendered files to publish area. - - Job waits until all rendering jobs are finished, triggers `publish` command - where it reads from prepared .json file with metadata about what should - be published, renames prepared images and publishes them. - - When triggered it produces .log file next to .json file in work area. - """ - label = "Create publish job in RR" - order = pyblish.api.IntegratorOrder + 0.2 - icon = "tractor" - targets = ["local"] - hosts = ["fusion", "maya", "nuke", "celaction", "aftereffects", "harmony"] - families = ["render.farm", "prerender.farm", - "renderlayer", "imagesequence", "vrayscene"] - aov_filter = {"maya": [r".*([Bb]eauty).*"], - "aftereffects": [r".*"], # for everything from AE - "harmony": [r".*"], # for everything from AE - "celaction": [r".*"]} - - skip_integration_repre_list = [] - - # mapping of instance properties to be transferred to new instance - # for every specified family - instance_transfer = { - "slate": ["slateFrames", "slate"], - "review": ["lutPath"], - "render2d": ["bakingNukeScripts", "version"], - "renderlayer": ["convertToScanline"] - } - - # list of family names to transfer to new family if present - families_transfer = ["render3d", "render2d", "ftrack", "slate"] - - environ_keys = [ - "FTRACK_API_USER", - "FTRACK_API_KEY", - "FTRACK_SERVER", - "AYON_APP_NAME", - "AYON_USERNAME", - "AYON_SG_USERNAME", - ] - priority = 50 - - def process(self, instance): - context = instance.context - self.context = context - self.anatomy = instance.context.data["anatomy"] - - if not instance.data.get("farm"): - self.log.info("Skipping local instance.") - return - - instance_skeleton_data = create_skeleton_instance( - instance, - families_transfer=self.families_transfer, - instance_transfer=self.instance_transfer) - - do_not_add_review = False - if instance.data.get("review") is False: - self.log.debug("Instance has review explicitly disabled.") - do_not_add_review = True - - if isinstance(instance.data.get("expectedFiles")[0], dict): - instances = create_instances_for_aov( - instance, instance_skeleton_data, - self.aov_filter, self.skip_integration_repre_list, - do_not_add_review) - - else: - representations = prepare_representations( - instance_skeleton_data, - instance.data.get("expectedFiles"), - self.anatomy, - self.aov_filter, - self.skip_integration_repre_list, - do_not_add_review, - instance.context, - self - ) - - if "representations" not in instance_skeleton_data.keys(): - instance_skeleton_data["representations"] = [] - - # add representation - instance_skeleton_data["representations"] += representations - instances = [instance_skeleton_data] - - # attach instances to product - if instance.data.get("attachTo"): - instances = attach_instances_to_product( - instance.data.get("attachTo"), instances - ) - - self.log.info("Creating RoyalRender Publish job ...") - - if not instance.data.get("rrJobs"): - self.log.error(("There is no prior RoyalRender " - "job on the instance.")) - raise KnownPublishError( - "Can't create publish job without prior rendering jobs first") - - rr_job = self.get_job(instance, instances) - instance.data["rrJobs"].append(rr_job) - - # publish job file - publish_job = { - "folderPath": instance_skeleton_data["folderPath"], - "frameStart": instance_skeleton_data["frameStart"], - "frameEnd": instance_skeleton_data["frameEnd"], - "fps": instance_skeleton_data["fps"], - "source": instance_skeleton_data["source"], - "user": instance.context.data["user"], - "version": instance.context.data["version"], # workfile version - "intent": instance.context.data.get("intent"), - "comment": instance.context.data.get("comment"), - "job": attr.asdict(rr_job), - "instances": instances - } - - metadata_path, rootless_metadata_path = \ - create_metadata_path(instance, self.anatomy) - - self.log.info("Writing json file: {}".format(metadata_path)) - with open(metadata_path, "w") as f: - json.dump(publish_job, f, indent=4, sort_keys=True) - - def get_job(self, instance, instances): - """Create RR publishing job. - - Based on provided original instance and additional instances, - create publishing job and return it to be submitted to farm. - - Args: - instance (Instance): Original instance. - instances (list of Instance): List of instances to - be published on farm. - - Returns: - RRJob: RoyalRender publish job. - - """ - data = instance.data.copy() - product_name = data["productName"] - jobname = "Publish - {}".format(product_name) - - # Transfer the environment from the original job to this dependent - # job, so they use the same environment - metadata_path, rootless_metadata_path = \ - create_metadata_path(instance, self.anatomy) - - anatomy_data = instance.context.data["anatomyData"] - - environment = RREnvList({ - "AYON_PROJECT_NAME": anatomy_data["project"]["name"], - "AYON_FOLDER_PATH": instance.context.data["folderPath"], - "AYON_TASK_NAME": anatomy_data["task"]["name"], - "AYON_USERNAME": anatomy_data["user"] - }) - - # add environments from self.environ_keys - for env_key in self.environ_keys: - if os.getenv(env_key): - environment[env_key] = os.environ[env_key] - - # pass environment keys from self.environ_job_filter - # and collect all pre_ids to wait for - jobs_pre_ids = [] - for job in instance.data["rrJobs"]: # type: RRJob - jobs_pre_ids.append(job.PreID) - - priority = self.priority or instance.data.get("priority", 50) - - # rr requires absolute path or all jobs won't show up in rrControl - abs_metadata_path = self.anatomy.fill_root(rootless_metadata_path) - - # command line set in E01__OpenPype__PublishJob.cfg, here only - # additional logging - args = [ - ">", os.path.join(os.path.dirname(abs_metadata_path), - "rr_out.log"), - "2>&1" - ] - - job = RRJob( - Software="OpenPype", - Renderer="Once", - SeqStart=1, - SeqEnd=1, - SeqStep=1, - SeqFileOffset=0, - Version=os.environ["AYON_BUNDLE_NAME"], - SceneName=abs_metadata_path, - # command line arguments - CustomAddCmdFlags=" ".join(args), - IsActive=True, - ImageFilename="execOnce.file", - ImageDir="", - ImageExtension="", - ImagePreNumberLetter="", - SceneOS=get_rr_platform(), - rrEnvList=environment.serialize(), - Priority=priority, - CustomSHotName=jobname, - CompanyProjectName=instance.context.data["projectName"] - ) - - # add assembly jobs as dependencies - if instance.data.get("tileRendering"): - self.log.info("Adding tile assembly jobs as dependencies...") - job.WaitForPreIDs += instance.data.get("assemblySubmissionJobs") - elif instance.data.get("bakingSubmissionJobs"): - self.log.info("Adding baking submission jobs as dependencies...") - job.WaitForPreIDs += instance.data["bakingSubmissionJobs"] - else: - job.WaitForPreIDs += jobs_pre_ids - - return job diff --git a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/submit_jobs_to_royalrender.py b/server_addon/royalrender/client/ayon_royalrender/plugins/publish/submit_jobs_to_royalrender.py deleted file mode 100644 index 72a27dab2d..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/plugins/publish/submit_jobs_to_royalrender.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- -"""Submit jobs to RoyalRender.""" -import tempfile - -import pyblish.api -from ayon_royalrender.api import ( - RRJob, - Api as rrApi, - SubmitterParameter -) -from ayon_core.pipeline.publish import KnownPublishError - - -class SubmitJobsToRoyalRender(pyblish.api.ContextPlugin): - """Find all jobs, create submission XML and submit it to RoyalRender.""" - label = "Submit jobs to RoyalRender" - order = pyblish.api.IntegratorOrder + 0.3 - targets = ["local"] - - def __init__(self): - super(SubmitJobsToRoyalRender, self).__init__() - self._rr_root = None - self._rr_api = None - self._submission_parameters = [] - - def process(self, context): - - # iterate over all instances and try to find RRJobs - jobs = [] - instance_rr_path = None - for instance in context: - if isinstance(instance.data.get("rrJob"), RRJob): - jobs.append(instance.data.get("rrJob")) - if instance.data.get("rrJobs"): - if all( - isinstance(job, RRJob) - for job in instance.data.get("rrJobs")): - jobs += instance.data.get("rrJobs") - if instance.data.get("rr_root"): - instance_rr_path = instance.data["rr_root"] - - if jobs: - self._rr_root = instance_rr_path - if not self._rr_root: - raise KnownPublishError( - ("Missing RoyalRender root. " - "You need to configure RoyalRender module.")) - self._rr_api = rrApi(self._rr_root) - self._submission_parameters = self.get_submission_parameters() - self.process_submission(jobs) - return - - self.log.info("No RoyalRender jobs found") - - def process_submission(self, jobs): - # type: ([RRJob]) -> None - - idx_pre_id = 0 - for job in jobs: - job.PreID = idx_pre_id - if idx_pre_id > 0: - job.WaitForPreIDs.append(idx_pre_id - 1) - idx_pre_id += 1 - - submission = rrApi.create_submission( - jobs, - self._submission_parameters) - - xml = tempfile.NamedTemporaryFile(suffix=".xml", delete=False) - with open(xml.name, "w") as f: - f.write(submission.serialize()) - - self.log.info("submitting job(s) file: {}".format(xml.name)) - self._rr_api.submit_file(file=xml.name) - - def create_file(self, name, ext, contents=None): - temp = tempfile.NamedTemporaryFile( - dir=self.tempdir, - suffix=ext, - prefix=name + '.', - delete=False, - ) - - if contents: - with open(temp.name, 'w') as f: - f.write(contents) - - return temp.name - - def get_submission_parameters(self): - return [SubmitterParameter("RequiredMemory", "0")] diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_job.py b/server_addon/royalrender/client/ayon_royalrender/rr_job.py deleted file mode 100644 index 62a82d45e8..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_job.py +++ /dev/null @@ -1,305 +0,0 @@ -# -*- coding: utf-8 -*- -"""Python wrapper for RoyalRender XML job file.""" -import sys -from xml.dom import minidom as md -import attr -from collections import namedtuple, OrderedDict - - -CustomAttribute = namedtuple("CustomAttribute", ["name", "value"]) - - -def get_rr_platform(): - # type: () -> str - """Returns name of platform used in rr jobs.""" - if sys.platform.lower() in ["win32", "win64"]: - return "windows" - elif sys.platform.lower() == "darwin": - return "mac" - else: - return "linux" - - -class RREnvList(dict): - def serialize(self): - # VariableA=ValueA~~~VariableB=ValueB - return "~~~".join( - ["{}={}".format(k, v) for k, v in sorted(self.items())]) - - @staticmethod - def parse(data): - # type: (str) -> RREnvList - """Parse rrEnvList string and return it as RREnvList object.""" - out = RREnvList() - for var in data.split("~~~"): - k, v = var.split("=", maxsplit=1) - out[k] = v - return out - - -@attr.s -class RRJob(object): - """Mapping of Royal Render job file to a data class.""" - - # Required - # -------- - - # Name of your render application. Same as in the render config file. - # (Maya, Softimage) - Software = attr.ib() # type: str - - # The OS the scene was created on, all texture paths are set on - # that OS. Possible values are windows, linux, osx - SceneOS = attr.ib() # type: str - - # Renderer you use. Same as in the render config file - # (VRay, Mental Ray, Arnold) - Renderer = attr.ib() # type: str - - # Version you want to render with. (5.11, 2010, 12) - Version = attr.ib() # type: str - - # Name of the scene file with full path. - SceneName = attr.ib() # type: str - - # Is the job enabled for submission? - # enabled by default - IsActive = attr.ib() # type: bool - - # Sequence settings of this job - SeqStart = attr.ib() # type: int - SeqEnd = attr.ib() # type: int - SeqStep = attr.ib() # type: int - SeqFileOffset = attr.ib() # type: int - - # If you specify ImageDir, then ImageFilename has no path. If you do - # NOT specify ImageDir, then ImageFilename has to include the path. - # Same for ImageExtension. - # Important: Do not forget any _ or . in front or after the frame - # numbering. Usually ImageExtension always starts with a . (.tga, .exr) - ImageDir = attr.ib() # type: str - ImageFilename = attr.ib() # type: str - ImageExtension = attr.ib() # type: str - - # Some applications always add a . or _ in front of the frame number. - # Set this variable to that character. The user can then change - # the filename at the rrSubmitter and the submitter keeps - # track of this character. - ImagePreNumberLetter = attr.ib() # type: str - - # If you render a single file, e.g. Quicktime or Avi, then you have to - # set this value. Videos have to be rendered at once on one client. - ImageSingleOutputFile = attr.ib(default=False) # type: bool - - # Semi-Required (required for some render applications) - # ----------------------------------------------------- - - # The database of your scene file. In Maya and XSI called "project", - # in Lightwave "content dir" - SceneDatabaseDir = attr.ib(default=None) # type: str - - # Required if you want to split frames on multiple clients - ImageWidth = attr.ib(default=None) # type: int - ImageHeight = attr.ib(default=None) # type: int - Camera = attr.ib(default=None) # type: str - Layer = attr.ib(default=None) # type: str - Channel = attr.ib(default=None) # type: str - - # Optional - # -------- - - # Used for the RR render license function. - # E.g. If you render with mentalRay, then add mentalRay. If you render - # with Nuke and you use Furnace plugins in your comp, add Furnace. - # TODO: determine how this work for multiple plugins - RequiredPlugins = attr.ib(default=None) # type: str - - # Frame Padding of the frame number in the rendered filename. - # Some render config files are setting the padding at render time. - ImageFramePadding = attr.ib(default=None) # type: int - - # Some render applications support overriding the image format at - # the render commandline. - OverrideImageFormat = attr.ib(default=None) # type: str - - # rrControl can display the name of additonal channels that are - # rendered. Each channel requires these two values. ChannelFilename - # contains the full path. - ChannelFilename = attr.ib(default=None) # type: str - ChannelExtension = attr.ib(default=None) # type: str - - # A value between 0 and 255. Each job gets the Pre ID attached as small - # letter to the main ID. A new main ID is generated for every machine - # for every 5/1000s. - PreID = attr.ib(default=None) # type: int - - # When the job is received by the server, the server checks for other - # jobs send from this machine. If a job with the PreID was found, then - # this jobs waits for the other job. Note: This flag can be used multiple - # times to wait for multiple jobs. - WaitForPreIDs = attr.ib(factory=list) # type: list - - # List of submitter options per job - # list item must be of `SubmitterParameter` type - SubmitterParameters = attr.ib(factory=list) # type: list - - # List of Custom job attributes - # Royal Render support custom attributes in format or - # - # list item must be of `CustomAttribute` named tuple - CustomAttributes = attr.ib(factory=list) # type: list - - # This is used to hold command line arguments for Execute job - CustomAddCmdFlags = attr.ib(default=None) # type: str - - # Additional information for subsequent publish script and - # for better display in rrControl - UserName = attr.ib(default=None) # type: str - CustomSeQName = attr.ib(default=None) # type: str - CustomSHotName = attr.ib(default=None) # type: str - CustomVersionName = attr.ib(default=None) # type: str - CustomUserInfo = attr.ib(default=None) # type: str - SubmitMachine = attr.ib(default=None) # type: str - Color_ID = attr.ib(default=2) # type: int - CompanyProjectName = attr.ib(default=None) # type: str - - RequiredLicenses = attr.ib(default=None) # type: str - - # Additional frame info - Priority = attr.ib(default=50) # type: int - TotalFrames = attr.ib(default=None) # type: int - Tiled = attr.ib(default=None) # type: str - - # Environment - # only used in RR 8.3 and newer - rrEnvList = attr.ib(default=None, type=str) # type: str - - -class SubmitterParameter: - """Wrapper for Submitter Parameters.""" - def __init__(self, parameter, *args): - # type: (str, list) -> None - self._parameter = parameter - self._values = args - - def serialize(self): - # type: () -> str - """Serialize submitter parameter as a string value. - - This can be later on used as text node in job xml file. - - Returns: - str: concatenated string of parameter values. - - """ - return '"{param}={val}"'.format( - param=self._parameter, val="~".join(self._values)) - - -@attr.s -class SubmitFile(object): - """Class wrapping Royal Render submission XML file.""" - - # Syntax version of the submission file. - syntax_version = attr.ib(default="6.0") # type: str - - # Delete submission file after processing - DeleteXML = attr.ib(default=1) # type: int - - # List of the submitter options per job. - # list item must be of `SubmitterParameter` type - SubmitterParameters = attr.ib(factory=list) # type: list - - # List of the jobs in submission batch. - # list item must be of type `RRJob` - Jobs = attr.ib(factory=list) # type: list - - @staticmethod - def _process_submitter_parameters(parameters, dom, append_to): - # type: (list[SubmitterParameter], md.Document, md.Element) -> None - """Take list of :class:`SubmitterParameter` and process it as XML. - - This will take :class:`SubmitterParameter`, create XML element - for them and convert value to Royal Render compatible string - (options and values separated by ~) - - Args: - parameters (list of SubmitterParameter): List of parameters. - dom (xml.dom.minidom.Document): XML Document - append_to (xml.dom.minidom.Element): Element to append to. - - """ - for param in parameters: - if not isinstance(param, SubmitterParameter): - raise AttributeError( - "{} is not of type `SubmitterParameter`".format(param)) - xml_parameter = dom.createElement("SubmitterParameter") - xml_parameter.appendChild(dom.createTextNode(param.serialize())) - append_to.appendChild(xml_parameter) - - def serialize(self): - # type: () -> str - """Return all data serialized as XML. - - Returns: - str: XML data as string. - - """ - def filter_data(a, v): - """Skip private attributes.""" - if a.name.startswith("_"): - return False - if v is None: - return False - return True - - root = md.Document() - # root element: - job_file = root.createElement('RR_Job_File') - job_file.setAttribute("syntax_version", self.syntax_version) - - # handle Submitter Parameters for batch - # foo=bar~baz~goo - self._process_submitter_parameters( - self.SubmitterParameters, root, job_file) - root.appendChild(job_file) - for job in self.Jobs: # type: RRJob - if not isinstance(job, RRJob): - raise AttributeError( - "{} is not of type `SubmitterParameter`".format(job)) - xml_job = root.createElement("Job") - # handle Submitter Parameters for job - self._process_submitter_parameters( - job.SubmitterParameters, root, xml_job - ) - job_custom_attributes = job.CustomAttributes - - serialized_job = attr.asdict( - job, dict_factory=OrderedDict, filter=filter_data) - serialized_job.pop("CustomAttributes") - serialized_job.pop("SubmitterParameters") - # we are handling `WaitForPreIDs` separately. - wait_pre_ids = serialized_job.pop("WaitForPreIDs", []) - - for custom_attr in job_custom_attributes: # type: CustomAttribute - serialized_job["Custom{}".format( - custom_attr.name)] = custom_attr.value - - for item, value in serialized_job.items(): - xml_attr = root.createElement(item) - xml_attr.appendChild( - root.createTextNode(str(value)) - ) - xml_job.appendChild(xml_attr) - - # WaitForPreID - can be used multiple times - for pre_id in wait_pre_ids: - xml_attr = root.createElement("WaitForPreID") - xml_attr.appendChild( - root.createTextNode(str(pre_id)) - ) - xml_job.appendChild(xml_attr) - - job_file.appendChild(xml_job) - - return root.toprettyxml(indent="\t") diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/README.md b/server_addon/royalrender/client/ayon_royalrender/rr_root/README.md deleted file mode 100644 index 0a9777833e..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## OpenPype RoyalRender integration plugins - -### Installation - -Copy content of this folder to your `RR_ROOT` (place where RoyalRender studio wide installation is). \ No newline at end of file diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py b/server_addon/royalrender/client/ayon_royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py deleted file mode 100644 index 8405f69b3e..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/plugins/control_job/perjob/m50__openpype_publish_render.py +++ /dev/null @@ -1,205 +0,0 @@ -# -*- coding: utf-8 -*- -"""This is RR control plugin that runs on the job by user interaction. - -It asks user for context to publish, getting it from ayon_core. In order to -run it needs `OPENPYPE_ROOT` to be set to know where to execute OpenPype. - -""" -import rr # noqa -import rrGlobal # noqa -import subprocess -import os -import glob -import platform -import tempfile -import json - - -class OpenPypeContextSelector: - """Class to handle publishing context determination in RR.""" - - def __init__(self): - self.job = rr.getJob() - self.context = {} - - self.openpype_executable = "openpype_gui" - if platform.system().lower() == "windows": - self.openpype_executable = "{}.exe".format( - self.openpype_executable) - - op_path = os.environ.get("OPENPYPE_ROOT") - print("initializing ... {}".format(op_path)) - if not op_path: - print("Warning: OpenPype root is not found.") - - if platform.system().lower() == "windows": - print(" * trying to find OpenPype on local computer.") - op_path = os.path.join( - os.environ.get("PROGRAMFILES"), - "OpenPype", "openpype_console.exe" - ) - if not os.path.exists(op_path): - # try to find in user local context - op_path = os.path.join( - os.environ.get("LOCALAPPDATA"), - "Programs", - "OpenPype", "openpype_console.exe" - ) - if not os.path.exists(op_path): - raise Exception("Error: OpenPype was not found.") - - op_path = os.path.dirname(op_path) - print(" - found OpenPype installation {}".format(op_path)) - - self.openpype_root = op_path - - def _process_metadata_file(self): - """Find and process metadata file. - - Try to find metadata json file in job folder to get context from. - - Returns: - dict: Context from metadata json file. - - """ - image_dir = self.job.imageDir - metadata_files = glob.glob( - "{}{}*_metadata.json".format(image_dir, os.path.sep)) - if not metadata_files: - return {} - - raise NotImplementedError( - "Processing existing metadata not implemented yet.") - - def process_job(self): - """Process selected job. - - This should process selected job. If context can be determined - automatically, no UI will be show and publishing will directly - proceed. - """ - if not self.context and not self.show(): - return - - self.context["user"] = self.job.userName - self.run_publish() - - def show(self): - """Show UI for context selection. - - Because of RR UI limitations, this must be done using OpenPype - itself. - - """ - tf = tempfile.TemporaryFile(delete=False) - context_file = tf.name - op_args = [os.path.join(self.openpype_root, self.openpype_executable), - "contextselection", tf.name] - - tf.close() - print(">>> running {}".format(" ".join(op_args))) - - subprocess.call(op_args) - - with open(context_file, "r") as cf: - self.context = json.load(cf) - - os.unlink(context_file) - print(">>> context: {}".format(self.context)) - - if not self.context or \ - not self.context.get("project") or \ - not self.context.get("folder") or \ - not self.context.get("task"): - self._show_rr_warning("Context selection failed.") - return False - - # self.context["app_name"] = self.job.renderer.name - # there should be mapping between OpenPype and Royal Render - # app names and versions, but since app_name is not used - # currently down the line (but it is required by OP publish command - # right now). - # self.context["app_name"] = "maya/2022" - return True - - @staticmethod - def _show_rr_warning(text): - warning_dialog = rrGlobal.getGenericUI() - warning_dialog.addItem(rrGlobal.genUIType.label, "infoLabel", "") - warning_dialog.setText("infoLabel", text) - warning_dialog.addItem( - rrGlobal.genUIType.layoutH, "btnLayout", "") - warning_dialog.addItem( - rrGlobal.genUIType.closeButton, "Ok", "btnLayout") - warning_dialog.execute() - del warning_dialog - - def run_publish(self): - """Run publish process.""" - env = {"AYON_PROJECT_NAME": str(self.context.get("project")), - "AYON_FOLDER_PATH": str(self.context.get("folder")), - "AYON_TASK_NAME": str(self.context.get("task")), - # "AYON_APP_NAME": str(self.context.get("app_name")) - } - - print(">>> setting environment:") - for k, v in env.items(): - print(" {}: {}".format(k, v)) - - publishing_paths = [os.path.join(self.job.imageDir, - os.path.dirname( - self.job.imageFileName))] - - # add additional channels - channel_idx = 0 - channel = self.job.channelFileName(channel_idx) - while channel: - channel_path = os.path.dirname( - os.path.join(self.job.imageDir, channel)) - if channel_path not in publishing_paths: - publishing_paths.append(channel_path) - channel_idx += 1 - channel = self.job.channelFileName(channel_idx) - - args = [os.path.join(self.openpype_root, self.openpype_executable), - 'publish', '-t', "rr_control", "--gui" - ] - - args += publishing_paths - - print(">>> running {}".format(" ".join(args))) - orig = os.environ.copy() - orig.update(env) - try: - subprocess.call(args, env=orig) - except subprocess.CalledProcessError as e: - self._show_rr_warning(" Publish failed [ {} ]".format( - e.returncode - )) - - -print("running selector") -selector = OpenPypeContextSelector() - -# try to set context from environment -for key, env_keys in ( - ("project", ["AYON_PROJECT_NAME", "AVALON_PROJECT"]), - ("folder", ["AYON_FOLDER_PATH", "AVALON_ASSET"]), - ("task", ["AYON_TASK_NAME", "AVALON_TASK"]), - # ("app_name", ["AYON_APP_NAME", "AVALON_APP_NAME"]) -): - value = "" - for env_key in env_keys: - value = os.getenv(env_key) - if value: - break - selector.context[key] = value - -# if anything inside is None, scratch the whole thing and -# ask user for context. -for _, v in selector.context.items(): - if not v: - selector.context = {} - break - -selector.process_job() diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype.png b/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype.png deleted file mode 100644 index 68c5aec1174c9ee4b0f4ec21fe0aea8c2ac48545..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2075 zcmah~c~BE)6pvFuK|w{7O6zo8IviqlmmpYH0z$$i1Y-yqnOY6W29_Kfb{9wxMNwL$ z9u>uds#Qlv1;wK}B9=0O6to~k1rHDhhkBrRAXXW~_S=9^ulA2k_WORn_j~Vq?|pAq zOQXZ=Y@Ka+Jf2-dxL5}M?YU>9HTeA^oZrCX4gZ;xD_Dgj3Rh8jM5(6Y3B;r~0-VS5 z4>TE-DlNf6@k9b?5WBb_y)#>ljzqF6O4)`jVwtSAWl;m zPo=Oz7zQ{rAkLDSA$0>YaD0#mltWN21VjH#Crso==p6DM-Iz$R6q8692M_R;i$VB3 zLy*1?o0ycw50NF3|EBBpZykeSLsCH^29o+#Om7@8a@WduW|&?+J%l`ya_mP~MY z!6Wp_1R{zsB(RA>*lYSbz0TcVGBNtammJ| zq>0ce#H5}uFhF-Ojv;WtM?ev!qv#mW*aR@LI2)`4Zowp!8bIFfKoKI5l%_PK4q%Kd zEEtLi5&3%g`TFt&NWTmy8xCwqjajT@0ZV`hy!n_nU*IkG^2G%{xDV!udEpplQMRzb zZBQ#&<^S=yo`(E^g+zjflMGAIX3JK8qsI`*{j2_^jf= z6PrK!UiAJJ!oO}Qudd z_Lr)XZa@SyX$#-n*n$1A$%mg}_zveWB(9KzrkPMfc} zw62ZLftGz8?5n~8(sw71$hE3`8Q{C-w#~JjZByshEfQvJnA0#Swr=Uj zi~N$3;-q83l9q3a-Fyl|BJ(6I1yL2flTIMBdim4Q{3Y|Q-|Zd;|JW5f<7g&tDcySd z)u`UGgp(Z}TV{jrg^e+M0mI`VF5ENBv@eh=P*#LENbbI2Z9|#%veL2YY5lXvOV$Ox zHylSq?h6_#m@Z4+xX$^BRaKTK#J$urXV>dbWR5Los;c0xQjBAVI|cobEB&&m;ebdV zm;LLxt**#iZJcaOOIs^b?(hF%_{@XyHB%Zpn%Z3-bb2<}OJ@zEu=Ck+nv%6P_>rK;E zRmRW7(tpU1h*pyt;Ye)y`YJnE`$3lNlM%?C8LSy?JHjT^H$;$?7~|ac7;! zUhBYR&4(5ud#!)!vx=-*TEZMCdVHa?Hb=MTYIc3S)!E8E)rIFNG5O}l+FRe3c+5Ec zY23CQLF4Y5az2-6YGXa&_Kkh5^S1Qqd>2pHf_+xaD~9c!Ybt+VdnEKsZ&mP~WYwPN(dhL@)b>RjtOnMl*vYE|tG dhq}yr)u@6>KAYxurHcEeM}$O+i-Q+u{R76+2nzrJ diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype__PublishJob.cfg b/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype__PublishJob.cfg deleted file mode 100644 index 864eeaf15a..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype__PublishJob.cfg +++ /dev/null @@ -1,71 +0,0 @@ -IconApp= E01__OpenPype.png -Name= OpenPype -rendererName= Once -Version= 1 -Version_Minor= 0 -Type=Execute -TYPEv9=Execute -ExecuteJobType=Once - - -################################# [Windows] [Linux] [Osx] ################################## - - -CommandLine=> - -CommandLine= - - -::win CommandLine= set "CUDA_VISIBLE_DEVICES=" -::lx CommandLine= setenv CUDA_VISIBLE_DEVICES -::osx CommandLine= setenv CUDA_VISIBLE_DEVICES - - -CommandLine= - - -CommandLine= - - -CommandLine= - - -CommandLine= "" --headless publish - --targets royalrender - --targets farm - - - -CommandLine= - - - - -################################## Render Settings ################################## - - - -################################## Submitter Settings ################################## -StartMultipleInstances= 0~0 -SceneFileExtension= *.json -AllowImageNameChange= 0 -AllowImageDirChange= 0 -SequenceDivide= 0~1 -PPSequenceCheck=0~0 -PPCreateSmallVideo=0~0 -PPCreateFullVideo=0~0 -AllowLocalRenderOut= 0~0 - - -################################## Client Settings ################################## - -IconApp=E01__OpenPype.png - -licenseFailLine= - -errorSearchLine= - -permanentErrorSearchLine = - -Frozen_MinCoreUsage=0.3 -Frozen_Minutes=30 diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype___global.inc b/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype___global.inc deleted file mode 100644 index ba38337340..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_config/E01__OpenPype___global.inc +++ /dev/null @@ -1,2 +0,0 @@ -IconApp= E01__OpenPype.png -Name= OpenPype diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_install_paths/OpenPype.cfg b/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_install_paths/OpenPype.cfg deleted file mode 100644 index 07f7547d29..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_install_paths/OpenPype.cfg +++ /dev/null @@ -1,12 +0,0 @@ -[Windows] -Executable= openpype_console.exe -Path= OS; \OpenPype\*\openpype_console.exe -Path= 32; \OpenPype\*\openpype_console.exe - -[Linux] -Executable= openpype_console -Path= OS; /opt/openpype/*/openpype_console - -[Mac] -Executable= openpype_console -Path= OS; /Applications/OpenPype*/Content/MacOS/openpype_console diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/OpenPypeEnvironment.cfg b/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/OpenPypeEnvironment.cfg deleted file mode 100644 index 70f0bc2e24..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/OpenPypeEnvironment.cfg +++ /dev/null @@ -1,11 +0,0 @@ -PrePostType= pre -CommandLine= - -CommandLine= rrPythonconsole" > "render_apps/_prepost_scripts/PreOpenPypeInjectEnvironments.py" - -CommandLine= - - -CommandLine= "" -CommandLine= - diff --git a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/PreOpenPypeInjectEnvironments.py b/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/PreOpenPypeInjectEnvironments.py deleted file mode 100644 index 891de9594c..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/rr_root/render_apps/_prepost_scripts/PreOpenPypeInjectEnvironments.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -os.environ["OPENYPYPE_TESTVAR"] = "OpenPype was here" diff --git a/server_addon/royalrender/client/ayon_royalrender/version.py b/server_addon/royalrender/client/ayon_royalrender/version.py deleted file mode 100644 index d1eafe6568..0000000000 --- a/server_addon/royalrender/client/ayon_royalrender/version.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- -"""Package declaring AYON addon 'royalrender' version.""" -__version__ = "0.2.1" diff --git a/server_addon/royalrender/package.py b/server_addon/royalrender/package.py deleted file mode 100644 index 781445c4ad..0000000000 --- a/server_addon/royalrender/package.py +++ /dev/null @@ -1,10 +0,0 @@ -name = "royalrender" -title = "Royal Render" -version = "0.2.1" - -client_dir = "ayon_royalrender" - -ayon_required_addons = { - "core": ">0.3.2", -} -ayon_compatible_addons = {} diff --git a/server_addon/royalrender/server/__init__.py b/server_addon/royalrender/server/__init__.py deleted file mode 100644 index 5b10678136..0000000000 --- a/server_addon/royalrender/server/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Type - -from ayon_server.addons import BaseServerAddon - -from .settings import RoyalRenderSettings, DEFAULT_VALUES - - -class RoyalRenderAddon(BaseServerAddon): - settings_model: Type[RoyalRenderSettings] = RoyalRenderSettings - - async def get_default_settings(self): - settings_model_cls = self.get_settings_model() - return settings_model_cls(**DEFAULT_VALUES) diff --git a/server_addon/royalrender/server/settings.py b/server_addon/royalrender/server/settings.py deleted file mode 100644 index 6e077feb3e..0000000000 --- a/server_addon/royalrender/server/settings.py +++ /dev/null @@ -1,75 +0,0 @@ -from ayon_server.settings import ( - BaseSettingsModel, - SettingsField, - MultiplatformPathModel, -) - - -class CustomPath(MultiplatformPathModel): - _layout = "expanded" - - -class ServerListSubmodel(BaseSettingsModel): - _layout = "expanded" - name: str = SettingsField("", title="Name") - value: CustomPath = SettingsField( - default_factory=CustomPath - ) - - -class CollectSequencesFromJobModel(BaseSettingsModel): - review: bool = SettingsField( - True, title="Generate reviews from sequences" - ) - - -class PublishPluginsModel(BaseSettingsModel): - CollectSequencesFromJob: CollectSequencesFromJobModel = SettingsField( - default_factory=CollectSequencesFromJobModel, - title="Collect Sequences from the Job" - ) - - -class RoyalRenderSettings(BaseSettingsModel): - enabled: bool = True - # WARNING/TODO this needs change - # - both system and project settings contained 'rr_path' - # where project settings did choose one of rr_path from system settings - # that is not possible in AYON - rr_paths: list[ServerListSubmodel] = SettingsField( - default_factory=list, - title="Royal Render Root Paths", - scope=["studio"], - ) - # This was 'rr_paths' in project settings and should be enum of - # 'rr_paths' from system settings, but that's not possible in AYON - selected_rr_paths: list[str] = SettingsField( - default_factory=list, - title="Selected Royal Render Paths", - section="---", - ) - publish: PublishPluginsModel = SettingsField( - default_factory=PublishPluginsModel, - title="Publish plugins", - ) - - -DEFAULT_VALUES = { - "enabled": False, - "rr_paths": [ - { - "name": "default", - "value": { - "windows": "", - "darwin": "", - "linux": "" - } - } - ], - "selected_rr_paths": ["default"], - "publish": { - "CollectSequencesFromJob": { - "review": True - } - } -}