Afx Javascript Temp
Jump to navigation
Jump to search
Auto-expose rewrite/debug
// save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui
// v1.1 - made the 8bit switch w/o user input, fixed bugs
{
// currentSlider holds a reference to the Detector property after setupDetector runs,
// making it accessible to bakeKeys and applyKeys.
var currentSlider = "none";
var savedBitDepth = app.project.bitsPerChannel;
function watchFolderUI(thisObj){
// Determine if the script is running as a dockable panel or a floating window
pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Auto Expose", [100, 100, 300, 100]);
var res =
"group { \
alignment: ['fill','fill'], \
alignChildren: ['fill','top'], \
orientation: 'column', \
txt1: StaticText {text: 'Step1: Select a footage or layer in a comp and move to a frame where there is a change in animation. Click setup button and increase resolution slider until the (red) slider value called \\'Detector\\' picks up a change (a value > 0)',properties:{multiline:true}} , \
setupDetector: Button {text: 'Setup detector guide layer' } , \
txt2: StaticText {text: 'Step2: Bake the detected animation as time remapped keys, this can take some time, keep an eye on your \\\"Info\\\" panel. If some animation is not detected, change resolution slider and run again',properties:{multiline:true}} , \
bakeKeys: Button {text: 'Bake to Keys'} , \
txt3: StaticText {text: 'Step3: Select layers on which to apply time remapping (\\\"exposed\\\" keys). If you want sequential keys instead, turn expression on the time remapping',properties:{multiline:true}} , \
applyKeys: Button {text: 'Apply as time remapping'} , \
}";
pan.grp = pan.add(res);
// Assign click handlers
pan.grp.setupDetector.onClick = function () {
setupDetector();
}
pan.grp.bakeKeys.onClick = function () {bakeKeys();}
pan.grp.applyKeys.onClick = function () {applyKeys();}
// Layout and resizing management for the UI
pan.layout.layout(true);
pan.layout.resize();
pan.onResizing = pan.onResize = function () {this.layout.resize();}
return pan;
}
// Execute the UI function
watchFolderUI(this) ;
// Helper function for writing debug messages
function e(s){
$.writeln(s);
}
function setupDetector(){
// Check if a composition is active
if (!(app.project.activeItem instanceof CompItem)) {
alert("Please select a Composition and one layer.");
return;
}
// Check if one layer is selected
var selectedLayers = app.project.activeItem.selectedLayers;
if (selectedLayers.length !== 1) {
alert("Please select exactly one layer to set up the detector on.");
return;
}
app.beginUndoGroup("Auto expose setup Detector");
app.project.bitsPerChannel = 8;
//move playhead to frame 1 instead of 0 if user hasn't moved, so that the script shows a change (should be a fully black frame if no animation is detected)
if(app.project.activeItem.time == 0){
app.project.activeItem.time = app.project.activeItem.frameDuration;
}
var curlayer = selectedLayers[0];
// 1. Duplicate the layer and precompose the duplicate
var duplicatelayer = curlayer.duplicate();
// Move the original layer before the duplicate for consistent indexing
curlayer.moveBefore(duplicatelayer);
// Get the index of the duplicate layer before precomposing
var futureprecompindex = duplicatelayer.index;
// Precompose the duplicate layer
var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index], duplicatelayer.name + "_anim_detection", true);
// Get the layer *in the active comp* that references the new precomp
var precomplayer = app.project.activeItem.layer(futureprecompindex);
precomplayer.guideLayer = true;
// 2. Solo the detector precomp layer
var allLayers = app.project.activeItem.layers;
for(i=1;i<=allLayers.length;i++){
if(allLayers[i].enabled && allLayers[i].index !== precomplayer.index){
allLayers[i].solo = false;
}
}
precomplayer.enabled = true;
precomplayer.solo = true;
// 3. Inside the Precomposition (precomp): setup the difference layer
var toplayer = precomp.layers[1]; // The duplicated layer that was precomposed
// Remove existing effects from the top layer inside the precomp
var props = toplayer.property("ADBE Effect Parade");
while(props.numProperties>0){
props.property(1).remove();
}
// Duplicate the top layer inside the precomp
var newlayer = toplayer.duplicate();
// 1 frame shift + difference blendmode = highlight pixel changes
newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;
newlayer.timeRemapEnabled = true; // Enable Time Remapping
// Shift the duplicated layer's content by one frame
newlayer.startTime += app.project.activeItem.frameDuration;
// Shift the layer's inPoint to compensate
newlayer.inPoint = precomp.frameDuration;
var explainer = new MarkerValue("1 frame shift + difference blendmode = highlight pixel changes");
newlayer.property("ADBE Marker").setValueAtTime(.5, explainer); // Use match name "ADBE Marker"
// Add black solid at the bottom to ensure correct difference result
var blackSolid = precomp.layers.addSolid([0,0,0], "Black", precomp.width, precomp.height, 1);
blackSolid.moveToEnd();
// 4. Add Slider Controls to the Precomp Layer in the main comp (precomplayer)
var effectsGroup = precomplayer.property("ADBE Effect Parade");
// Resolution Slider
var sliderctrl = effectsGroup.addProperty("ADBE Slider Control");
sliderctrl.name = "Resolution";
var resolutionslider = sliderctrl.property("ADBE Slider Control-0001");
resolutionslider.setValue(3);
// Detector Slider
var detectorctrl = effectsGroup.addProperty("ADBE Slider Control");
detectorctrl.name = "Detector";
var detectorslider = detectorctrl.property("ADBE Slider Control-0001");
// Expression to sample the difference precomp. It samples the previous frame's result
// because the precomp content itself is already a difference calculation between the current
// and previous frame (due to the -1 frame shift on the layer inside the precomp).
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];\
// Sample image at time-1 frameDuration because the comp content itself is the difference.\
a+= sampleImage(center, sampledistance , postEffect = true, t = time-thisComp.frameDuration);\
}\
}\
\
// Normalize to 0-1, scale up for sensitivity, then subtract to baseline 0 for no change\
(a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\
";
detectorexpression = "\
var resolution = effect('Resolution')('ADBE Slider Control-0001');\
resolution = (resolution<1)?1:resolution;\
var cw = thisComp.width/resolution;\
var ch = thisComp.height/resolution;\
var a = [0,0,0,0];\
for(i=0;i<resolution;i++){\
for(j=0;j<resolution;j++){\
center = [cw/resolution/2+cw * j ,ch/resolution/2+ch/resolution * i ];\
sampledistance = [cw/2,ch/2];\
a+= sampleImage(center, sampledistance , postEffect = true, t = time);\
}\
}\
// show accumulated delta\
(a[0]+a[1]+a[2])*1000;\
";
detectorslider.expression = detectorexpression;
currentSlider = detectorslider; // Assign to the global variable
app.endUndoGroup();
}
function bakeKeys(){
// Guardrail: check if setupDetector has been run
if (currentSlider === "none" || !(currentSlider instanceof Property)) {
alert("Please run 'Step 1: setup detector on layer' first.");
return;
}
app.beginUndoGroup("Auto expose bake to keys");
// 1. Bake slider keys (initial movement detection)
bakeCommand = app.findMenuCommandId("Convert Expression to Keyframes");
currentSlider.selected = true;
app.executeCommand( bakeCommand );
// 2. Apply secondary expression to convert movement value (>0) to 1, or 0
detectorexpression = "f = effect('Detector')('ADBE Slider Control-0001');\nf>0?1:0;";
currentSlider.expression = detectorexpression;
currentSlider.expressionEnabled = true;
// 3. Bake the 0 or 1 expression (clean expression)
currentSlider.selected = true;
app.executeCommand( bakeCommand );
// 4. Clean up keys and set the correct key values
// travel backwards through keys and remove keys that are == 0
// set the the value of kept frames to be that of the time they're on
for(i=currentSlider.numKeys;i>0;i--){
// If the value is 1 (movement detected)
if(currentSlider.keyValue(i) > 0){
// Set the value to the key's time, this is the desired exposed time
currentSlider.setValueAtKey(i, currentSlider.keyTime(i));
currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
}else{
// Remove keys where no movement was detected (value == 0)
currentSlider.removeKey(i);
}
}
// 5. Add '0' key on first frame (Time 0)
var compStartTime = app.project.activeItem.time;
// Add key at the comp's start time (usually 0)
currentSlider.addKey(compStartTime);
var keyIndex = 1;
if (currentSlider.numKeys > 0) {
// If the first key is not at compStartTime, insert the new key at index 1
if (currentSlider.keyTime(1) !== compStartTime) {
currentSlider.setValueAtTime(compStartTime, 0); // Adds key, returns index
keyIndex = currentSlider.nearestKeyIndex(compStartTime);
}
} else {
// No keys left, add the first key
currentSlider.setValueAtTime(compStartTime, 0);
keyIndex = 1;
}
currentSlider.setValueAtKey(keyIndex, 0);
currentSlider.setInterpolationTypeAtKey(keyIndex,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
app.project.bitsPerChannel = savedBitDepth;
app.endUndoGroup();
}
function applyKeys(){
// Guardrail: check if detection and baking has been run
if (currentSlider === "none" || !(currentSlider instanceof Property)) {
alert("Please run 'Step 1: setup detector on layer' first, then 'Step 2: bake to keys'.");
return;
}
var comp = app.project.activeItem;
if (!(comp instanceof CompItem)) {
alert("Please select a Composition and layers to apply keys to.");
return;
}
var layers = comp.selectedLayers;
if (layers.length === 0) {
alert("Please select layers to apply keys to.");
return;
}
app.beginUndoGroup("Auto expose apply keys");
for(var i = 0; i < layers.length; i++){
layers[i].timeRemapEnabled = true;
// Use the match name for Time Remap property
var remap = layers[i].property("ADBE Time Remapping");
// Remove existing keys first for a clean application
while (remap.numKeys > 0) {
remap.removeKey(1);
}
// Copy the baked keys from the detector slider to the layer's Time Remap
for(var j=1;j<=currentSlider.numKeys;j++){
var v = currentSlider.keyValue(j); // Value (the frame time)
var t = currentSlider.keyTime(j); // Time (the moment of exposure)
remap.addKey(t);
// Setting value at time is the robust way to set keys
remap.setValueAtTime(t, v);
// Find the index of the key we just added
var keyIndex = remap.nearestKeyIndex(t);
remap.setInterpolationTypeAtKey(keyIndex,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
}
// Expression for sequential exposure (if enabled by user):
remap.expression = "\
//toggle this on to set sequential time remap\n\
a = timeRemap;\n\
nk = a.nearestKey(time);\n\
curframe = 0;\n\
if(nk.time > time){\n\
// nearest key is in the future, use the previous key index\n\
curframe = nk.index-1;\n\
}else{\n\
// nearest key is now or in the past, use its index\n\
curframe = nk.index;\n\
}\n\
// Get the value of the key (which is the actual time) and hold it\n\
if (curframe > 0) {\n\
a.keyValue(curframe);\n\
} else {\n\
// Before the first key, hold the time 0.\n\
0;\n\
}\n\
";
remap.expressionEnabled = false; // Start with keyframes active, expression disabled
}
app.endUndoGroup();
}
}
PNG to image source
var path = '/c/temp/temp2/';
items = ['blue','red','red_o','green','green_o','gray','gray_o'];
var myFile = new File("/c/temp/temp2/pngs.txt");
myFile.open("w");
myFile.encoding = "UTF-8";
var text = "";
for (var i = 0, len = items.length; i < len; i++) {
item = items[i];
var f = File(path+item+'.png');
f.encoding = 'BINARY';
f.open('e');
var binary;
binary = f.read().toSource();
//var myFile = new File("/c/temp/temp2/"+item+".txt");
// myFile.open("w");
// myFile.encoding = "UTF-8";
text += item + " = ";
temp = binary.toString();
temp = temp.replace('(new String(','');
temp = temp.replace('))',';');
text += temp;
text += '\n';
//myFile.write(binary.toString());
//myFile.write('\n\n');
// myFile.close();
//$.writeln(binary);
f.close();
}
myFile.write(text);
myFile.close();
Watchfolder watcher
tb integrated
// watchwatchfolder: a tool to look at what's in the watchfolder
// v1.0 by bernie - mbernadat@gmail.com
//
//
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License.
//
// known limitations (v1.0), to be fixed:
// -windows only for now
// -only looks at 1 renderqueue element per AEP file.
//
//
// I coded this like a dirty monkey. I feel sorry if you have to look at this.
// v1.1 updated this 15 year old script for a job, surprised that it still worked-ish!
//
{
var wf = app.settings.haveSetting("watchwatchfolder", "wfloc")?new Folder(app.settings.getSetting("watchwatchfolder", "wfloc")):null;
var scriptFile = new File($.fileName);
var scriptFolder = scriptFile.parent; //png icons should be here
b=0; //global string that will store the
var cancelTaskID;
var pal;
var timer = 0;
var refreshRate = 5; //seconds
var reloadEditTxt;
var firstTime = true;
var shotinfos = new Array();
//global ui names
var refreshBtn;
var list;
var wfStText;
var wfPbar;
var chckBox0;
var chckBox1;
var chckBox2;
var chckBox3;
//states
// 0 - queued, paused -> gray_o
// 1 - queued, ready to start -> green_o
// 1 - done, no errors -> green
// 2 - canceled, errors -> red
// 3 - paused (?) -> gray
// 4 - in progress -> blue
//ui pngs
blue = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00DCIDAT8\u008D\u00A5\u0093\u00B1N\x02A\x14E\u00CF{\u00BB\u0089T\x04$F\u00D8\u0086\u00CE\u00BF\u00D0\u00DE\u00D2X\u0091\u00F8!\u00FE\x0B\u008D\u0095%\r\u00FE\u0085\u00DD~\x01\x1D\x18-\u008C\x15[8\u00D7\u008250\u0082\u00D9\u00D9\u00EC\u00ADf\u0092w\u00CF\u00DC\u00FB\u00921It\u0091wr\x03\u00F9\u00E1\u00C5\x16\u00EF7`s\u00CC\u00F3\u00FF\f;iK\u00F0\u0099\u00EE\u0087e<\u0098e\x0F\x04]AS-Ux\u00B8\x06\u00CA\u00B8\u00824M\u00CAmV!{\u0083\u00BF;\u0090M\u0092\x00\u00B2@\u00F6}\x02\u0080.\u0092\x00(#\u009CL\u00C0 \u00CD\u00CF\x19=\u00DFD\x00{Z\u00F50\x1A\u00B6_\u00CBq\u00DD\u009E\x7F\u00C5\t\u00FA\u0083K\u00D4\u00B8\u00FE\u009D\x02\x1F\u00BF\u00C7\u0083\x17\u00C3\x04c\x03<7\u00D8\x05\u00FEr\f0\x15\u00C0\u00AB\u00EEF\u008FI)j\u00ED+\x18\x05\u00B0nc\u008E\x01b\\Wh\u00A5}\x05g\u0089\u00E9\u00B3-\u00C0\u00BA~\u00E7\x1F\u0090\u00E3<q\u00B2\\\x19\u00D4\x00\x00\x00\x00IEND\u00AEB`\u0082";
red = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00E5IDAT8\u008D\u00A5\u0093;N\x031\x14E\u00CF\u00F5D\u0090\u0092\x06\u0085\u00A4\u00A1c\x01\x14tD\u00CA&\u0090\"X\b{\u00A1\u00A1\u00A2\u00A0%\u008B\x18\th\u00B2\t\x06)\x05P\u0091(\u00E3KE\x14gD\u00F0h^eK>\u00E7}l\u00CB6]\"t\u00A2\u0081\u00DE\u00F6\u00E6\u00F1\u00E2|l\u00B8\u00B3\u00D4\u00FB\x0B\x00\x10|G1\u009D\u0096\u00AF\u00F3\u00F4`\b7\u00D8g\u00FA'\u00AB\u00F0R\u00E8\x12\u0098'-\u00D8\u009C\u00E6\u0094m\u00C2\x12\u00FC\x0E\u00BB3\u00B0\u00879\x02\u00E1\u0088\u00D5\x14H:\u00CE\x11\u0080\nQ7\x05\u00C6G9\u00B8\u00F1\u00E1Z\u00FD*\x11\u00DCO&}vneO\u0084\u00EB\u00B2\u00FCJ\x04\x07\u00AB\u00CF\x01\u0090\u00FB\u00AA\x16\u00BF\u008BM\u00C6\u00A2\u00D6\x10\u00A8\u0084\x1E\u00F6\u0091V\u00B4\u00EAb\u00D6\x10\x10\x19\x11x\u00BE*_n3\u00AB\x00\u00B6\u0087(F\u008A\u00BC\u00B5\u0081S\x01\u009C\u0080\u00AB\u00B6\u0082M\x0B\x0Ez\n\u00EB\u00F0\u00D1V\u00A0\u00AE\u00DF\u00F9\x07\x1F\u0090C\u00FA\u00D1U\r\x03\x00\x00\x00\x00IEND\u00AEB`\u0082";
red_o = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00FEIDAT8\u008D\u00A5\u00931N\x02A\x18\u0085\u00BF7\x18C,L\u00AC\x168\u00C6\u00C6@\u00A5\u00E1\x10\u00C6hg\u00EB\t\u00F0\f^\u00C2\x16\u00B0\u00E4\x02\u0094\u009B(\x16\u009E\u00C1D\u00E9\u00A4\u00D1\u00A8\u00EC\u00FC6\u00EE\u00C2\u00AC\u00BA,\u00D9\u0097L\u00F16\u00EF\u00FFf\u00E7MFfF\x1D\u00B9Z\u00D3\u00C0\u00CE\u00BA\x19w\u00E3c\u0093\u00AE\u008B\u00DF\u00FF\u00D0\u0087\x17\u0097\u00E7\u00C9\u00EC1\b\u009A\u00DC\u0089`*\u00D9m\u00D9\u00B47\x06\u0082# \x04\b\u00DA^~t\u0096<\u00CC\u00CA\x00\u00E3\u00DE\u00A1\f?\u0087B\x07\u0086E\u0098\u00E6\x1B~?\u00C8\x15Kl\u0089t#`=W\x04DK5_*\x00V93\u00C3\u00CC\u00B8\u00E9\u00F7\u009B\u00C3n\u00FC\u0096\u00F9\u00FF\u00D6O\u00EE=\u00F3y\u0089\u00BB\u009F\u008B\b\u00F45\u00EC\u00C5\u0083\u00B2\u00AD\u00F7\u00D0\u00BE\u00C1S\u00E6s@#U\x1BX8\u00DCA\x19\u00C0\u00E4Mi\u00E3\u00E2\x17\x00O\x07\u00C7\u00DDir\x7FU\u00A1\u0083\\\u00AB\x12EG\u009E\u00E7m\u0086C\x00\u00B4\u00C0\u00AA\u00DC@\u00A0\u00FC\b\u00E64qK\u00F7\u00BA-@u\u009F\u00F37\u0085Ir\x0E\u00B9\u008B\u00D4\u0082\x00\x00\x00\x00IEND\u00AEB`\u0082";
green = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00EFIDAT8\u008D\u00A5\u00931J\x03Q\x14E\u00CF}3hJ\x1B\u00D1\u00A4\u00B1s\x01\u00F6\n\u00AEA\x10\u00826\u00EE\"{\u00B1\u00B1\u00B2\u0090t\u008Ak\x10\u00D4&\u009Bp\x04\x0BM\u0095\u0080\u00F97\u00C5\u00A0f\u008C&3\u00CC\u00AB\u00FE\u0087\x7F\u00CF\u00BD\u00EF=\u00BEl\u00D3\u00A6\u00A2\u0095\x1A\u00C8\x17/\x077'G\u00D8\u0097\u00B2\u00F2\u00FF\x04\x00\u00C8\x13R\u00F4\u009F\u00FB\u00C3Q\u00E5a\u00A0s\u00C3>Z\u00EDji*\u00A5C`Tm\u00C1\u00EC\u00D5\u0089\x1D\u00F6\u00D4\u00E8\u00B54\u00AD\u00E8S\u00B7\x0E\u00C0R\x12\u00B1\f\u0090\u00B4]\x07 \u00C8f\u009A\u00FD\u0091\u00C0l\u00C1\u00FA\u00B5\u00DAlv>'E\x05p|u\u00D1\x01r\u00D6M\u00B0D\u00C4\u00C3\u00D9\u00FD\u00B8\x02\u00F8\u00D8\x18\u00EFP\u00C7\x1E\x00\u00BD}\u009D\u00BE\u00D7\u00A8\u008C.PH\u00BA^%MN\u00CE\u00A4\u00BB%\x00\u00B8\x07\u00F1\u00F8t:\x1C\u00D4KQ\u00D6\u00C2\x10\u00A3\u0087xi\"\u00FE\x05H\u00BB\u00C6ES\u00C0\u00CF\f\x1C\u00B7\x11\u00F1\u00DE\x14\u00A0\u00B6\u00DFy\x0E\u00F4\x1CG\u00E7\u00C3S\u00DC\u00E7\x00\x00\x00\x00IEND\u00AEB`\u0082";
green_o = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00FFIDAT8\u008D\u00A5\u0093\u00B1J\x03a\x10\u0084g\u00F6b\b\x16\u0082\u00D5%y\u008C`\u00AB\u00E0+\x18\x11\u00C5FK\u009F >\u0083Oa\u00A91`!X[\n\x1A\x0B\u009FA\u00D0t&\u0095\u00A2\u00B7c\u00A1\u00B9\u00DC\u00FD\u00E2\x1F\u00C3M\u00B70\u00F3\u00B1\u00CC\u00B2\u0094\u0084*\u00B2Ji\x00\u00B5\u00E2\u00D0\u00E9w7(\u009C\x00\u00AC\u00FD\x15\u00F8\u0096\u00DE\u00E1<\x1A\u00EE\r\x1EKF\x13\u00B6A\u00DE\u0088\u00D9E4\u00EFI\x0F\u00F4u\x00e\x00\u0088\u0096\u00BB\u00CE\x1Fv/\u0087\u00B1\u00FCZ\u00BFK\x17G@\u00D0\u0081\u00A4\u0094\u00B0Q|\u00FD\u00B2/(\u0091\u00CD\u008C\u00D9\\@\u00D1\x17\x00\u00946>\u00DF^\u00E6\x03f\u00BE\x1C\u00B0yz\u00D8\x00\u00B8t\u00BB\x7F=\u0089E\x7F|\u00F5\u00A9//q\\\u009F\u00A4\u0084>:g[\u00BD\u00E8\u00F2\u00CB\\\u0081\u00F04\u009Ds\x00\x13\u00B4\x00\u008E\u00CD\u00B8\x1A\x03\u00B8\\\ty\u00F0\x0B\x00\u00A8\r\u00D8\u00DD\u00FD\u00CE\u00E08\x06\bU(\u00D1\u00DA \u009E\x17\t\x07\x00o\n\u00FA\u00C7\x05\u00CA\u009Au \u00BB2\u00B3\u00D7E\x01\u00AC\u00FA\u00CE_\u00A28Y\x00\x13L\u00B1)\x00\x00\x00\x00IEND\u00AEB`\u0082";
gray = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00EEIDAT8\u008D\u00A5\u00931N\u00C3@\x10E\u00DF\x1F[@\x15\u00D1 H\x1A:n\x01\u0097\b\r2\u00CA9\u00B8\x0B\u008Di((\u00D2\u00C0-\u00E8r\t\x1C\u0089\x02\u00D1@\u0080\u00EC\u00A7!\x01;\"\u00B1\u0095\u00A9f\u00A5\u00FDo\u00FF\x1F\u00ED\u00C86\u00DBTl\u00A5\x06\u00F2\u00BF\u0087\u00F1\u00DD\u00CD\x19\u00E8\x1A)\u00FFO\x00 \u00D2{\n]\f\u0087\u00A3I\u00ED\u00A2B\u00976'\u00B0>\u0096a\x16s\u009D\x02\u0093\u00E6K\u00C7ml\u00DB\u009A!O\u00A19\x03\u00BB\u00DF\x06\x10rJ\u008AU\u0080\u00D1A+\x07(\u00CB\u00E6i\n\u008D!\x1A\u00F6\x01\u00B4\x11\u00C0\u00EE\u00DBWV\u00D5\x1C\u0094e\u00B9'\u00C87\u0089\x7FDQ\x14\u00C5k\r\u00D0\u00EB\u00ED\x1C\u009Av\u00BF\u00CA\u00F0\u00BC\u00E8\x7F#|~\u00F4\x15Q!n\u00D7\u0089el\u00FBa\x05\u00A0,\x1B$\u00FBqx>\u00BAj\u00E3bQ\u00CB\b\u00C9\x1E ?u\x11\u00D7\x00\u0081\u008F\x04UW\u00C02B\u008A\u00B8OI/]\x01\u00DAv\u009D\u00BF\x01\u00E7\u008FN\x13\u00B9\u00E9m\u0087\x00\x00\x00\x00IEND\u00AEB`\u0082";
gray_o = "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00FFIDAT8\u008D\u00A5\u00931N\u00C3@\x14Dg\u00BEC\u0094\x02Y\u00A2r\u0092\u00BB\u00903 9T\u0096\x02-'\bg\u00E0\x14&\x1D\u0089\x14\n.@\u008Fh8\x03R\u0092\x0Eh\x10$\u00DE\u00A1!N\u00BC\u00A0\u00B5\"O\u00F7wg\u009E\u00FE\u00FF\u00AB\u00A5$4\u00915J\x03h\u00ED\x17\u00F3\u00E9\u00ED\x00\u00E4\u008D\x7F\u00FE\u008F\u00BEd\u00B8J\u00D3\u00D1\u008Bg\u00E4\x10\u00C4\u00A3I\u00B3P\u00DA\u0081c+x\n\u00C0\x03\x10=\bwg\u00E7\x17\u00CF!\u00C0|6\u00A1\u00A8\x15\u00E0\u00ED\u0080P\u00E2h\u00AB\u009A\u00F6+\u00BE\n@`7*\\-`\u00DF\u00D7\u00AA^ \u00F9\u00DCD\u00CBz\u00C0\u00CEWv\u0090\u00E7y\u0087\u00C0Q\u0096e\x1F\u00A1\u00F0\u00AF\u00AF\u00BD\u00F5\u0095\x1D\u00C4q;Q\u00B1Y\u00DFO'\u00E3\x10 >\u00B6\x18\u00C2\u00EB\u00B6\u00DE\u008D\u00B0\u00FE\u00EE\u00D1\u00EC]\u0086\u0093\x10\u0080\u0082$]\u00FE\x010\u008A\u00FANzJ\u0087\u00A3\u00EB\x10\u00C0W\u00B9\x03'\u00F5A-\x0E\tW\x00\x06u\t\u00D4\u00BE\u0080\u00AFr\x04g\u00F6\u00E0\x1C\u00DF\x0E\x05\u00B0\u00E9w\u00FE\x01\u00C6z]\u00D4\u0097\x17j\u00EE\x00\x00\x00\x00IEND\u00AEB`\u0082";
var states = [gray_o,green_o,green, red, gray, blue];
function getLogonly(file){
if(file.name.indexOf("Logs)") != -1){
return true;
}
return false;
}
function pathToWinPath(path){
str = path.replace(/\//, "");
str = str.replace(/\//, ":/");
str = str.replace(/%20/g, " ");
str = str.replace(/\//g, "\\");
return str;
}
function localToRessource(path){
//windows, for now, the only one available!
str = "/"+path.replace(":\\", "/");
str = str.replace(/\\/g, "/");
str = str.replace(" ", "%20");
str = str.substring(0,str.lastIndexOf("/"));
return str;
}
function returnFolderArray(location){
fold = new Folder(location.toString());
foldArray = fold.getFiles()
folderOnly = new Array();
for(i=0;i<foldArray.length;i++){
if(foldArray[i] instanceof Folder){
folderOnly[folderOnly.length] = foldArray[i];
}
}
return folderOnly;
}
function logInfo(logFolderLocation){
curFold = new Folder(logFolderLocation.toString());
folds = returnFolderArray(logFolderLocation);
txtfiles = fold.getFiles("*RCF.txt"); //there should only be one
htmlfiles = fold.getFiles("*.htm");
var htmlfile;
if(htmlfiles[0]){
htmlfile = htmlfiles[0].toString();
}
var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+"rrr"];
var fcontents = "";
if(txtfiles[0]){
f = txtfiles[0].toString();
f = new File(f);
f.open("r","TEXT","????");
fcontents = f.read();
f.close();
var findItems = new RegExp("^(item)[0-9]{1,}(=\()(.*)(\))$","mi");
v=findItems.exec(fcontents);
if(v){
if(v[4].indexOf("Stopped") != -1){
info=["name",1,v[4].substring(1,v[4].indexOf(","))];
}else if(v[4].indexOf("In Progress") != -1){
info=["name",2,"Rendering"];
}else if(v[4].indexOf("Finished") != -1){
info=["name",0,""];
}
}else{
// $.writeln("Buggy file");
info=[logFolderLocation.name.toString(),1,"--Bug--",logFolderLocation.toString()+"eeee"];
}
fold = new Folder(folds[0].toString());
files = fold.getFiles("*.txt");
firstFile = files[files.length-1]; //used to be first file, works better with last text file
if(firstFile == undefined || firstFile == null){
info=[logFolderLocation.name.toString(),1,"Error",htmlfile];
}else{
firstFile.open("r","TEXT","????");
contents = firstFile.read();
firstFile.close();
lines = contents.split("\n");
info[3] = logFolderLocation.toString();
info[0] = logFolderLocation.name.toString();
for(i=0;i<lines.length;i++){
phrase ="Rendering started on";
info[4] = "n/a";
if(s = lines[i].indexOf(phrase) != -1){
info[4] = lines[i].substring(s+phrase.length);
}
phrase ="Output To: ";
if(info[1]==0 ||info[1]==2){
if(s = lines[i].indexOf(phrase) != -1){
s = lines[i].indexOf(phrase);
v = lines[i].substring(s+phrase.length);
wfold = v.substring(0,v.lastIndexOf("\\"));
f = wfold.substring(wfold.lastIndexOf("\\")+1,wfold.length);
info[3] =localToRessource(wfold);
$.writeln(info[3])
info[0] = f;
}
}
}
}
}
$.writeln(info);
return info;
}
function hasStarted(watchedFolderLocation){
fold = new Folder(watchedFolderLocation.toString());
files = fold.getFiles(getLogonly);
if(files.length>0){
return true;
}else{
return false;
}
}
function getFilePath(watchedFolderLocation){
fold = new Folder(watchedFolderLocation.toString());
files = fold.getFiles(getLogonly);
alert(files[0].name);
return files[0].name;
}
/*
function getPercentage(folder){
myFolder = new Folder(folder);
files = myFolder.getFiles("*DandyWatch.txt");
if(files.length != 1){
return false;
}
myTextFile = files[0];
myTextFile.open("r","TEXT","????");
contents = myTextFile.read();
myTextFile.close();
lines = contents.split("\n");
outPutFolder = new Folder(lines[0]);
files = outPutFolder.getFiles();
f = files.length;
writeLn(f+"/"+(lines[1]+1));
return parseFloat(files.length/(lines[1]+1));
//var percentage = new Array();
}
//getPercentage(watchfolderLocation+"/"+shot);
*/
function explore(location){
fold = new Folder(location.toString());
return fold.execute();
}
function retrieveArray(){
var state = 4; //queued, default state
var vArray = new Array();
l =0;
counter=0;
counter2=0;
folders = returnFolderArray(wf);
for(folder in folders){
l++;
var out = "";
if(folders[folder].name != "anonymous"){ //??
counter2++;
if(hasStarted(folders[folder])){
state = 0;
out = logInfo(folders[folder]);
//$.writeln(out);
//out[3] = localToRessource(out[3]);
}else{
state = 4;
out = [folders[folder].name,4,"",folders[folder].toString()];
}
vArray[counter2] =out;
}
wfPbar.value = l/folders.length*100;
if (pal instanceof Window){
pal.update();
}else{
if(counter < Math.round(wfPbar.value/5,0)*5){
counter +=5;
clearOutput();
writeLn(counter+"%");
}else{
write(".");
}
}
}
return vArray;
}
function gather(){
refreshBtn.text = "refreshing...";
refreshBtn.enabled = false;
wfStText.visible = false;
wfPbar.visible = true;
shotinfos = retrieveArray();
list.removeAll();
for(i=1;i<shotinfos.length;i++){
var p = shotinfos[i];
addItem(p[0],p[1],p[2]);
}
writeLn("Done");
wfStText.visible = true;
wfPbar.visible = false;
//set (or not) the timer
refreshBtn.enabled = true;
if(parseInt(reloadEditTxt.text) > 0){
timer = parseInt(reloadEditTxt.text)/refreshRate;
refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
}else{
refreshBtn.text = "Refresh";
}
}
function watchWatchFolder(thisObj){
//UI Design
{
pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", "WatchFolder", undefined, {resizeable:true});
var winGfx = pal.graphics;
var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);
pal.bounds = [300,200,600,600];
var wfLocBtn = pal.add("button", [10,10,105,35],"Set Watchfolder");
wfStTextContents = ( wf != null)?app.settings.getSetting("watchwatchfolder", "wfloc"):"(not set...)";
wfStText = pal.add("edittext",[115,12,285,33], wfStTextContents);
wfStText.active = false;
wfStText.enabled = false;
wfPbar = pal.add("progressbar",[115,12,285,33],0,100);
wfPbar.visible = false;
var sortStText = pal.add("statictext",[17,42,80,60], "Sort by: ");
var sortByName = pal.add('radiobutton',[62,40,120,57], 'name');
var sortByStatus = pal.add('radiobutton',[115,40,170,57], 'status');
sortByName.value = true;
var bottomElements = pal.add("panel",[10,310,260,360],undefined,{borderStyle:"none"});
bl = 0;
var arrayV = new Array("green","red","orange","graygreen","gray");
bottomElements.add('image',[5,bl,20,bl+14],scriptFolder.toString()+"/flag_"+arrayV[0]+".png");
chckBox0 = bottomElements.add('checkbox',[22,bl,39,bl+16], '');
chckBox0.value = true;
chckBox0.helpTip = "Show finished renders";
bottomElements.add('image',[45,bl,60,bl+14],scriptFolder.toString()+"/flag_"+arrayV[1]+".png");
chckBox1 = bottomElements.add('checkbox',[62,bl,79,bl+16], '');
chckBox1.value = true;
chckBox1.helpTip = "Show errors";
bottomElements.add('image',[85,bl,100,bl+14],scriptFolder.toString()+"/flag_"+arrayV[2]+".png");
chckBox2 = bottomElements.add('checkbox',[103,bl,120,bl+16], '');
chckBox2.value = true;
chckBox2.helpTip = "Show rendering";
bottomElements.add('image',[125,bl,140,bl+14],scriptFolder.toString()+"/flag_"+arrayV[4]+".png");
chckBox3 = bottomElements.add('checkbox',[143,bl,160,bl+16], '');
chckBox3.value = true;
chckBox3.helpTip = "Show queued";
bl = 25;
var reloadStTxt = bottomElements.add("statictext",[5,bl+2,50,bl+21], "Update: ");
reloadEditTxt = bottomElements.add("edittext",[55,bl,90,bl+20], 20);
reloadEditTxt.enabled=false;
refreshBtn = bottomElements.add("button",[100,bl,260,bl+20], "Start");
reloadEditTxt.helpTip = "in seconds, 0 for manual refresh only.";
list = pal.add ("ListBox", [10, 65, 260,320], "desc",{numberOfColumns: 2,showHeaders: true});
//list.columnTitles = Array("First Name", "Last"); doesn't work in CS5 apparently
}
//UI Callbacks
{
wfLocBtn.onClick = function(){
wfLoc = Folder.selectDialog("Select watchfolder");
if(wfLoc != null){
app.settings.saveSetting("watchwatchfolder", "wfloc",wfLoc.toString());
wf = new Folder(wfLoc.toString());
if(wf){
wfStText.text = wfLoc.toString();
}
}
}
pal.onResize = function(){
//because using layouts is too confusing for me
list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];
wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];
wfPbar.bounds = wfStText.bounds;
bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];
}
list.onDoubleClick = function(){//onChange
sel = list.selection;
if(list.selection != null){
sL=shotinfos.length;
for(i=1;i<sL;i++){
a=shotinfos[i];
if(a[0] == list.selection.toString()){
explore(a[3]);
break;
}
}
}
}
refreshBtn.onClick = function(){
if((timer>0 && !firstTime) || !pal.visible){
timer = 0;
// $.writeln("stopped");
refreshBtn.text = "Start";
firstTime = true;
cancelTask(cancelTaskID);
}else{
if(reloadEditTxt.text == 0 || firstTime){
gather();
firstTime = false;
}
}
}
reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};
reloadEditTxt.onChange = function(){
if(reloadEditTxt.text < 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){
timer = 0;
reloadEditTxt.text = 0;
refreshBtn.text = "Refresh";
}else{
if(timer <= 0){
refreshBtn.text = "Start (~ "+eggTimer(reloadEditTxt.text)+")";
}
}
reloadEditTxt.enabled=false;
}
}
//Initialize the whole shebang
if (pal instanceof Window){
pal.center();
pal.show();
}
return pal;
}
//reloadEditTxt.notify("onChange");
ui = watchWatchFolder(this);
}
function eggTimer(time){
if(time<=59){
time = time+"s";
}else if(time<=60*4){
time = Math.floor(time/60)+"m "+(time%60)+"s";
}else if(time<60*60){
time = Math.round(time/60,0)+"m";
}else{
time = Math.round(time/(60*60),0)+"h";
}
return time;
}
function loop(){
timer--;
// $.writeln(timer+" pal.visible:"+pal.visible);
refreshBtn.text = "Click to stop ("+eggTimer(timer*5)+")";
if(timer<=0 || !pal.visible){
cancelTask(cancelTaskID);
if(reloadEditTxt.value == 0 || !pal.visible){
}else{
gather();
}
}
}
function cancelTask(id){
app.cancelTask(id);
}
function addItem(name,state,msg){
//var arrayV = new Array(green,green_o,gray,gray_o,red,red_o);
var item = list.add ('item',name);
item.image = File.decode(states[state]);
//palette.add("image", undefined, File.decode(gray), {name: "image1"}, [10,10]);
//item.image = File(scriptFolder.toString()+"/flag_"+arrayV[state]+".png");
item.subItems[0].text =msg;
}
SimpleWatchfolder WIP
some improvements
/*
WatchFolder.js v1.2
--------------------
By bernie @ berniebernie.fr
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder
In essence it's a simplified Collect Files > Project only.
Windows only.
Was added:
- A check for missing footage
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead
TBD
- Some way to parse logs to figure what has rendered and what hasn't, cause Adobe's .html file is crap TBH. This is the 'watchfolder watcher' script that I tried to do long time ago.
- set some WF options (flags)
*/
{
watchfolderLocation = "none";
watchfolderLocation = (app.settings.haveSetting("watchfolderPrefs", "watchfolderLocation")) ? (app.settings.getSetting("watchfolderPrefs", "watchfolderLocation")) : watchfolderLocation;
function sendToWF(wf) {
var m=0;
var errorList=""
for(i=1;i<=app.project.numItems;i++){
if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
if(app.project.items[i].footageMissing){
m++;
errorList += app.project.items[i].name + "\n"; // + " > " + app.project.items[i].mainSource.file.path.toString + "\n";
}
}
}
if(m!=0){
alert("There are "+m+" missing footage(s) in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button\n\n"+errorList);
}else{
// the important part of this script. Creates the RCF (render control file), the undocumented file that will launch the watchfolder process.
// the way it is created is kind of black-boxy, but this setup works for me.
var saved = app.project.file;
var curFile = app.project.file.name;
curFile = curFile.substring(0, curFile.length - 4);
var myFolder = new Folder(wf + "/" + curFile + "_wf/");
myFolder.create();
writeLn("Copying AEP to watchfolder.");
var mySaveFile = new File(myFolder.toString() + "/" + curFile + ".aep");
saved.copy(mySaveFile);
var myTextFile = new File(myFolder.toString() + "/" + curFile + "_RCF.txt");
myTextFile.open("w", "TEXT", "????");
var text = "After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n";
myTextFile.write(text);
myTextFile.close();
writeLn("Sent to watchfolder...");
}
}
function setWatchFolder() {
var tmpfile = new File(String(Folder.desktop) + "/save this temp file with any name in the watchfolder");
var selectedFolder = tmpfile.saveDlg('Select Watchfolder Location');
if (selectedFolder) {
app.settings.saveSetting("watchfolderPrefs", "watchfolderLocation", selectedFolder.path);
watchfolderLocation = selectedFolder.path;
return true;
} else {
return false;
}
}
function startWatchingFolder() {
aerenderExe = '"' + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + '\\AfterFX.exe" -m -re -wf "' + Folder(watchfolderLocation).fsName + '"';
batch = new File(Folder.desktop.toString() + "/launch_WatchFolder.bat");
if (batch.open("w", "TEXT", "????") == true) {
batch.write("@echo off\n");
// todo add start "" "C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\AfterFX.exe" -noui -m -re -wf .......
batch.write(aerenderExe);
batch.close();
batch.execute();
} else {
alert("unable to launch the AfterEffects worker");
}
}
function startAERender() {
//to add: multiframe rendering
aerenderExe = '"' + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + '\\aerender.exe" -continueOnMissingFootage -project "' + File(app.project.file).fsName + '"';
batch = new File(Folder.temp.toString() + "/launch_aerender.bat");
if (batch.open("w", "TEXT", "????") == true) {
batch.write("@echo off\n");
batch.write(aerenderExe);
batch.write("\nPAUSE");
batch.close();
batch.execute();
} else {
alert("unable to launch the aerender.exe");
}
}
function updateUI(dialog) {
}
function watchFolderUI(thisObj) {
var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
if (securitySetting != 1) {
alert("You need to check 'Allow Scripts to Write Files and Access Network' in your preferences for this script to work");
} else {
var panelGlobal = thisObj;
/*
Code for Import https://scriptui.joonas.me
*/
// DIALOG
// ======
var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window("palette", "Simple Watchfolder", [100, 100, 300, 300]);
if (!(panelGlobal instanceof Panel)) dialog.text = "Simple Watchfolder";
dialog.orientation = "column";
dialog.alignChildren = ["fill", "top"];
dialog.spacing = 10;
dialog.margins = 16;
var grp = dialog.add("group", undefined, {
name: "group0"
});
grp.alignement = ["fill", "fill"];
grp.alignChildren = ["fill", "top"];
grp.orientation = "column";
grp.add("statictext", undefined, "Simple UI to send the current queue to a watchfolder. Please save before sending to WF as it will copy the current .aep to the watchfolder", {
multiline: true
});
var group1 = grp.add("group", undefined, {name: "group1"});
group1.orientation = "row";
group1.alignChildren = ["fill", "top"];
group1.spacing = 10;
group1.margins = 0;
var setWF = group1.add("button", undefined, undefined, {name: "setWF"});
setWF.text = "Set WatchFolder";
var openWF = group1.add("button", undefined, undefined, {name: "openWF"});
openWF.enabled = (watchfolderLocation=="none")?false:true;
openWF.text = "Open WF";
var curWF = grp.add("statictext", undefined, undefined, {name: "statictext2"});
curWF.helpTip = "shows current watchfolder";
curWF.text = "(choose watchfolder)";
var sendWF = grp.add("button", undefined, undefined, {name: "sendWF"});
sendWF.helpTip = "save file first!";
sendWF.text = "Send .aep to Watchfolder";
var sendAER = grp.add("button", undefined, undefined, {name: "sendAER"});
sendAER.helpTip = "local cmdline render";
sendAER.text = "render local aerender.exe batch";
var group2 = grp.add("group", undefined, {name: "group2"});
group2.orientation = "row";
group2.alignChildren = ["fill", "top"];
group2.spacing = 10;
group2.margins = 0;
var launchWorker = group2.add("button", undefined, undefined, {name: "launchWorker"});
launchWorker.helpTip = "Saves a .bat file to your desktop that launches a worker";
launchWorker.text = "Launch a worker";
launchWorker.enabled = (watchfolderLocation=="none")?false:true;
var switchToWorker = group2.add("button", undefined, undefined, {name: "switchToWorker"});
switchToWorker.helpTip = "Turns the current After Effects into a watchfolder worker";
switchToWorker.text = "Set as worker";
switchToWorker.enabled = (watchfolderLocation=="none")?false:true;
dialog.layout.layout(true);
dialog.layout.resize();
dialog.onResizing = dialog.onResize = function() {
this.layout.resize();
}
if (watchfolderLocation != "none") {
curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
}
setWF.onClick = function() {
swf = setWatchFolder();
if (swf) {
curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;
}
}
launchWorker.onClick = function() {
startWatchingFolder();
}
switchToWorker.onClick = function() {
app.watchFolder(watchfolderLocation);
}
sendAER.onClick = function() {
startAERender();
}
openWF.onClick = function() {
var myFolder = new Folder(watchfolderLocation);
if (ScriptUI.environment.keyboardState.shiftKey === true) {
alert("lol");
}
myFolder.execute();
//watchfolderLocation
}
sendWF.onClick = function() {
if (watchfolderLocation == "none") {
setWatchFolder();
} else {
sendToWF(watchfolderLocation);
}
}
}
}
watchFolderUI(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])
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
Very wip. Features and UI don't fully work.
I hate having to go and fill out the outputs of renderqueue items by hand. This would/will automate the creation of render outputs according to variables like the comp's name and folder, the name of the After Effects project, the current date etc...
It still needs work, which will happend if someone ever hires me for AE work again :)
//todo: be able to select existing RQ items instead of simply comps, and edit their existing paths
function e(s) {
$.writeln(s)
}
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 = "Here are variables you can use:\n";
explanationText += "\t{compname} {compfolder} {projpath} {compid}\n";
explanationText += "\t{projname} {date} (using date:y/m/d)";
explanationText += "\t[#], [##], [####] (padding)\n";
explanationText += "You can write '/../' to go 'up' one directory ";
// 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, "\\");
path = path.charAt(0).toUpperCase() + path.slice(1);
} 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")));
}
?>