Merge pull request #1901 from pypeclub/feature/AE_local_render

Feature AE local render
This commit is contained in:
Petr Kalis 2021-08-06 18:23:47 +02:00 committed by GitHub
commit cdda7c19e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 296 additions and 129 deletions

View file

@ -0,0 +1,17 @@
from openpype.hosts.aftereffects.plugins.create import create_render
import logging
log = logging.getLogger(__name__)
class CreateLocalRender(create_render.CreateRender):
""" Creator to render locally.
Created only after default render on farm. So family 'render.local' is
used for backward compatibility.
"""
name = "renderDefault"
label = "Render Locally"
family = "renderLocal"

View file

@ -1,10 +1,14 @@
from openpype.lib import abstract_collect_render
from openpype.lib.abstract_collect_render import RenderInstance
import pyblish.api
import attr
import os
import re
import attr
import tempfile
from avalon import aftereffects
import pyblish.api
from openpype.settings import get_project_settings
from openpype.lib import abstract_collect_render
from openpype.lib.abstract_collect_render import RenderInstance
@attr.s
@ -13,6 +17,8 @@ class AERenderInstance(RenderInstance):
comp_name = attr.ib(default=None)
comp_id = attr.ib(default=None)
fps = attr.ib(default=None)
projectEntity = attr.ib(default=None)
stagingDir = attr.ib(default=None)
class CollectAERender(abstract_collect_render.AbstractCollectRender):
@ -21,6 +27,11 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
label = "Collect After Effects Render Layers"
hosts = ["aftereffects"]
# internal
family_remapping = {
"render": ("render.farm", "farm"), # (family, label)
"renderLocal": ("render", "local")
}
padding_width = 6
rendered_extension = 'png'
@ -62,14 +73,16 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
fps = work_area_info.frameRate
# TODO add resolution when supported by extension
if inst["family"] == "render" and inst["active"]:
if inst["family"] in self.family_remapping.keys() \
and inst["active"]:
remapped_family = self.family_remapping[inst["family"]]
instance = AERenderInstance(
family="render.farm", # other way integrate would catch it
families=["render.farm"],
family=remapped_family[0],
families=[remapped_family[0]],
version=version,
time="",
source=current_file,
label="{} - farm".format(inst["subset"]),
label="{} - {}".format(inst["subset"], remapped_family[1]),
subset=inst["subset"],
asset=context.data["assetEntity"]["name"],
attachTo=False,
@ -105,6 +118,30 @@ class CollectAERender(abstract_collect_render.AbstractCollectRender):
instance.outputDir = self._get_output_dir(instance)
settings = get_project_settings(os.getenv("AVALON_PROJECT"))
reviewable_subset_filter = \
(settings["deadline"]
["publish"]
["ProcessSubmittedJobOnFarm"]
["aov_filter"])
if inst["family"] == "renderLocal":
# for local renders
instance.anatomyData["version"] = instance.version
instance.anatomyData["subset"] = instance.subset
instance.stagingDir = tempfile.mkdtemp()
instance.projectEntity = project_entity
if self.hosts[0] in reviewable_subset_filter.keys():
for aov_pattern in \
reviewable_subset_filter[self.hosts[0]]:
if re.match(aov_pattern, instance.subset):
instance.families.append("review")
instance.review = True
break
self.log.info("New instance:: {}".format(instance))
instances.append(instance)
return instances

View file

@ -0,0 +1,82 @@
import os
import six
import sys
import openpype.api
from avalon import aftereffects
class ExtractLocalRender(openpype.api.Extractor):
"""Render RenderQueue locally."""
order = openpype.api.Extractor.order - 0.47
label = "Extract Local Render"
hosts = ["aftereffects"]
families = ["render"]
def process(self, instance):
stub = aftereffects.stub()
staging_dir = instance.data["stagingDir"]
self.log.info("staging_dir::{}".format(staging_dir))
stub.render(staging_dir)
# pull file name from Render Queue Output module
render_q = stub.get_render_info()
if not render_q:
raise ValueError("No file extension set in Render Queue")
_, ext = os.path.splitext(os.path.basename(render_q.file_name))
ext = ext[1:]
first_file_path = None
files = []
self.log.info("files::{}".format(os.listdir(staging_dir)))
for file_name in os.listdir(staging_dir):
files.append(file_name)
if first_file_path is None:
first_file_path = os.path.join(staging_dir,
file_name)
resulting_files = files
if len(files) == 1:
resulting_files = files[0]
repre_data = {
"frameStart": instance.data["frameStart"],
"frameEnd": instance.data["frameEnd"],
"name": ext,
"ext": ext,
"files": resulting_files,
"stagingDir": staging_dir
}
if instance.data["review"]:
repre_data["tags"] = ["review"]
instance.data["representations"] = [repre_data]
ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg")
# Generate thumbnail.
thumbnail_path = os.path.join(staging_dir,
"thumbnail.jpg")
args = [
ffmpeg_path, "-y",
"-i", first_file_path,
"-vf", "scale=300:-1",
"-vframes", "1",
thumbnail_path
]
self.log.debug("Thumbnail args:: {}".format(args))
try:
output = openpype.lib.run_subprocess(args)
except TypeError:
self.log.warning("Error in creating thumbnail")
six.reraise(*sys.exc_info())
instance.data["representations"].append({
"name": "thumbnail",
"ext": "jpg",
"files": os.path.basename(thumbnail_path),
"stagingDir": staging_dir,
"tags": ["thumbnail"]
})

View file

@ -53,7 +53,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
order = pyblish.api.ValidatorOrder
label = "Validate Scene Settings"
families = ["render.farm"]
families = ["render.farm", "render"]
hosts = ["aftereffects"]
optional = True

View file

@ -44,7 +44,8 @@ class ExtractBurnin(openpype.api.Extractor):
"harmony",
"fusion",
"aftereffects",
"tvpaint"
"tvpaint",
"aftereffects"
# "resolve"
]
optional = True

