mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #615 from pypeclub/feature/harmony-tweaks
Harmony: More efficient publishing
This commit is contained in:
commit
5c4f9c1ca9
6 changed files with 118 additions and 55 deletions
53
pype/plugins/harmony/publish/collect_scene.py
Normal file
53
pype/plugins/harmony/publish/collect_scene.py
Normal 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
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue