removed royalrender addon

This commit is contained in:
Jakub Trllo 2024-06-27 11:32:25 +02:00
parent b29baa31d2
commit 63083c7652
23 changed files with 0 additions and 2029 deletions

View file

@ -1,9 +0,0 @@
from .version import __version__
from .addon import RoyalRenderAddon
__all__ = (
"__version__",
"RoyalRenderAddon",
)

View file

@ -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")]
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<index>(?P<padding>0*)\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)))

View file

@ -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)

View file

@ -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

View file

@ -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="<SceneFolder>",
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

View file

@ -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")]

View file

@ -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):
# <rrEnvList>VariableA=ValueA~~~VariableB=ValueB</rrEnvList>
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 <CustomFoo> or
# <CustomSomeOtherAttr>
# 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: <RR_Job_File syntax_version="6.0">
job_file = root.createElement('RR_Job_File')
job_file.setAttribute("syntax_version", self.syntax_version)
# handle Submitter Parameters for batch
# <SubmitterParameter>foo=bar~baz~goo</SubmitterParameter>
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")

View file

@ -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).

View file

@ -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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

View file

@ -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=<envFileExecute <rrEnvFile>>
CommandLine=<rrEnvLine>
::win CommandLine= set "CUDA_VISIBLE_DEVICES=<GpuListC>"
::lx CommandLine= setenv CUDA_VISIBLE_DEVICES <GpuListC>
::osx CommandLine= setenv CUDA_VISIBLE_DEVICES <GpuListC>
CommandLine=
<SetEnvGlobal>
CommandLine=
<SetEnvSoft>
CommandLine=
<ResetExitCode>
CommandLine= "<Exe>" --headless publish <Scene>
--targets royalrender
--targets farm
<AdditionalCommandlineParam>
<CustomFlags>
CommandLine=
<CheckExitCode>
################################## 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

View file

@ -1,2 +0,0 @@
IconApp= E01__OpenPype.png
Name= OpenPype

View file

@ -1,12 +0,0 @@
[Windows]
Executable= openpype_console.exe
Path= OS; <ProgramFiles(x86)>\OpenPype\*\openpype_console.exe
Path= 32; <ProgramFiles(x86)>\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

View file

@ -1,11 +0,0 @@
PrePostType= pre
CommandLine=
<ResetExitCode>
CommandLine= <OsxApp "<rrBin64>rrPythonconsole" > "<RR_DIR>render_apps/_prepost_scripts/PreOpenPypeInjectEnvironments.py"
CommandLine=
<CheckExitCode> <FN>
CommandLine= "<RenderAppPath:OpenPype>"
CommandLine=
<CheckExitCode> <FN>

View file

@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
import os
os.environ["OPENYPYPE_TESTVAR"] = "OpenPype was here"

View file

@ -1,3 +0,0 @@
# -*- coding: utf-8 -*-
"""Package declaring AYON addon 'royalrender' version."""
__version__ = "0.2.1"

View file

@ -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 = {}

View file

@ -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)

View file

@ -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
}
}
}