Difference between revisions of "Afx Javascript Temp"
Jump to navigation
Jump to search
m (→Local Proxy) |
(No difference)
|
Revision as of 20:03, 1 April 2021
Local Proxy
/*
* simpleLocalProxy.jsx v1.0
* https://github.com/berniebernie/after-effects-scripts
*
* Copyright 2015, bernie@berniebernie.fr
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*
* This script embeds js-md5 from github: https://github.com/emn178/js-md5 for practical reasons
*
* Script that can be launched or put in the scriptui folder of After Effects to be used as a panel
*
* Copies footage from its current location to one chosen by the user (defaults to /tmp/) and allows to
* switch original-proxy with a button
* To be used when file i/o is slow
*
*
*/
/*
* js-md5 v0.1.2
* https://github.com/emn178/js-md5
*
* Copyright 2014, emn178@gmail.com
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
/************************************************************************************************************
*
*
*
* js-md5.js
*
*
*
************************************************************************************************************/
(function(root, undefined){
'use strict';
var HEX_CHARS = "0123456789abcdef";
var HEX_TABLE = {
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15,
'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15
};
var R = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];
var K = [0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE,
0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,
0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,
0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821,
0XF61E2562, 0XC040B340, 0X265E5A51, 0XE9B6C7AA,
0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8,
0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED,
0XA9E3E905, 0XFCEFA3F8, 0X676F02D9, 0X8D2A4C8A,
0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C,
0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70,
0X289B7EC6, 0XEAA127FA, 0XD4EF3085, 0X04881D05,
0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665,
0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039,
0X655B59C3, 0X8F0CCC92, 0XFFEFF47D, 0X85845DD1,
0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1,
0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391];
var jsmd5 = function(message) {
var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message);
var h0 = 0x67452301;
var h1 = 0xEFCDAB89;
var h2 = 0x98BADCFE;
var h3 = 0x10325476;
for(var i = 0, length = blocks.length;i < length;i += 16)
{
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var f, g, tmp, x, y;
for(var j = 0;j < 64;++j)
{
if(j < 16)
{
// f = (b & c) | ((~b) & d);
f = d ^ (b & (c ^ d));
g = j;
}
else if(j < 32)
{
// f = (d & b) | ((~d) & c);
f = c ^ (d & (b ^ c));
g = (5 * j + 1) % 16;
}
else if(j < 48)
{
f = b ^ c ^ d;
g = (3 * j + 5) % 16;
}
else
{
f = c ^ (b | (~d));
g = (7 * j) % 16;
}
tmp = d;
d = c
c = b
// leftrotate
x = (a + f + K[j] + blocks[i + g]);
y = R[j];
b += (x << y) | (x >>> (32 - y));
a = tmp;
}
h0 = (h0 + a) | 0;
h1 = (h1 + b) | 0;
h2 = (h2 + c) | 0;
h3 = (h3 + d) | 0;
}
return toHexString(h0) + toHexString(h1) + toHexString(h2) + toHexString(h3);
};
var toHexString = function(num) {
var hex = "";
for(var i = 0; i < 4; i++)
{
var offset = i << 3;
hex += HEX_CHARS.charAt((num >> (offset + 4)) & 0x0F) + HEX_CHARS.charAt((num >> offset) & 0x0F);
}
return hex;
};
var hasUTF8 = function(message) {
var i = message.length;
while(i--)
if(message.charCodeAt(i) > 127)
return true;
return false;
};
var ASCIItoBlocks = function(message) {
// a block is 32 bits(4 bytes), a chunk is 512 bits(64 bytes)
var length = message.length;
var chunkCount = ((length + 8) >> 6) + 1;
var blockCount = chunkCount << 4; // chunkCount * 16
var blocks = [];
var i;
for(i = 0;i < blockCount;++i)
blocks[i] = 0;
for(i = 0;i < length;++i)
blocks[i >> 2] |= message.charCodeAt(i) << ((i % 4) << 3);
blocks[i >> 2] |= 0x80 << ((i % 4) << 3);
blocks[blockCount - 2] = length << 3; // length * 8
return blocks;
};
var UTF8toBlocks = function(message) {
var uri = encodeURIComponent(message);
var blocks = [];
for(var i = 0, bytes = 0, length = uri.length;i < length;++i)
{
var c = uri.charCodeAt(i);
if(c == 37) // %
blocks[bytes >> 2] |= ((HEX_TABLE[uri.charAt(++i)] << 4) | HEX_TABLE[uri.charAt(++i)]) << ((bytes % 4) << 3);
else
blocks[bytes >> 2] |= c << ((bytes % 4) << 3);
++bytes;
}
var chunkCount = ((bytes + 8) >> 6) + 1;
var blockCount = chunkCount << 4; // chunkCount * 16
var index = bytes >> 2;
blocks[index] |= 0x80 << ((bytes % 4) << 3);
for(var i = index + 1;i < blockCount;++i)
blocks[i] = 0;
blocks[blockCount - 2] = bytes << 3; // bytes * 8
return blocks;
};
if(typeof(module) != 'undefined')
module.exports = jsmd5;
else if(root)
root.jsmd5 = jsmd5;
}(this));
/************************************************************************************************************
*
*
*
* simpleLocalProxy.jsx
*
*
*
************************************************************************************************************/
function e(str){
$.writeln(str);
}
function pathToLocalizedPath(path){
f = new File(path);
return f.fsName.toString();
}
function sequenceFilesWildcard(path){
//returns false or an array with everything before a sequence's image number, and the extension: /c/path/file.0555.exr > { /c/pathfile. ; .exr }
var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g;
var match = myRegexp.exec(path);
if(!match){
return false;
}else{
return match;
}
}
function grabPaths(footage){
//returns false or the path of the given footage, if it's a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr > /c/path/file.*.exr
var f = footage;
returnpath = false;
if(f instanceof FootageItem && f.file != null){
var source = f.mainSource.file.toString();
if(!f.mainSource.isStill){
var pathFromRegex = sequenceFilesWildcard(source);
if(pathFromRegex){
returnpath = pathFromRegex[1]+"*"+pathFromRegex[2];
}else{
returnpath = source;
}
}else{
returnpath = source;
}
}
return returnpath;
}
function grabFootagePathsAndCopyToLocal(){
//main worker function
var sel = app.project.selection;
if(sel.length < 1 || !(sel[0] instanceof FootageItem)){
alert("Select footage(s) and try again");
}else{
var debugcheck = (getPref("debug","false")=="true")?true:false;
var useforcecopy = (getPref("forcecopy","false")=="true")?true:false;
var isMacintosh = ($.os.toLowerCase().indexOf("windows")==-1);
var localSaveDir = getPref("localSaveDir",Folder.temp.toString());
var batchFile = (isMacintosh)?"# bash file used to copy After Effects footage to local storage":"@echo off\nREM batch file to copy After Effects footage to local storage";
for(i=0;i<app.project.selection.length;i++){
if(!sel[i].useProxy){
//grab path from current selection item
var curPath = grabPaths(sel[i]);
var fileName = curPath.split('/').pop();
var dir = curPath.substring(0,curPath.lastIndexOf('/')+1);
var outputDir = "";
if(getPref("usemd5",true)=="true"){
outputDir = localSaveDir + "/"+ jsmd5(dir);
}else{
outputDir = localSaveDir + dir;
}
if(isMacintosh){
//macos uses rsync to copy files
batchFile += "\nrsync -v -a ";
batchFile += ((useforcecopy)?"-I ":"");
batchFile += dir+fileName;
batchFile += " "+outputDir;
}else{
//windows uses robocopy
batchFile += "\nrobocopy ";
batchFile += pathToLocalizedPath(dir);
//robocopy filename requires a wildcard, even if it's a single file
batchFile += " \""+fileName.replace(/%20/g, " ")+"*\" ";
batchFile += pathToLocalizedPath(outputDir);
batchFile += ((useforcecopy)?" /XO":"")+" /FFT"+((debugcheck)?"":" /NJH /NJS");
}
}
}
batchFile += ((debugcheck)?(isMacintosh?"\nread a":"\npause"):""); //nested tertiary operators, sue me
var txtFile;
if(isMacintosh){
txtFile = new File(localSaveDir+"/AFX_footage_copy.command");
}else{
txtFile = new File(localSaveDir+"/AFX_footage_copy.bat");
}
if(txtFile.open("w","TEXT","????") == true){
txtFile.write(batchFile);
txtFile.close();
if(isMacintosh){
system.callSystem("chmod +x " + txtFile.toString() +"");
system.callSystem(txtFile.toString());
}else{
txtFile.execute();
}
}else{
alert("Write permission denied on\n"+localSaveDir);
}
//txtFile.remove();
}
}
function grabFootagePathsAndSwitchProxy(){
var sel = app.project.selection;
if(sel.length < 1){
alert("Select footage first");
}else{
for(i=0;i<app.project.selection.length;i++){
item = sel[i];
if(item.useProxy){
item.useProxy = false;
}else{
var curPath = grabPaths(item);
var fileName = item.mainSource.file.toString().split('/').pop();
var dir = curPath.substring(0,curPath.lastIndexOf('/')+1);
var localSaveDir = getPref("localSaveDir",Folder.temp.toString());
var outputDir = "";
if(getPref("usemd5",true)=="true"){
outputDir = localSaveDir + "/"+ jsmd5(dir);
}else{
outputDir = localSaveDir + dir;
}
var proxyFilePath = outputDir+"/"+fileName;
var proxyFile = new File(proxyFilePath);
e(proxyFilePath);
if(proxyFile.exists){
if(item.mainSource.isStill){
item.setProxy(proxyFile);
}else{
//dirty workaround if the file is a movie file (.mpg, qucktime etc...)
try {
item.setProxyWithSequence(proxyFile,false);
}
catch(err) {
item.setProxy(proxyFile);
}
}
item.proxySource.alphaMode = item.mainSource.alphaMode;
item.proxySource.premulColor = item.mainSource.premulColor;
item.proxySource.invertAlpha= item.mainSource.invertAlpha;
}else{
if(getPref("debug",false)=="true"){
alert(">>> NO PROXY FOUND\n"+proxyFilePath);
}
}
}
}
}
}
function setPref(pref,value){
app.settings.saveSetting("simpleLocalProxy", pref, value);
}
function getPref(pref,defaultValue){
prefsVar = "simpleLocalProxy";
if(app.settings.haveSetting(prefsVar, pref)){
return app.settings.getSetting(prefsVar, pref);
}else{
app.settings.saveSetting(prefsVar, pref, defaultValue);
setPref(pref,defaultValue)
return defaultValue;
}
}
function simpleLocalProxy(thisObj) {
pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Simple Local Proxy", [100, 100, 300, 300]);
var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
if (securitySetting != 1) {
pan.add("statictext",[15,15,300,45],"Set prefs and re-launch");
alert("You need to check \"Allow Scripts to Write Files and Access Network\" in your preferences for this script to work");
}else{
var localFolder = getPref("localSaveDir",Folder.temp.toString());
localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,"\\\\");
// UI DESCRIPTION
res = "group { alignment: ['fill','fill'], alignChildren: ['fill','top'], orientation: 'column', \
cols: Group {orientation:'row',align:'left', alignChildren:['fill','top'],\
col1: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
saveDirText: StaticText {text: 'Local Copy folder setup: '},\
saveDirOptions: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
usemd5rbox: RadioButton {text: ' Simple',helpTip:'Uses a unique folder name per footage, requires md5.js',value:true},\
usepathrbox: RadioButton {text: ' Copy Folder Structure',helpTip:'Copies the target folder structure inside the local folder'}}},\
col2: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
optionsText: StaticText {text: 'Options: '},\
optionsGrp: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
forcecopyChkbox: Checkbox {text: ' Force copy',helpTip:'Overwrite local files (if your render has changed for instance)'},\
debugChkbox: Checkbox {text: ' Debug',helpTip:'Show full batch process and pause at end of copies'}}}},\
cols2: Group {orientation:'row',align:'left', alignChildren:['fill','top'],\
localSaveDirBut: Button {text: ' Choose Local Folder ' ,preferredSize:[-1,30]} , \
browseBut: Button {text: ' Browse ' , preferredSize:[-1,30]}} , \
curentDirTxt: EditText {text: '" + localFolder +"',enabled:false},\
copyFootageBut: Button {text: ' Copy Footage(s) to Local Folder ' ,helpTip:'Launches a background batch copy of selected footage',preferredSize:[-1,30]} , \
switchproxyBut: Button {text: ' Switch Local Proxy/Original ',helpTip:'Switches from original to proxy path and back, warning if no local copy has been found' ,preferredSize:[-1,30]} , \
}";
//UI DRAW
pan.grp = pan.add(res);
pan.layout.layout(true);
//radio buttons and checkboxes prefs
var usemd5 = getPref("usemd5",true);
pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5=="true")?true:false;
pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5=="true")?false:true;
var useforcecopy = getPref("forcecopy","false");
pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy=="true")?true:false;
var debugcheck = getPref("debug","false");
pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck=="true")?true:false;
pan.layout.resize();
pan.onResizing = pan.onResize = function () {this.layout.resize();}
// UI ACTIONS
//browse button
pan.grp.cols2.browseBut.onClick = function(){
localSaveDir = new Folder(getPref("localSaveDir",Folder.temp.toString()));
localSaveDir.execute();
}
//choose local folder button
pan.grp.cols2.localSaveDirBut.onClick = function(){
localSaveDir = new Folder(getPref("localSaveDir",Folder.temp.toString()));
o = localSaveDir.selectDlg("Choose folder to copy footage to");
if(o!=null){
setPref("localSaveDir",o.toString());
pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,"\\\\");
}
}
pan.grp.copyFootageBut.onClick = function(){
//copy footages button (launches the 'guts' of this script)
//save prefs
var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;
var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;
var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;
setPref("usemd5",usemd5);
setPref("forcecopy",useforcecopy);
setPref("debug",debugcheck);
grabFootagePathsAndCopyToLocal();
}
pan.grp.switchproxyBut.onClick = function(){
//checks for a local copy of the footage, and switches to a proxy accordingly
//save prefs
var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;
var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;
var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;
setPref("usemd5",usemd5);
setPref("forcecopy",useforcecopy);
setPref("debug",debugcheck);
grabFootagePathsAndSwitchProxy();
}
}
if (pan instanceof Window) pan.show() ;
}
simpleLocalProxy(this);
needs (optional) md5.js from https://github.com/blueimp/JavaScript-MD5/blob/master/js/md5.js next to the current file
function e(str){
$.writeln(str);
}
function pathToWinPath(path){
/*var str = path.toString().replace(/\//, "");
str = str.replace(/\//, ":/");
str = str.replace(/%20/g, " ");
str = str.replace(/\//g, "\\");*/
f = new File(path);
return f.fsName.toString();
}
function ismd5available(){
var currentScript = new File($.fileName);
var md5Script = new File(currentScript.path.toString()+"/md5.js");
//if(md5Script.exists){
// $.evalFile(md5Script);
//e(calcMD5("hello bobby"));
//}
if(md5Script.exists){
return (md5Script.toString());
}else{
return false;
}
}
function sequenceFilesWildcard(path){
//returns everything before a sequence's image number, and the extesion: /c/path/file.0555.exr > { /c/pathfile. ; .exr }
var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g;
var match = myRegexp.exec(path);
if(!match){
return false;
}else{
return match;
}
}
function grabPaths(footage){
var f = footage;
returnpath = false;
if(f instanceof FootageItem && f.file != null){
var source = f.mainSource.file.toString();
if(!f.mainSource.isStill){
var pathFromRegex = sequenceFilesWildcard(source);
if(pathFromRegex){
returnpath = pathFromRegex[1]+"*"+pathFromRegex[2];
//e("sequence: "+pathFromRegex[1]+"*"+pathFromRegex[2]);
}else{
returnpath = source;
//e("movie: "+source);
}
}else{
returnpath = source;
//e("still: "+source);
}
}
return returnpath;
}
function grabFootagePathsAndCopyToLocal(){
//main worker function
var sel = app.project.selection;
if(sel.length < 1){
alert("Select footage first");
}else{
var debugcheck = (getPref("debug","false")=="true")?true:false;
var useforcecopy = (getPref("forcecopy","false")=="true")?true:false;
var batchFile = "@echo off\nREM batch file to copy After Effects footage to local storage";
for(i=0;i<app.project.selection.length;i++){
if(!sel[i].useProxy){
//grab path from current selection item
var curPath = grabPaths(sel[i]);
var fileName = curPath.split('/').pop();
var dir = curPath.substring(0,curPath.lastIndexOf('/')+1);
var localSaveDir = getPref("localSaveDir",Folder.temp.toString());
var outputDir = "";
if(getPref("usemd5",true)=="true"){
var md5Script = new File(ismd5available());
$.evalFile(md5Script);
outputDir =localSaveDir + "/"+ md5(dir);
}else{
outputDir =localSaveDir + dir;
}
//windows only for now
batchFile += "\nrobocopy ";
batchFile += pathToWinPath(dir);
//robocopy filename requires a wildcard, even if it's a single file
e(fileName);
batchFile += " \""+fileName.replace(/%20/g, " ")+"*\" ";
batchFile += pathToWinPath(outputDir);
batchFile += ((useforcecopy)?" /XO":"")+" /FFT"+((debugcheck)?"":" /NJH /NJS");
//e(batchFile);
}
}
batchFile += ((debugcheck)?"\npause":"");
var txtFile = new File(localSaveDir+"/AFX footage copy.bat");
txtFile.open("w","TEXT","????");
txtFile.write(batchFile);
txtFile.close();
txtFile.execute();
//txtFile.remove();
}
}
function grabFootagePathsAndSwitchProxy(){
var sel = app.project.selection;
if(sel.length < 1){
alert("Select footage first");
}else{
for(i=0;i<app.project.selection.length;i++){
item = sel[i];
if(item.useProxy){
item.useProxy = false;
}else{
var curPath = grabPaths(item);
var fileName = item.mainSource.file.toString().split('/').pop();
var dir = curPath.substring(0,curPath.lastIndexOf('/')+1);
var localSaveDir = getPref("localSaveDir",Folder.temp.toString());
var outputDir = "";
if(getPref("usemd5",true)=="true"){
var md5Script = new File(ismd5available());
$.evalFile(md5Script);
outputDir =localSaveDir + "/"+ md5(dir);
}else{
outputDir =localSaveDir + dir;
}
var proxyFilePath = outputDir+"/"+fileName;
var proxyFile = new File(proxyFilePath);
e(proxyFilePath);
if(proxyFile.exists){
if(item.mainSource.isStill){
item.setProxy(proxyFile);
}else{
//dirty workaround if the file is a movie file (.mpg, qucktime etc...)
try {
item.setProxyWithSequence(proxyFile,false);
}
catch(err) {
item.setProxy(proxyFile);
}
}
item.proxySource.alphaMode = item.mainSource.alphaMode;
item.proxySource.premulColor = item.mainSource.premulColor;
item.proxySource.invertAlpha= item.mainSource.invertAlpha;
}else{
if(getPref("debug",false)=="true"){
alert(">>> NO PROXY FOUND\n"+proxyFilePath);
}
}
}
}
}
}
function setPref(pref,value){
app.settings.saveSetting("simpleLocalProxy", pref, value);
}
function getPref(pref,defaultValue){
prefsVar = "simpleLocalProxy";
if(app.settings.haveSetting(prefsVar, pref)){
return app.settings.getSetting(prefsVar, pref);
}else{
app.settings.saveSetting(prefsVar, pref, defaultValue);
setPref(pref,defaultValue)
return defaultValue;
}
}
function simpleLocalProxy(thisObj) {
pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Simple Local Proxy", [100, 100, 300, 300]);
var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
if (securitySetting != 1) {
pan.add("statictext",[15,15,300,45],"Set prefs and re-launch");
alert("You need to check \"Allow Scripts to Write Files and Access Network\" in your preferences for this script to work");
}else{
var localFolder = getPref("localSaveDir",Folder.temp.toString());
localFolder = pathToWinPath(localFolder).replace(/\\/g,"\\\\");
res = "group { alignment: ['fill','fill'], alignChildren: ['fill','top'], orientation: 'column', \
cols: Group {orientation:'row',align:'left', alignChildren:['fill','top'],\
col1: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
saveDirText: StaticText {text: 'Local Copy folder setup: '},\
saveDirOptions: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
usemd5rbox: RadioButton {text: ' Simple',helpTip:'Uses a unique folder name per footage, requires md5.js',enabled:false,active:false,value:true},\
usepathrbox: RadioButton {text: ' Copy Folder Structure',helpTip:'Copies the target folder structure inside the local folder'}}},\
col2: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
optionsText: StaticText {text: 'Options: '},\
optionsGrp: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
forcecopyChkbox: Checkbox {text: ' Force copy',helpTip:'Overwrite local files (if your render has changed for instance)'},\
debugChkbox: Checkbox {text: ' Debug',helpTip:'Show full batch process and pause at end of copies'}}}},\
cols2: Group {orientation:'row',align:'left', alignChildren:['fill','top'],\
localSaveDirBut: Button {text: ' Choose Local Folder ' ,preferredSize:[-1,30]} , \
browseBut: Button {text: ' Browse ' , preferredSize:[-1,30]}} , \
curentDirTxt: EditText {text: '" + localFolder +"',enabled:false},\
copyFootageBut: Button {text: ' Copy Footage(s) to Local Folder ' ,helpTip:'Launches a background batch copy of selected footage',preferredSize:[-1,30]} , \
switchproxyBut: Button {text: ' Switch Local Proxy/Original ',helpTip:'Switches from original to proxy path and back, warning if no local copy has been found' ,preferredSize:[-1,30]} , \
}";
//UI DRAW
pan.grp = pan.add(res);
pan.layout.layout(true);
//radio buttons and checkboxes prefs
if(ismd5available()){
var usemd5 = getPref("usemd5",true);
pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5=="true")?true:false;
pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5=="true")?false:true;
pan.grp.cols.col1.saveDirOptions.usemd5rbox.enabled = true;
}else{
setPref("usemd5",false);
pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = false;
pan.grp.cols.col1.saveDirOptions.usepathrbox.value = true;
pan.grp.cols.col1.saveDirOptions.usemd5rbox.enabled = false;
}
var useforcecopy = getPref("forcecopy","false");
pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy=="true")?true:false;
var debugcheck = getPref("debug","false");
pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck=="true")?true:false;
pan.layout.resize();
pan.onResizing = pan.onResize = function () {this.layout.resize();}
// UI ACTIONS
//browse button
pan.grp.cols2.browseBut.onClick = function(){
localSaveDir = new Folder(getPref("localSaveDir",Folder.temp.toString()));
localSaveDir.execute();
}
//choose local folder button
pan.grp.cols2.localSaveDirBut.onClick = function(){
localSaveDir = new Folder(getPref("localSaveDir",Folder.temp.toString()));
o = localSaveDir.selectDlg("Choose folder to copy footage to");
if(o!=null){
setPref("localSaveDir",o.toString());
pan.grp.curentDirTxt.text = pathToWinPath(o.toString());//.replace(/\\/g,"\\\\");
}
}
//copy footages button (launches the 'guts' of this script)
pan.grp.copyFootageBut.onClick = function(){
//save prefs
var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;
var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;
var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;
setPref("usemd5",usemd5);
setPref("forcecopy",useforcecopy);
setPref("debug",debugcheck);
//this.text = md5("opying");
grabFootagePathsAndCopyToLocal();
}
//checks for a local copy of the footage, and switches to a proxy accordingly
pan.grp.switchproxyBut.onClick = function(){
//save prefs
var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;
var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;
var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;
setPref("usemd5",usemd5);
setPref("forcecopy",useforcecopy);
setPref("debug",debugcheck);
//this.text = md5("opying");
grabFootagePathsAndSwitchProxy();
}
}
if (pan instanceof Window) pan.show() ;
}
simpleLocalProxy(this);
Keyframes and Layers counter
wip, doesn't work with animated shapes for now
layersCount = 0;
keyframesCount = 0;
collection = app.project.items;
for(i = 1;i<=collection.length;i++){
curItem = collection[i];
if(curItem instanceof CompItem){
len = curItem.layers.length;
layersCount += len;
for(j = 1;j<=len;j++){
curLayer = curItem.layers[j];
for(k = 1;k <= curLayer.numProperties;k++){
for(l = 1;l <= curLayer.property(k).numProperties; l++){
//$.writeln(keyframesCount);
if(curLayer.property(k).property(l).numProperties>0){
for(m = 1;m <= curLayer.property(k).property(l).numProperties; m++){
nKeys = curLayer.property(k).property(l).property(m).numKeys;
if(nKeys > 0){
keyframesCount += nKeys;
}
//ugh i wish i was better at recursiveness
//$.writeln(curLayer.property(k).property(l).property(m).matchName);
//$.writeln(curLayer.property(k).property(l).property(m).numKeys);
}
}else{
nkeys = curLayer.property(k).property(l).numKeys;
if(nKeys > 0){
keyframesCount += nKeys;
}
//keyframesCount += curLayer.property(k).property(l).numKeys;
//$.writeln(curLayer.property(k).property(l).matchName);
//$.writeln(curLayer.property(k).property(l).numKeys);
}
}
}
}
}
}
alert(keyframesCount+" keyframes found and\n"+layersCount+" layers counted in the project");
function iterateProperties(prop){
// $.writeln(prop.numProperties);
if(prop.numProperties > 0){
for(i = 1;i<=prop.numProperties;i++){
iterateProperties(prop.property(i));
}
}else{
// $.writeln(prop.matchName);
}
//$.writeln(prop.matchName);
}
//alert(app.project.items[2])
Auto Expose
function e(s){
$.writeln(s);
}
app.beginUndoGroup("Analyze animation");
var layers = app.project.activeItem.selectedLayers;
curlayer = layers[0];
var duplicatelayer = curlayer.duplicate();
curlayer.moveBefore(duplicatelayer);
var futureprecompindex = duplicatelayer.index;
var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+"_anim_detection",true);
var precomplayer = app.project.activeItem.layer(futureprecompindex);
precomplayer.guideLayer = true;
var toplayer = precomp.layers[1];
var props = toplayer.property("ADBE Effect Parade");
while(props.numProperties>0){
props.property(1).remove();
}
var newlayer = toplayer.duplicate();
newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;
newlayer.startTime += app.project.activeItem.frameDuration;
newlayer.timeRemapEnabled = true;
newlayer.inPoint -= app.project.activeItem.frameDuration;
var explainer = new MarkerValue("1 frame shift + difference blendmode = highlight pixel changes");
newlayer.property("Marker").setValueAtTime(.5, explainer);
var blackSolid = precomp.layers.addSolid([0,0,0], "Black", precomp.width, precomp.height, 1);
blackSolid.moveToEnd();
var sliderctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
sliderctrl.name = "Resolution";
sliderctrl.property("ADBE Slider Control-0001").setValue(3);
var detectorctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
detectorctrl.name = "Detector";
detectorexpression = "\
resolution = effect(\"Resolution\")(\"ADBE Slider Control-0001\");\
resolution = (resolution<1)?1:resolution;\
a = [0,0,0,0];\
for(i=0;i<resolution;i++){\
for(j=0;j<resolution;j++){\
center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\
sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\
a+= sampleImage(center, sampledistance , postEffect = true, t = time);\
}\
}\
\
(a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\
";
detectorctrl.property("ADBE Slider Control-0001").expression = detectorexpression;
detectorctrl.property("ADBE Slider Control-0001").selected = true;
app.executeCommand(app.findMenuCommandId("Convert Expression to Keyframes"));
detectorctrl.property("ADBE Slider Control-0001").selected = false;
app.endUndoGroup();
var rez = parseFloat(prompt("Resolution to detect movement. 10 is a good start, don't go above 50",10));
//step 1: iterate through sampleImage, add more rez if no change is detected, bake keys at end -- the heavy bit
//rez = 10;
exp = "steps = "+rez+";\ntot = 0;\nfor(j=height/steps/2;j<height;j = j + height/steps){\n\t";
exp += "for(i= width/steps/2;i<width;i = i + width/steps){\n\t\t";
exp += "s = sampleImage([i,j], [width/steps/2,height/steps/2], postEffect = true, t = time);\n\t\ttot += (s[0]+s[1]+s[2]+s[3]);\n";
exp += "\t}\n}\ntot";
s = app.project.activeItem.selectedLayers[0];
v = s.Effects.addProperty("ADBE Slider Control");
slider = v.property("ADBE Slider Control-0001");
slider.expression = exp;
slider.selected = true;
app.executeCommand(2639);
//step 2: if current value is similar to the previous' frame value, write 0 else, write 1, bake result
exp = "f = effect('"+v.name+"')('ADBE Slider Control-0001');\n";
expB = exp + "f-f.valueAtTime(time-thisComp.frameDuration)==0?0:1;";
slider.expression = expB;
app.executeCommand(2639);
mainKeys = [];
for(i=1;i<=slider.numKeys;i++){
mainKeys[mainKeys.length] = slider.keyValue(i);
}
//step 3: iterate through every frame and add which frame it corresponds to
exp += "a = 0;\nfor(i=0;i<=time*(1/thisComp.frameDuration);i++){\n\t";
exp += "if(f.valueAtTime(i*thisComp.frameDuration)==1){\n\t\ta = i*thisComp.frameDuration;\n\t}\n}\na;";
slider.expression = exp;
app.executeCommand(2639);
slider.expression = "";
slider.expressionEnabled = false;
//step 4: clean up keys
for(i=mainKeys.length-1;i>0;i--){
if(mainKeys[i]==0){
slider.removeKey(i+1);
}else{
slider.setInterpolationTypeAtKey(i+1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
}
}
slider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
Batch render project items
Select project footage and it will render them with the chosen rq preset
template = "QTJPG2000"; // chosen by hand, for now
selectedItems = app.project.selection;
rq = app.project.renderQueue;
for(i=0;i<selectedItems.length;i++){
item = selectedItems[i];
path = item.mainSource.file.path.toString()+'/'+item.name;
p = app.project.items.addComp(item.name,item.width,item.height,1.0,item.duration,item.frameRate);
p.layers.add(item);
rqitem = rq.items.add(p);
rqitem.outputModules[1].file = new File(path);
rqitem.outputModules[1].applyTemplate(template);
}
Auto Comp to Renderqueue with Template
//todo: be able to select existing RQ items instead of simply comps, and edit their existing paths
function e(s){$.writeln(s)} //debug fn
var win;
function renderAll(){
//prefTemplate = (app.settings.haveSetting("renderCompWithTemplate", "template"))?(app.settings.getSetting("renderCompWithTemplate", "template")):false;
sel = app.project.selection;
if(sel.length>0){
//needs a comp to create a render item to get templates
getTemplateUI(sel[0]);
//if ui worked fine, there should be a template setting string, if there's a bug or user cancelled, fuck it.
prefTemplate = (app.settings.haveSetting("renderCompWithTemplate", "template"))?(app.settings.getSetting("renderCompWithTemplate", "template")):false;
if(prefTemplate != false && prefTemplate != "null" ){
for(i=0;i<sel.length;i++){
renderCompWithTemplate(sel[i],prefTemplate)
}
}else{
writeLn("Send to renderqueue cancelled.");
}
}else{
alert("Select at least one comp or render queue element");
}
}
function getTemplateUI(nullComp){
// get previous render settings, so we can use it as default selection later
prefTemplate = (app.settings.haveSetting("renderCompWithTemplate", "template"))?(app.settings.getSetting("renderCompWithTemplate", "template")):false;
// void render settings if user clicks cancel, to be changed
app.settings.saveSetting("renderCompWithTemplate", "template","null");
ai = app.project.activeItem;
// must have at least one comp selected, and the project saved to know where to place the renders
// eventually the user shouldn't have to save his AEP, but have it appear as a warning somewhere that he should
if( nullComp != null && nullComp instanceof CompItem && app.project.file != null ){
rq = app.project.renderQueue;
//create null render comp to fetch templates, delete it right afterwards
rqitem = rq.items.add( nullComp );
rqtemplates = rqitem.outputModules[1].templates;
rqitem.remove();
// dockable window to do
//res = "dialog { properties:{ resizeable:true }, preferredSize: [690, 20], alignChildren: 'fill',orientation:'column',\
res = "dialog { \
properties:{ resizeable:true }, alignChildren: 'fill',orientation:'column',\
templatesPnl: Panel { \
orientation:'column', alignChildren:['left', 'top'],text: 'Choose a rendering template',\
templates: DropDownList {}, \
}, \
filePnl: Panel { \
orientation:'column', alignChildren:['left', 'top'],text: 'Display Paths',\
pathRbs: Group { \
orientation:'row', alignment: 'left', alignChildren:['left', 'bottom'] \
txt: StaticText { alignment:'left', text:'File path style:' }, \
unixStylePathRb: RadioButton { text:'Unix (mac)' }, \
winStylePathRb: RadioButton { text:'Windows', value:true } \
}, \
showPathText: StaticText {alignment: 'fill'}, \
edithPathGrp: Group { \
orientation:'row', spacing:2, alignment: 'left', \
editPathBox: EditText { alignment: ['fill','center'], margins: 8, text:'', properties:{borderless:true}}, \
testBtn: Button { alignement:[center','center'], text:'Test', properties:{name:'test'} } ,\
}, \
explainPathText: StaticText {alignment: 'fill', properties:{multiline: true } }, \
explainPathTextGrp: Group { \
orientation:'row', alignment: 'left', alignChildren:['left', 'center'] \
c1: StaticText {alignment: 'fill', properties:{multiline: true } }, \
c2: StaticText {alignment: 'fill', properties:{multiline: true } } \
} \
}, \
buttons: Group { orientation: 'row', alignment: 'right', \
okBtn: Button { text:'OK', properties:{name:'ok'} }, \
cancelBtn: Button { text:'Cancel', properties:{name:'cancel'} }, \
}, \
}";
// get/set the display path preference (it will always be unix style internally) 1=windows 0=mac/unix
//input base text
explanationText = "You can use the following variables ( and can't use * ? \" < > or | ): \n";
/*explanationText += "\t{compname}\t\t{compfolder}\n";
explanationText += "\t{projpath}\t\t\t{compid}\n";
explanationText += "\t{projname}\t\t\t{date}\n";
explanationText += "\t[#], [##], [####]... \t(padding)\n";*/
// create window resource
win = new Window (res);
//set the preferred template
preferredItem = 0;
for(i=0;i<rqtemplates.length;i++){
//skip hidden templates
if(rqtemplates[i].indexOf('_HIDDEN') != 0 ){
item = win.templatesPnl.templates.add ('item',rqtemplates[i]);
if(rqtemplates[i] == prefTemplate){
preferredItem = i;
}
}
}
win.templatesPnl.templates.selection = win.templatesPnl.templates.items[preferredItem];
// can't add newline \n character in res, so fill in text now
win.filePnl.explainPathText.text = explanationText;
// set os choice radio button, figure out if it's in prefs
// eventually users shouldn't see this, the script should be tailored for mac or windows
os = $.os.toLowerCase().indexOf("windows") != -1;
if( app.settings.haveSetting("renderCompWithTemplate", "oschoice") ){
os = ( app.settings.getSetting("renderCompWithTemplate", "oschoice") == 1 ) ? 1 : 0 ;
}else{
app.settings.saveSetting("renderCompWithTemplate", "oschoice", os);
}
win.filePnl.pathRbs.unixStylePathRb.value = !os;
// set path template box, if it doesn't have one, use a default;
inputBox = win.filePnl.edithPathGrp.editPathBox;
outPutBox = win.filePnl.showPathText;
var pathTemplate = "";
if( app.settings.haveSetting("renderCompWithTemplate", "pathtemplate") ){
pathTemplate = app.settings.getSetting("renderCompWithTemplate", "pathtemplate");
}else{
pathTemplate = "{projpath}/RENDER/{date:ymd}_{compname}/{compname}.[####]";
app.settings.saveSetting("renderCompWithTemplate", "pathtemplate", pathTemplate);
}
inputBox.text = pathTemplate;
//add ui callbacks
//call once, to fill the result text box
updatePath(inputBox, outPutBox);
inputBox.onChange = inputBox.onChanging = function() { updatePath(inputBox, outPutBox) };
/*-----TO BE CUT---------*/
win.filePnl.pathRbs.unixStylePathRb.onClick = win.filePnl.pathRbs.winStylePathRb.onClick = function() {
//save preferences
app.settings.saveSetting("renderCompWithTemplate", "oschoice", (!win.filePnl.pathRbs.unixStylePathRb.value)?1:0);
updatePath(inputBox, outPutBox);
}
/*-----END CUT-----------*/
win.filePnl.edithPathGrp.testBtn.onClick = function(){
fp = descriptionToFilePath("",1,inputBox.text, 0);
fpFolder = new Folder(fp);
e(fpFolder.absoluteURI);
}
win.buttons.okBtn.onClick = function(){
//save preferences
prefTemplate = win.templatesPnl.templates.selection;
app.settings.saveSetting("renderCompWithTemplate", "pathtemplate", inputBox.text);
//app.settings.saveSetting("renderCompWithTemplate", "oschoice", (!win.filePnl.pathRbs.unixStylePathRb.value)?1:0);
app.settings.saveSetting("renderCompWithTemplate", "template",prefTemplate);
win.close();
}
win.layout.layout(true);
win.onResizing = win.onResize = function () {this.layout.resize()};
win.center();
win.show();
}else{
//alert dialogs suck, but they are efficient
alert("Select at least one comp to render, and make sure your After Effects file is saved to disk.");
}
}
function resizeUI(windowObj){
//nasty function which creates a new textbox to figure out how big the editbox is, so no text is clipped
var textbox = windowObj.filePnl.edithPathGrp.editPathBox;
//var testbutton = windowObj.filePnl.edithPathGrp.testBtn;
g = windowObj.filePnl.add("group", [0,0,0,0]);
g.enabled = g.visible = false;
tmp = g.add("edittext", undefined, textbox.text, {enabled:false, visible:false});
var preferredWidth = tmp.preferredSize[0];
//e("newpref: "+r.preferredSize[0]);
windowObj.filePnl.remove(g);
textbox.size = [preferredWidth, textbox.preferredSize[1] ];
//testbutton.size = testbutton.preferredSize;
windowObj.layout.layout(true);
}
function updatePath( inputRes, outputRes){
os = ( app.settings.getSetting("renderCompWithTemplate", "oschoice") == 1 ) ? 1 : 0 ;
input = inputRes.text;
input = descriptionToFilePath("",1,input, 1);
input = setPathStyle(input, !os) ; //backslashes to slashes
outputRes.text = input;
resizeUI(inputRes.parent.parent.parent);
//windowObj.layout.layout(true);
}
function descriptionToFilePath(comp, rqid, descriptionString,forDisplay){
// takes a description like {projpath}/{projname}_{compname}/{compname}_[#####] and transforms it into a usable unix path
// if there is no comp fed, use the first in the selection.
// 'forDisplay' int decides if it's for display (add frame count and turn %20s to spaces) or actual path to be used
// 'rqid' is the number of the comp in the renderqueue, default is 1
var str = descriptionString;
if(comp == "" || comp == null){
comp = app.project.selection[0];
}
var projName = app.project.file.name.toString();
//standard stuff, comp path name etc....
str = str.replace(/\{projpath\}/gi, app.project.file.path.toString());
projName = projName.substr(0, projName.lastIndexOf('.')) || projName;
str = str.replace(/\{projname\}/gi, projName);
str = str.replace(/\{compname\}/gi, comp.name);
var parentFolder = (comp.parentFolder.name == "Root") ? "" : comp.parentFolder.name ;
str = str.replace(/\{compfolder\}/gi, parentFolder);
//if we use {id}, make sure it's padded
var rq = app.project.renderQueue;
str = str.replace(/\{id\}/gi, pad(rq.items.length.toString().length, rqid.toString(),"0") );
//figure out padding, if it's forDisplay, show it to the user
startPad = str.indexOf("[#");
endPad = str.lastIndexOf("#]");
if(startPad > 0 && endPad > 0 && endPad > startPad && forDisplay){
startFrame = comp.workAreaStart/comp.frameDuration;
str = str.substring(0,startPad)+pad(endPad-startPad, startFrame.toString(), "0")+str.substring(endPad+2,str.length);
}
//date
var datesArray = str.split("{date:");
var tempstr = "";
if(datesArray.length > 0){
var today = new Date();
var dd = today.getDate().toString();
var mm = (today.getMonth()+1).toString();
var yyyy = today.getFullYear();
tempstr = datesArray[0];
for(i = 1 ; i < datesArray.length; i++ ){
endBracketPos = datesArray[i].indexOf("}");
nextBracketPos = datesArray[i].indexOf("{");
nextBracketPos = (nextBracketPos == -1)? 9999 : nextBracketPos; //case if date is the last used tag
if(endBracketPos > -1 && endBracketPos < nextBracketPos){
dateString = datesArray[i].substring(0, endBracketPos );
dateString = dateString.replace(/y/gi, yyyy);
dateString = dateString.replace(/d/gi, pad(2, dd, "0"));
dateString = dateString.replace(/m/gi, pad(2, mm, "0"));
tempstr += dateString + datesArray[i].substring(endBracketPos+1,datesArray[i].length);
}else{
tempstr = str;
}
}
str = tempstr;
}
if(forDisplay){
str += ".ext";
}
return str;
}
function renderCompWithTemplate(comp,template){
//sends the comp to the renderqueue with the selected file path, creating folders and subfolders if needed
rq = app.project.renderQueue;
rqitem = rq.items.add(comp);
rqtemplates = rqitem.outputModules[1].templates;
filename = app.project.file.name;
filepath = app.project.file.toString();
projpath = filepath.slice(0,filepath.length-filename.length);
savePath = projpath+comp.name+"/"+filename.slice(0,filename.length-4)+"/";
saveFile = comp.name+".[####].tif";
saveFolder = new Folder(savePath);
saveFolder.create();
rqitem.outputModules[1].file = new File(savePath+"/"+saveFile);
rqitem.outputModules[1].applyTemplate(template);
}
function setPathStyle(path, toUnixPath){
// takes a string and creates either a unix path like /c/my%20folder/ if toUnixPath is set to true
// or a windows path like c:\my folder\. Returns false if there's a problem (tbd)
if(!toUnixPath){
//unix -> win
path = path.replace(/\//, "");
path = path.replace(/\//, ":/");
path = path.replace(/%20/g, " ");
path = path.replace(/\//g, "\\");
}else{
path = "/"+path.replace(":\\", "/");
path = path.replace(/\\/g, "/");
path = path.replace(" ", "%20");
//path = path.substring(0,path.lastIndexOf("/"));
}
return path;
}
function pad(width, string, padding) {
return (width <= string.length) ? string : pad(width, padding + string, padding);
}
renderAll();
create effect creator
ui
function e(s){
$.writeln(s);
}
/*
var windowObj;
function ui(thisObj) {
windowObj = (thisObj instanceof Panel) ? thisObj : new Window("palette", "ui_test", [100, 100, 300, 300]);
addButton("flip",windowObj);
return windowObj;
}*/
function addButton(buttonname,ui){
//ui.add("button", [0, 0, 20, 20], But_01[0]);
return ui.add("button", undefined, buttonname);
}
function ui(thisObj){
pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Watchfolder", [100, 100, 300, 300]);
var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
if (securitySetting != 1) {
pan.add("statictext",[15,15,300,45],"Set prefs and re-launch");
alert("You need to check \"Allow Scripts to Write Files and Access Network\" in your preferences for this script to work");
}else{
var res =
"group { \
alignment: ['fill','fill'], \
alignChildren: ['fill','top'], \
orientation: 'column', \
setWF: Button {text: 'add btn' ,preferredSize:[-1,30]} , \
sendWF: Button {text: 'Send To Watchfolder' ,preferredSize:[-1,30]} , \
}";
pan.grp = pan.add(res);
pan.grp.setWF.onClick = function () {
addButton("joe",pan.grp);
resfreshUI(pan);
}
pan.grp.sendWF.onClick = function () {
}
resfreshUI(pan);
/*
pan.layout.layout(true);
pan.layout.resize();
pan.onResizing = pan.onResize = function () {this.layout.resize();}*/
return pan;
}
}
function resfreshUI(src){
src.layout.layout(true);
src.layout.resize();
src.onResizing = pan.onResize = function () {this.layout.resize();}
}
ui(this);
fn
function e(s){
$.writeln(s);
}
// to be done:
// save keys
// save masks
// save text if text layer, camera zoom if camera etc
// use FFX as custom values
function grabEffects(layers){
var buffer = "";
var chosenLayerName = "curLayer";
for (i=0;i<layers.length;i++){
effs = layers[i].property("ADBE Effect Parade");
for(j=1;j<=effs.numProperties;j++){
//create the effects, no matter if properties below have values
buffer += "\n\tprop = "+ chosenLayerName +".Effects.addProperty(\""+ effs.property(j).matchName +"\");\n";
buffer += "\tprop.name = \""+ effs.property(j).name +"\";\n";
buffer += "\tprop.enabled = "+ effs.property(j).enabled +";\n";
for(k=1;k<=effs.property(j).numProperties;k++){
curProp = effs.property(j).property(k);
//only add value if it's non-default
if(curProp.isModified){
curPropValue = propType(curProp);
// if it returns -1, it's (probably) a useless 'parent' property, skip
if( curPropValue != -1){
// /!\ if it'sa custom value type, there's little to no way of using it in a script, so we can comment it out and warn the user -- there are also false positives, ^but they should be ignored with '-1' as curpropvalue
buffer += "\t\t// "+curProp.name+((curPropValue===false)?" /!\\ Can't use this custom data! could be a false negative, but unlikely ":"")+"\n";
buffer += "\t\t"+((curPropValue===false)?"//":"") + "prop.property(\""+curProp.matchName+"\").setValue(" + curPropValue + ");\n";
// if there is an expression and it's enabled, use it
if(curProp.expression != "" && curProp.expressionEnabled){
buffer += "\t\t"+((curPropValue===false)?"//":"") + "prop.property(\""+curProp.matchName+"\").expression = \""+curProp.expression+"\";\n";
}
}
}
}
}
}
return buffer;
}
function outputCode(){
effectsLayers = app.project.activeItem.selectedLayers; //use effects from all selected layers
buffer = "";
if(effectsLayers.length>0){
buffer = "var selectedLayers = app.project.activeItem.selectedLayers;\n";
buffer += "for (i=0;i<selectedLayers.length;i++){\n";
buffer += "\n\tcurLayer = selectedLayers[i];\n";
buffer += grabEffects(effectsLayers) + "\n}";
//e(buffer);
}
return buffer;
}
e(outputCode());
e("\n------------------------------------------------------");
function propType(property){
returnvalue = false;
e("--->"+property.name+" "+property.propertyValueType+"\n"); //debug
//e("--->"+property.name+" "+property.canVaryOverTime+"\n"); //debug
switch(property.propertyValueType){
case PropertyValueType.ThreeD_SPATIAL:
returnvalue = "["+property.value[0]+","+property.value[1]+","+property.value[2]+"]";
break;
case PropertyValueType.ThreeD:
returnvalue = "["+property.value[0]+","+property.value[1]+","+property.value[2]+"]";
break;
case PropertyValueType.TwoD_SPATIAL:
returnvalue = "["+property.value[0]+","+property.value[1]+"]";
break;
case PropertyValueType.TwoD:
returnvalue = "["+property.value[0]+","+property.value[1]+"]";
break;
case PropertyValueType.OneD:
returnvalue = property.value;
break;
case PropertyValueType.COLOR:
returnvalue = "["+property.value[0]+","+property.value[1]+","+property.value[2]+","+property.value[3]+"]";
break;
case PropertyValueType.LAYER_INDEX:
returnvalue = property.value;
break;
case PropertyValueType.MASK_INDEX:
returnvalue = property.value;
break;
//no way to store custom value but FFX, tbd
case PropertyValueType.CUSTOM_VALUE:
returnvalue = false;
break;
// if we land on default, it is _most likely_ a property 'parent' with no real use
default:
//e("using default: --->"+property.name+" "+property.propertyValueType+"\n")
returnvalue = -1;
break;
}
return (returnvalue);
}
left to right
function e(s){
//debug
$.writeln(s);
}
function replaceLR(s){
s = s.replace(/left/g, "right");
s = s.replace(/Left/g, "Right");
s = s.replace(/LEFT/, "RIGHT");
return s;
}
function checkImportLeftRight(layer){
// is it a footage layer (includes solids etc.. not Comps)
if(layer.source instanceof FootageItem){
//is it a footage item (.exr)
if(layer.source.file != null){
path = layer.source.mainSource.file.toString();
//check path, if it has 'left' in it, grab 'right' and import and replace footage
if(path.toLowerCase().indexOf("left") > -1){
rightPath = replaceLR(path);
rightFile = new File(replaceLR(path));
//if file exists, import right file, replace layer with it
if(rightFile.exists){
var io = new ImportOptions(rightFile);
if(io.canImportAs(ImportAsType.FOOTAGE)){
io.importAs = ImportAsType.FOOTAGE;
io.sequence = true;
src = app.project.importFile(io);
return(layer.replaceSource(src,1));
}else{
return false
}
}else{
alert("file not found! "+rightPath);
return false
}
}
}
}
}
/*
a = app.project.activeItem;
for(i=1;i<=a.layers.length;i++){
//e(a.layers[i].name);
e(checkImportLeftRight(a.layers[i]));
//if it's footage
}
for(i = 1;i<app.project.numItems;i++){
e(i+" "+app.project.items[i].name);
}
// 3 and 4
*/
function new3dComp(leftcomp,rightcomp){
threedcomp = leftcomp.name.replace(/left/g,"3D");
w = leftcomp.width;
h = leftcomp.height;
duration = leftcomp.duration;
frameRate = leftcomp.frameRate;
newComp = app.project.items.addComp(threedcomp, w , h , 1.0, duration, frameRate);
newComp.layers.add(rightcomp);
newComp.layers.add(leftcomp);
threed = newComp.layers.addSolid([1,1,1],"3D Glasses",w,h,1.0);
threed.adjustmentLayer = true;
threedglasses = threed.Effects.addProperty("ADBE 3D Glasses2");
threedglasses.property("Left View").setValue(2);
threedglasses.property("Right View").setValue(3);
threedglasses.property("3D View").setValue(12);
}
new3dComp(app.project.items[3],app.project.items[4]);
//e(app.project.activeItem.name+" --- ")
Output .SRT from layer markers
function pad10(n){
return (n<10)?"0"+n:n;
}
function pad100(n){
return (n<100)?"0"+pad10(n):n;
}
function formatTime(time){
time =~~ (time*1000)/1000;
var hrs = ~~(time / 3600);
var mins = ~~((time % 3600) / 60);
var secs = ~~(time % 60);
var ms = ~~((time-Math.floor(time))*1000);
time = pad10(hrs)+":"+pad10(mins)+":"+pad10(secs)+","+pad100(ms);
return time;
}
data = "";
var ai = app.project.activeItem;
if ( ai instanceof CompItem && ai.selectedLayers.length == 1) {
var m = ai.selectedLayers[0].marker;
for(i=1;i<=m.numKeys;i++){
data += i;
data += "\n"+ formatTime(m.keyTime(i))+" --> "+ formatTime(m.keyTime(i)+m.keyValue(i).duration);
data += "\n"+m.keyValue(i).comment;
data += "\n";
}
}
alert(data)
//result:
//
//1
//00:03:16,945 --> 00:03:17,364
//testing comment 1
//
//2
//01:59:47,228 --> 01:59:49,396
//second subtitle at 2 hours in
//
Shift keys
function e(str){
$.writeln(str);
}
function getLayerFromProperty(prop){
return prop.propertyGroup(prop.propertyDepth)
}
var c = app.project.activeItem;
if( c != null){
var props = c.selectedProperties;
//e(props[0].matchName);
for(i = 0;i<props.length;i++){
e("\n---------------------------\n"+props[i].matchName+" / "+getLayerFromProperty(props[i]).name);
k = props[i].selectedKeys;
for(keyVar in k){
//k[keyVar]
// e( );
e(props[i].keyTime(k[keyVar]));
//e(k[keyVar]);
}
//CANT FUCKING NUDGE KEYFRAMES
// e(props[i].selectedKeys);
}
}
Selection tool palette
{
//tmpdir = $.getenv("tmp");
function toolWindow(thisObj){
function drawUI(){
var my_palette = new Window("palette","Selection Tool");
my_palette.bounds = [300,200,300,285];
/*var button1 = addScriptButton(my_palette,[l_button_left, 5, l_button_right, 25],
"Find and Replace Text", demosDirectory, "Find and Replace Text.jsx");
var button3 = addScriptButton(my_palette,[l_button_left, 30, l_button_right, 50],
"Scale Composition", demosDirectory, "Scale Composition.jsx");
var button4 = addScriptButton(my_palette,[l_button_left, 55, l_button_right, 75],
"Scale Selected Layers", demosDirectory, "Scale Selected Layers.jsx");
var button6 = addScriptButton(my_palette,[r_button_left, 5, r_button_right, 25],
"Sort Layers by In Point", demosDirectory, "Sort Layers by In Point.jsx");
var button8 = addScriptButton(my_palette,[r_button_left, 30, r_button_right, 50],
"Render and Email", myDirectory, "Render and Email.jsx");
var button12 = addHelpButton(my_palette,[r_button_left, 55, r_button_right, 75]);*/
my_palette.show();
}
drawUI();
}
toolWindow(this);
}
Make .bat file (WIP)
function pathToWinPath(path){
var str = path.toString().replace(/\//, "");
str = str.replace(/\//, ":/");
str = str.replace(/%20/g, " ");
str = str.replace(/\//g, "\\");
return str;
}
function createBat(){
d = new Date();
m = d.getMonth()+1;
j = d.getDate();
if(m<10){
m="0"+m;
}
if(j<10){
j="0"+j;
}
var txtFile = new File("~/Desktop/ae_render"+m+"_"+j+".bat");
alert(pathToWinPath(app.project.file));
txtFile.open("w","TEXT","????");
txtFile.close();
}
res =
"dialog { \
allGroups: Panel {\
orientation:'column',\
alignChildren:'fill',\
text:'Options', \
chckOff: Checkbox {text:'Shutdown PC when finished'}, \
chckAppend: Checkbox {text:'Append (instead of overwriting)'},\
aePath: Group {orientation:'row',align:'fill', alignChildren:['fill','center'],\
aeLocBtn: Button {text:' aerender.exe location : '}, \
aeLocTst: StaticText {text:'C:\\Program fil...'}} \
okCancel: Group {orientation:'row',align:'fill', alignChildren:'center',\
okBtn: Button { text:'OK', properties:{name:'ok'}} , \
cancelBtn: Button { text:'Cancel', properties:{name:'cancel'}},\
}\
}\
}";
win = new Window (res);
win.allGroups.chckAppend.value = true;
win.center();
win.show();
Carnet De Voyage rangement
//rangement pour carnet de voyage
app.beginUndoGroup("CDV Rangement");
boolVal = false;
sel = app.project.selection;
if(sel.length == 1){
shot = sel[0];
allItems = app.project.items;
sortItems = new Array();
for(i=1;i<=allItems.length;i++){
if(!(allItems[i] instanceof FolderItem)){
if(allItems[i].parentFolder.name == "Root"){
sortItems[sortItems.length] = allItems[i];
}
}else{
if(allItems[i].parentFolder.name == "Root"){
sortItems[sortItems.length] = allItems[i];
}
}
}
t = app.project.items.addFolder("ELMTS");
for(i=0;i<=sortItems.length-1;i++){
if(sortItems[i] != shot){
sortItems[i].parentFolder = t;
}
}
}
if(app.project.renderQueue.item(1).outputModules.length == 2){ //copie d'un output module à la main nécéssaire
fileP = app.project.renderQueue.item(1).outputModules[1].file.toString();
fileP = fileP.replace("/m/EPISODE","/b/EPISODE");
fileP = new File(fileP);
app.project.renderQueue.item(1).outputModules[2].file = fileP;
app.project.renderQueue.item(1).outputModules[2].applyTemplate("PNGSeq");
var curFile = app.project.file.name;
curFile = curFile.substring(0,curFile.length-4);
var pth = app.project.file.path+"/"+curFile+"_MOV+PNG.aep";
var mySaveFile = new File(pth);
app.project.save(mySaveFile);
}else{
writeLn("Duplicate output modules first");
}
app.endUndoGroup();
Chat
JSX
var myPalette = buildUI(this);
if (myPalette != null && myPalette instanceof Window) {
myPalette.show()
}
function buildUI (thisObject) {
if (thisObject instanceof Panel) {
var myWindow = thisObject;
} else {
var myWindow = new Window ("palette", "My Window");
}
g = myWindow.add("group");
g.orientation = "row";
g.alignChildren = "left";
//g.alignement = "top";
bt1 = g.add("button",undefined,"test");
bt1.onClick = bob;
//myWindow.myPanel.titleText = myWindow.myPanel.add("staticText");
//myWindow.myPanel.titleText.text = "Move After Render v1.0";
myWindow.layout.layout(true);
//myWindow.layout = new AutoLayoutManager(myWindow);
//myWindow.layout.resize();
return myWindow;
}
h = 0;
function bob(){
// alert();
w = this.parent.parent;
p = this.parent;
h++;
bt2 = p.add("button",undefined,"test"+h);
bt2.alignement = ["left","top"];
if(h%3==0){
bt2.helpTip = "TTTTT";
}
bt2.onClick = flup;
//w.update();
w.layout.layout(1);
// w.layout.resize();
// w.show();
}
function flup(){
f = new Folder($.fileName);
f = new Folder(f.parent+"/shelves/");
if(!f.exists){f.create()};
cfg = new File(f.parent+"/shelves/shelves.cfg");
cfg.open('e');
cfg.write(this.onClick);
cfg.close();
cfg.execute();
}
function pop(){
f = new Folder($.fileName);
fi = new File(f.parent+"/shelves/testShelf.jsx");
fi.open('e');
eval(fi.read());
}
----------------
function grab(){
var reply = "";
c = new Socket;
if (c.open ("berniebernie.fr:80")) {
if(c.writeln ("GET /dump/afx/chat.php HTTP/1.0\nHost: berniebernie.fr\n")){
reply = decodeURIComponent(c.read(1000));
reply = reply.split("--afx chat file log--");
reply = reply[1];
}else{
return 0;
}
c.close();
}else{
return 0;
}
return reply;
}
function talk(str){
var reply = "";
c = new Socket;
if (c.open ("berniebernie.fr:80")) {
if(c.writeln ("GET /dump/afx/chat.php?msg="+str+" HTTP/1.0\nHost: berniebernie.fr\n")){
reply = decodeURIComponent(c.read(1000));
reply = reply.split("--afx chat file log--");
reply = reply[1];
}else{
return 0;
}
c.close();
}else{
return 0;
}
return reply;
}
//alert(grab());
alert(talk("flipflap"));
////////////////
app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY"
Php
<?php
$myFile = "testFile.txt";
if(isset($_GET['msg']) && $_GET['msg'] != ""){
$fh = fopen($myFile, 'a') or die("can't open file");
fwrite($fh, ($_GET['msg']."\n"));
fclose($fh);
}else{
//$fh = fopen($myFile, 'r') or die("can't open file");
//include$str = $str, true);
//header("Content-Type: plain/text");
echo encodeURIComponent(file_get_contents("testFile.txt"));
//echo nl2br(htmlentities(file_get_contents("testFile.txt")));
}
?>