mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
189 lines
6.6 KiB
Python
189 lines
6.6 KiB
Python
import os
|
|
import nuke
|
|
import pyblish.api
|
|
import pype
|
|
|
|
|
|
class ExtractDataForReview(pype.api.Extractor):
|
|
"""Extracts movie and thumbnail with baked in luts
|
|
|
|
must be run after extract_render_local.py
|
|
|
|
"""
|
|
|
|
order = pyblish.api.ExtractorOrder + 0.01
|
|
label = "Data for review"
|
|
optional = True
|
|
|
|
families = ["write"]
|
|
hosts = ["nuke"]
|
|
family_targets = [".local", ".review"]
|
|
|
|
def process(self, instance):
|
|
|
|
families = [f for f in instance.data["families"]
|
|
for search in self.family_targets
|
|
if search in f]
|
|
if not families:
|
|
return
|
|
self.log.debug("here:")
|
|
# Store selection
|
|
selection = [i for i in nuke.allNodes() if i["selected"].getValue()]
|
|
self.log.debug("here:")
|
|
# Deselect all nodes to prevent external connections
|
|
[i["selected"].setValue(False) for i in nuke.allNodes()]
|
|
self.log.debug("here:")
|
|
self.log.debug("creating staging dir:")
|
|
self.staging_dir(instance)
|
|
self.render_review_representation(instance,
|
|
representation="mov")
|
|
self.log.debug("review mov:")
|
|
self.transcode_mov(instance)
|
|
self.render_review_representation(instance,
|
|
representation="jpeg")
|
|
# Restore selection
|
|
[i["selected"].setValue(False) for i in nuke.allNodes()]
|
|
[i["selected"].setValue(True) for i in selection]
|
|
|
|
def transcode_mov(self, instance):
|
|
import subprocess
|
|
|
|
collection = instance.data["collection"]
|
|
staging_dir = instance.data["stagingDir"]
|
|
file_name = collection.format("{head}mov")
|
|
|
|
review_mov = os.path.join(staging_dir, file_name)
|
|
|
|
if instance.data.get("baked_colorspace_movie"):
|
|
args = [
|
|
"ffmpeg", "-y",
|
|
"-i", instance.data["baked_colorspace_movie"],
|
|
"-pix_fmt", "yuv420p",
|
|
"-crf", "18",
|
|
"-timecode", "00:00:00:01",
|
|
]
|
|
|
|
args.append(review_mov)
|
|
|
|
self.log.debug("Executing args: {0}".format(args))
|
|
|
|
self.log.info("transcoding review mov: {0}".format(review_mov))
|
|
p = subprocess.Popen(
|
|
args,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
stdin=subprocess.PIPE,
|
|
cwd=os.path.dirname(args[-1])
|
|
)
|
|
|
|
output = p.communicate()[0]
|
|
|
|
if p.returncode != 0:
|
|
raise ValueError(output)
|
|
|
|
self.log.debug("Removing `{0}`...".format(
|
|
instance.data["baked_colorspace_movie"]))
|
|
os.remove(instance.data["baked_colorspace_movie"])
|
|
|
|
instance.data["files"].append(file_name)
|
|
|
|
def render_review_representation(self,
|
|
instance,
|
|
representation="mov"):
|
|
|
|
assert instance.data['files'], "Instance data files should't be empty!"
|
|
|
|
import clique
|
|
import nuke
|
|
temporary_nodes = []
|
|
staging_dir = instance.data["stagingDir"]
|
|
|
|
collection = instance.data.get("collection", None)
|
|
|
|
self.log.warning("instance.data['files']: {}".format(instance.data['files']))
|
|
if not collection:
|
|
collections, remainder = clique.assemble(*instance.data['files'])
|
|
collection = collections[0]
|
|
instance.data["collection"] = collection
|
|
|
|
# Create nodes
|
|
first_frame = min(collection.indexes)
|
|
last_frame = max(collection.indexes)
|
|
|
|
self.log.warning("first_frame: {}".format(first_frame))
|
|
self.log.warning("last_frame: {}".format(last_frame))
|
|
|
|
node = previous_node = nuke.createNode("Read")
|
|
|
|
node["file"].setValue(
|
|
os.path.join(staging_dir,
|
|
os.path.basename(collection.format(
|
|
"{head}{padding}{tail}"))).replace("\\", "/"))
|
|
|
|
node["first"].setValue(first_frame)
|
|
node["origfirst"].setValue(first_frame)
|
|
node["last"].setValue(last_frame)
|
|
node["origlast"].setValue(last_frame)
|
|
temporary_nodes.append(node)
|
|
|
|
reformat_node = nuke.createNode("Reformat")
|
|
reformat_node["format"].setValue("HD_1080")
|
|
reformat_node["resize"].setValue("fit")
|
|
reformat_node["filter"].setValue("Lanczos6")
|
|
reformat_node["black_outside"].setValue(True)
|
|
reformat_node.setInput(0, previous_node)
|
|
previous_node = reformat_node
|
|
temporary_nodes.append(reformat_node)
|
|
|
|
viewer_process_node = nuke.ViewerProcess.node()
|
|
dag_node = None
|
|
if viewer_process_node:
|
|
dag_node = nuke.createNode(viewer_process_node.Class())
|
|
dag_node.setInput(0, previous_node)
|
|
previous_node = dag_node
|
|
temporary_nodes.append(dag_node)
|
|
# Copy viewer process values
|
|
excludedKnobs = ["name", "xpos", "ypos"]
|
|
for item in viewer_process_node.knobs().keys():
|
|
if item not in excludedKnobs and item in dag_node.knobs():
|
|
x1 = viewer_process_node[item]
|
|
x2 = dag_node[item]
|
|
x2.fromScript(x1.toScript(False))
|
|
else:
|
|
self.log.warning("No viewer node found.")
|
|
|
|
# create write node
|
|
write_node = nuke.createNode("Write")
|
|
|
|
if representation in "mov":
|
|
file = collection.format("{head}baked.mov")
|
|
path = os.path.join(staging_dir, file).replace("\\", "/")
|
|
instance.data["baked_colorspace_movie"] = path
|
|
write_node["file"].setValue(path)
|
|
write_node["file_type"].setValue("mov")
|
|
write_node["raw"].setValue(1)
|
|
write_node.setInput(0, previous_node)
|
|
temporary_nodes.append(write_node)
|
|
|
|
elif representation in "jpeg":
|
|
file = collection.format("{head}jpeg")
|
|
path = os.path.join(staging_dir, file).replace("\\", "/")
|
|
instance.data["thumbnail"] = path
|
|
write_node["file"].setValue(path)
|
|
write_node["file_type"].setValue("jpeg")
|
|
write_node["raw"].setValue(1)
|
|
write_node.setInput(0, previous_node)
|
|
temporary_nodes.append(write_node)
|
|
|
|
# retime for
|
|
first_frame = int(last_frame)/2
|
|
last_frame = int(last_frame)/2
|
|
# add into files for integration as representation
|
|
instance.data["files"].append(file)
|
|
|
|
# Render frames
|
|
nuke.execute(write_node.name(), int(first_frame), int(last_frame))
|
|
|
|
# Clean up
|
|
for node in temporary_nodes:
|
|
nuke.delete(node)
|