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

Commit

Permalink
#663 - Added loader for different types
Browse files Browse the repository at this point in the history
Added basic Creator
  • Loading branch information
kalisp committed Oct 30, 2020
1 parent c3018ba commit 180a45a
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 23 deletions.
109 changes: 96 additions & 13 deletions pype/modules/websocket_server/stubs/aftereffects_server_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import logging
log = logging.getLogger(__name__)


class AfterEffectsServerStub():
"""
Stub for calling function on client (Photoshop js) side.
Expand Down Expand Up @@ -47,7 +48,11 @@ def read(self, layer, layers_meta=None):
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')
)
Expand Down Expand Up @@ -85,7 +90,10 @@ def imprint(self, layer, data, all_layers=None, layers_meta=None):
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:
Expand All @@ -103,8 +111,8 @@ def get_active_document_full_name(self):
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

Expand All @@ -113,35 +121,96 @@ def get_active_document_name(self):
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
Expand All @@ -151,6 +220,20 @@ def is_saved(self):
# 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
Expand Down
52 changes: 52 additions & 0 deletions pype/plugins/aftereffects/create/create_render.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,55 @@
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,
Expand All @@ -44,7 +74,7 @@ def update(self, container, representation):
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
Expand All @@ -64,7 +94,7 @@ def remove(self, container):
"""
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)
Expand Down
Binary file added pype/resources/app_icons/aftereffects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 180a45a

Please sign in to comment.