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

AfterEffects: publish multiple compositions #4092

Merged
Binary file modified openpype/hosts/aftereffects/api/extension.zxp
Binary file not shown.
4 changes: 2 additions & 2 deletions openpype/hosts/aftereffects/api/extension/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ function main(websocket_url){

RPC.addRoute('AfterEffects.get_render_info', function (data) {
log.warn('Server called client route "get_render_info":', data);
return runEvalScript("getRenderInfo()")
return runEvalScript("getRenderInfo(" + data.comp_id +")")
.then(function(result){
log.warn("get_render_info: " + result);
return result;
Expand Down Expand Up @@ -289,7 +289,7 @@ function main(websocket_url){
RPC.addRoute('AfterEffects.render', function (data) {
log.warn('Server called client route "render":', data);
var escapedPath = EscapeStringForJSX(data.folder_url);
return runEvalScript("render('" + escapedPath +"')")
return runEvalScript("render('" + escapedPath +"', " + data.comp_id + ")")
.then(function(result){
log.warn("render: " + result);
return result;
Expand Down
155 changes: 105 additions & 50 deletions openpype/hosts/aftereffects/api/extension/jsx/hostscript.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -395,41 +395,84 @@ function saveAs(path){
app.project.save(fp = new File(path));
}

function getRenderInfo(){
function getRenderInfo(comp_id){
/***
Get info from render queue.
Currently pulls only file name to parse extension and
Currently pulls only file name to parse extension and
if it is sequence in Python
Args:
comp_id (int): id of composition
Return:
(list) [{file_name:"xx.png", width:00, height:00}]
**/
var item = app.project.itemByID(comp_id);
if (!item){
return _prepareError("Composition with '" + comp_id + "' wasn't found! Recreate publishable instance(s)")
}

var comp_name = item.name;
var output_metadata = []
try{
var render_item = app.project.renderQueue.item(1);
if (render_item.status == RQItemStatus.DONE){
render_item.duplicate(); // create new, cannot change status if DONE
render_item.remove(); // remove existing to limit duplications
render_item = app.project.renderQueue.item(1);
// render_item.duplicate() should create new item on renderQueue
// BUT it works only sometimes, there are some weird synchronization issue
// this method will be called always before render, so prepare items here
// for render to spare the hassle
for (i = 1; i <= app.project.renderQueue.numItems; ++i){
var render_item = app.project.renderQueue.item(i);
if (render_item.comp.id != comp_id){
continue;
}

if (render_item.status == RQItemStatus.DONE){
render_item.duplicate(); // create new, cannot change status if DONE
render_item.remove(); // remove existing to limit duplications
continue;
}
}

render_item.render = true; // always set render queue to render
var item = render_item.outputModule(1);
// properly validate as `numItems` won't change magically
var comp_id_count = 0;
for (i = 1; i <= app.project.renderQueue.numItems; ++i){
var render_item = app.project.renderQueue.item(i);
if (render_item.comp.id != comp_id){
continue;
}
comp_id_count += 1;
var item = render_item.outputModule(1);

for (j = 1; j<= render_item.numOutputModules; ++j){
var file_url = item.file.toString();
output_metadata.push(
JSON.stringify({
"file_name": file_url,
"width": render_item.comp.width,
"height": render_item.comp.height
})
);
}
}
} catch (error) {
return _prepareError("There is no render queue, create one");
}
var file_url = item.file.toString();

return JSON.stringify({
"file_name": file_url,
"width": render_item.comp.width,
"height": render_item.comp.height
})
if (comp_id_count > 1){
return _prepareError("There cannot be more items in Render Queue for '" + comp_name + "'!")
}

if (comp_id_count == 0){
return _prepareError("There is no item in Render Queue for '" + comp_name + "'! Add composition to Render Queue.")
}

return '[' + output_metadata.join() + ']';
}

function getAudioUrlForComp(comp_id){
/**
* Searches composition for audio layer
*
*
* Only single AVLayer is expected!
* Used for collecting Audio
*
*
* Args:
* comp_id (int): id of composition
* Return:
Expand Down Expand Up @@ -457,7 +500,7 @@ function addItemAsLayerToComp(comp_id, item_id, found_comp){
/**
* Adds already imported FootageItem ('item_id') as a new
* layer to composition ('comp_id').
*
*
* Args:
* comp_id (int): id of target composition
* item_id (int): FootageItem.id
Expand All @@ -480,17 +523,17 @@ function addItemAsLayerToComp(comp_id, item_id, found_comp){
function importBackground(comp_id, composition_name, files_to_import){
/**
* Imports backgrounds images to existing or new composition.
*
*
* If comp_id is not provided, new composition is created, basic
* values (width, heights, frameRatio) takes from first imported
* image.
*
*
* Args:
* comp_id (int): id of existing composition (null if new)
* composition_name (str): used when new composition
* composition_name (str): used when new composition
* files_to_import (list): list of absolute paths to import and
* add as layers
*
*
* Returns:
* (str): json representation (id, name, members)
*/
Expand All @@ -512,7 +555,7 @@ function importBackground(comp_id, composition_name, files_to_import){
}
}
}

if (files_to_import){
for (i = 0; i < files_to_import.length; ++i){
item = _importItem(files_to_import[i]);
Expand All @@ -524,8 +567,8 @@ function importBackground(comp_id, composition_name, files_to_import){
if (!comp){
folder = app.project.items.addFolder(composition_name);
imported_ids.push(folder.id);
comp = app.project.items.addComp(composition_name, item.width,
item.height, item.pixelAspect,
comp = app.project.items.addComp(composition_name, item.width,
item.height, item.pixelAspect,
1, 26.7); // hardcode defaults
imported_ids.push(comp.id);
comp.parentFolder = folder;
Expand All @@ -534,7 +577,7 @@ function importBackground(comp_id, composition_name, files_to_import){
item.parentFolder = folder;

addItemAsLayerToComp(comp.id, item.id, comp);
}
}
}
var item = {"name": comp.name,
"id": folder.id,
Expand All @@ -545,19 +588,19 @@ function importBackground(comp_id, composition_name, files_to_import){
function reloadBackground(comp_id, composition_name, files_to_import){
/**
* Reloads existing composition.
*
*
* It deletes complete composition with encompassing folder, recreates
* from scratch via 'importBackground' functionality.
*
*
* Args:
* comp_id (int): id of existing composition (null if new)
* composition_name (str): used when new composition
* composition_name (str): used when new composition
* files_to_import (list): list of absolute paths to import and
* add as layers
*
*
* Returns:
* (str): json representation (id, name, members)
*
*
*/
var imported_ids = []; // keep track of members of composition
comp = app.project.itemByID(comp_id);
Expand Down Expand Up @@ -620,7 +663,7 @@ function reloadBackground(comp_id, composition_name, files_to_import){
function _get_file_name(file_url){
/**
* Returns file name without extension from 'file_url'
*
*
* Args:
* file_url (str): full absolute url
* Returns:
Expand All @@ -635,7 +678,7 @@ function _delete_obsolete_items(folder, new_filenames){
/***
* Goes through 'folder' and removes layers not in new
* background
*
*
* Args:
* folder (FolderItem)
* new_filenames (array): list of layer names in new bg
Expand All @@ -660,14 +703,14 @@ function _delete_obsolete_items(folder, new_filenames){
function _importItem(file_url){
/**
* Imports 'file_url' as new FootageItem
*
*
* Args:
* file_url (str): file url with content
* Returns:
* (FootageItem)
*/
file_name = _get_file_name(file_url);

//importFile prepared previously to return json
item_json = importFile(file_url, file_name, JSON.stringify({"ImportAsType":"FOOTAGE"}));
item_json = JSON.parse(item_json);
Expand All @@ -689,30 +732,42 @@ function isFileSequence (item){
return false;
}

function render(target_folder){
function render(target_folder, comp_id){
var out_dir = new Folder(target_folder);
var out_dir = out_dir.fsName;
for (i = 1; i <= app.project.renderQueue.numItems; ++i){
var render_item = app.project.renderQueue.item(i);
var om1 = app.project.renderQueue.item(i).outputModule(1);
var file_name = File.decode( om1.file.name ).replace('℗', ''); // Name contains special character, space?

var omItem1_settable_str = app.project.renderQueue.item(i).outputModule(1).getSettings( GetSettingsFormat.STRING_SETTABLE );
var composition = render_item.comp;
if (composition.id == comp_id){
if (render_item.status == RQItemStatus.DONE){
var new_item = render_item.duplicate();
render_item.remove();
render_item = new_item;
}

if (render_item.status == RQItemStatus.DONE){
render_item.duplicate();
render_item.remove();
continue;
}
render_item.render = true;

var targetFolder = new Folder(target_folder);
if (!targetFolder.exists) {
targetFolder.create();
}
var om1 = app.project.renderQueue.item(i).outputModule(1);
var file_name = File.decode( om1.file.name ).replace('℗', ''); // Name contains special character, space?

var omItem1_settable_str = app.project.renderQueue.item(i).outputModule(1).getSettings( GetSettingsFormat.STRING_SETTABLE );

var targetFolder = new Folder(target_folder);
if (!targetFolder.exists) {
targetFolder.create();
}

om1.file = new File(targetFolder.fsName + '/' + file_name);
om1.file = new File(targetFolder.fsName + '/' + file_name);
}else{
if (render_item.status != RQItemStatus.DONE){
render_item.render = false;
}
}

}
app.beginSuppressDialogs();
app.project.renderQueue.render();
app.endSuppressDialogs(false);
}

function close(){
Expand Down
15 changes: 8 additions & 7 deletions openpype/hosts/aftereffects/api/ws_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +418,18 @@ def saveAs(self, project_path, as_copy):

return self._handle_return(res)

def get_render_info(self):
def get_render_info(self, comp_id):
""" Get render queue info for render purposes

Returns:
(AEItem): with 'file_name' field
(list) of (AEItem): with 'file_name' field
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.get_render_info'))
('AfterEffects.get_render_info',
comp_id=comp_id))

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

def get_audio_url(self, item_id):
""" Get audio layer absolute url for comp
Expand Down Expand Up @@ -522,7 +522,7 @@ def add_item_as_layer(self, comp_id, item_id):
if records:
return records.pop()

def render(self, folder_url):
def render(self, folder_url, comp_id):
"""
Render all renderqueueitem to 'folder_url'
Args:
Expand All @@ -531,7 +531,8 @@ def render(self, folder_url):
"""
res = self.websocketserver.call(self.client.call
('AfterEffects.render',
folder_url=folder_url))
folder_url=folder_url,
comp_id=comp_id))
return self._handle_return(res)

def get_extension_version(self):
Expand Down
Loading