mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +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)
|
||||
)
|
||||
new_exp[aov] = replaced_files
|
||||
# [] might be too much here, TODO
|
||||
self._instance.data["expectedFiles"] = [new_exp]
|
||||
else:
|
||||
new_exp = []
|
||||
|
|
@ -527,7 +528,8 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin):
|
|||
new_exp.append(
|
||||
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(
|
||||
orig_scene, new_scene
|
||||
))
|
||||
|
|
|
|||
|
|
@ -252,6 +252,9 @@ class AfterEffectsServerStub():
|
|||
Args:
|
||||
item_id (int):
|
||||
|
||||
Returns:
|
||||
(namedtuple)
|
||||
|
||||
"""
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('AfterEffects.get_work_area',
|
||||
|
|
@ -305,6 +308,35 @@ class AfterEffectsServerStub():
|
|||
image_path=project_path,
|
||||
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):
|
||||
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):
|
||||
# extend generic, composition name is needed
|
||||
comp_name = attr.ib(default=None)
|
||||
comp_id = attr.ib(default=None)
|
||||
|
||||
|
||||
class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
||||
|
|
@ -39,13 +40,11 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
|||
continue
|
||||
|
||||
work_area_info = aftereffects.stub().get_work_area(int(item_id))
|
||||
frameStart = round(float(work_area_info.workAreaStart) *
|
||||
float(work_area_info.frameRate))
|
||||
frameStart = work_area_info.workAreaStart
|
||||
|
||||
frameEnd = round(float(work_area_info.workAreaStart) *
|
||||
float(work_area_info.frameRate) +
|
||||
frameEnd = round(work_area_info.workAreaStart +
|
||||
float(work_area_info.workAreaDuration) *
|
||||
float(work_area_info.frameRate))
|
||||
float(work_area_info.frameRate)) - 1
|
||||
|
||||
if inst["family"] == "render" and inst["active"]:
|
||||
instance = AERenderInstance(
|
||||
|
|
@ -83,6 +82,7 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
|||
raise ValueError("There is no composition for item {}".
|
||||
format(item_id))
|
||||
instance.comp_name = comp.name
|
||||
instance.comp_id = item_id
|
||||
instance._anatomy = context.data["anatomy"]
|
||||
instance.anatomyData = context.data["anatomyData"]
|
||||
|
||||
|
|
@ -108,18 +108,29 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
|
|||
start = render_instance.frameStart
|
||||
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)
|
||||
expected_files = []
|
||||
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),
|
||||
self.rendered_extension
|
||||
))
|
||||
if "#" not in render_q.file_name: # single frame (mov)W
|
||||
path = os.path.join(base_dir, "{}_{}_{}.{}".format(
|
||||
render_instance.asset,
|
||||
render_instance.subset,
|
||||
"v{:03d}".format(render_instance.version),
|
||||
ext.replace('.', '')
|
||||
))
|
||||
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
|
||||
|
||||
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)
|
||||
AWSAssetFile0 = attr.ib(default=None)
|
||||
Version = attr.ib(default=None)
|
||||
MultiProcess = attr.ib(default=None)
|
||||
|
||||
|
||||
class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
|
||||
|
||||
label = "Submit AE to Deadline"
|
||||
order = pyblish.api.IntegratorOrder
|
||||
order = pyblish.api.IntegratorOrder + 0.1
|
||||
hosts = ["aftereffects"]
|
||||
families = ["render.farm"] # cannot be "render' as that is integrated
|
||||
use_published = False
|
||||
use_published = True
|
||||
|
||||
chunk_size = 1000000
|
||||
|
||||
def get_job_info(self):
|
||||
dln_job_info = DeadlineJobInfo(Plugin="AfterEffects")
|
||||
|
|
@ -39,9 +42,12 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline
|
|||
dln_job_info.Plugin = "AfterEffects"
|
||||
dln_job_info.UserName = context.data.get(
|
||||
"deadlineUser", getpass.getuser())
|
||||
frame_range = "{}-{}".format(self._instance.data["frameStart"],
|
||||
self._instance.data["frameEnd"])
|
||||
dln_job_info.Frames = frame_range
|
||||
if self._instance.data["frameEnd"] > self._instance.data["frameStart"]:
|
||||
frame_range = "{}-{}".format(self._instance.data["frameStart"],
|
||||
self._instance.data["frameEnd"])
|
||||
dln_job_info.Frames = frame_range
|
||||
|
||||
dln_job_info.ChunkSize = self.chunk_size
|
||||
dln_job_info.OutputFilename = \
|
||||
os.path.basename(self._instance.data["expectedFiles"][0])
|
||||
dln_job_info.OutputDirectory = \
|
||||
|
|
@ -77,21 +83,25 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline
|
|||
script_path = context.data["currentFile"]
|
||||
|
||||
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,
|
||||
'{}.{}.{}'.format(arr[0], hashed, arr[2]))
|
||||
if len(self._instance.data["expectedFiles"]) > 1:
|
||||
# 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.Version = "17.5"
|
||||
deadline_plugin_info.SceneFile = script_path
|
||||
deadline_plugin_info.SceneFile = self.scene_path
|
||||
deadline_plugin_info.Output = render_path.replace("\\", "/")
|
||||
|
||||
return attr.asdict(deadline_plugin_info)
|
||||
|
|
|
|||
|
|
@ -600,6 +600,13 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
|||
"files": os.path.basename(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:
|
||||
rep.update({
|
||||
"fps": instance.get("fps"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue