Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #738 from pypeclub/bugfix/AE_issues
Browse files Browse the repository at this point in the history
#735 - fixes for After Effects
  • Loading branch information
mkolar authored Nov 23, 2020
2 parents b7c734d + a1bf691 commit 107052d
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 64 deletions.
50 changes: 50 additions & 0 deletions pype/modules/websocket_server/stubs/aftereffects_server_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ def replace_item(self, item, path, item_name):
item_id=item.id,
path=path, item_name=item_name))

def rename_item(self, item, item_name):
""" Replace item with item_name
Args:
item (dict):
item_name (string): label on item in Project list
"""
self.websocketserver.call(self.client.call
('AfterEffects.rename_item',
item_id=item.id,
item_name=item_name))

def delete_item(self, item):
""" Deletes FootageItem with new file
Args:
Expand Down Expand Up @@ -234,6 +247,43 @@ def set_label_color(self, item_id, color_idx):
color_idx=color_idx
))

def get_work_area(self, item_id):
""" Get work are information for render purposes
Args:
item_id (int):
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_work_area',
item_id=item_id
))

records = self._to_records(res)
if records:
return records.pop()

log.debug("Couldn't get work area")

def set_work_area(self, item, start, duration, frame_rate):
"""
Set work area to predefined values (from Ftrack).
Work area directs what gets rendered.
Beware of rounding, AE expects seconds, not frames directly.
Args:
item (dict):
start (float): workAreaStart in seconds
duration (float): in seconds
frame_rate (float): frames in seconds
"""
self.websocketserver.call(self.client.call
('AfterEffects.set_work_area',
item_id=item.id,
start=start,
duration=duration,
frame_rate=frame_rate
))