View file

@ -44,7 +44,8 @@ class ExtractReview(pyblish.api.InstancePlugin):
"standalonepublisher",
"fusion",
"tvpaint",
"resolve"
"resolve",
"aftereffects"
]
# Supported extensions

View file

@ -11,6 +11,30 @@
"deadline"
]
},
"ProcessSubmittedJobOnFarm": {
"enabled": true,
"deadline_department": "",
"deadline_pool": "",
"deadline_group": "",
"deadline_chunk_size": 1,
"deadline_priority": 50,
"publishing_script": "",
"skip_integration_repre_list": [],
"aov_filter": {
"maya": [
".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*"
],
"nuke": [
".*"
],
"aftereffects": [
".*"
],
"celaction": [
".*"
]
}
},
"MayaSubmitDeadline": {
"enabled": true,
"optional": false,

View file

@ -298,6 +298,17 @@
"add_ftrack_family": true
}
]
},
{
"hosts": [
"aftereffects"
],
"families": [
"render"
],
"tasks": [],
"add_ftrack_family": true,
"advanced_filtering": []
}
]
},

View file

@ -173,28 +173,6 @@
}
]
},
"ProcessSubmittedJobOnFarm": {
"enabled": true,
"deadline_department": "",
"deadline_pool": "",
"deadline_group": "",
"deadline_chunk_size": 1,
"deadline_priority": 50,
"publishing_script": "",
"skip_integration_repre_list": [],
"aov_filter": {
"maya": [
".+(?:\\.|_)([Bb]eauty)(?:\\.|_).*"
],
"nuke": [],
"aftereffects": [
".*"
],
"celaction": [
".*"
]
}
},
"CleanUp": {
"paterns": [],
"remove_temp_renders": false
@ -257,6 +235,16 @@
],
"tasks": [],
"template": "{family}{Task}"
},
{
"families": [
"renderLocal"
],
"hosts": [
"aftereffects"
],
"tasks": [],
"template": "render{Task}{Variant}"
}
]
},

View file

