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

After Effects: Review were not being sent to ftrack #738

Merged
merged 6 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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