def save(self):
"""
Saves active document
Expand Down
45 changes: 20 additions & 25 deletions pype/plugins/aftereffects/create/create_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,36 @@ class CreateRender(api.Creator):

name = "renderDefault"
label = "Render on Farm"
family = "render.farm"
family = "render"

def process(self):
# Photoshop can have multiple LayerSets with the same name, which does
# not work with Avalon.
txt = "Instance with name \"{}\" already exists.".format(self.name)
stub = aftereffects.stub() # only after After Effects is up
for layer in stub.get_items(comps=True,
folders=False,
footages=False):
if self.name.lower() == layer.name.lower():
msg = Qt.QtWidgets.QMessageBox()
msg.setIcon(Qt.QtWidgets.QMessageBox.Warning)
msg.setText(txt)
msg.exec_()
return False
log.debug("options:: {}".format(self.options))
print("options:: {}".format(self.options))
if (self.options or {}).get("useSelection"):
log.debug("useSelection")
print("useSelection")
items = stub.get_selected_items(comps=True,
folders=False,
footages=False)
else:
items = stub.get_items(comps=True,
folders=False,
footages=False)
log.debug("items:: {}".format(items))
print("items:: {}".format(items))
self._show_msg("Please select only single composition at time.")
return False

if not items:
raise ValueError("Nothing to create. Select composition " +
"if 'useSelection' or create at least " +
"one composition.")
self._show_msg("Nothing to create. Select composition " +
"if 'useSelection' or create at least " +
"one composition.")
return False

for item in items:
txt = "Instance with name \"{}\" already exists.".format(self.name)
if self.name.lower() == item.name.lower():
self._show_msg(txt)
return False

stub.imprint(item, self.data)
stub.set_label_color(item.id, 14) # Cyan options 0 - 16
stub.rename_item(item, self.data["subset"])

def _show_msg(self, txt):
msg = Qt.QtWidgets.QMessageBox()
msg.setIcon(Qt.QtWidgets.QMessageBox.Warning)
msg.setText(txt)
msg.exec_()
51 changes: 27 additions & 24 deletions pype/plugins/aftereffects/publish/collect_render.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from pype.lib import abstract_collect_render
from pype.lib.abstract_collect_render import RenderInstance
import pyblish.api
import copy
import attr
import os

Expand Down Expand Up @@ -38,10 +37,20 @@ def get_instances(self, context):
# loaded asset container skip it
if schema and 'container' in schema:
continue
if inst["family"] == "render.farm" and inst["active"]:

work_area_info = aftereffects.stub().get_work_area(int(item_id))
frameStart = round(float(work_area_info.workAreaStart) *
float(work_area_info.frameRate))

frameEnd = round(float(work_area_info.workAreaStart) *
float(work_area_info.frameRate) +
float(work_area_info.workAreaDuration) *
float(work_area_info.frameRate))

if inst["family"] == "render" and inst["active"]:
instance = AERenderInstance(
family=inst["family"],
families=[inst["family"]],
family="render.farm", # other way integrate would catch it
families=["render.farm"],
version=version,
time="",
source=current_file,
Expand All @@ -63,8 +72,8 @@ def get_instances(self, context):
tileRendering=False,
tilesX=0,
tilesY=0,
frameStart=int(asset_entity["data"]["frameStart"]),
frameEnd=int(asset_entity["data"]["frameEnd"]),
frameStart=frameStart,
frameEnd=frameEnd,
frameStep=1,
toBeRenderedOn='deadline'
)
Expand Down Expand Up @@ -99,8 +108,7 @@ def get_expected_files(self, render_instance):
start = render_instance.frameStart
end = render_instance.frameEnd

# render to folder of workfile
base_dir = os.path.dirname(render_instance.source)
base_dir = self._get_output_dir(render_instance)
expected_files = []
for frame in range(start, end + 1):
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
Expand All @@ -116,26 +124,21 @@ def get_expected_files(self, render_instance):

def _get_output_dir(self, render_instance):
"""
Returns dir path of published asset. Required for
'submit_publish_job'.
It is different from rendered files (expectedFiles), these are
collected first in some 'staging' area, published later.
Returns dir path of rendered files, used in submit_publish_job
for metadata.json location.
Should be in separate folder inside of work area.
Args:
render_instance (RenderInstance): to pull anatomy and parts used
in url
render_instance (RenderInstance):
Returns:
(str): absolute path to published files
(str): absolute path to rendered files
"""
anatomy = render_instance._anatomy
anatomy_data = copy.deepcopy(render_instance.anatomyData)
anatomy_data["family"] = render_instance.family
anatomy_data["version"] = render_instance.version
anatomy_data["subset"] = render_instance.subset

anatomy_filled = anatomy.format(anatomy_data)
# render to folder of workfile
base_dir = os.path.dirname(render_instance.source)
file_name, _ = os.path.splitext(
os.path.basename(render_instance.source))
base_dir = os.path.join(base_dir, 'renders', 'aftereffects', file_name)

# for submit_publish_job
return anatomy_filled["render"]["folder"]
return base_dir
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import pyblish.api
import os
import attr
import json
import getpass
from avalon import api

Expand All @@ -17,7 +16,6 @@ class DeadlinePluginInfo():
StartupDirectory = attr.ib(default=None)
Arguments = attr.ib(default=None)
ProjectPath = attr.ib(default=None)
SceneFile = attr.ib(default=None)
AWSAssetFile0 = attr.ib(default=None)
Version = attr.ib(default=None)

Expand All @@ -27,7 +25,7 @@ class AfterEffectsSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline
label = "Submit AE to Deadline"
order = pyblish.api.IntegratorOrder
hosts = ["aftereffects"]
families = ["render.farm"]
families = ["render.farm"] # cannot be "render' as that is integrated
use_published = False

def get_job_info(self):
Expand Down
25 changes: 13 additions & 12 deletions pype/plugins/global/publish/submit_publish_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
families = ["render.farm", "prerener",
"renderlayer", "imagesequence", "vrayscene"]

aov_filter = {"maya": ["beauty"]}
aov_filter = {"maya": [r".+(?:\.|_)([Bb]eauty)(?:\.|_).*"],
"aftereffects": [r".*"], # for everything from AE
"celaction": [r".*"]}

enviro_filter = [
"FTRACK_API_USER",
Expand Down Expand Up @@ -447,8 +449,12 @@ def _create_instances_for_aov(self, instance_data, exp_files):

preview = False
if app in self.aov_filter.keys():
if aov in self.aov_filter[app]:
preview = True
for aov_pattern in self.aov_filter[app]:
if re.match(aov_pattern,
aov
):
preview = True
break

new_instance = copy(instance_data)
new_instance["subset"] = subset_name
Expand Down Expand Up @@ -519,23 +525,19 @@ def _get_representations(self, instance, exp_files):
preview = False
# if filtered aov name is found in filename, toggle it for
# preview video rendering
for app in self.aov_filter:
for app in self.aov_filter.keys():
if os.environ.get("AVALON_APP", "") == app:
for aov in self.aov_filter[app]:
if re.match(
r".+(?:\.|_)({})(?:\.|_).*".format(aov),
aov,
list(collection)[0]
):
preview = True
break
break

if bake_render_path:
preview = False

if "celaction" in pyblish.api.registered_hosts():
preview = True

staging = os.path.dirname(list(collection)[0])
success, rootless_staging_dir = (
self.anatomy.find_root_template_from_path(staging)
Expand All @@ -557,7 +559,7 @@ def _get_representations(self, instance, exp_files):
# If expectedFile are absolute, we need only filenames
"stagingDir": staging,
"fps": instance.get("fps"),
"tags": ["review", "preview"] if preview else [],
"tags": ["review"] if preview else [],
}

# poor man exclusion
Expand Down Expand Up @@ -709,8 +711,7 @@ def process(self, instance):
"resolutionWidth": data.get("resolutionWidth", 1920),
"resolutionHeight": data.get("resolutionHeight", 1080),
"multipartExr": data.get("multipartExr", False),
"jobBatchName": data.get("jobBatchName", ""),
"review": data.get("review", True)
"jobBatchName": data.get("jobBatchName", "")
}

if "prerender" in instance.data["families"]:
Expand Down

0 comments on commit 107052d

Please sign in to comment.