@ -52,6 +52,101 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "ProcessSubmittedJobOnFarm",
"label": "ProcessSubmittedJobOnFarm",
"checkbox_key": "enabled",
"is_group": true,
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "text",
"key": "deadline_department",
"label": "Deadline department"
},
{
"type": "text",
"key": "deadline_pool",
"label": "Deadline Pool"
},
{
"type": "text",
"key": "deadline_group",
"label": "Deadline Group"
},
{
"type": "number",
"key": "deadline_chunk_size",
"label": "Deadline Chunk Size"
},
{
"type": "number",
"key": "deadline_priority",
"label": "Deadline Priotity"
},
{
"type": "splitter"
},
{
"type": "text",
"key": "publishing_script",
"label": "Publishing script path"
},
{
"type": "list",
"key": "skip_integration_repre_list",
"label": "Skip integration of representation with ext",
"object_type": {
"type": "text"
}
},
{
"type": "dict",
"key": "aov_filter",
"label": "Reviewable subsets filter",
"children": [
{
"type": "list",
"key": "maya",
"label": "Maya",
"object_type": {
"type": "text"
}
},
{
"type": "list",
"key": "nuke",
"label": "Nuke",
"object_type": {
"type": "text"
}
},
{
"type": "list",
"key": "aftereffects",
"label": "After Effects",
"object_type": {
"type": "text"
}
},
{
"type": "list",
"key": "celaction",
"label": "Celaction",
"object_type": {
"type": "text"
}
}
]
}
]
},
{
"type": "dict",
"collapsible": true,

View file

@ -556,101 +556,6 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "ProcessSubmittedJobOnFarm",
"label": "ProcessSubmittedJobOnFarm",
"checkbox_key": "enabled",
"is_group": true,
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "text",
"key": "deadline_department",
"label": "Deadline department"
},
{
"type": "text",
"key": "deadline_pool",
"label": "Deadline Pool"
},
{
"type": "text",
"key": "deadline_group",
"label": "Deadline Group"
},
{
"type": "number",
"key": "deadline_chunk_size",
"label": "Deadline Chunk Size"
},
{
"type": "number",
"key": "deadline_priority",
"label": "Deadline Priotity"
},
{
"type": "splitter"
},
{
"type": "text",
"key": "publishing_script",
"label": "Publishing script path"
},
{
"type": "list",
"key": "skip_integration_repre_list",
"label": "Skip integration of representation with ext",
"object_type": {
"type": "text"
}
},
{
"type": "dict",
"key": "aov_filter",
"label": "Reviewable subsets filter",
"children": [
{
"type": "list",
"key": "maya",
"label": "Maya",
"object_type": {
"type": "text"
}
},
{
"type": "list",
"key": "nuke",
"label": "Nuke",
"object_type": {
"type": "text"
}
},
{
"type": "list",
"key": "aftereffects",
"label": "After Effects",
"object_type": {
"type": "text"
}
},
{
"type": "list",
"key": "celaction",
"label": "Celaction",
"object_type": {
"type": "text"
}
}
]
}
]
},
{
"type": "dict",
"collapsible": true,

View file

@ -22,7 +22,7 @@ Drag extension.zxp and drop it to Anastasyi's Extension Manager. The extension w
## Implemented functionality
AfterEffects implementation currently allows you to import and add various media to composition (image plates, renders, audio files, video files etc.)
and send prepared composition for rendering to Deadline.
and send prepared composition for rendering to Deadline or render locally.
## Usage
@ -53,6 +53,12 @@ will be changed.
### Publish
#### RenderQueue
AE's Render Queue is required for publishing locally or on a farm. Artist needs to configure expected result format (extension, resolution) in the Render Queue in an Output module. Currently its expected to have only single render item and single output module in the Render Queue.
AE might throw some warning windows during publishing locally, so please pay attention to them in a case publishing seems to be stuck in a `Extract Local Render`.
When you are ready to share your work, you will need to publish it. This is done by opening the `Publish` by clicking the corresponding button in the OpenPype Panel.
![Publish](assets/aftereffects_publish.png)