#663 - Added loader for different types

Added basic Creator
This commit is contained in:
Petr Kalis 2020-10-30 12:46:00 +01:00
parent c3018baa07
commit 180a45ac2e
4 changed files with 188 additions and 23 deletions

View file

@ -10,6 +10,7 @@ from collections import namedtuple
import logging
log = logging.getLogger(__name__)
class AfterEffectsServerStub():
"""
Stub for calling function on client (Photoshop js) side.
@ -47,7 +48,11 @@ class AfterEffectsServerStub():
return layers_meta.get(str(layer.id))
def get_metadata(self):
layers_data = {}
"""
Get stored JSON with metadata from AE.Metadata.Label field
Returns:
(dict)
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_metadata')
)
@ -85,7 +90,10 @@ class AfterEffectsServerStub():
layers_meta[str(layer.id)] = data
# Ensure only valid ids are stored.
if not all_layers:
all_layers = self.get_items(False)
# loaders create FootagetItem now
all_layers = self.get_items(comps=True,
folders=False,
footages=True)
item_ids = [int(item.id) for item in all_layers]
cleaned_data = {}
for id in layers_meta:
@ -103,8 +111,8 @@ class AfterEffectsServerStub():
Returns just a name of active document via ws call
Returns(string): file name
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_active_document_full_name'))
res = self.websocketserver.call(self.client.call(
'AfterEffects.get_active_document_full_name'))
return res
@ -113,35 +121,96 @@ class AfterEffectsServerStub():
Returns just a name of active document via ws call
Returns(string): file name
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_active_document_name'))
res = self.websocketserver.call(self.client.call(
'AfterEffects.get_active_document_name'))
return res
def get_items(self, layers=True):
def get_items(self, comps, folders=False, footages=False):
"""
Get all items from Project panel according to arguments.
There are mutliple different types:
CompItem (could have multiple layers - source for Creator)
FolderItem (collection type, currently not used
FootageItem (imported file - created by Loader)
Args:
comps (bool): return CompItems
folders (bool): return FolderItem
footages (bool: return FootageItem
Returns:
(list) of namedtuples
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_items',
layers=layers)
comps=comps,
folders=folders,
footages=footages)
)
return self._to_records(res)
def import_file(self, path, item_name):
def get_selected_items(self, comps, folders=False, footages=False):
"""
Same as get_items but using selected items only
Args:
comps (bool): return CompItems
folders (bool): return FolderItem
footages (bool: return FootageItem
Returns:
(list) of namedtuples
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_selected_items',
comps=comps,
folders=folders,
footages=footages)
)
return self._to_records(res)
def import_file(self, path, item_name, import_options=None):
"""
Imports file as a FootageItem. Used in Loader
Args:
path (string): absolute path for asset file
item_name (string): label for created FootageItem
import_options (dict): different files (img vs psd) need different
config
"""
res = self.websocketserver.call(self.client.call(
'AfterEffects.import_file',
path=path,
item_name=item_name)
item_name=item_name,
import_options=import_options)
)
return self._to_records(res).pop()
records = self._to_records(res)
if records:
return records.pop()
log.debug("Couldn't import {} file".format(path))
def replace_item(self, item, path, item_name):
""" item is currently comp, might be layer, investigate TODO """
""" Replace FootageItem with new file
Args:
item (dict):
path (string):absolute path
item_name (string): label on item in Project list
"""
self.websocketserver.call(self.client.call
('AfterEffects.replace_item',
item_id=item.id,
path=path, item_name=item_name))
def delete_item(self, item):
""" item is currently comp, might be layer, investigate TODO """
""" Deletes FootageItem with new file
Args:
item (dict):
"""
self.websocketserver.call(self.client.call
('AfterEffects.delete_item',
item_id=item.id
@ -151,6 +220,20 @@ class AfterEffectsServerStub():
# TODO
return True
def set_label_color(self, item_id, color_idx):
"""
Used for highlight additional information in Project panel.
Green color is loaded asset, blue is created asset
Args:
item_id (int):
color_idx (int): 0-16 Label colors from AE Project view
"""
self.websocketserver.call(self.client.call
('AfterEffects.set_label_color',
item_id=item_id,
color_idx=color_idx
))
def save(self):
"""
Saves active document

View file

@ -0,0 +1,52 @@
from avalon import api
from avalon.vendor import Qt
from avalon import aftereffects
import logging
log = logging.getLogger(__name__)
class CreateRender(api.Creator):
"""Render folder for publish."""
name = "renderDefault"
label = "Render"
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))
if not items:
raise ValueError("Nothing to create. Select composition " +
"if 'useSelection' or create at least " +
"one composition.")
for item in items:
stub.imprint(item, self.data)
stub.set_label_color(item.id, 14) # Cyan options 0 - 16

View file

@ -5,25 +5,55 @@ import re
stub = aftereffects.stub()
class ImageLoader(api.Loader):
class ResourceLoader(api.Loader):
"""Load images
Stores the imported asset in a container named after the asset.
"""
families = ["image"]
families = ["image",
"render2d",
"source",
"plate",
"render",
"prerender",
"review",
"preview",
"workfile"]
representations = ["*"]
def load(self, context, name=None, namespace=None, data=None):
print("Load:::")
layer_name = lib.get_unique_layer_name(stub.get_items(False),
context["asset"]["name"],
name)
comp_name = lib.get_unique_layer_name(stub.get_items(comps=True),
context["asset"]["name"],
name)
import_options = {}
file = self.fname
repr_cont = context["representation"]["context"]
if "#" not in file:
frame = repr_cont.get("frame")
if frame:
padding = len(frame)
file = file.replace(frame, "#" * padding)
import_options['sequence'] = True
if not file:
repr_id = context["representation"]["_id"]
self.log.warning(
"Representation id `{}` is failing to load".format(repr_id))
return
file = file.replace("\\", "/")
if '.psd' in file:
import_options['ImportAsType'] = 'ImportAsType.COMP'
#with photoshop.maintained_selection():
comp = stub.import_file(self.fname, layer_name)
comp = stub.import_file(self.fname, comp_name, import_options)
self[:] = [comp]
namespace = namespace or layer_name
namespace = namespace or comp_name
return aftereffects.containerise(
name,
@ -44,7 +74,7 @@ class ImageLoader(api.Loader):
layer_name = "{}_{}".format(context["asset"], context["subset"])
# switching assets
if namespace_from_container != layer_name:
layer_name = lib.get_unique_layer_name(stub.get_items(False),
layer_name = lib.get_unique_layer_name(stub.get_items(comps=True),
context["asset"],
context["subset"])
else: # switching version - keep same name
@ -64,7 +94,7 @@ class ImageLoader(api.Loader):
"""
layer = container.pop("layer")
stub.imprint(layer, {})
stub.delete_layer(layer.id)
stub.delete_item(layer.id)
def switch(self, container, representation):
self.update(container, representation)

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB