Merge branch 'feature/PYPE-405-unify-preview-quicktime-creatio' of bitbucket.org:pypeclub/pype into bugfix/PYPE-428-dazzle-feedback-publish-errors

This commit is contained in:
jezschaj 2019-07-17 11:38:21 +02:00
commit fbb4cb2a72
12 changed files with 261 additions and 180 deletions

View file

@ -18,6 +18,9 @@ class CollectFtrackApi(pyblish.api.ContextPlugin):
ftrack_log = logging.getLogger('ftrack_api')
ftrack_log.setLevel(logging.WARNING)
ftrack_log = logging.getLogger('ftrack_api_old')
ftrack_log.setLevel(logging.WARNING)
# Collect session
session = ftrack_api.Session()
context.data["ftrackSession"] = session

View file

@ -49,14 +49,14 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
for comp in instance.data['representations']:
self.log.debug('component {}'.format(comp))
if comp.get('thumbnail'):
if comp.get('thumbnail') or ("thumbnail" in comp.get('tags', [])):
location = self.get_ftrack_location(
'ftrack.server', ft_session
)
component_data = {
"name": "thumbnail" # Default component name is "main".
}
elif comp.get('preview'):
elif comp.get('preview') or ("preview" in comp.get('tags', [])):
'''
Ftrack bug requirement:
- Start frame must be 0
@ -120,7 +120,9 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
componentList.append(component_item)
# Create copy with ftrack.unmanaged location if thumb or prev
if comp.get('thumbnail') or comp.get('preview'):
if comp.get('thumbnail') or comp.get('preview') \
or ("preview" in comp.get('tags', [])) \
or ("thumbnail" in comp.get('tags', [])):
unmanaged_loc = self.get_ftrack_location(
'ftrack.unmanaged', ft_session
)
@ -148,7 +150,6 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin):
componentList.append(component_item_src)
self.log.debug('componentsList: {}'.format(str(componentList)))
instance.data["ftrackComponentsList"] = componentList

View file

@ -1,92 +0,0 @@
# import os
# import pyblish.api
# import subprocess
# from pype.vendor import clique
# from pypeapp import config
#
#
# class ExtractReview(pyblish.api.InstancePlugin):
# """Resolve any dependency issies
#
# This plug-in resolves any paths which, if not updated might break
# the published file.
#
# The order of families is important, when working with lookdev you want to
# first publish the texture, update the texture paths in the nodes and then
# publish the shading network. Same goes for file dependent assets.
# """
#
# label = "Extract Review"
# order = pyblish.api.ExtractorOrder
# # families = ["imagesequence", "render", "write", "source"]
# # hosts = ["shell"]
#
# def process(self, instance):
# # adding plugin attributes from presets
# publish_presets = config.get_presets()["plugins"]["global"]["publish"]
# plugin_attrs = publish_presets[self.__class__.__name__]
#
#
# fps = instance.data.get("fps")
# start = instance.data.get("startFrame")
# stagingdir = os.path.normpath(instance.data.get("stagingDir"))
#
# collected_frames = os.listdir(stagingdir)
# collections, remainder = clique.assemble(collected_frames)
#
# full_input_path = os.path.join(
# stagingdir, collections[0].format('{head}{padding}{tail}')
# )
# self.log.info("input {}".format(full_input_path))
#
# filename = collections[0].format('{head}')
# if not filename.endswith('.'):
# filename += "."
# movFile = filename + "mov"
# full_output_path = os.path.join(stagingdir, movFile)
#
# self.log.info("output {}".format(full_output_path))
#
# config_data = instance.context.data['output_repre_config']
#
# proj_name = os.environ.get('AVALON_PROJECT', '__default__')
# profile = config_data.get(proj_name, config_data['__default__'])
#
# input_args = []
# # overrides output file
# input_args.append("-y")
# # preset's input data
# input_args.extend(profile.get('input', []))
# # necessary input data
# input_args.append("-start_number {}".format(start))
# input_args.append("-i {}".format(full_input_path))
# input_args.append("-framerate {}".format(fps))
#
# output_args = []
# # preset's output data
# output_args.extend(profile.get('output', []))
# # output filename
# output_args.append(full_output_path)
# mov_args = [
# "ffmpeg",
# " ".join(input_args),
# " ".join(output_args)
# ]
# subprocess_mov = " ".join(mov_args)
# sub_proc = subprocess.Popen(subprocess_mov)
# sub_proc.wait()
#
# if not os.path.isfile(full_output_path):
# raise("Quicktime wasn't created succesfully")
#
# if "representations" not in instance.data:
# instance.data["representations"] = []
#
# representation = {
# 'name': 'mov',
# 'ext': 'mov',
# 'files': movFile,
# "stagingDir": stagingdir,
# "preview": True
# }
# instance.data["representations"].append(representation)

View file

@ -2,6 +2,7 @@ import os
import subprocess
import pype.api
import json
import pyblish
class ExtractBurnin(pype.api.Extractor):
@ -14,7 +15,8 @@ class ExtractBurnin(pype.api.Extractor):
"""
label = "Quicktime with burnins"
families = ["burnin"]
order = pyblish.api.ExtractorOrder + 0.03
families = ["review", "burnin"]
optional = True
def process(self, instance):
@ -29,25 +31,30 @@ class ExtractBurnin(pype.api.Extractor):
"start_frame": int(instance.data['startFrame']),
"version": "v" + str(instance.context.data['version'])
}
self.log.debug("__ burnin_data1: {}".format(burnin_data))
for i, repre in enumerate(instance.data["representations"]):
self.log.debug("__ i: `{}`, repre: `{}`".format(i, repre))
for repre in instance.data["representations"]:
if (not repre.get("burnin", False) or
"burnin" not in repre.get("tags", [])):
if "burnin" not in repre.get("tags", []):
continue
stagingdir = self.staging_dir(instance)
stagingdir = repre["stagingDir"]
filename = "{0}".format(repre["files"])
movieFileBurnin = filename + "Burn" + ".mov"
name = "_burnin"
movieFileBurnin = filename.replace(".mov", "") + name + ".mov"
full_movie_path = os.path.join(stagingdir, repre["files"])
full_burnin_path = os.path.join(stagingdir, movieFileBurnin)
self.log.debug("__ full_burnin_path: {}".format(full_burnin_path))
burnin_data = {
"input": full_movie_path.replace("\\", "/"),
"output": full_burnin_path.replace("\\", "/"),
"burnin_data": burnin_data
}
}
self.log.debug("__ burnin_data2: {}".format(burnin_data))
json_data = json.dumps(burnin_data)
scriptpath = os.path.join(os.environ['PYPE_MODULE_ROOT'],
@ -55,9 +62,19 @@ class ExtractBurnin(pype.api.Extractor):
"scripts",
"otio_burnin.py")
p = subprocess.Popen(
['python', scriptpath, json_data]
)
p.wait()
self.log.debug("__ scriptpath: {}".format(scriptpath))
repre['files']: movieFileBurnin
try:
p = subprocess.Popen(
[os.getenv("PYPE_PYTHON_EXE"), scriptpath, json_data]
)
p.wait()
except Exception as e:
raise RuntimeError("Burnin script didn't work: `{}`".format(e))
if os.path.exists(full_burnin_path):
repre_update = {
"files": movieFileBurnin,
"name": repre["name"] + name
}
instance.data["representations"][i].update(repre_update)

View file

@ -0,0 +1,152 @@
import os
import pyblish.api
import subprocess
from pype.vendor import clique
from pypeapp import config
class ExtractReview(pyblish.api.InstancePlugin):
"""Extracting Review mov file for Ftrack
Compulsory attribute of representation is tags list with "review",
otherwise the representation is ignored.
All new represetnations are created and encoded by ffmpeg following
presets found in `pype-config/presets/plugins/global/publish.json:ExtractReview:outputs`. To change the file extension
filter values use preset's attributes `ext_filter`
"""
label = "Extract Review"
order = pyblish.api.ExtractorOrder + 0.02
families = ["review"]
def process(self, instance):
# adding plugin attributes from presets
publish_presets = config.get_presets()["plugins"]["global"]["publish"]
plugin_attrs = publish_presets[self.__class__.__name__]
output_profiles = plugin_attrs.get("outputs", {})
inst_data = instance.data
fps = inst_data.get("fps")
start_frame = inst_data.get("startFrame")
self.log.debug("Families In: `{}`".format(instance.data["families"]))
# get representation and loop them
representations = instance.data["representations"]
# filter out mov and img sequences
representations_new = list()
for repre in representations:
if repre['ext'] in plugin_attrs["ext_filter"]:
tags = repre.get("tags", [])
self.log.info("Try repre: {}".format(repre))
if "review" in tags:
staging_dir = repre["stagingDir"]
for name, profile in output_profiles.items():
if "mov" not in repre['ext']:
# get output presets and loop them
collections, remainder = clique.assemble(
repre["files"])
full_input_path = os.path.join(
staging_dir, collections[0].format(
'{head}{padding}{tail}')
)
filename = collections[0].format('{head}')
if filename.endswith('.'):
filename = filename[:-1]
else:
full_input_path = os.path.join(
staging_dir, repre["files"])
filename = repre["files"].split(".")[0]
mov_file = filename + "_{0}.{1}".format(name, "mov")
full_output_path = os.path.join(staging_dir, mov_file)
self.log.info("input {}".format(full_input_path))
self.log.info("output {}".format(full_output_path))
repre_new = repre.copy()
self.log.debug("Profile name: {}".format(name))
new_tags = tags[:]
p_tags = profile.get('tags', [])
self.log.info("p_tags: `{}`".format(p_tags))
# add families
[instance.data["families"].append(t) for t in p_tags
if t not in instance.data["families"]]
# add to
[new_tags.append(t) for t in p_tags
if t not in new_tags]
self.log.info("new_tags: `{}`".format(new_tags))
input_args = []
# overrides output file
input_args.append("-y")
# preset's input data
input_args.extend(profile.get('input', []))
# necessary input data
# adds start arg only if image sequence
if "mov" not in repre_new['ext']:
input_args.append("-start_number {}".format(
start_frame))
input_args.append("-i {}".format(full_input_path))
input_args.append("-framerate {}".format(fps))
output_args = []
# preset's output data
output_args.extend(profile.get('output', []))
# output filename
output_args.append(full_output_path)
mov_args = [
"ffmpeg",
" ".join(input_args),
" ".join(output_args)
]
subprocess_mov = " ".join(mov_args)
# run subprocess
sub_proc = subprocess.Popen(subprocess_mov)
sub_proc.wait()
if not os.path.isfile(full_output_path):
self.log.error(
"Quicktime wasn't created succesfully")
# create representation data
repre_new.update({
'name': name,
'ext': 'mov',
'files': mov_file,
"tags": new_tags,
"outputName": name
})
repre_new.pop("preview")
repre_new.pop("thumbnail")
# adding representation
representations_new.append(repre_new)
else:
representations_new.append(repre)
else:
representations_new.append(repre)
self.log.debug(
"new representations: {}".format(representations_new))
instance.data["representations"] = representations_new
self.log.debug("Families Out: `{}`".format(instance.data["families"]))

View file

@ -343,6 +343,9 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin):
template_data["representation"] = repre['ext']
if repre.get("outputName"):
template_data["output"] = repre['outputName']
src = os.path.join(stagingdir, fname)
anatomy_filled = anatomy.format(template_data)
dst = os.path.normpath(

View file

@ -0,0 +1,46 @@
import pyblish.api
@pyblish.api.log
class CollectRenderTarget(pyblish.api.InstancePlugin):
"""Collect families for all instances"""
order = pyblish.api.CollectorOrder + 0.2
label = "Collect Render Target"
hosts = ["nuke", "nukeassist"]
families = ['write']
def process(self, instance):
node = instance[0]
self.log.info('processing {}'.format(node))
families = []
if instance.data.get('families'):
families += instance.data['families']
# set for ftrack to accept
# instance.data["families"] = ["ftrack"]
if node["render"].value():
# dealing with local/farm rendering
if node["render_farm"].value():
families.append("render.farm")
else:
families.append("render.local")
else:
families.append("render.frames")
# to ignore staging dir op in integrate
instance.data['transfer'] = False
families.append('ftrack')
instance.data["families"] = families
# Sort/grouped by family (preserving local index)
instance.context[:] = sorted(instance.context, key=self.sort_by_family)
def sort_by_family(self, instance):
"""Sort by family"""
return instance.data.get("families", instance.data.get("family"))

View file

@ -27,8 +27,8 @@ class ExtractScript(pype.api.Extractor):
shutil.copy(current_script, path)
if "representations" not in instance.data:
instance.data["representations"] = []
instance.data["representations"] = list()
representation = {
'name': 'nk',
'ext': '.nk',

View file

@ -68,7 +68,7 @@ class CollectNukeInstances(pyblish.api.ContextPlugin):
"avalonKnob": avalon_knob_data,
"publish": node.knob('publish').value(),
"step": 1,
"fps": int(nuke.root()['fps'].value())
"fps": nuke.root()['fps'].value()
})

View file

@ -11,7 +11,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
order = pyblish.api.CollectorOrder + 0.1
label = "Collect Writes"
hosts = ["nuke", "nukeassist"]
families = ["render.local", "render", "render.farm"]
families = ["render", "render.local", "render.farm"]
def process(self, instance):
@ -96,5 +96,4 @@ class CollectNukeWrites(pyblish.api.InstancePlugin):
"colorspace": node["colorspace"].value(),
})
self.log.debug("instance.data: {}".format(instance.data))

View file

@ -21,7 +21,6 @@ class NukeRenderLocal(pype.api.Extractor):
def process(self, instance):
node = instance[0]
context = instance.context
self.log.debug("instance collected: {}".format(instance.data))

View file

@ -2,10 +2,9 @@ import os
import nuke
import pyblish.api
import pype
from pype.vendor import ffmpeg
class ExtractDataForReview(pype.api.Extractor):
class ExtractReviewData(pype.api.Extractor):
"""Extracts movie and thumbnail with baked in luts
must be run after extract_render_local.py
@ -13,8 +12,7 @@ class ExtractDataForReview(pype.api.Extractor):
"""
order = pyblish.api.ExtractorOrder + 0.01
label = "Extract Review"
optional = True
label = "Extract Review Data"
families = ["review"]
hosts = ["nuke"]
@ -35,63 +33,15 @@ class ExtractDataForReview(pype.api.Extractor):
if "still" not in instance.data["families"]:
self.render_review_representation(instance,
representation="mov")
self.log.debug("review mov:")
self.transcode_mov(instance)
self.log.debug("instance.data: {}".format(instance.data))
self.render_review_representation(instance,
representation="jpeg")
else:
self.log.debug("instance: {}".format(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):
collection = instance.data["collection"]
stagingDir = instance.data["stagingDir"].replace("\\", "/")
file_name = collection.format("{head}mov")
review_mov = os.path.join(stagingDir, file_name).replace("\\", "/")
self.log.info("transcoding review mov: {0}".format(review_mov))
if instance.data.get("baked_colorspace_movie"):
input_movie = instance.data["baked_colorspace_movie"]
out, err = (
ffmpeg
.input(input_movie)
.output(
review_mov,
pix_fmt='yuv420p',
crf=18,
timecode="00:00:00:01"
)
.overwrite_output()
.run()
)
self.log.debug("Removing `{0}`...".format(
instance.data["baked_colorspace_movie"]))
os.remove(instance.data["baked_colorspace_movie"])
if "representations" not in instance.data:
instance.data["representations"] = []
representation = {
'name': 'review',
'ext': 'mov',
'files': file_name,
"stagingDir": stagingDir,
"anatomy_template": "render",
"thumbnail": False,
"preview": True,
'startFrameReview': instance.data['startFrame'],
'endFrameReview': instance.data['endFrame'],
'frameRate': instance.context.data["framerate"]
}
instance.data["representations"].append(representation)
def render_review_representation(self,
instance,
representation="mov"):
@ -172,6 +122,7 @@ class ExtractDataForReview(pype.api.Extractor):
temporary_nodes.append(write_node)
thumbnail = False
preview = True
tags = ["review"]
elif representation in "jpeg":
file = fhead + "jpeg"
@ -184,29 +135,31 @@ class ExtractDataForReview(pype.api.Extractor):
temporary_nodes.append(write_node)
thumbnail = True
preview = False
tags = ["thumbnail"]
# retime for
first_frame = int(last_frame) / 2
last_frame = int(last_frame) / 2
# add into files for integration as representation
if "representations" not in instance.data:
instance.data["representations"] = []
repre = {
'name': representation,
'ext': representation,
'files': file,
"stagingDir": stagingDir,
"anatomy_template": "render",
"thumbnail": thumbnail,
"preview": preview
}
instance.data["representations"].append(repre)
repre = {
'name': representation,
'ext': representation,
'files': file,
"stagingDir": stagingDir,
"startFrame": first_frame,
"endFrame": last_frame,
"anatomy_template": "render",
"thumbnail": thumbnail,
"preview": preview,
"tags": tags
}
instance.data["representations"].append(repre)
# Render frames
nuke.execute(write_node.name(), int(first_frame), int(last_frame))
self.log.debug("representations: {}".format(instance.data["representations"]))
# Clean up
for node in temporary_nodes:
nuke.delete(node)