mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Merge pull request #760 from pypeclub/feature/AE-pull-ext
After Effects fixes
This commit is contained in:
commit
e331518ddb
7 changed files with 134 additions and 31 deletions
|
|
@ -520,6 +520,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin):
|
||||||
f.replace(orig_scene, new_scene)
|
f.replace(orig_scene, new_scene)
|
||||||
)
|
)
|
||||||
new_exp[aov] = replaced_files
|
new_exp[aov] = replaced_files
|
||||||
|
# [] might be too much here, TODO
|
||||||
self._instance.data["expectedFiles"] = [new_exp]
|
self._instance.data["expectedFiles"] = [new_exp]
|
||||||
else:
|
else:
|
||||||
new_exp = []
|
new_exp = []
|
||||||
|
|
@ -527,7 +528,8 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin):
|
||||||
new_exp.append(
|
new_exp.append(
|
||||||
f.replace(orig_scene, new_scene)
|
f.replace(orig_scene, new_scene)
|
||||||
)
|
)
|
||||||
self._instance.data["expectedFiles"] = [new_exp]
|
self._instance.data["expectedFiles"] = new_exp
|
||||||
|
|
||||||
self.log.info("Scene name was switched {} -> {}".format(
|
self.log.info("Scene name was switched {} -> {}".format(
|
||||||
orig_scene, new_scene
|
orig_scene, new_scene
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,9 @@ class AfterEffectsServerStub():
|
||||||
Args:
|
Args:
|
||||||
item_id (int):
|
item_id (int):
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(namedtuple)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
res = self.websocketserver.call(self.client.call
|
res = self.websocketserver.call(self.client.call
|
||||||
('AfterEffects.get_work_area',
|
('AfterEffects.get_work_area',
|
||||||
|
|
@ -305,6 +308,35 @@ class AfterEffectsServerStub():
|
||||||
image_path=project_path,
|
image_path=project_path,
|
||||||
as_copy=as_copy))
|
as_copy=as_copy))
|
||||||
|
|
||||||
|
def get_render_info(self):
|
||||||
|
""" Get render queue info for render purposes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(namedtuple): with 'file_name' field
|
||||||
|
"""
|
||||||
|
res = self.websocketserver.call(self.client.call
|
||||||
|
('AfterEffects.get_render_info'))
|
||||||
|
|
||||||
|
records = self._to_records(res)
|
||||||
|
if records:
|
||||||
|
return records.pop()
|
||||||
|
|
||||||
|
log.debug("Render queue needs to have file extension in 'Output to'")
|
||||||
|
|
||||||
|
def get_audio_url(self, item_id):
|
||||||
|
""" Get audio layer absolute url for comp
|
||||||
|
|
||||||
|
Args:
|
||||||
|
item_id (int): composition id
|
||||||
|
Returns:
|
||||||
|
(str): absolute path url
|
||||||
|
"""
|
||||||
|
res = self.websocketserver.call(self.client.call
|
||||||
|
('AfterEffects.get_audio_url',
|
||||||
|
item_id=item_id))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.client.close()
|
self.client.close()
|
||||||
|
|
||||||
|
|
|
||||||
27
pype/plugins/aftereffects/publish/collect_audio.py
Normal file
27
pype/plugins/aftereffects/publish/collect_audio.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pyblish.api
|
||||||
|
|
||||||
|
from avalon import aftereffects
|
||||||
|
|
||||||
|
|
||||||
|
class CollectAudio(pyblish.api.ContextPlugin):
|
||||||
|
"""Inject audio file url for rendered composition into context.
|
||||||
|
Needs to run AFTER 'collect_render'. Use collected comp_id to check
|
||||||
|
if there is an AVLayer in this composition
|
||||||
|
"""
|
||||||
|
|
||||||
|
order = pyblish.api.CollectorOrder + 0.499
|
||||||
|
label = "Collect Audio"
|
||||||
|
hosts = ["aftereffects"]
|
||||||
|
|
||||||
|
def process(self, context):
|
||||||
|
for instance in context:
|
||||||
|
if instance.data["family"] == 'render.farm':
|
||||||
|
comp_id = instance.data["comp_id"]
|
||||||
|
if not comp_id:
|
||||||
|
self.log.debug("No comp_id filled in instance")
|
||||||
|
return
|
||||||
|
context.data["audioFile"] = os.path.normpath(
|
||||||
|
aftereffects.stub().get_audio_url(comp_id)
|
||||||
|
).replace("\\", "/")
|
||||||
|
|
@ -11,6 +11,7 @@ from avalon import aftereffects
|
||||||
class AERenderInstance(RenderInstance):
|
class AERenderInstance(RenderInstance):
|
||||||
# extend generic, composition name is needed
|
# extend generic, composition name is needed
|
||||||
comp_name = attr.ib(default=None)
|
comp_name = attr.ib(default=None)
|
||||||
|
comp_id = attr.ib(default=None)
|
||||||
|
|
||||||
|
|
||||||
class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
||||||
|
|
@ -39,13 +40,11 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
work_area_info = aftereffects.stub().get_work_area(int(item_id))
|
work_area_info = aftereffects.stub().get_work_area(int(item_id))
|
||||||
frameStart = round(float(work_area_info.workAreaStart) *
|
frameStart = work_area_info.workAreaStart
|
||||||
float(work_area_info.frameRate))
|
|
||||||
|
|
||||||
frameEnd = round(float(work_area_info.workAreaStart) *
|
frameEnd = round(work_area_info.workAreaStart +
|
||||||
float(work_area_info.frameRate) +
|
|
||||||
float(work_area_info.workAreaDuration) *
|
float(work_area_info.workAreaDuration) *
|
||||||
float(work_area_info.frameRate))
|
float(work_area_info.frameRate)) - 1
|
||||||
|
|
||||||
if inst["family"] == "render" and inst["active"]:
|
if inst["family"] == "render" and inst["active"]:
|
||||||
instance = AERenderInstance(
|
instance = AERenderInstance(
|
||||||
|
|
@ -83,6 +82,7 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
||||||
raise ValueError("There is no composition for item {}".
|
raise ValueError("There is no composition for item {}".
|
||||||
format(item_id))
|
format(item_id))
|
||||||
instance.comp_name = comp.name
|
instance.comp_name = comp.name
|
||||||
|
instance.comp_id = item_id
|
||||||
instance._anatomy = context.data["anatomy"]
|
instance._anatomy = context.data["anatomy"]
|
||||||
instance.anatomyData = context.data["anatomyData"]
|
instance.anatomyData = context.data["anatomyData"]
|
||||||
|
|
||||||
|
|
@ -108,18 +108,29 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
||||||
start = render_instance.frameStart
|
start = render_instance.frameStart
|
||||||
end = render_instance.frameEnd
|
end = render_instance.frameEnd
|
||||||
|
|
||||||
|
# pull file name from Render Queue Output module
|
||||||
|
render_q = aftereffects.stub().get_render_info()
|
||||||
|
_, ext = os.path.splitext(os.path.basename(render_q.file_name))
|
||||||
base_dir = self._get_output_dir(render_instance)
|
base_dir = self._get_output_dir(render_instance)
|
||||||
expected_files = []
|
expected_files = []
|
||||||
for frame in range(start, end + 1):
|
if "#" not in render_q.file_name: # single frame (mov)W
|
||||||
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
|
path = os.path.join(base_dir, "{}_{}_{}.{}".format(
|
||||||
render_instance.asset,
|
render_instance.asset,
|
||||||
render_instance.subset,
|
render_instance.subset,
|
||||||
"v{:03d}".format(render_instance.version),
|
"v{:03d}".format(render_instance.version),
|
||||||
str(frame).zfill(self.padding_width),
|
ext.replace('.', '')
|
||||||
self.rendered_extension
|
))
|
||||||
))
|
|
||||||
expected_files.append(path)
|
expected_files.append(path)
|
||||||
|
else:
|
||||||
|
for frame in range(start, end + 1):
|
||||||
|
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
|
||||||
|
render_instance.asset,
|
||||||
|
render_instance.subset,
|
||||||
|
"v{:03d}".format(render_instance.version),
|
||||||
|
str(frame).zfill(self.padding_width),
|
||||||
|
ext.replace('.', '')
|
||||||
|
))
|
||||||
|
expected_files.append(path)
|
||||||
return expected_files
|
return expected_files
|
||||||
|
|
||||||
def _get_output_dir(self, render_instance):
|
def _get_output_dir(self, render_instance):
|
||||||
|
|
|
||||||
14
pype/plugins/aftereffects/publish/extract_save_scene.py
Normal file
14
pype/plugins/aftereffects/publish/extract_save_scene.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import pype.api
|
||||||
|
from avalon import aftereffects
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractSaveScene(pype.api.Extractor):
|
||||||
|
"""Save scene before extraction."""
|
||||||
|
|
||||||
|
order = pype.api.Extractor.order - 0.49
|
||||||
|
label = "Extract Save Scene"
|
||||||
|
hosts = ["aftereffects"]
|
||||||
|
families = ["workfile"]
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
aftereffects.stub().save()
|
||||||
|
|
@ -18,15 +18,18 @@ class DeadlinePluginInfo():
|
||||||
ProjectPath = attr.ib(default=None)
|
ProjectPath = attr.ib(default=None)
|
||||||
AWSAssetFile0 = attr.ib(default=None)
|
AWSAssetFile0 = attr.ib(default=None)
|
||||||
Version = attr.ib(default=None)
|
Version = attr.ib(default=None)
|
||||||
|
MultiProcess = attr.ib(default=None)
|
||||||
|
|
||||||
|
|
||||||
class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
|
class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
|
||||||
|
|
||||||
label = "Submit AE to Deadline"
|
label = "Submit AE to Deadline"
|
||||||
order = pyblish.api.IntegratorOrder
|
order = pyblish.api.IntegratorOrder + 0.1
|
||||||
hosts = ["aftereffects"]
|
hosts = ["aftereffects"]
|
||||||
families = ["render.farm"] # cannot be "render' as that is integrated
|
families = ["render.farm"] # cannot be "render' as that is integrated
|
||||||
use_published = False
|
use_published = True
|
||||||
|
|
||||||
|
chunk_size = 1000000
|
||||||
|
|
||||||
def get_job_info(self):
|
def get_job_info(self):
|
||||||
dln_job_info = DeadlineJobInfo(Plugin="AfterEffects")
|
dln_job_info = DeadlineJobInfo(Plugin="AfterEffects")
|
||||||
|
|
@ -39,9 +42,12 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline
|
||||||
dln_job_info.Plugin = "AfterEffects"
|
dln_job_info.Plugin = "AfterEffects"
|
||||||
dln_job_info.UserName = context.data.get(
|
dln_job_info.UserName = context.data.get(
|
||||||
"deadlineUser", getpass.getuser())
|
"deadlineUser", getpass.getuser())
|
||||||
frame_range = "{}-{}".format(self._instance.data["frameStart"],
|
if self._instance.data["frameEnd"] > self._instance.data["frameStart"]:
|
||||||
self._instance.data["frameEnd"])
|
frame_range = "{}-{}".format(self._instance.data["frameStart"],
|
||||||
dln_job_info.Frames = frame_range
|
self._instance.data["frameEnd"])
|
||||||
|
dln_job_info.Frames = frame_range
|
||||||
|
|
||||||
|
dln_job_info.ChunkSize = self.chunk_size
|
||||||
dln_job_info.OutputFilename = \
|
dln_job_info.OutputFilename = \
|
||||||
os.path.basename(self._instance.data["expectedFiles"][0])
|
os.path.basename(self._instance.data["expectedFiles"][0])
|
||||||
dln_job_info.OutputDirectory = \
|
dln_job_info.OutputDirectory = \
|
||||||
|
|
@ -77,21 +83,25 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline
|
||||||
script_path = context.data["currentFile"]
|
script_path = context.data["currentFile"]
|
||||||
|
|
||||||
render_path = self._instance.data["expectedFiles"][0]
|
render_path = self._instance.data["expectedFiles"][0]
|
||||||
# replace frame info ('000001') with Deadline's required '[#######]'
|
|
||||||
# expects filename in format project_asset_subset_version.FRAME.ext
|
|
||||||
render_dir = os.path.dirname(render_path)
|
|
||||||
file_name = os.path.basename(render_path)
|
|
||||||
arr = file_name.split('.')
|
|
||||||
assert len(arr) == 3, \
|
|
||||||
"Unable to parse frames from {}".format(file_name)
|
|
||||||
hashed = '[{}]'.format(len(arr[1]) * "#")
|
|
||||||
|
|
||||||
render_path = os.path.join(render_dir,
|
if len(self._instance.data["expectedFiles"]) > 1:
|
||||||
'{}.{}.{}'.format(arr[0], hashed, arr[2]))
|
# replace frame ('000001') with Deadline's required '[#######]'
|
||||||
|
# expects filename in format project_asset_subset_version.FRAME.ext
|
||||||
|
render_dir = os.path.dirname(render_path)
|
||||||
|
file_name = os.path.basename(render_path)
|
||||||
|
arr = file_name.split('.')
|
||||||
|
assert len(arr) == 3, \
|
||||||
|
"Unable to parse frames from {}".format(file_name)
|
||||||
|
hashed = '[{}]'.format(len(arr[1]) * "#")
|
||||||
|
|
||||||
|
render_path = os.path.join(render_dir,
|
||||||
|
'{}.{}.{}'.format(arr[0], hashed,
|
||||||
|
arr[2]))
|
||||||
|
|
||||||
|
deadline_plugin_info.MultiProcess = True
|
||||||
deadline_plugin_info.Comp = self._instance.data["comp_name"]
|
deadline_plugin_info.Comp = self._instance.data["comp_name"]
|
||||||
deadline_plugin_info.Version = "17.5"
|
deadline_plugin_info.Version = "17.5"
|
||||||
deadline_plugin_info.SceneFile = script_path
|
deadline_plugin_info.SceneFile = self.scene_path
|
||||||
deadline_plugin_info.Output = render_path.replace("\\", "/")
|
deadline_plugin_info.Output = render_path.replace("\\", "/")
|
||||||
|
|
||||||
return attr.asdict(deadline_plugin_info)
|
return attr.asdict(deadline_plugin_info)
|
||||||
|
|
|
||||||
|
|
@ -600,6 +600,13 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
||||||
"files": os.path.basename(remainder),
|
"files": os.path.basename(remainder),
|
||||||
"stagingDir": os.path.dirname(remainder),
|
"stagingDir": os.path.dirname(remainder),
|
||||||
}
|
}
|
||||||
|
if "render" in instance.get("families"):
|
||||||
|
rep.update({
|
||||||
|
"fps": instance.get("fps"),
|
||||||
|
"tags": ["review"]
|
||||||
|
})
|
||||||
|
self._solve_families(instance, True)
|
||||||
|
|
||||||
if remainder in bake_render_path:
|
if remainder in bake_render_path:
|
||||||
rep.update({
|
rep.update({
|
||||||
"fps": instance.get("fps"),
|
"fps": instance.get("fps"),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue