Merge pull request #615 from pypeclub/feature/harmony-tweaks

Harmony: More efficient publishing
This commit is contained in:
Milan Kolar 2020-10-12 11:56:47 +02:00 committed by GitHub
commit 5c4f9c1ca9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 55 deletions

View file

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
"""Collect scene data."""
import os
import pyblish.api
from avalon import harmony
class CollectScene(pyblish.api.ContextPlugin):
"""Collect basic scene information."""
label = "Scene Data"
order = pyblish.api.CollectorOrder
hosts = ["harmony"]
def process(self, context):
sig = harmony.signature()
func = """function %s()
{
return [
about.getApplicationPath(),
scene.currentProjectPath(),
scene.currentScene(),
scene.getFrameRate(),
scene.getStartFrame(),
scene.getStopFrame(),
sound.getSoundtrackAll().path(),
scene.defaultResolutionX(),
scene.defaultResolutionY()
]
}
%s
""" % (sig, sig)
result = harmony.send(
{"function": func, "args": []}
)["result"]
context.data["applicationPath"] = result[0]
context.data["scenePath"] = os.path.join(
result[1], result[2] + ".xstage")
context.data["frameRate"] = result[3]
context.data["frameStart"] = result[4]
context.data["frameEnd"] = result[5]
context.data["audioPath"] = result[6]
context.data["resolutionWidth"] = result[7]
context.data["resolutionHeight"] = result[8]
all_nodes = harmony.send(
{"function": "node.subNodes", "args": ["Top"]}
)["result"]
context.data["allNodes"] = all_nodes

View file

@ -21,30 +21,17 @@ class ExtractRender(pyblish.api.InstancePlugin):
def process(self, instance):
# Collect scene data.
sig = harmony.signature()
func = """function %s(write_node)
{
return [
about.getApplicationPath(),
scene.currentProjectPath(),
scene.currentScene(),
scene.getFrameRate(),
scene.getStartFrame(),
scene.getStopFrame(),
sound.getSoundtrackAll().path()
]
}
%s
""" % (sig, sig)
result = harmony.send(
{"function": func, "args": [instance[0]]}
)["result"]
application_path = result[0]
scene_path = os.path.join(result[1], result[2] + ".xstage")
frame_rate = result[3]
frame_start = result[4]
frame_end = result[5]
audio_path = result[6]
application_path = instance.context.data.get("applicationPath")
scene_path = instance.context.data.get("scenePath")
frame_rate = instance.context.data.get("frameRate")
frame_start = instance.context.data.get("frameStart")
frame_end = instance.context.data.get("frameEnd")
audio_path = instance.context.data.get("audioPath")
if audio_path and os.path.exists(audio_path):
self.log.info(f"Using audio from {audio_path}")
instance.data["audio"] = [{"filename": audio_path}]
instance.data["fps"] = frame_rate
@ -57,7 +44,7 @@ class ExtractRender(pyblish.api.InstancePlugin):
}
%s
""" % (sig, sig)
result = harmony.send(
harmony.send(
{
"function": func,
"args": [instance[0], path + "/" + instance.data["name"]]
@ -67,6 +54,7 @@ class ExtractRender(pyblish.api.InstancePlugin):
# Execute rendering. Ignoring error cause Harmony returns error code
# always.
self.log.info(f"running [ {application_path} -batch {scene_path}")
proc = subprocess.Popen(
[application_path, "-batch", scene_path],
stdout=subprocess.PIPE,
@ -74,12 +62,16 @@ class ExtractRender(pyblish.api.InstancePlugin):
stdin=subprocess.PIPE
)
output, error = proc.communicate()
self.log.info("Click on the line below to see more details.")
self.log.info(output.decode("utf-8"))
# Collect rendered files.
self.log.debug(path)
self.log.debug(f"collecting from: {path}")
files = os.listdir(path)
self.log.debug(files)
assert files, (
"No rendered files found, render failed."
)
self.log.debug(f"files there: {files}")
collections, remainder = clique.assemble(files, minimum_items=1)
assert not remainder, (
"There should not be a remainder for {0}: {1}".format(

View file

@ -15,9 +15,9 @@ class ExtractTemplate(pype.api.Extractor):
def process(self, instance):
staging_dir = self.staging_dir(instance)
filepath = os.path.join(staging_dir, "{}.tpl".format(instance.name))
filepath = os.path.join(staging_dir, f"{instance.name}.tpl")
self.log.info("Outputting template to {}".format(staging_dir))
self.log.info(f"Outputting template to {staging_dir}")
dependencies = []
self.get_dependencies(instance[0], dependencies)
@ -30,9 +30,7 @@ class ExtractTemplate(pype.api.Extractor):
unique_backdrops = [backdrops[x] for x in set(backdrops.keys())]
# Get non-connected nodes within backdrops.
all_nodes = harmony.send(
{"function": "node.subNodes", "args": ["Top"]}
)["result"]
all_nodes = instance.context.data.get("allNodes")
for node in [x for x in all_nodes if x not in dependencies]:
within_unique_backdrops = bool(
[x for x in self.get_backdrops(node) if x in unique_backdrops]
@ -52,15 +50,15 @@ class ExtractTemplate(pype.api.Extractor):
# Prep representation.
os.chdir(staging_dir)
shutil.make_archive(
"{}".format(instance.name),
f"{instance.name}",
"zip",
os.path.join(staging_dir, "{}.tpl".format(instance.name))
os.path.join(staging_dir, f"{instance.name}.tpl")
)
representation = {
"name": "tpl",
"ext": "zip",
"files": "{}.zip".format(instance.name),
"files": f"{instance.name}.zip",
"stagingDir": staging_dir
}
instance.data["representations"] = [representation]

View file

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
"""Extract work file."""
import os
import shutil
from zipfile import ZipFile
import pype.api
from avalon import harmony
@ -14,13 +17,12 @@ class ExtractWorkfile(pype.api.Extractor):
families = ["workfile"]
def process(self, instance):
"""Plugin entry point."""
# Export template.
backdrops = harmony.send(
{"function": "Backdrop.backdrops", "args": ["Top"]}
)["result"]
nodes = harmony.send(
{"function": "node.subNodes", "args": ["Top"]}
)["result"]
nodes = instance.context.data.get("allNodes")
staging_dir = self.staging_dir(instance)
filepath = os.path.join(staging_dir, "{}.tpl".format(instance.name))
@ -29,15 +31,19 @@ class ExtractWorkfile(pype.api.Extractor):
# Prep representation.
os.chdir(staging_dir)
shutil.make_archive(
"{}".format(instance.name),
f"{instance.name}",
"zip",
os.path.join(staging_dir, "{}.tpl".format(instance.name))
os.path.join(staging_dir, f"{instance.name}.tpl")
)
# Check if archive is ok
with ZipFile(os.path.basename(f"{instance.name}.zip")) as zr:
if zr.testzip() is not None:
raise Exception("File archive is corrupted.")
representation = {
"name": "tpl",
"ext": "zip",
"files": "{}.zip".format(instance.name),
"files": f"{instance.name}.zip",
"stagingDir": staging_dir
}
instance.data["representations"] = [representation]

View file

@ -10,7 +10,6 @@ class ValidateAudio(pyblish.api.InstancePlugin):
If you are sure that you want to send render without audio, you can
disable this validator before clicking on "publish"
"""
order = pyblish.api.ValidatorOrder

