mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge pull request #4362 from ynput/feature/OP-4617_Global-Update-rendered-frames-in-latest-version
Nuke: update rendered frames in latest version
This commit is contained in:
commit
816a2c42da
5 changed files with 175 additions and 19 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
import pyblish.api
|
||||
from openpype.hosts.aftereffects.api import list_instances
|
||||
from openpype.hosts.aftereffects.api import AfterEffectsHost
|
||||
|
||||
|
||||
class PreCollectRender(pyblish.api.ContextPlugin):
|
||||
|
|
@ -25,7 +25,7 @@ class PreCollectRender(pyblish.api.ContextPlugin):
|
|||
self.log.debug("Not applicable for New Publisher, skip")
|
||||
return
|
||||
|
||||
for inst in list_instances():
|
||||
for inst in AfterEffectsHost().list_instances():
|
||||
if inst.get("creator_attributes"):
|
||||
raise ValueError("Instance created in New publisher, "
|
||||
"cannot be published in Pyblish.\n"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import os
|
||||
import shutil
|
||||
|
||||
import pyblish.api
|
||||
import clique
|
||||
import nuke
|
||||
|
||||
from openpype.pipeline import publish
|
||||
from openpype.lib import collect_frames
|
||||
|
||||
|
||||
class NukeRenderLocal(publish.ExtractorColormanaged):
|
||||
|
|
@ -13,6 +15,8 @@ class NukeRenderLocal(publish.ExtractorColormanaged):
|
|||
Extract the result of savers by starting a comp render
|
||||
This will run the local render of Fusion.
|
||||
|
||||
Allows to use last published frames and overwrite only specific ones
|
||||
(set in instance.data.get("frames_to_fix"))
|
||||
"""
|
||||
|
||||
order = pyblish.api.ExtractorOrder
|
||||
|
|
@ -21,7 +25,6 @@ class NukeRenderLocal(publish.ExtractorColormanaged):
|
|||
families = ["render.local", "prerender.local", "still.local"]
|
||||
|
||||
def process(self, instance):
|
||||
families = instance.data["families"]
|
||||
child_nodes = (
|
||||
instance.data.get("transientData", {}).get("childNodes")
|
||||
or instance
|
||||
|
|
@ -32,17 +35,16 @@ class NukeRenderLocal(publish.ExtractorColormanaged):
|
|||
if x.Class() == "Write":
|
||||
node = x
|
||||
|
||||
self.log.debug("instance collected: {}".format(instance.data))
|
||||
|
||||
node_subset_name = instance.data.get("name", None)
|
||||
|
||||
first_frame = instance.data.get("frameStartHandle", None)
|
||||
|
||||
last_frame = instance.data.get("frameEndHandle", None)
|
||||
node_subset_name = instance.data["subset"]
|
||||
|
||||
self.log.info("Starting render")
|
||||
self.log.info("Start frame: {}".format(first_frame))
|
||||
self.log.info("End frame: {}".format(last_frame))
|
||||
|
||||
filenames = []
|
||||
node_file = node["file"]
|
||||
# Collecte expected filepaths for each frame
|
||||
# Collect expected filepaths for each frame
|
||||
# - for cases that output is still image is first created set of
|
||||
# paths which is then sorted and converted to list
|
||||
expected_paths = list(sorted({
|
||||
|
|
@ -50,22 +52,37 @@ class NukeRenderLocal(publish.ExtractorColormanaged):
|
|||
for frame in range(first_frame, last_frame + 1)
|
||||
}))
|
||||
# Extract only filenames for representation
|
||||
filenames = [
|
||||
filenames.extend([
|
||||
os.path.basename(filepath)
|
||||
for filepath in expected_paths
|
||||
]
|
||||
])
|
||||
|
||||
# Ensure output directory exists.
|
||||
out_dir = os.path.dirname(expected_paths[0])
|
||||
if not os.path.exists(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
|
||||
# Render frames
|
||||
nuke.execute(
|
||||
str(node_subset_name),
|
||||
int(first_frame),
|
||||
int(last_frame)
|
||||
)
|
||||
frames_to_render = [(first_frame, last_frame)]
|
||||
|
||||
frames_to_fix = instance.data.get("frames_to_fix")
|
||||
if instance.data.get("last_version_published_files") and frames_to_fix:
|
||||
frames_to_render = self._get_frames_to_render(frames_to_fix)
|
||||
anatomy = instance.context.data["anatomy"]
|
||||
self._copy_last_published(anatomy, instance, out_dir,
|
||||
filenames)
|
||||
|
||||
for render_first_frame, render_last_frame in frames_to_render:
|
||||
|
||||
self.log.info("Starting render")
|
||||
self.log.info("Start frame: {}".format(render_first_frame))
|
||||
self.log.info("End frame: {}".format(render_last_frame))
|
||||
|
||||
# Render frames
|
||||
nuke.execute(
|
||||
str(node_subset_name),
|
||||
int(render_first_frame),
|
||||
int(render_last_frame)
|
||||
)
|
||||
|
||||
ext = node["file_type"].value()
|
||||
colorspace = node["colorspace"].value()
|
||||
|
|
@ -106,6 +123,7 @@ class NukeRenderLocal(publish.ExtractorColormanaged):
|
|||
out_dir
|
||||
))
|
||||
|
||||
families = instance.data["families"]
|
||||
# redefinition of families
|
||||
if "render.local" in families:
|
||||
instance.data['family'] = 'render'
|
||||
|
|
@ -133,3 +151,58 @@ class NukeRenderLocal(publish.ExtractorColormanaged):
|
|||
self.log.info('Finished render')
|
||||
|
||||
self.log.debug("_ instance.data: {}".format(instance.data))
|
||||
|
||||
def _copy_last_published(self, anatomy, instance, out_dir,
|
||||
expected_filenames):
|
||||
"""Copies last published files to temporary out_dir.
|
||||
|
||||
These are base of files which will be extended/fixed for specific
|
||||
frames.
|
||||
Renames published file to expected file name based on frame, eg.
|
||||
test_project_test_asset_subset_v005.1001.exr > new_render.1001.exr
|
||||
"""
|
||||
last_published = instance.data["last_version_published_files"]
|
||||
last_published_and_frames = collect_frames(last_published)
|
||||
|
||||
expected_and_frames = collect_frames(expected_filenames)
|
||||
frames_and_expected = {v: k for k, v in expected_and_frames.items()}
|
||||
for file_path, frame in last_published_and_frames.items():
|
||||
file_path = anatomy.fill_root(file_path)
|
||||
if not os.path.exists(file_path):
|
||||
continue
|
||||
target_file_name = frames_and_expected.get(frame)
|
||||
if not target_file_name:
|
||||
continue
|
||||
|
||||
out_path = os.path.join(out_dir, target_file_name)
|
||||
self.log.debug("Copying '{}' -> '{}'".format(file_path, out_path))
|
||||
shutil.copy(file_path, out_path)
|
||||
|
||||
# TODO shouldn't this be uncommented
|
||||
# instance.context.data["cleanupFullPaths"].append(out_path)
|
||||
|
||||
def _get_frames_to_render(self, frames_to_fix):
|
||||
"""Return list of frame range tuples to render
|
||||
|
||||
Args:
|
||||
frames_to_fix (str): specific or range of frames to be rerendered
|
||||
(1005, 1009-1010)
|
||||
Returns:
|
||||
(list): [(1005, 1005), (1009-1010)]
|
||||
"""
|
||||
frames_to_render = []
|
||||
|
||||
for frame_range in frames_to_fix.split(","):
|
||||
if frame_range.isdigit():
|
||||
render_first_frame = frame_range
|
||||
render_last_frame = frame_range
|
||||
elif '-' in frame_range:
|
||||
frames = frame_range.split('-')
|
||||
render_first_frame = int(frames[0])
|
||||
render_last_frame = int(frames[1])
|
||||
else:
|
||||
raise ValueError("Wrong format of frames to fix {}"
|
||||
.format(frames_to_fix))
|
||||
frames_to_render.append((render_first_frame,
|
||||
render_last_frame))
|
||||
return frames_to_render
|
||||
|
|
|
|||
|
|
@ -189,6 +189,6 @@ class FileTransaction(object):
|
|||
def _same_paths(self, src, dst):
|
||||
# handles same paths but with C:/project vs c:/project
|
||||
if os.path.exists(src) and os.path.exists(dst):
|
||||
return os.path.samefile(src, dst)
|
||||
return os.stat(src) == os.stat(dst)
|
||||
|
||||
return src == dst
|
||||
|
|
|
|||
80
openpype/plugins/publish/collect_frames_fix.py
Normal file
80
openpype/plugins/publish/collect_frames_fix.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import pyblish.api
|
||||
from openpype.lib.attribute_definitions import (
|
||||
TextDef,
|
||||
BoolDef
|
||||
)
|
||||
|
||||
from openpype.pipeline.publish import OpenPypePyblishPluginMixin
|
||||
from openpype.client.entities import (
|
||||
get_last_version_by_subset_name,
|
||||
get_representations
|
||||
)
|
||||
|
||||
|
||||
class CollectFramesFixDef(
|
||||
pyblish.api.InstancePlugin,
|
||||
OpenPypePyblishPluginMixin
|
||||
):
|
||||
"""Provides text field to insert frame(s) to be rerendered.
|
||||
|
||||
Published files of last version of an instance subset are collected into
|
||||
instance.data["last_version_published_files"]. All these but frames
|
||||
mentioned in text field will be reused for new version.
|
||||
"""
|
||||
order = pyblish.api.CollectorOrder + 0.495
|
||||
label = "Collect Frames to Fix"
|
||||
targets = ["local"]
|
||||
hosts = ["nuke"]
|
||||
families = ["render", "prerender"]
|
||||
enabled = True
|
||||
|
||||
def process(self, instance):
|
||||
attribute_values = self.get_attr_values_from_data(instance.data)
|
||||
frames_to_fix = attribute_values.get("frames_to_fix")
|
||||
rewrite_version = attribute_values.get("rewrite_version")
|
||||
|
||||
if frames_to_fix:
|
||||
instance.data["frames_to_fix"] = frames_to_fix
|
||||
|
||||
subset_name = instance.data["subset"]
|
||||
asset_name = instance.data["asset"]
|
||||
|
||||
project_entity = instance.data["projectEntity"]
|
||||
project_name = project_entity["name"]
|
||||
|
||||
version = get_last_version_by_subset_name(project_name,
|
||||
subset_name,
|
||||
asset_name=asset_name)
|
||||
if not version:
|
||||
self.log.warning("No last version found, "
|
||||
"re-render not possible")
|
||||
return
|
||||
|
||||
representations = get_representations(project_name,
|
||||
version_ids=[version["_id"]])
|
||||
published_files = []
|
||||
for repre in representations:
|
||||
if repre["context"]["family"] not in self.families:
|
||||
continue
|
||||
|
||||
for file_info in repre.get("files"):
|
||||
published_files.append(file_info["path"])
|
||||
|
||||
instance.data["last_version_published_files"] = published_files
|
||||
self.log.debug("last_version_published_files::{}".format(
|
||||
instance.data["last_version_published_files"]))
|
||||
|
||||
if rewrite_version:
|
||||
instance.data["version"] = version["name"]
|
||||
# limits triggering version validator
|
||||
instance.data.pop("latestVersion")
|
||||
|
||||
@classmethod
|
||||
def get_attribute_defs(cls):
|
||||
return [
|
||||
TextDef("frames_to_fix", label="Frames to fix",
|
||||
placeholder="5,10-15",
|
||||
regex="[0-9,-]+"),
|
||||
BoolDef("rewrite_version", label="Rewrite latest version",
|
||||
default=False),
|
||||
]
|
||||
|
|
@ -534,6 +534,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
|
|||
template_data["representation"] = repre["name"]
|
||||
template_data["ext"] = repre["ext"]
|
||||
|
||||
# allow overwriting existing version
|
||||
template_data["version"] = version["name"]
|
||||
|
||||
# add template data for colorspaceData
|
||||
if repre.get("colorspaceData"):
|
||||
colorspace = repre["colorspaceData"]["colorspace"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue