Merge pull request #5941 from ynput/enhancement/OP-6659_nuke-explicit-thumbnail-workflow

Nuke: Explicit Thumbnail workflow
This commit is contained in:
Jakub Ježek 2023-12-07 16:24:06 +01:00 committed by GitHub
commit e64f030a08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 231 additions and 529 deletions

View file

@ -21,6 +21,11 @@ from openpype.pipeline import (
CreatedInstance,
get_current_task_name
)
from openpype.pipeline.colorspace import (
get_display_view_colorspace_name,
get_colorspace_settings_from_publish_context,
set_colorspace_data_to_representation
)
from openpype.lib.transcoding import (
VIDEO_EXTENSIONS
)
@ -612,7 +617,7 @@ class ExporterReview(object):
def get_representation_data(
self, tags=None, range=False,
custom_tags=None
custom_tags=None, colorspace=None
):
""" Add representation data to self.data
@ -652,6 +657,14 @@ class ExporterReview(object):
if self.publish_on_farm:
repre["tags"].append("publish_on_farm")
# add colorspace data to representation
if colorspace:
set_colorspace_data_to_representation(
repre,
self.instance.context.data,
colorspace=colorspace,
log=self.log
)
self.data["representations"].append(repre)
def get_imageio_baking_profile(self):
@ -866,6 +879,13 @@ class ExporterReviewMov(ExporterReview):
return path
def generate_mov(self, farm=False, **kwargs):
# colorspace data
colorspace = None
# get colorspace settings
# get colorspace data from context
config_data, _ = get_colorspace_settings_from_publish_context(
self.instance.context.data)
add_tags = []
self.publish_on_farm = farm
read_raw = kwargs["read_raw"]
@ -951,6 +971,14 @@ class ExporterReviewMov(ExporterReview):
# assign viewer
dag_node["view"].setValue(viewer)
if config_data:
# convert display and view to colorspace
colorspace = get_display_view_colorspace_name(
config_path=config_data["path"],
display=display,
view=viewer
)
self._connect_to_above_nodes(dag_node, subset, "OCIODisplay... `{}`")
# Write node
write_node = nuke.createNode("Write")
@ -996,9 +1024,10 @@ class ExporterReviewMov(ExporterReview):
# ---------- generate representation data
self.get_representation_data(
tags=["review", "delete"] + add_tags,
tags=["review", "need_thumbnail", "delete"] + add_tags,
custom_tags=add_custom_tags,
range=True
range=True,
colorspace=colorspace
)
self.log.debug("Representation... `{}`".format(self.data))

View file

@ -276,7 +276,7 @@ class ExtractSlateFrame(publish.Extractor):
if not matching_repre:
self.log.info(
"Matching reresentation was not found."
"Matching representation was not found."
" Representation files were not filled with slate."
)
return
@ -294,7 +294,7 @@ class ExtractSlateFrame(publish.Extractor):
self.log.debug(
"__ matching_repre: {}".format(pformat(matching_repre)))
self.log.warning("Added slate frame to representation files")
self.log.info("Added slate frame to representation files")
def add_comment_slate_node(self, instance, node):

View file

@ -1,216 +0,0 @@
import sys
import os
import nuke
import pyblish.api
from openpype.pipeline import publish
from openpype.hosts.nuke import api as napi
from openpype.hosts.nuke.api.lib import set_node_knobs_from_settings
# Python 2/3 compatibility
if sys.version_info[0] >= 3:
unicode = str
class ExtractThumbnail(publish.Extractor):
"""Extracts movie and thumbnail with baked in luts
must be run after extract_render_local.py
"""
order = pyblish.api.ExtractorOrder + 0.011
label = "Extract Thumbnail"
families = ["review"]
hosts = ["nuke"]
# settings
use_rendered = False
bake_viewer_process = True
bake_viewer_input_process = True
nodes = {}
reposition_nodes = None
def process(self, instance):
if instance.data.get("farm"):
return
with napi.maintained_selection():
self.log.debug("instance: {}".format(instance))
self.log.debug("instance.data[families]: {}".format(
instance.data["families"]))
if instance.data.get("bakePresets"):
for o_name, o_data in instance.data["bakePresets"].items():
self.render_thumbnail(instance, o_name, **o_data)
else:
viewer_process_switches = {
"bake_viewer_process": True,
"bake_viewer_input_process": True
}
self.render_thumbnail(
instance, None, **viewer_process_switches)
def render_thumbnail(self, instance, output_name=None, **kwargs):
first_frame = instance.data["frameStartHandle"]
last_frame = instance.data["frameEndHandle"]
colorspace = instance.data["colorspace"]
# find frame range and define middle thumb frame
mid_frame = int((last_frame - first_frame) / 2)
# solve output name if any is set
output_name = output_name or ""
bake_viewer_process = kwargs["bake_viewer_process"]
bake_viewer_input_process_node = kwargs[
"bake_viewer_input_process"]
node = instance.data["transientData"]["node"] # group node
self.log.debug("Creating staging dir...")
if "representations" not in instance.data:
instance.data["representations"] = []
staging_dir = os.path.normpath(
os.path.dirname(instance.data['path']))
instance.data["stagingDir"] = staging_dir
self.log.debug(
"StagingDir `{0}`...".format(instance.data["stagingDir"]))
temporary_nodes = []
# try to connect already rendered images
previous_node = node
collection = instance.data.get("collection", None)
self.log.debug("__ collection: `{}`".format(collection))
if collection:
# get path
fhead = collection.format("{head}")
thumb_fname = list(collection)[mid_frame]
else:
fname = thumb_fname = os.path.basename(
instance.data.get("path", None))
fhead = os.path.splitext(fname)[0] + "."
self.log.debug("__ fhead: `{}`".format(fhead))
if "#" in fhead:
fhead = fhead.replace("#", "")[:-1]
path_render = os.path.join(
staging_dir, thumb_fname).replace("\\", "/")
self.log.debug("__ path_render: `{}`".format(path_render))
if self.use_rendered and os.path.isfile(path_render):
# check if file exist otherwise connect to write node
rnode = nuke.createNode("Read")
rnode["file"].setValue(path_render)
rnode["colorspace"].setValue(colorspace)
# turn it raw if none of baking is ON
if all([
not self.bake_viewer_input_process,
not self.bake_viewer_process
]):
rnode["raw"].setValue(True)
temporary_nodes.append(rnode)
previous_node = rnode
if self.reposition_nodes is None:
# [deprecated] create reformat node old way
reformat_node = nuke.createNode("Reformat")
ref_node = self.nodes.get("Reformat", None)
if ref_node:
for k, v in ref_node:
self.log.debug("k, v: {0}:{1}".format(k, v))
if isinstance(v, unicode):
v = str(v)
reformat_node[k].setValue(v)
reformat_node.setInput(0, previous_node)
previous_node = reformat_node
temporary_nodes.append(reformat_node)
else:
# create reformat node new way
for repo_node in self.reposition_nodes:
node_class = repo_node["node_class"]
knobs = repo_node["knobs"]
node = nuke.createNode(node_class)
set_node_knobs_from_settings(node, knobs)
# connect in order
node.setInput(0, previous_node)
previous_node = node
temporary_nodes.append(node)
# only create colorspace baking if toggled on
if bake_viewer_process:
if bake_viewer_input_process_node:
# get input process and connect it to baking
ipn = napi.get_view_process_node()
if ipn is not None:
ipn.setInput(0, previous_node)
previous_node = ipn
temporary_nodes.append(ipn)
dag_node = nuke.createNode("OCIODisplay")
dag_node.setInput(0, previous_node)
previous_node = dag_node
temporary_nodes.append(dag_node)
thumb_name = "thumbnail"
# only add output name and
# if there are more than one bake preset
if (
output_name
and len(instance.data.get("bakePresets", {}).keys()) > 1
):
thumb_name = "{}_{}".format(output_name, thumb_name)
# create write node
write_node = nuke.createNode("Write")
file = fhead[:-1] + thumb_name + ".jpg"
thumb_path = os.path.join(staging_dir, file).replace("\\", "/")
# add thumbnail to cleanup
instance.context.data["cleanupFullPaths"].append(thumb_path)
# make sure only one thumbnail path is set
# and it is existing file
instance_thumb_path = instance.data.get("thumbnailPath")
if not instance_thumb_path or not os.path.isfile(instance_thumb_path):
instance.data["thumbnailPath"] = thumb_path
write_node["file"].setValue(thumb_path)
write_node["file_type"].setValue("jpg")
write_node["raw"].setValue(1)
write_node.setInput(0, previous_node)
temporary_nodes.append(write_node)
repre = {
'name': thumb_name,
'ext': "jpg",
"outputName": thumb_name,
'files': file,
"stagingDir": staging_dir,
"tags": ["thumbnail", "publish_on_farm", "delete"]
}
instance.data["representations"].append(repre)
# Render frames
nuke.execute(write_node.name(), mid_frame, mid_frame)
self.log.debug(
"representations: {}".format(instance.data["representations"]))
# Clean up
for node in temporary_nodes:
nuke.delete(node)

View file

@ -1103,7 +1103,6 @@ def convert_colorspace(
target_colorspace=None,
view=None,
display=None,
additional_input_args=None,
additional_command_args=None,
logger=None,
):
@ -1125,7 +1124,6 @@ def convert_colorspace(
both 'view' and 'display' must be filled (if 'target_colorspace')
display (str): name for display-referred reference space (ocio valid)
both 'view' and 'display' must be filled (if 'target_colorspace')
additional_input_args (list): arguments for input file
additional_command_args (list): arguments for oiiotool (like binary
depth for .dpx)
logger (logging.Logger): Logger used for logging.
@ -1140,9 +1138,6 @@ def convert_colorspace(
# Collect channels to export
input_arg, channels_arg = get_oiio_input_and_channel_args(input_info)
if additional_input_args:
input_arg = "{} {}".format(input_arg, " ".join(additional_input_args))
# Prepare subprocess arguments
oiio_cmd = get_oiio_tool_args(
"oiiotool",

View file

@ -370,10 +370,6 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
environment = dict({key: os.environ[key] for key in keys
if key in os.environ}, **legacy_io.Session)
for _path in os.environ:
if _path.lower().startswith('openpype_'):
environment[_path] = os.environ[_path]
# to recognize render jobs
if AYON_SERVER_ENABLED:
environment["AYON_BUNDLE_NAME"] = os.environ["AYON_BUNDLE_NAME"]
@ -402,7 +398,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
self.log.debug("Submitting..")
self.log.debug(json.dumps(payload, indent=4, sort_keys=True))
# adding expectied files to instance.data
# adding expected files to instance.data
self.expected_files(
instance,
render_path,
@ -458,7 +454,7 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
def expected_files(
self,
instance,
path,
filepath,
start_frame,
end_frame
):
@ -467,21 +463,44 @@ class NukeSubmitDeadline(pyblish.api.InstancePlugin,
if not instance.data.get("expectedFiles"):
instance.data["expectedFiles"] = []
dirname = os.path.dirname(path)
file = os.path.basename(path)
dirname = os.path.dirname(filepath)
file = os.path.basename(filepath)
# since some files might be already tagged as publish_on_farm
# we need to avoid adding them to expected files since those would be
# duplicated into metadata.json file
representations = instance.data.get("representations", [])
# check if file is not in representations with publish_on_farm tag
for repre in representations:
# Skip if 'publish_on_farm' not available
if "publish_on_farm" not in repre.get("tags", []):
continue
# in case where single file (video, image) is already in
# representation file. Will be added to expected files via
# submit_publish_job.py
if file in repre.get("files", []):
self.log.debug(
"Skipping expected file: {}".format(filepath))
return
# in case path is hashed sequence expression
# (e.g. /path/to/file.####.png)
if "#" in file:
pparts = file.split("#")
padding = "%0{}d".format(len(pparts) - 1)
file = pparts[0] + padding + pparts[-1]
# in case input path was single file (video or image)
if "%" not in file:
instance.data["expectedFiles"].append(path)
instance.data["expectedFiles"].append(filepath)
return
# shift start frame by 1 if slate is present
if instance.data.get("slate"):
start_frame -= 1
# add sequence files to expected files
for i in range(start_frame, (end_frame + 1)):
instance.data["expectedFiles"].append(
os.path.join(dirname, (file % i)).replace("\\", "/"))

View file

@ -127,17 +127,25 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
other_representations = []
has_movie_review = False
for repre in instance_repres:
self.log.debug("Representation {}".format(repre))
repre_tags = repre.get("tags") or []
# exclude representations with are going to be published on farm
if "publish_on_farm" in repre_tags:
continue
self.log.debug("Representation {}".format(repre))
# include only thumbnail representations
if repre.get("thumbnail") or "thumbnail" in repre_tags:
thumbnail_representations.append(repre)
# include only review representations
elif "ftrackreview" in repre_tags:
review_representations.append(repre)
if self._is_repre_video(repre):
has_movie_review = True
else:
# include all other representations
other_representations.append(repre)
# Prepare ftrack locations

View file

@ -145,6 +145,9 @@ def get_transferable_representations(instance):
trans_rep = representation.copy()
# remove publish_on_farm from representations tags
trans_rep["tags"].remove("publish_on_farm")
staging_dir = trans_rep.get("stagingDir")
if staging_dir:

View file

@ -89,8 +89,8 @@ class ExtractBurnin(publish.Extractor):
self.main_process(instance)
# Remove any representations tagged for deletion.
# QUESTION Is possible to have representation with "delete" tag?
# Remove only representation tagged with both
# tags `delete` and `burnin`
for repre in tuple(instance.data["representations"]):
if all(x in repre.get("tags", []) for x in ['delete', 'burnin']):
self.log.debug("Removing representation: {}".format(repre))

View file

@ -89,8 +89,18 @@ class ExtractReview(pyblish.api.InstancePlugin):
# Make sure cleanup happens and pop representations with "delete" tag.
for repre in tuple(instance.data["representations"]):
tags = repre.get("tags") or []
if "delete" in tags and "thumbnail" not in tags:
instance.data["representations"].remove(repre)
# Representation is not marked to be deleted
if "delete" not in tags:
continue
# The representation can be used as thumbnail source
if "thumbnail" in tags or "need_thumbnail" in tags:
continue
self.log.debug(
"Removing representation: {}".format(repre)
)
instance.data["representations"].remove(repre)
def _get_outputs_for_instance(self, instance):
host_name = instance.context.data["hostName"]
@ -321,19 +331,26 @@ class ExtractReview(pyblish.api.InstancePlugin):
# Create copy of representation
new_repre = copy.deepcopy(repre)
new_tags = new_repre.get("tags") or []
# Make sure new representation has origin staging dir
# - this is because source representation may change
# it's staging dir because of ffmpeg conversion
new_repre["stagingDir"] = src_repre_staging_dir
# Remove "delete" tag from new repre if there is
if "delete" in new_repre["tags"]:
new_repre["tags"].remove("delete")
if "delete" in new_tags:
new_tags.remove("delete")
if "need_thumbnail" in new_tags:
new_tags.remove("need_thumbnail")
# Add additional tags from output definition to representation
for tag in output_def["tags"]:
if tag not in new_repre["tags"]:
new_repre["tags"].append(tag)
if tag not in new_tags:
new_tags.append(tag)
# Return tags to new representation
new_repre["tags"] = new_tags
# Add burnin link from output definition to representation
for burnin in output_def["burnins"]:

View file

@ -376,9 +376,13 @@ class ExtractReviewSlate(publish.Extractor):
# Remove any representations tagged for deletion.
for repre in inst_data.get("representations", []):
if "delete" in repre.get("tags", []):
self.log.debug("Removing representation: {}".format(repre))
inst_data["representations"].remove(repre)
tags = repre.get("tags", [])
if "delete" not in tags:
continue
if "need_thumbnail" in tags:
continue
self.log.debug("Removing representation: {}".format(repre))
inst_data["representations"].remove(repre)
self.log.debug(inst_data["representations"])

View file

@ -28,7 +28,14 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
"imagesequence", "render", "render2d", "prerender",
"source", "clip", "take", "online", "image"
]
hosts = ["shell", "fusion", "resolve", "traypublisher", "substancepainter"]
hosts = [
"shell",
"fusion",
"resolve",
"traypublisher",
"substancepainter",
"nuke",
]
enabled = False
integrate_thumbnail = False
@ -44,6 +51,26 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
ffmpeg_args = None
def process(self, instance):
# run main process
self._main_process(instance)
# Make sure cleanup happens to representations which are having both
# tags `delete` and `need_thumbnail`
for repre in tuple(instance.data["representations"]):
tags = repre.get("tags") or []
# skip representations which are going to be published on farm
if "publish_on_farm" in tags:
continue
if (
"delete" in tags
and "need_thumbnail" in tags
):
self.log.debug(
"Removing representation: {}".format(repre)
)
instance.data["representations"].remove(repre)
def _main_process(self, instance):
subset_name = instance.data["subset"]
instance_repres = instance.data.get("representations")
if not instance_repres:
@ -76,7 +103,13 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
self.log.debug("Skipping crypto passes.")
return
filtered_repres = self._get_filtered_repres(instance)
# first check for any explicitly marked representations for thumbnail
explicit_repres = self._get_explicit_repres_for_thumbnail(instance)
if explicit_repres:
filtered_repres = explicit_repres
else:
filtered_repres = self._get_filtered_repres(instance)
if not filtered_repres:
self.log.info(
"Instance doesn't have representations that can be used "
@ -168,6 +201,24 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
if not thumbnail_created:
continue
if len(explicit_repres) > 1:
repre_name = "thumbnail_{}".format(repre["outputName"])
else:
repre_name = "thumbnail"
# add thumbnail path to instance data for integrator
instance_thumb_path = instance.data.get("thumbnailPath")
if (
not instance_thumb_path
or not os.path.isfile(instance_thumb_path)
):
self.log.debug(
"Adding thumbnail path to instance data: {}".format(
full_output_path
)
)
instance.data["thumbnailPath"] = full_output_path
new_repre_tags = ["thumbnail"]
# for workflows which needs to have thumbnails published as
# separate representations `delete` tag should not be added
@ -175,7 +226,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
new_repre_tags.append("delete")
new_repre = {
"name": "thumbnail",
"name": repre_name,
"ext": "jpg",
"files": jpeg_file,
"stagingDir": dst_staging,
@ -184,12 +235,21 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
}
# adding representation
self.log.debug(
"Adding thumbnail representation: {}".format(new_repre)
)
instance.data["representations"].append(new_repre)
# There is no need to create more then one thumbnail
break
if explicit_repres:
# this key will then align assetVersion ftrack thumbnail sync
new_repre["outputName"] = (
repre.get("outputName") or repre["name"])
self.log.debug(
"Adding explicit thumbnail representation: {}".format(
new_repre))
else:
self.log.debug(
"Adding thumbnail representation: {}".format(new_repre)
)
# There is no need to create more then one thumbnail
break
if not thumbnail_created:
self.log.warning("Thumbnail has not been created.")
@ -208,12 +268,42 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
return True
return False
def _get_explicit_repres_for_thumbnail(self, instance):
src_repres = instance.data.get("representations") or []
# This is mainly for Nuke where we have multiple representations for
# one instance and representations are tagged for thumbnail.
# First check if any of the representations have
# `need_thumbnail` in tags and add them to filtered_repres
need_thumb_repres = [
repre for repre in src_repres
if "need_thumbnail" in repre.get("tags", [])
if "publish_on_farm" not in repre.get("tags", [])
]
if not need_thumb_repres:
return []
self.log.info(
"Instance has representation with tag `need_thumbnail`. "
"Using only this representations for thumbnail creation. "
)
self.log.debug(
"Representations: {}".format(need_thumb_repres)
)
return need_thumb_repres
def _get_filtered_repres(self, instance):
filtered_repres = []
src_repres = instance.data.get("representations") or []
for repre in src_repres:
self.log.debug(repre)
tags = repre.get("tags") or []
if "publish_on_farm" in tags:
# only process representations with are going
# to be published locally
continue
valid = "review" in tags or "thumb-nuke" in tags
if not valid:
continue
@ -286,7 +376,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
display=repre_display or oiio_default_display,
view=repre_view or oiio_default_view,
target_colorspace=oiio_default_colorspace,
additional_input_args=resolution_arg,
additional_command_args=resolution_arg,
logger=self.log,
)
except Exception:

View file

@ -72,7 +72,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin):
)
def _prepare_instances(self, context):
context_thumbnail_path = context.get("thumbnailPath")
context_thumbnail_path = context.data.get("thumbnailPath")
valid_context_thumbnail = bool(
context_thumbnail_path
and os.path.exists(context_thumbnail_path)
@ -93,9 +93,12 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin):
# Find thumbnail path on instance
thumbnail_source = instance.data.get("thumbnailSource")
thumbnail_path = (thumbnail_source or
self._get_instance_thumbnail_path(
published_repres))
thumbnail_path = instance.data.get("thumbnailPath")
thumbnail_path = (
thumbnail_source
or thumbnail_path
or self._get_instance_thumbnail_path(published_repres)
)
if thumbnail_path:
self.log.debug((
"Found thumbnail path for instance \"{}\"."
@ -133,7 +136,7 @@ class IntegrateThumbnailsAYON(pyblish.api.ContextPlugin):
thumb_repre_doc = None
for repre_info in published_representations.values():
repre_doc = repre_info["representation"]
if repre_doc["name"].lower() == "thumbnail":
if "thumbnail" in repre_doc["name"].lower():
thumb_repre_doc = repre_doc
break

View file

@ -842,28 +842,6 @@ def _convert_nuke_project_settings(ayon_settings, output):
collect_instance_data.pop(
"sync_workfile_version_on_product_types"))
# TODO 'ExtractThumbnail' does not have ideal schema in v3
ayon_extract_thumbnail = ayon_publish["ExtractThumbnail"]
new_thumbnail_nodes = {}
for item in ayon_extract_thumbnail["nodes"]:
name = item["nodeclass"]
value = []
for knob in _convert_nuke_knobs(item["knobs"]):
knob_name = knob["name"]
# This may crash
if knob["type"] == "expression":
knob_value = knob["expression"]
else:
knob_value = knob["value"]
value.append([knob_name, knob_value])
new_thumbnail_nodes[name] = value
ayon_extract_thumbnail["nodes"] = new_thumbnail_nodes
if "reposition_nodes" in ayon_extract_thumbnail:
for item in ayon_extract_thumbnail["reposition_nodes"]:
item["knobs"] = _convert_nuke_knobs(item["knobs"])
# --- ImageIO ---
# NOTE 'monitorOutLut' is maybe not yet in v3 (ut should be)
_convert_host_imageio(ayon_nuke)

View file

@ -379,68 +379,6 @@
"optional": true,
"active": true
},
"ExtractThumbnail": {
"enabled": true,
"use_rendered": true,
"bake_viewer_process": true,
"bake_viewer_input_process": true,
"nodes": {
"Reformat": [
[
"type",
"to format"
],
[
"format",
"HD_1080"
],
[
"filter",
"Lanczos6"
],
[
"black_outside",
true
],
[
"pbb",
false
]
]
},
"reposition_nodes": [
{
"node_class": "Reformat",
"knobs": [
{
"type": "text",
"name": "type",
"value": "to format"
},
{
"type": "text",
"name": "format",
"value": "HD_1080"
},
{
"type": "text",
"name": "filter",
"value": "Lanczos6"
},
{
"type": "bool",
"name": "black_outside",
"value": true
},
{
"type": "bool",
"name": "pbb",
"value": false
}
]
}
]
},
"ExtractReviewData": {
"enabled": false
},

View file

@ -125,81 +125,6 @@
"type": "label",
"label": "Extractors"
},
{
"type": "dict",
"collapsible": true,
"checkbox_key": "enabled",
"key": "ExtractThumbnail",
"label": "ExtractThumbnail",
"is_group": true,
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "boolean",
"key": "use_rendered",
"label": "Use rendered images"
},
{
"type": "boolean",
"key": "bake_viewer_process",
"label": "Bake viewer process"
},
{
"type": "boolean",
"key": "bake_viewer_input_process",
"label": "Bake viewer input process"
},
{
"type": "collapsible-wrap",
"label": "Nodes",
"collapsible": true,
"children": [
{
"type": "label",
"label": "Nodes attribute will be deprecated in future releases. Use reposition_nodes instead."
},
{
"type": "raw-json",
"key": "nodes",
"label": "Nodes [depricated]"
},
{
"type": "label",
"label": "Reposition knobs supported only. You can add multiple reformat nodes <br/>and set their knobs. Order of reformat nodes is important. First reformat node <br/>will be applied first and last reformat node will be applied last."
},
{
"key": "reposition_nodes",
"type": "list",
"label": "Reposition nodes",
"object_type": {
"type": "dict",
"children": [
{
"key": "node_class",
"label": "Node class",
"type": "text"
},
{
"type": "schema_template",
"name": "template_nuke_knob_inputs",
"template_data": [
{
"label": "Node knobs",
"key": "knobs"
}
]
}
]
}
}
]
}
]
},
{
"type": "dict",
"collapsible": true,

View file

@ -51,17 +51,6 @@ class NodeModel(BaseSettingsModel):
return value
class ThumbnailRepositionNodeModel(BaseSettingsModel):
node_class: str = Field(title="Node class")
knobs: list[KnobModel] = Field(title="Knobs", default_factory=list)
@validator("knobs")
def ensure_unique_names(cls, value):
"""Ensure name fields within the lists have unique names."""
ensure_unique_names(value)
return value
class CollectInstanceDataModel(BaseSettingsModel):
sync_workfile_version_on_product_types: list[str] = Field(
default_factory=list,
@ -89,22 +78,6 @@ class ValidateKnobsModel(BaseSettingsModel):
return validate_json_dict(value)
class ExtractThumbnailModel(BaseSettingsModel):
enabled: bool = Field(title="Enabled")
use_rendered: bool = Field(title="Use rendered images")
bake_viewer_process: bool = Field(title="Bake view process")
bake_viewer_input_process: bool = Field(title="Bake viewer input process")
nodes: list[NodeModel] = Field(
default_factory=list,
title="Nodes (deprecated)"
)
reposition_nodes: list[ThumbnailRepositionNodeModel] = Field(
title="Reposition nodes",
default_factory=list
)
class ExtractReviewDataModel(BaseSettingsModel):
enabled: bool = Field(title="Enabled")
@ -153,16 +126,29 @@ class IntermediateOutputModel(BaseSettingsModel):
name: str = Field(title="Output name")
filter: BakingStreamFilterModel = Field(
title="Filter", default_factory=BakingStreamFilterModel)
read_raw: bool = Field(title="Read raw switch")
viewer_process_override: str = Field(title="Viewer process override")
bake_viewer_process: bool = Field(title="Bake viewer process")
read_raw: bool = Field(
False,
title="Read raw switch"
)
viewer_process_override: str = Field(
"",
title="Viewer process override"
)
bake_viewer_process: bool = Field(
True,
title="Bake viewer process"
)
bake_viewer_input_process: bool = Field(
True,
title="Bake viewer input process node (LUT)"
)
reformat_nodes_config: ReformatNodesConfigModel = Field(
default_factory=ReformatNodesConfigModel,
title="Reformat Nodes")
extension: str = Field(title="File extension")
extension: str = Field(
"mov",
title="File extension"
)
add_custom_tags: list[str] = Field(
title="Custom tags", default_factory=list)
@ -267,11 +253,6 @@ class PublishPuginsModel(BaseSettingsModel):
title="Validate workfile attributes",
default_factory=OptionalPluginModel
)
ExtractThumbnail: ExtractThumbnailModel = Field(
title="Extract Thumbnail",
default_factory=ExtractThumbnailModel,
section="Extractors"
)
ExtractReviewData: ExtractReviewDataModel = Field(
title="Extract Review Data",
default_factory=ExtractReviewDataModel
@ -350,78 +331,6 @@ DEFAULT_PUBLISH_PLUGIN_SETTINGS = {
"optional": True,
"active": True
},
"ExtractThumbnail": {
"enabled": True,
"use_rendered": True,
"bake_viewer_process": True,
"bake_viewer_input_process": True,
"nodes": [
{
"name": "Reformat01",
"nodeclass": "Reformat",
"dependency": "",
"knobs": [
{
"type": "text",
"name": "type",
"text": "to format"
},
{
"type": "text",
"name": "format",
"text": "HD_1080"
},
{
"type": "text",
"name": "filter",
"text": "Lanczos6"
},
{
"type": "boolean",
"name": "black_outside",
"boolean": True
},
{
"type": "boolean",
"name": "pbb",
"boolean": False
}
]
}
],
"reposition_nodes": [
{
"node_class": "Reformat",
"knobs": [
{
"type": "text",
"name": "type",
"text": "to format"
},
{
"type": "text",
"name": "format",
"text": "HD_1080"
},
{
"type": "text",
"name": "filter",
"text": "Lanczos6"
},
{
"type": "boolean",
"name": "black_outside",
"boolean": True
},
{
"type": "boolean",
"name": "pbb",
"boolean": False
}
]
}
]
},
"ExtractReviewData": {
"enabled": False
},

View file

@ -1 +1 @@
__version__ = "0.1.6"
__version__ = "0.1.7"