View file

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
"""Validate scene settings."""
import os
import json
import pyblish.api
@ -14,9 +17,17 @@ class ValidateSceneSettingsRepair(pyblish.api.Action):
on = "failed"
def process(self, context, plugin):
"""Repair action entry point."""
pype.hosts.harmony.set_scene_settings(
pype.hosts.harmony.get_asset_settings()
)
if not os.patch.exists(context.data["scenePath"]):
self.log.info("correcting scene name")
scene_dir = os.path.dirname(context.data["currentFile"])
scene_path = os.path.join(
scene_dir, os.path.basename(scene_dir) + ".xstage"
)
harmony.save_scene_as(scene_path)
class ValidateSceneSettings(pyblish.api.InstancePlugin):
@ -31,6 +42,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
frame_check_filter = ["_ch_", "_pr_", "_intd_", "_extd_"]
def process(self, instance):
"""Plugin entry point."""
expected_settings = pype.hosts.harmony.get_asset_settings()
self.log.info(expected_settings)
@ -46,20 +58,20 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
for string in self.frame_check_filter):
expected_settings.pop("frameEnd")
sig = harmony.signature()
func = """function %s()
{
return {
"fps": scene.getFrameRate(),
"frameStart": scene.getStartFrame(),
"frameEnd": scene.getStopFrame(),
"resolutionWidth": scene.defaultResolutionX(),
"resolutionHeight": scene.defaultResolutionY()
};
# handle case where ftrack uses only two decimal places
# 23.976023976023978 vs. 23.98
fps = instance.context.data.get("frameRate")
if isinstance(instance.context.data.get("frameRate"), float):
fps = float(
"{:.2f}".format(instance.context.data.get("frameRate")))
current_settings = {
"fps": fps,
"frameStart": instance.context.data.get("frameStart"),
"frameEnd": instance.context.data.get("frameEnd"),
"resolutionWidth": instance.context.data.get("resolutionWidth"),
"resolutionHeight": instance.context.data.get("resolutionHeight"),
}
%s
""" % (sig, sig)
current_settings = harmony.send({"function": func})["result"]
invalid_settings = []
for key, value in expected_settings.items():
@ -74,3 +86,6 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
json.dumps(invalid_settings, sort_keys=True, indent=4)
)
assert not invalid_settings, msg
assert os.path.exists(instance.context.data.get("scenePath")), (
"Scene file not found (saved under wrong name)"
)