Difference between revisions of "Afx Javascript"

From bernie's
Jump to navigation Jump to search
 
(11 intermediate revisions by the same user not shown)
Line 510: Line 510:


=== Auto Expose Animation ===
=== Auto Expose Animation ===
Adds 'hold' keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD
Adds 'hold' keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD, also 2025 update vibecodin' an update to make it more consistent: https://berniebernie.fr/wiki/Afx_Javascript_Temp#Auto-expose_rewrite/debug .
 
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.


<youtube width="1000" height="800" >9-3XVlME2tY</youtube>
<youtube width="1000" height="800" >9-3XVlME2tY</youtube>
Line 516: Line 518:


<pre>
<pre>
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui
{
{
      
      
Line 530: Line 532:
                 orientation: 'column', \
                 orientation: 'column', \
                     setupDetector: Button {text: 'Step 1: setup detector on layer' ,preferredSize:[-1,30]} , \
                     setupDetector: Button {text: 'Step 1: setup detector on layer' ,preferredSize:[-1,30]} , \
                     txt1: StaticText {text: 'Increase resolution slider until Detector slider picks up changes (> 0)',properties:{multiline:true}} , \
                     txt1: StaticText {text: 'Temporarily set project to 8bits (will debug later). Move to a frame where there is a change and increase resolution slider until the slider called 'Detector' picks up a change (value > 0)',properties:{multiline:true}} , \
                     bakeKeys: Button {text: 'Step 2: bake to keys' ,preferredSize:[-1,30],enabled:true} , \
                     bakeKeys: Button {text: 'Step 2: bake to keys' ,preferredSize:[-1,30],enabled:true} , \
                     txt2: StaticText {text: 'Bake to hold keys (0 and 1s) and check if they correspond to animation, otherwise increase resolution and run again',properties:{multiline:true}} , \
                     txt2: StaticText {text: 'Bakes detected animation as time remapped keys to animation, this can be long, watch your "Info" panel. If some animation is not detected, change resoltion and run again',properties:{multiline:true}} , \
                     applyKeys: Button {text: 'Step 3: apply as time remapping on selected layers' ,preferredSize:[-1,30]} , \
                     applyKeys: Button {text: 'Step 3: apply as time remapping on selected layers' ,preferredSize:[-1,30]} , \
                     txt3: StaticText {text: '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}} , \
                     txt3: StaticText {text: '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}} , \
Line 846: Line 848:
===Simple Watchfolder===
===Simple Watchfolder===
http://i.imgur.com/poTWO.png
http://i.imgur.com/poTWO.png
https://i.imgur.com/wvtgDqE.png
I know a lot of people use media encoder but it's been honestly underwhelming for my uses, so this is what I use when network rendering multiple comps when there a couple users and a couple more machines


I've been told that media encorder kind of does the same thing but I've never looked into scripting it
<pre>
<pre>
/*
/*
Watchfolder.js v1.0
WatchFolder.js v1.2
--------------------
--------------------
By bernie @ berniebernie.fr
By bernie @ berniebernie.fr


This script is a simple palette to automatically send your after effects file to be processed by the watchfolder
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.
In essence it's a simplified Collect Files > Project only.


To be added:
Windows only.
 
 
Was added:
- A check for missing footage
- A check for missing footage
- Some way to parse logs to figure what has rendered and what hasn't
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead
- Some UIs to turn AFX into a render slave & set some WF options
 
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 = "none";
watchfolderLocation = (app.settings.haveSetting("watchfolderPrefs", "watchfolderLocation"))?(app.settings.getSetting("watchfolderPrefs", "watchfolderLocation")):watchfolderLocation;
    watchfolderLocation = (app.settings.haveSetting("watchfolderPrefs", "watchfolderLocation")) ? (app.settings.getSetting("watchfolderPrefs", "watchfolderLocation")) : watchfolderLocation;
 
 


function sendToWF(wf){
    function sendToWF(wf) {
        var c = confirm(app.project.file.name+" needs to be saved first. Save ?",false,"Save Project");
var m=0;
         var saved = app.project.file.toString();
         for(i=1;i<=app.project.numItems;i++){
        if(c){
             if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
            app.project.save();
                            if(app.project.items[i].footageMissing){
            var curFile = app.project.file.name;
                                m++;
            curFile = curFile.substring(0,curFile.length-4);
                            }
            var myFolder = new Folder(wf+"/"+curFile+"_watch/");
             myFolder.create();
            curFile += "_watchfolder";
            var mySaveFile = new File(myFolder.toString()+"/"+curFile+".aep");       
            app.project.save(mySaveFile);
            var myTextFile = new File(myFolder.toString()+"/"+curFile.substring(0,22)+"_RCF.txt");   
            myTextFile.open("w","TEXT","????");
            var text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n" ;
            myTextFile.write(text);
            myTextFile.close();
            app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
            var opFile = new File(saved);
            if(opFile){
                app.open(opFile);
            }           
            writeln("Sent to watchfolder...");           
    }
}
function setWatchFolder(){
        var y = confirm("Watchfolder is currently set to \n\n\""+watchfolderLocation+"\"\n\nChange it ?");
        if(y){
            var v = Folder.selectDialog ("New watchfolder location").toString();
            if(v!=null && v!="undefined"){
                app.settings.saveSetting("watchfolderPrefs", "watchfolderLocation",v);   
                watchfolderLocation = v;
             }
             }
         }
         }
if(m!=0){
alert("There are "+m+" missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button");
}else{
        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 watchFolderUI(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: 'Set watchfolder' ,preferredSize:[-1,30]} , \
                            sendWF: Button {text: 'Send To Watchfolder' ,preferredSize:[-1,30]} , \
                    }";
            pan.grp = pan.add(res);       
            pan.grp.setWF.onClick = function () {
                    setWatchFolder();
                    }
            pan.grp.sendWF.onClick = function () {
                if(watchfolderLocation=="none"){
                    setWatchFolder();
                    }else{
                       
                    sendToWF(watchfolderLocation);
                }}
            pan.layout.layout(true);
            pan.layout.resize();
            pan.onResizing = pan.onResize = function () {this.layout.resize();}
            return pan;
    }
}
watchFolderUI(this) ;
}
</pre>


===Show original source location (WIN only)===
     function setWatchFolder() {
http://i.imgur.com/04O3r.png
         var tmpfile = new File(String(Folder.desktop) + "/save this temp file with any name in the watchfolder");
<pre>
         var selectedFolder = tmpfile.saveDlg('Select Watchfolder Location');
{
         if (selectedFolder) {
     function pathToWinPath(path){
 
         var str = path.toString().replace(/\//, "");
            app.settings.saveSetting("watchfolderPrefs", "watchfolderLocation", selectedFolder.path);
         str = str.replace(/\//, ":/");
            watchfolderLocation = selectedFolder.path;
         str = str.replace(/%20/g, " ");
            return true;
        str = str.replace(/\//g, "\\");
         } else {
         return str;
            return false;
        }
 
     }
     }


     sel = app.project.selection;
     function startWatchingFolder() {
    if(sel.length == 0){
         aerenderExe = '"' + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + '\\AfterFX.exe" -m -re -wf "' + Folder(watchfolderLocation).fsName + '"';
         alert("You need to select source(s) in the project panel");
         batch = new File(Folder.desktop.toString() + "/launch_WatchFolder.bat");
    }else{
        if (batch.open("w", "TEXT", "????") == true) {
         for(i=0;i<app.project.selection.length;i++){
             batch.write("@echo off\n");
             a = prompt("'OK' continues, 'cancel' stops displaying original sources\n\n[ "+sel[i].name+" ]",pathToWinPath(sel[i].mainSource.file.path.toString()));  
            batch.write(aerenderExe);
             if(!a){break}
            batch.close();
            batch.execute();
        } else {
             alert("unable to launch the AfterEffects worker");
         }
         }
     }
     }
}
    function startAERender() {
</pre>
//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) {


    }


===List selected layers effects and their properties matchNames ===
    function watchFolderUI(thisObj) {
<pre>
        var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
//windows only
         if (securitySetting != 1) {
if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
            alert("You need to check 'Allow Scripts to Write Files and Access Network' in your preferences for this script to work");
    var txtFile = new File("~/Desktop/effectList.txt");
        } else {
    txtFile.open("w","TEXT","????");
    var col = app.project.activeItem.selectedLayers;
    for (i=0;i<col.length;i++){
         effs = col[i].property("ADBE Effect Parade");
            for(j=1;j<=effs.numProperties;j++){
                txtFile.write("\n( "+effs.property(j).matchName+" ) "+effs.property(j).name+"\n------------------------------------------\n");
                for(k=1;k<=effs.property(j).numProperties;k++){
                    txtFile.write(effs.property(j).property(k).matchName+" --> "+effs.property(j).property(k).name);
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE && effs.property(j).property(k).value != undefined){
                        txtFile.write(" [ "+effs.property(j).property(k).value.toString()+" ] ");
                    }
                    txtFile.write("\n")


                    }
            }
    }


    txtFile.write("\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n");
    txtFile.write("v = s.Effects.addProperty(\"CC RepeTile\");\n");
    txtFile.write("v.property(\"CC RepeTile-0001\").setValue(10);");   
    txtFile.close();
    txtFile.execute();
}else{
    alert("Set scripting Prefs to enable to write to disk");
}
</pre>


http://i.imgur.com/IEUwe.gif
            var panelGlobal = thisObj;


===List effects===


<pre>
            /*
function projEffects(){
            Code for Import https://scriptui.joonas.me
var effects = new Array();
            */
var effects2 = new Array();
 
for(var i = 1; i <= app.project.numItems; i++){
            // DIALOG
if(app.project.item(i) instanceof CompItem){
            // ======
  var comp = app.project.item(i);
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window("palette", "Simple Watchfolder", [100, 100, 300, 300]);
for(j = 1; j <=  comp.layers.length;j++){
            if (!(panelGlobal instanceof Panel)) dialog.text = "Simple Watchfolder";
effs = comp.layer(j).property("ADBE Effect Parade");
            dialog.orientation = "column";
for(k=1;k<=effs.numProperties;k++){
 
keyName = effs.property(k).matchName;
            dialog.alignChildren = ["fill", "top"];
effects[keyName]  = 1;
            dialog.spacing = 10;
}
            dialog.margins = 16;
 
 
}
            var grp = dialog.add("group", undefined, {
}
                name: "group0"
}
            });
for(a in effects){
            grp.alignement = ["fill", "fill"];
effects2[effects2.length] = a;
            grp.alignChildren = ["fill", "top"];
}
            grp.orientation = "column";
effects2.sort();
return effects2;
}
alert(projEffects().join("\n"))


</pre>


http://i.imgur.com/hFNiQ.jpg
            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
            });


===Multiple sequences import (Windows)===
<pre>
// Multiple sequences import (windows)
// ----------------------------------------------
//  bernie@berniebernie.fr
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file
//  Access to files network required --- no error checking.
//  > only works on windows so far
//  > only finds exrs/pngs/jpgs
//  > probably fails if your sequences dont have the same start frame
//  > will work with /path/image.####.jpg or similar




            var group1 = grp.add("group", undefined, {name: "group1"});
            group1.orientation = "row";
            group1.alignChildren = ["fill", "top"];
            group1.spacing = 10;
            group1.margins = 0;


function uniq_fast(a) {
            var setWF = group1.add("button", undefined, undefined, {name: "setWF"});
    var seen = {};
            setWF.text = "Set WatchFolder";
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
        var item = a[i];
        if(seen[item] !== 1) {
              seen[item] = 1;
              out[j++] = item;
        }
    }
    return out;
}


            var openWF = group1.add("button", undefined, undefined, {name: "openWF"});
    openWF.enabled = (watchfolderLocation=="none")?false:true;
            openWF.text = "Open WF";


function e(s){
    $.writeln(s);
}
curScript = new File($.fileName);


startT = Date.now();
            var curWF = grp.add("statictext", undefined, undefined, {name: "statictext2"});
            curWF.helpTip = "shows current watchfolder";
            curWF.text = "(choose watchfolder)";


sourceFolder = app.project.selection[0].mainSource.file.parent;
tmpFolder = Folder.temp;
tmpBat = Folder.temp.toString()+"/ae_dirlist.bat";
tmpFilesList = Folder.temp.toString()+"/ae_imageslist.txt";
var listFile = new File(tmpFilesList);


            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 batFile = new File(tmpBat);
            var group2 = grp.add("group", undefined, {name: "group2"});
batFile.open("w","TEXT","????");
            group2.orientation = "row";
batFile.write("REM Auto generated by this after effects script file: "+curScript.fsName);
            group2.alignChildren = ["fill", "top"];
batFile.write("\npushd \""+sourceFolder.fsName+"\"");
            group2.spacing = 10;
batFile.write("\ndir /b /on *.exr *.png *.jpg *.jpeg > \""+listFile.fsName+"\"");
            group2.margins = 0;
batFile.close();
 
system.callSystem(batFile.fsName);
            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;




listFile.open("r","TEXT","????");
contents = listFile.read();
arrayContents = contents.split("\n");
listFile.close();
timer = (Date.now()-startT)/1000;
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");


            dialog.layout.layout(true);
            dialog.layout.resize();
            dialog.onResizing = dialog.onResize = function() {
                this.layout.resize();
            }


var myRe = new RegExp("[0-9]{2,}(\)){0,1}\.[a-z]+$");
var myArray = myRe.exec(arrayContents[0]);
endlength = myArray[0].length;


for(i = 0;i<arrayContents.length;i++){
    if (watchfolderLocation != "none") {
    arrayContents[i] = arrayContents[i].slice(0, -endlength);
                curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
}
}
unique = uniq_fast(arrayContents);


dialog = confirm(unique.length+" sequences found. Load them ? \nIt might take a long time !",false,"Confirm Loading");


if(dialog){    
            setWF.onClick = function() {
    for(i = 0;i<unique.length;i++){
                swf = setWatchFolder();
        if(unique[i].length>0){
                if (swf) {
            sequenceStartFile = new File(sourceFolder.toString()+"/"+unique[i]+myArray[0]);
                    curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
              
    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;
             if (sequenceStartFile) {
                }
             writeLn("Loading "+(i+1)+"/"+unique.length);
             }
                try {
             launchWorker.onClick = function() {
                    // Create a variable containing ImportOptions.
                startWatchingFolder();
                    var importOptions = new ImportOptions(sequenceStartFile);
             }
                    importOptions.sequence = true;
    switchToWorker.onClick = function() {
                    try {
app.watchFolder(watchfolderLocation);
    }
            sendAER.onClick = function() {
                startAERender();
            }


                        app.project.importFile(importOptions);
            openWF.onClick = function() {
                    } catch (error) {
        var myFolder = new Folder(watchfolderLocation);
                      e(error.toString());
        myFolder.execute();
                    }
                //watchfolderLocation
                 } catch (error) {
            }
                     e(error.toString());
            sendWF.onClick = function() {
                if (watchfolderLocation == "none") {
                    setWatchFolder();
                 } else {
                     sendToWF(watchfolderLocation);
                 }
                 }
             }
             }
         }
         }
     }
     }
}else{
     watchFolderUI(this);
     writeLn("Canceled");
}
}
timer = (Date.now()-startT)/1000;
e("after regext: "+timer);
</pre>
</pre>


=== Split to Renderqueue ===
===Show original source location (WIN only)===
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif
http://i.imgur.com/04O3r.png
<pre>
<pre>
// Split Layers to Renderqueue v2.0
{
// ----------------------------------------------
    function pathToWinPath(path){
//
        var str = path.toString().replace(/\//, "");
// This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp
        str = str.replace(/\//, ":/");
//  using the in and out points. I use this along with 'Magnum' the edit detector to split & render sequences.
        str = str.replace(/%20/g, " ");
//
        str = str.replace(/\//g, "\\");
// v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the 'base' renderqueue item that we derive everything from
        return str;
    }


function pad(n, width, z) {
    sel = app.project.selection;
  z = z || '0';
    if(sel.length == 0){
  n = n + '';
        alert("You need to select source(s) in the project panel");
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
    }else{
        for(i=0;i<app.project.selection.length;i++){
            a = prompt("'OK' continues, 'cancel' stops displaying original sources\n\n[ "+sel[i].name+" ]",pathToWinPath(sel[i].mainSource.file.path.toString()));  
            if(!a){break}
        }
    }
}
}
</pre>


app.beginUndoGroup("Split Layers to Renderqueue");


if(app.project.renderQueue.items.length == 1){
    FE = app.project.renderQueue.items[1];
    ai = FE.comp;
    path = FE.outputModule(1).file.path;
    for(i=1;i<=ai.layers.length;i++){


        RI = app.project.renderQueue.items[1].duplicate();
===List selected layers effects and their properties matchNames ===
        RI.timeSpanStart = ai.layers[i].inPoint;
<pre>
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;
//windows only
        $.writeln(ai.workAreaDuration+" "+ai.layers[i].outPoint);
if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
        RI.outputModule(1).file = new File(path+"/"+pad(i,2,"0")+"_"+ai.layers[i].name);
    var txtFile = new File("~/Desktop/effectList.txt");
    txtFile.open("w","TEXT","????");
    var col = app.project.activeItem.selectedLayers;
    for (i=0;i<col.length;i++){
        effs = col[i].property("ADBE Effect Parade");
            for(j=1;j<=effs.numProperties;j++){
                txtFile.write("\n( "+effs.property(j).matchName+" ) "+effs.property(j).name+"\n------------------------------------------\n");
                for(k=1;k<=effs.property(j).numProperties;k++){
                    txtFile.write(effs.property(j).property(k).matchName+" --> "+effs.property(j).property(k).name);
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE && effs.property(j).property(k).value != undefined){
                        txtFile.write(" [ "+effs.property(j).property(k).value.toString()+" ] ");
                    }
                    txtFile.write("\n")
 
                    }
            }
     }
     }
    app.project.renderQueue.items[1].render= false;
}else{
    alert("Only 1 element should be in renderqueue");
}
app.endUndoGroup;
</pre>


=== Docked Panel SNIP ===
    txtFile.write("\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n");
<pre>
    txtFile.write("v = s.Effects.addProperty(\"CC RepeTile\");\n");
//script panel
    txtFile.write("v.property(\"CC RepeTile-0001\").setValue(10);");  
{
    txtFile.close();
var nested_file = new File("U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx");
    txtFile.execute();
nested_file.open("r");
}else{
eval(nested_file.read());
    alert("Set scripting Prefs to enable to write to disk");
nested_file.close();
}
}
</pre>
</pre>
http://i.imgur.com/IEUwe.gif
==== Disable effects ====
Disable/enables all the effects that are similar to the currently selected one throughout the project. This was fully GPT generated on first try. Impressed!
<pre>
<pre>
//called file
(function toggleEffectGlobally() {
function createUI(thisObj) {
    app.beginUndoGroup("Toggle Effect Globally");
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",[100, 100, 300, 300]);
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
//impButton.onClick = openfile;
return myPanel;
}
var myToolsPanel = createUI(this);
</pre>


===Watchfolder watcher (WIP)===
    var sel = app.project.activeItem && app.project.activeItem.selectedProperties;
http://i.imgur.com/lhO3k.gif
    if (!sel || sel.length === 0) {
        alert("Please select an effect first.");
        return;
    }


Needs in the ScriptUI folder http://i.imgur.com/Nfw8h.png http://i.imgur.com/rgPGl.png http://i.imgur.com/N93S7.png http://i.imgur.com/VTGZl.png http://i.imgur.com/PjAkj.png (flag_orange.png, flag_green.png, flag_red.png,flag_graygreen.png)
    var selectedEffect = null;
<pre>
 
// watchwatchfolder: a tool to look at what's in the watchfolder
    // Find the selected effect property group
//  v1.0 by bernie - mbernadat@gmail.com
    for (var i = 0; i < sel.length; i++) {
//
        if (sel[i].matchName && sel[i].parentProperty && sel[i].parentProperty.matchName === "ADBE Effect Parade") {
//
            selectedEffect = sel[i];
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License.  
            break;
//
        }
//  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.


/*
    if (!selectedEffect) {
todo: check if icons are here
        alert("Please select an effect in the timeline.");
*/
        return;
    }


{
    var effectName = selectedEffect.matchName;
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__";
    var effectEnabled = selectedEffect.enabled;
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
    // Loop through all comps in the project
var refreshBtn;
    for (var p = 1; p <= app.project.numItems; p++) {
var list;
        var item = app.project.item(p);
var wfStText;
        if (item instanceof CompItem) {
var wfPbar;
            for (var l = 1; l <= item.numLayers; l++) {
var chckBox0;
                var layer = item.layer(l);
var chckBox1;
                if (layer.property("ADBE Effect Parade")) {
var chckBox2;
                    var effects = layer.property("ADBE Effect Parade");
var chckBox3;
                    for (var e = 1; e <= effects.numProperties; e++) {
                        var eff = effects.property(e);
                        if (eff.matchName === effectName) {
                            eff.enabled = !effectEnabled;
                        }
                    }
                }
            }
        }
    }
 
    app.endUndoGroup();
})();
</pre>


===List effects===


function getLogonly(file){
<pre>
    if(file.name.indexOf("Logs)") != -1){
function projEffects(){
        return true;
var effects = new Array();
    }
var effects2 = new Array();
        return false;
for(var i = 1; i <= app.project.numItems; i++){
  }
if(app.project.item(i) instanceof CompItem){
function pathToWinPath(path){
  var comp = app.project.item(i);
str = path.replace(/\//, "");
for(j = 1; j <= comp.layers.length;j++){
str = str.replace(/\//, ":/");
effs = comp.layer(j).property("ADBE Effect Parade");
str = str.replace(/%20/g, " ");
for(k=1;k<=effs.numProperties;k++){
str = str.replace(/\//g, "\\");
keyName = effs.property(k).matchName;
return str;
effects[keyName]  = 1;
}
}
function localToRessource(path){
 
    //windows, for now, the only one available!
}
    str = "/"+path.replace(":\\", "/");
}
    str = str.replace(/\\/g, "/");
}
    str = str.replace(" ", "%20");
for(a in effects){
    str = str.substring(0,str.lastIndexOf("/"));
effects2[effects2.length] = a;
    return str;
}
}
effects2.sort();
function returnFolderArray(location){
return effects2;
    fold = new Folder(location.toString());
}
    foldArray = fold.getFiles()
alert(projEffects().join("\n"))
    folderOnly = new Array();
 
    for(i=0;i<foldArray.length;i++){
</pre>
        if(foldArray[i] instanceof Folder){
 
            folderOnly[folderOnly.length] = foldArray[i];
http://i.imgur.com/hFNiQ.jpg
            }
 
        }
===Multiple sequences import (Windows)===
    return folderOnly;
<pre>
}
// Multiple sequences import (windows)
function logInfo(logFolderLocation){
// ----------------------------------------------
      
//  bernie@berniebernie.fr
     curFold = new Folder(logFolderLocation.toString());
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file
     folds = returnFolderArray(logFolderLocation);
//  Access to files network required --- no error checking.
     txtfiles = fold.getFiles("*RCF.txt"); //there should only be one
//  > only works on windows so far
     htmlfiles = fold.getFiles("*.htm");
//  > only finds exrs/pngs/jpgs
    var htmlfile;
//  > probably fails if your sequences dont have the same start frame
    if(htmlfiles[0]){
//  > will work with /path/image.####.jpg or similar
        htmlfile = htmlfiles[0].toString();
 
 
 
function uniq_fast(a) {
     var seen = {};
     var out = [];
     var len = a.length;
     var j = 0;
     for(var i = 0; i < len; i++) {
        var item = a[i];
        if(seen[item] !== 1) {
              seen[item] = 1;
              out[j++] = item;
        }
     }
     }
  // $.writeln("file >>> "+txtfiles[0].toString());
    return out;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+"rrr"];
}
    var fcontents = "";
 
    if(txtfiles[0]){
 
        f = txtfiles[0].toString();
function e(s){
        f = new File(f);
    $.writeln(s);
        f.open("r","TEXT","????");
}
        fcontents = f.read();
curScript = new File($.fileName);
        f.close();
 
        var findItems = new RegExp("^(item)[0-9]{1,}(=\()(.*)(\))$","mi");
startT = Date.now();
        v=findItems.exec(fcontents);
 
        if(v){
sourceFolder = app.project.selection[0].mainSource.file.parent;
            if(v[4].indexOf("Stopped") != -1){
tmpFolder = Folder.temp;
                info=["name",1,v[4].substring(1,v[4].indexOf(","))];
tmpBat = Folder.temp.toString()+"/ae_dirlist.bat";
            }else if(v[4].indexOf("In Progress") != -1){
tmpFilesList = Folder.temp.toString()+"/ae_imageslist.txt";
                info=["name",2,"Rendering"];
var listFile = new File(tmpFilesList);
            }else if(v[4].indexOf("Finished") != -1){
                info=["name",0,""];
            }


        }else{
 
          // $.writeln("Buggy file");
 
            info=[logFolderLocation.name.toString(),1,"--Bug--",logFolderLocation.toString()+"eeee"];
var batFile = new File(tmpBat);
        }
batFile.open("w","TEXT","????");
        fold = new Folder(folds[0].toString());
batFile.write("REM Auto generated by this after effects script file: "+curScript.fsName);
        files = fold.getFiles("*.txt");  
batFile.write("\npushd \""+sourceFolder.fsName+"\"");
        firstFile = files[files.length-1]; //used to be first file, works better with last text file
batFile.write("\ndir /b /on *.exr *.png *.jpg *.jpeg > \""+listFile.fsName+"\"");
        if(firstFile == undefined || firstFile == null){
batFile.close();
              info=[logFolderLocation.name.toString(),1,"Error",htmlfile];
system.callSystem(batFile.fsName);
        }else{
 
                //info = ["debug",logFolderLocation.toString(),logFolderLocation.name.toString()];
 
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];
listFile.open("r","TEXT","????");  
             
contents = listFile.read();
                firstFile.open("r","TEXT","????");
arrayContents = contents.split("\n");
                contents = firstFile.read();
listFile.close();
                firstFile.close();
timer = (Date.now()-startT)/1000;
                lines = contents.split("\n");
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");
              //  $.writeln(logFolderLocation.toString()+" <<< ")
 
              info[3] = logFolderLocation.toString();
 
              info[0] = logFolderLocation.name.toString();
var myRe = new RegExp("[0-9]{2,}(\)){0,1}\.[a-z]+$");
                for(i=0;i<lines.length;i++){
var myArray = myRe.exec(arrayContents[0]);
                    //$.writeln("Doing shit");
endlength = myArray[0].length;
                    phrase ="Rendering started on";
 
                    info[4] = "n/a";
for(i = 0;i<arrayContents.length;i++){
                    if(s = lines[i].indexOf(phrase) != -1){
    arrayContents[i] = arrayContents[i].slice(0, -endlength);
                        info[4] = lines[i].substring(s+phrase.length);      
}
                    }
unique = uniq_fast(arrayContents);
                    phrase ="Output To: ";
 
                 
dialog = confirm(unique.length+" sequences found. Load them ? \nIt might take a long time !",false,"Confirm Loading");
                  // info[2] = "n/a+";
 
//                    info[1] = 1;
if(dialog){    
                   
    for(i = 0;i<unique.length;i++){
                    if(info[1]==0 ||info[1]==2){
        if(unique[i].length>0){
                      // $.writeln( lines[i].indexOf(phrase));
            sequenceStartFile = new File(sourceFolder.toString()+"/"+unique[i]+myArray[0]);
                            if(s = lines[i].indexOf(phrase) != -1){
           
                                s = lines[i].indexOf(phrase);
            if (sequenceStartFile) {
                                v = lines[i].substring(s+phrase.length);
            writeLn("Loading "+(i+1)+"/"+unique.length);
                                wfold = v.substring(0,v.lastIndexOf("\\"));
                try {
                              //$.writeln(wfold);
                    // Create a variable containing ImportOptions.
                                f = wfold.substring(wfold.lastIndexOf("\\")+1,wfold.length);
                    var importOptions = new ImportOptions(sequenceStartFile);
                                ///info[3] = pathToWinPath(wfold);
                    importOptions.sequence = true;
                                info[3] =localToRessource(wfold);
                    try {
                                $.writeln(info[3])
 
                                info[0] = f;
                        app.project.importFile(importOptions);
                              // info[2] = " ";
                    } catch (error) {
                            }
                      e(error.toString());
                        }
                    }
                  // info[1] = ;
                } catch (error) {
               
                    e(error.toString());
                 }
                 }
              //  $.writeln(info);
            }
              // $.writeln(contents);
         }
         }
 
     }
     }
  $.writeln(info);
}else{
    return info;
    writeLn("Canceled");
           
}
}
function hasStarted(watchedFolderLocation){
timer = (Date.now()-startT)/1000;
    fold = new Folder(watchedFolderLocation.toString());
e("after regext: "+timer);
    files = fold.getFiles(getLogonly);
</pre>
    //$.writeln(files.length);
 
    if(files.length>0){
=== Split to Renderqueue ===
        return true;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif
    }else{
<pre>
        return false;
// Split Layers to Renderqueue v2.0
    }
// ----------------------------------------------
//
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp
//  using the in and out points. I use this along with 'Magnum' the edit detector to split & render sequences.
//
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the 'base' renderqueue item that we derive everything from
 
function pad(n, width, z) {
  z = z || '0';
  n = n + '';
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
}


function getFilePath(watchedFolderLocation){
app.beginUndoGroup("Split Layers to Renderqueue");
     fold = new Folder(watchedFolderLocation.toString());
 
    files = fold.getFiles(getLogonly);
if(app.project.renderQueue.items.length == 1){
    alert(files[0].name);
     FE = app.project.renderQueue.items[1];
     return files[0].name;
    ai = FE.comp;
    path = FE.outputModule(1).file.path;
    for(i=1;i<=ai.layers.length;i++){
 
        RI = app.project.renderQueue.items[1].duplicate();
        RI.timeSpanStart = ai.layers[i].inPoint;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;
        $.writeln(ai.workAreaDuration+" "+ai.layers[i].outPoint);
        RI.outputModule(1).file =  new File(path+"/"+pad(i,2,"0")+"_"+ai.layers[i].name);
     }
    app.project.renderQueue.items[1].render= false;
}else{
    alert("Only 1 element should be in renderqueue");
}
}
/*
app.endUndoGroup;
function getPercentage(folder){
</pre>
    myFolder = new Folder(folder);
 
    files = myFolder.getFiles("*DandyWatch.txt");
=== Docked Panel SNIP ===
    if(files.length != 1){
<pre>
        return false;
//script panel
    }
{
    myTextFile = files[0];   
var nested_file = new File("U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx");
    myTextFile.open("r","TEXT","????");
nested_file.open("r");
    contents = myTextFile.read();
eval(nested_file.read());
    myTextFile.close();
nested_file.close();
    lines = contents.split("\n");
}
    outPutFolder = new Folder(lines[0]);
</pre>
    files = outPutFolder.getFiles();
<pre>
    f = files.length;
//called file
    writeLn(f+"/"+(lines[1]+1));
function createUI(thisObj) {
    return parseFloat(files.length/(lines[1]+1));
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",[100, 100, 300, 300]);
    //var percentage = new Array();
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
//impButton.onClick = openfile;
return myPanel;
}
}
//getPercentage(watchfolderLocation+"/"+shot);
var myToolsPanel = createUI(this);
*/
</pre>


function explore(location){
===Watchfolder watcher (WIP)===
    fold = new Folder(location.toString());
http://i.imgur.com/lhO3k.gif
    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()];
            }
            //$.writeln(">>> "+state+" - "+out+" - "+folders[folder].name);
           
          // $.writeln(out);
            vArray[counter2] =out;
        }
      // $.writeln(vArray[counter2]);
        wfPbar.value = l/folders.length*100;


        if (pal instanceof Window){
Needs in the ScriptUI folder http://i.imgur.com/Nfw8h.png http://i.imgur.com/rgPGl.png http://i.imgur.com/N93S7.png http://i.imgur.com/VTGZl.png http://i.imgur.com/PjAkj.png (flag_orange.png, flag_green.png, flag_red.png,flag_graygreen.png)
            pal.update();
<pre>
        }else{
//  watchwatchfolder: a tool to look at what's in the watchfolder
            if(counter < Math.round(wfPbar.value/5,0)*5){
//  v1.0 by bernie - mbernadat@gmail.com
                counter +=5;
//
                clearOutput();
//
                writeLn(counter+"%");
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License.  
            }else{
//
                write(".");
//  known limitations (v1.0), to be fixed:
            }
//  -windows only for now
        }
//  -only looks at 1 renderqueue element per AEP file.
    }
//
    return vArray;
//
}
// I coded this like a dirty monkey. I feel sorry if you have to look at this.
function gather(){
 
                refreshBtn.text = "refreshing...";
/*
                refreshBtn.enabled = false;
todo: check if icons are here
                wfStText.visible = false;
*/
                wfPbar.visible = true;
                shotinfos = retrieveArray();
                list.removeAll();
                for(i=1;i<shotinfos.length;i++){
                    var p = shotinfos[i];
                                      // $.writeln("Debug");$.writeln(p);
                    addItem(p[0],p[1],p[2]);


                }
{
                writeLn("Done");
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__";
                wfStText.visible = true;
var wf = app.settings.haveSetting("watchwatchfolder", "wfloc")?new Folder(app.settings.getSetting("watchwatchfolder", "wfloc")):null;
                wfPbar.visible = false;
var scriptFile = new File($.fileName);
               
var scriptFolder = scriptFile.parent; //png icons should be here
                //set (or not) the timer
b=0; //global string that will store the  
                refreshBtn.enabled = true;
var cancelTaskID;
                if(parseInt(reloadEditTxt.text) > 0){
var pal;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;
var timer = 0;
var refreshRate = 5; //seconds
var reloadEditTxt;
var firstTime = true;
var shotinfos = new Array();


                    refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
//global ui names
                    cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);              
var refreshBtn;
                }else{
var list;
                    refreshBtn.text = "Refresh";
var wfStText;
                }
var wfPbar;
}
var chckBox0;
function watchWatchFolder(thisObj){
var chckBox1;
var chckBox2;
var chckBox3;


        //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');


 
function getLogonly(file){
           
    if(file.name.indexOf("Logs)") != -1){
                sortByName.value = true;
        return true;
            var bottomElements = pal.add("panel",[10,310,260,360],undefined,{borderStyle:"none"});
    }
           
        return false;
            bl = 0;
}
            var arrayV = new Array("green","red","orange","graygreen","gray");    
function pathToWinPath(path){
           
str = path.replace(/\//, "");
            bottomElements.add('image',[5,bl,20,bl+14],scriptFolder.toString()+"/flag_"+arrayV[0]+".png");
str = str.replace(/\//, ":/");
            chckBox0 = bottomElements.add('checkbox',[22,bl,39,bl+16], '');
str = str.replace(/%20/g, " ");
            chckBox0.value = true;
str = str.replace(/\//g, "\\");
            chckBox0.helpTip = "Show finished renders";
return str;
           
}
            bottomElements.add('image',[45,bl,60,bl+14],scriptFolder.toString()+"/flag_"+arrayV[1]+".png");
function localToRessource(path){
            chckBox1 = bottomElements.add('checkbox',[62,bl,79,bl+16], '');
    //windows, for now, the only one available!
            chckBox1.value = true;
    str = "/"+path.replace(":\\", "/");
            chckBox1.helpTip = "Show errors";
    str = str.replace(/\\/g, "/");
           
    str = str.replace(" ", "%20");
            bottomElements.add('image',[85,bl,100,bl+14],scriptFolder.toString()+"/flag_"+arrayV[2]+".png");
    str = str.substring(0,str.lastIndexOf("/"));
            chckBox2 = bottomElements.add('checkbox',[103,bl,120,bl+16], '');
    return str;
             chckBox2.value = true;
}
             chckBox2.helpTip = "Show rendering";
function returnFolderArray(location){
         
    fold = new Folder(location.toString());
            bottomElements.add('image',[125,bl,140,bl+14],scriptFolder.toString()+"/flag_"+arrayV[4]+".png");
    foldArray = fold.getFiles()
            chckBox3 = bottomElements.add('checkbox',[143,bl,160,bl+16], '');
    folderOnly = new Array();
            chckBox3.value = true;
    for(i=0;i<foldArray.length;i++){
            chckBox3.helpTip = "Show queued";
        if(foldArray[i] instanceof Folder){
           
             folderOnly[folderOnly.length] = foldArray[i];
            bl = 25;
             }
            var reloadStTxt bottomElements.add("statictext",[5,bl+2,50,bl+21], "Update: ");
        }
            reloadEditTxt =  bottomElements.add("edittext",[55,bl,90,bl+20], 600);
    return folderOnly;
            reloadEditTxt.enabled=false;
}
            refreshBtn =  bottomElements.add("button",[100,bl,260,bl+20], "Start");
function logInfo(logFolderLocation){
            reloadEditTxt.helpTip = "in seconds, 0 for manual refresh only.";
   
            list = pal.add ("ListBox", [10, 65, 260,320], "desc",{numberOfColumns: 2,showHeaders: true});      
    curFold = new Folder(logFolderLocation.toString());
           
    folds = returnFolderArray(logFolderLocation);
           
    txtfiles = fold.getFiles("*RCF.txt"); //there should only be one
            //list.columnTitles = Array("First Name", "Last"); doesn't work in CS5 apparently
    htmlfiles fold.getFiles("*.htm");
           
    var htmlfile;
        }
    if(htmlfiles[0]){
   
        htmlfile = htmlfiles[0].toString();
   
    }
         //UI Callbacks
  // $.writeln("file >>> "+txtfiles[0].toString());
         {
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+"rrr"];
            wfLocBtn.onClick = function(){
    var fcontents = "";
                wfLoc = Folder.selectDialog("Select watchfolder");
    if(txtfiles[0]){
                if(wfLoc != null){
        f = txtfiles[0].toString();
                    app.settings.saveSetting("watchwatchfolder", "wfloc",wfLoc.toString());
        f = new File(f);
                    wf = new Folder(wfLoc.toString());
        f.open("r","TEXT","????");
                    if(wf){
         fcontents = f.read();
                        wfStText.text = wfLoc.toString();
         f.close();
                    }
        var findItems = new RegExp("^(item)[0-9]{1,}(=\()(.*)(\))$","mi");
                }
        v=findItems.exec(fcontents);
            }   
        if(v){
            pal.onResize = function(){
            if(v[4].indexOf("Stopped") != -1){
                 //because using layouts is too confusing for me
                 info=["name",1,v[4].substring(1,v[4].indexOf(","))]; 
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];
            }else if(v[4].indexOf("In Progress") != -1){
                 wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];
                 info=["name",2,"Rendering"];
                wfPbar.bounds = wfStText.bounds;
            }else if(v[4].indexOf("Finished") != -1){
                 bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];
                 info=["name",0,""];
             }
             }
            list.onDoubleClick = function(){//onChange
 
                sel = list.selection;
        }else{
             
          // $.writeln("Buggy file");
                if(list.selection != null){
            info=[logFolderLocation.name.toString(),1,"--Bug--",logFolderLocation.toString()+"eeee"];
                    // $.writeln(shotinfos.join("\n"));
        }
                    sL=shotinfos.length;
        fold = new Folder(folds[0].toString());
                    //$.writeln(sL);
        files = fold.getFiles("*.txt");  
                    for(i=1;i<sL;i++){
        firstFile = files[files.length-1]; //used to be first file, works better with last text file
                      a=shotinfos[i];
        if(firstFile == undefined || firstFile == null){
                      //$.writeln(a);
              info=[logFolderLocation.name.toString(),1,"Error",htmlfile];
                        //$.writeln(shotinfos[i][1]+" "+list.selection.toString());
        }else{
                        if(a[0] == list.selection.toString()){
                //info = ["debug",logFolderLocation.toString(),logFolderLocation.name.toString()];
                          // $.writeln("ww"+);
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];
                      //  $.writeln(a);
             
                            //explore(a[3]+((a[1]==2)?"/"+a[0]:""));
                 firstFile.open("r","TEXT","????");
                            explore(a[3]);
                contents = firstFile.read();
                            break;
                 firstFile.close();
                            }
                 lines = contents.split("\n");
                        }
              // $.writeln(logFolderLocation.toString()+" <<< ")
                    //$.writeln(sel[0].subItems[0].text);
              info[3] = logFolderLocation.toString();
                }
              info[0] = logFolderLocation.name.toString();
 
                 for(i=0;i<lines.length;i++){
 
                     //$.writeln("Doing shit");
            }       
                     phrase ="Rendering started on";
           
                     info[4] = "n/a";
           
                    if(s = lines[i].indexOf(phrase) != -1){
            refreshBtn.onClick = function(){
                         info[4] = lines[i].substring(s+phrase.length);      
                 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;
                    }
                 }
               
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;
                //refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
                //cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
            }
            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(reloadEditTxt.text >=10){
                }else{
                    if(timer <= 0){
                         refreshBtn.text = "Start (~ "+eggTimer(reloadEditTxt.text)+")";
                     }
                     }
                    phrase ="Output To: ";
                 
                  // info[2] = "n/a+";
//                    info[1] = 1;
                   
                    if(info[1]==0 ||info[1]==2){
                      // $.writeln( lines[i].indexOf(phrase));
                            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("\\"));
                              //$.writeln(wfold);
                                f =  wfold.substring(wfold.lastIndexOf("\\")+1,wfold.length);
                                ///info[3] = pathToWinPath(wfold);
                                info[3] =localToRessource(wfold);
                                $.writeln(info[3])
                                info[0] = f;
                              // info[2] = " ";
                            }
                        }
                  // info[1] = ;
               
                 }
                 }
            reloadEditTxt.enabled=false;                 
              //  $.writeln(info);
            }
               // $.writeln(contents);
         }
         }
      
 
      
     }
        //Initialize the whole shebang
  $.writeln(info);
            if (pal instanceof Window){
     return info;
                pal.center();
           
                pal.show();
}
            }
function hasStarted(watchedFolderLocation){
 
    fold = new Folder(watchedFolderLocation.toString());
          
    files = fold.getFiles(getLogonly);
      return pal;
    //$.writeln(files.length);
    }
    if(files.length>0){
//reloadEditTxt.notify("onChange");
         return true;
ui = watchWatchFolder(this);
    }else{
        return false;
    }
}
}


function eggTimer(time){
function getFilePath(watchedFolderLocation){
     if(time<=59){
     fold = new Folder(watchedFolderLocation.toString());
        time = time+"s";
     files = fold.getFiles(getLogonly);
    }else if(time<=60*4){
     alert(files[0].name);
        time = Math.floor(time/60)+"m "+(time%60)+"s";
     return files[0].name;
     }else if(time<60*60){
}
        time = Math.round(time/60,0)+"m";
/*
     }else{
function getPercentage(folder){
        time = Math.round(time/(60*60),0)+"h";
     myFolder = new Folder(folder);
    }
     files = myFolder.getFiles("*DandyWatch.txt");
     return time;
     if(files.length != 1){
    }
         return false;
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();
        }
     }
     }
    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();
}
}
function cancelTask(id){
//getPercentage(watchfolderLocation+"/"+shot);
        app.cancelTask(id);
*/
    }


function grow(palette,value){
function explore(location){
      
    fold = new Folder(location.toString());
    return fold.execute();      
}
}
function addItem(name,state,msg){
function retrieveArray(){
     //   alert(listItem.selection);
     var state = 4; //queued, default state
    var arrayV = new Array("green","red","orange","graygreen","gray");
    var vArray = new Array();
     //var texted = new Array("...","error:","rendering","...","queued");
     l =0;
     var item = list.add ('item',name);
    counter=0;
     //$.writeln(scriptFolder.toString()+"/flag_"+array[state]+".png");
    counter2=0;
    item.image = File(scriptFolder.toString()+"/flag_"+arrayV[state]+".png");
     folders = returnFolderArray(wf);
    //item.subItems[0].helpTip = texted(state);
     for(folder in folders){
    item.subItems[0].text =msg;
    l++;
 
        var out = "";
}
        if(folders[folder].name != "anonymous"){ //??
///////////////////////////////////////////
            counter2++;
          /* if((logFolder = getFilePath(watchfolderLocation+"/"+shot)) != undefined){
            if(hasStarted(folders[folder])){
                 $.writeln(logInfo(logFolder));
                state = 0;
                out = logInfo(folders[folder]);
                 //$.writeln(out);
                //out[3] = localToRessource(out[3]);
                                 
             }else{
             }else{
                 $.writeln("no log folder");
                 state = 4;
                }*/
                out = [folders[folder].name,4,"",folders[folder].toString()];
          // $.writeln();
            }
          // addItem(list,"G12_SC213_T1",2);
            //$.writeln(">>> "+state+" - "+out+" - "+folders[folder].name);
         
           
         
           // $.writeln(out);
           /*
            vArray[counter2] =out;
var item1 = list.add ('item', 'GB15_SC138_T1');
        }
item1.image = File("~/Desktop/flag_gray.png");
      // $.writeln(vArray[counter2]);
item1.subItems[0].text = 'Queued...';
        wfPbar.value = l/folders.length*100;
item1.enabled = false;
*/


           
        if (pal instanceof Window){
            //alert(getPercentage(watchfolderLocation+"/"+shot));
            pal.update();
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+"/"+shot)*100);
        }else{
         
            if(counter < Math.round(wfPbar.value/5,0)*5){
                      // alert("test");
                counter +=5;
              //$.writeln(files[file].path+"/"+files[file].name);
                clearOutput();
                            //  a+=  getFilePath(files[file].path+"/"+files[file].name).name+"\n";
                 writeLn(counter+"%");
                           
             }else{
/*                          
                write(".");
                           
             }
                            {
                               
        wfLocBtn.onClick = function(){
            app.scheduleTask("loop()",2000,1);
 
         
            fold = new Folder( watchfolderLocation.toString());
            files = fold.getFiles();
                 a= "";
             for(file in files){
 
              $.writeln(getFilePath(files[file].path+"/"+files[file].name));
 
             }
          alert(a);
 
            writeLn(pBar.value);
         }
         }
*/
    }
</pre>
    return vArray;
 
===Import pos from maya===
<pre>
function createUI(thisObj) {
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",
[100, 100, 300, 300]);
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
impButton.onClick = openfile;
return myPanel;
}
}
var myToolsPanel = createUI(this);
function gather(){
//myToolsPanel.show();
                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];
                                      // $.writeln("Debug");$.writeln(p);
                    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;


function openfile(){
                    refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
            var myFile = File.openDialog ("Select track file","*.txt");  
                    cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);              
            var fileOK = myFile.open("r","TEXT","????");
                }else{
//var fileD = OpenDlg ("Tracking Point File","*.txt",true);
                    refreshBtn.text = "Refresh";
//txt = fileD.readln ()
                }
alert("txt"+readTxt(myFile));
}
}
function watchWatchFolder(thisObj){


function readTxt(myFile){
        //UI Design
var myText = myFile.read();
        {
return myText;
            pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", "WatchFolder", undefined, {resizeable:true});
}
            var winGfx = pal.graphics;
</pre>
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);
===Dandelion/Amazing World Of Gumball Shotbuilder===
            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');




http://imgur.com/xKJWB.png
           
 
                sortByName.value = true;
 
            var bottomElements = pal.add("panel",[10,310,260,360],undefined,{borderStyle:"none"});
For education purposes only, the script was run in production to helb build shots, it:
           
* created a list of shots & shows possible given a folder structure ("GB##_SHOWNAME_SC##_T##")
            bl = 0;
* opened the last .AEP file found in said shot folder
            var arrayV = new Array("green","red","orange","graygreen","gray");     
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder
           
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the 'grab sources' button
            bottomElements.add('image',[5,bl,20,bl+14],scriptFolder.toString()+"/flag_"+arrayV[0]+".png");
* automatically sent files to the watchfolder to render on a small-ish farm.  
            chckBox0 = bottomElements.add('checkbox',[22,bl,39,bl+16], '');
Other buttons allowed to check for missing footage, open the comp's folder, open the current shows' latest animatic, etc
            chckBox0.value = true;
 
            chckBox0.helpTip = "Show finished renders";
 
           
<pre>
            bottomElements.add('image',[45,bl,60,bl+14],scriptFolder.toString()+"/flag_"+arrayV[1]+".png");
//  
            chckBox1 = bottomElements.add('checkbox',[62,bl,79,bl+16], '');
// Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
            chckBox1.value = true;
//  last update 21/11/10
            chckBox1.helpTip = "Show errors";
//
           
//  Known Bugs:
            bottomElements.add('image',[85,bl,100,bl+14],scriptFolder.toString()+"/flag_"+arrayV[2]+".png");
//     
            chckBox2 = bottomElements.add('checkbox',[103,bl,120,bl+16], '');
// -you can't have several sequences in a single folder and expect the script to pick up all the sequences
            chckBox2.value = true;
//  -this will not set color profiles
            chckBox2.helpTip = "Show rendering";
// //TODO
         
//  >>> CHECK IF WF FOLDER ALREADY EXISTS
            bottomElements.add('image',[125,bl,140,bl+14],scriptFolder.toString()+"/flag_"+arrayV[4]+".png");
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
            chckBox3 = bottomElements.add('checkbox',[143,bl,160,bl+16], '');
//  -v0.7 fixes
            chckBox3.value = true;
//       -new scene after WF works properly
            chckBox3.helpTip = "Show queued";
//        -changed output folder location to N:
           
//  -v0.6 fixes
            bl = 25;
//      -cancel watchfolder cancels watchfolder
            var reloadStTxt = bottomElements.add("statictext",[5,bl+2,50,bl+21], "Update: ");
//        -fixed GB##_SC_###_T1
            reloadEditTxt = bottomElements.add("edittext",[55,bl,90,bl+20], 600);
//      -added options
            reloadEditTxt.enabled=false;
//      -can work on a new location
            refreshBtn =  bottomElements.add("button",[100,bl,260,bl+20], "Start");
//  -v0.5 fixes
            reloadEditTxt.helpTip = "in seconds, 0 for manual refresh only.";
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)
            list = pal.add ("ListBox", [10, 65, 260,320], "desc",{numberOfColumns: 2,showHeaders: true});       
//     -will warn if there is missing footage before sending to WF
           
//      -removed set take, added 'missing' dialog.
           
//  -v0.4 fixes
            //list.columnTitles = Array("First Name", "Last"); doesn't work in CS5 apparently
//      -there shouldn't be a refresh problem on show change anymore
           
//  -v0.3 fixes
        }
//     -changed watchfolder location to anthony's mac
   
//     -changed save as dialog
   
//      -removed unused buttons
        //UI Callbacks
//     -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)
        {
// -v0.2 fixes
            wfLocBtn.onClick = function(){
//      -sequences weren't getting imported.
                wfLoc = Folder.selectDialog("Select watchfolder");
//     -fixed UI/little problems
                if(wfLoc != null){
//     -added folders, refresh button, save before watchfoldering
                    app.settings.saveSetting("watchwatchfolder", "wfloc",wfLoc.toString());
var version = "0.7";
                    wf = new Folder(wfLoc.toString());
 
                    if(wf){
//default locations
                        wfStText.text = wfLoc.toString();
var watchfolderLocation = "/c/__WATCHFOLDER__";
                    }
var rootFolder = "/c/";
                }
var forEditFolderLoaction = "//MAC0023DFDF5429/for%20edit";
            }   
            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){
                    // $.writeln(shotinfos.join("\n"));
                    sL=shotinfos.length;
                    //$.writeln(sL);
                    for(i=1;i<sL;i++){
                      a=shotinfos[i];
                      //$.writeln(a);
                        //$.writeln(shotinfos[i][1]+" "+list.selection.toString());
                        if(a[0] == list.selection.toString()){
                          // $.writeln("ww"+);
                      // $.writeln(a);
                            //explore(a[3]+((a[1]==2)?"/"+a[0]:""));
                            explore(a[3]);
                            break;
                            }
                        }
                    //$.writeln(sel[0].subItems[0].text);
                }




rootFolder = ((app.settings.haveSetting("dandybuildPrefs", "rootfolder")))?(app.settings.getSetting("dandybuildPrefs", "rootfolder")):rootFolder;
             }       
watchfolderLocation = ((app.settings.haveSetting("dandybuildPrefs", "watchfolderLocation")))?(app.settings.getSetting("dandybuildPrefs", "watchfolderLocation")):watchfolderLocation;
              
forEditFolderLoaction = ((app.settings.haveSetting("dandybuildPrefs", "forEditFolderLoaction")))?(app.settings.getSetting("dandybuildPrefs", "forEditFolderLoaction")):forEditFolderLoaction;
 
          //dirty hack
            a = ((app.settings.haveSetting("dandybuildPrefs", "optMissingFootage")))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true;
            b = ((app.settings.haveSetting("dandybuildPrefs", "optNuComp")))?(app.settings.getSetting("dandybuildPrefs", "optNuComp")):false;
            c = ((app.settings.haveSetting("dandybuildPrefs", "optSaveComp")))?(app.settings.getSetting("dandybuildPrefs", "optSaveComp")):true;
            if(a=="false") a = false;
            if(a=="true") a = true;
            if(b=="false") b = false;
            if(b=="true") b = true;
             if(c=="false") c = false;
             if(c=="true") c = true;
              
              
         
            refreshBtn.onClick = function(){
 
                if((timer>0 && !firstTime) || !pal.visible){
 
                        timer = 0;
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_";
                      // $.writeln("stopped");
var loadFile = ((app.settings.haveSetting("dandybuildPrefs", "loadscene")))?(app.settings.getSetting("dandybuildPrefs", "loadscene")):"";
                        refreshBtn.text = "Start";
checkOutputSettings();
                        firstTime = true;
 
                        cancelTask(cancelTaskID);
Array.prototype.has = function(value) {
                }else{
    var i;
                    if(reloadEditTxt.text == 0 || firstTime){
    for (i=0;i<this.length;i++) {
                            gather();
        if (this[i] == value) {
                            firstTime = false;
             return true;
                    }
                }
               
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;
                //refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
                //cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
            }
            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(reloadEditTxt.text >=10){
                }else{
                    if(timer <= 0){
                        refreshBtn.text = "Start (~ "+eggTimer(reloadEditTxt.text)+")";
                    }
                }
             reloadEditTxt.enabled=false;              
            }
         }
         }
     }
      
    return false;
   
}
        //Initialize the whole shebang
            if (pal instanceof Window){
                pal.center();
                pal.show();
            }
 
       
      return pal;
    }
//reloadEditTxt.notify("onChange");
ui = watchWatchFolder(this);
}


 
function eggTimer(time){
function addShotDialog(sel){
    if(time<=59){
     alert(sel);
        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){


function pathToWinPath(path){
        cancelTask(cancelTaskID);
    path = path.toString();
        if(reloadEditTxt.value == 0 || !pal.visible){
str = path.replace(/\//, "");
         
str = str.replace(/\//, ":/");
        }else{
str = str.replace(/%20/g, " ");
            gather();
str = str.replace(/\//g, "\\");
        }
return str;
    }
}
}
function cancelTask(id){
        app.cancelTask(id);
    }


function returnFolderArray(location){
function grow(palette,value){
    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 sendToWatchFolder(watchfolderlocation,compname){
    ocn = compname;
    if(app.settings.getSetting("dandybuildPrefs", "optSaveComp") == "true"){
        c = confirm("Save "+app.project.file.name+" ?",false,"Save Project");
        if(c){
            app.project.save();
        }
    }
    curFile = app.project.file;
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
    compname += "_WF";
    myFolder.create();
    mySaveFile = new File(myFolder.toString()+"/"+compname+".aep");       
    app.project.save(mySaveFile);
    myTextFile = new File(myFolder.toString()+"/"+compname.substring(0,22)+"_RCF.txt");   
    myTextFile.open("w","TEXT","????");
    text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n" ;
    myTextFile.write(text);
    myTextFile.close();
    writeInfo(watchfolderlocation,ocn);
    if(app.settings.getSetting("dandybuildPrefs", "optNuComp") == "false"){
        opFile = new File(curFile);
        if(opFile){
            app.open(opFile);
        }
    }else{
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
        app.project.new();
     }
   
   
    //explore(watchfolderlocation);
}
}
function addItem(name,state,msg){
    //    alert(listItem.selection);
    var arrayV = new Array("green","red","orange","graygreen","gray");
    //var texted = new Array("...","error:","rendering","...","queued");
    var item = list.add ('item',name);
    //$.writeln(scriptFolder.toString()+"/flag_"+array[state]+".png");
    item.image = File(scriptFolder.toString()+"/flag_"+arrayV[state]+".png");
    //item.subItems[0].helpTip = texted(state);
    item.subItems[0].text =msg;
}
///////////////////////////////////////////
          /* if((logFolder =  getFilePath(watchfolderLocation+"/"+shot)) != undefined){
                $.writeln(logInfo(logFolder));
            }else{
                $.writeln("no log folder");
                }*/
          // $.writeln();
          // addItem(list,"G12_SC213_T1",2);
         
         
          /*
var item1 = list.add ('item', 'GB15_SC138_T1');
item1.image = File("~/Desktop/flag_gray.png");
item1.subItems[0].text = 'Queued...';
item1.enabled = false;
*/


function writeInfo(watchfolderlocation,compname){
           
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
            //alert(getPercentage(watchfolderLocation+"/"+shot));
 
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+"/"+shot)*100);
    mySaveFile = new File(myFolder.toString()+"/"+compname+"_DandyWatch.txt");  
         
    mySaveFile.open("w","TEXT","????");
                      // alert("test");
    rq = app.project.renderQueue;
              //$.writeln(files[file].path+"/"+files[file].name);
    text ="";   
                            //  a+= getFilePath(files[file].path+"/"+files[file].name).name+"\n";
    for(i=1;i<=rq.numItems;i++){   
                           
        if(rq.items[i].render){
/*                           
            text += rq.items[i].outputModules[1].file.path+"\n";
                           
            text += (Math.round(rq.items[i].timeSpanDuration*25)+"\n");
                            {
            text += system.callSystem("hostname");
                               
        }
        wfLocBtn.onClick = function(){
    }
            app.scheduleTask("loop()",2000,1);
    mySaveFile.write(text);
    mySaveFile.close();
}


         
            fold = new Folder( watchfolderLocation.toString());
            files = fold.getFiles();
                a= "";
            for(file in files){


function explore(location){
              $.writeln(getFilePath(files[file].path+"/"+files[file].name));
    fold = new Folder(location.toString());
    return fold.execute();    
}


function checkForMissingFootage(dialogFlag){
        var dialog = dialogFlag;
        var missing = false;
        for(i=1;i<=app.project.numItems;i++){
            app.project.items[i].selected = false;
            if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
                            if(app.project.items[i].footageMissing){
                                app.project.items[i].selected = true;
                                missing = true;
                                if(dialog){
                                    dialog = prompt("Original folder of \""+(app.project.items[i].name)+"\" :",pathToWinPath(app.project.items[i].file),"Missing footage! (hit cancel to suppress further dialogs)");
                                }
                            }
             }
             }
          alert(a);
            writeLn(pBar.value);
         }
         }
        return missing;
*/
}
</pre>


function checkOutputSettings(){
===Parent to last selected layer===
     renderSettingsSet = ((app.settings.haveSetting("dandybuildPrefs", "tiffsettings")))?true:false;
Why isn't this by default? Bound to alt-P.
  // renderSettingsSet = 1;
<pre>
    if(!renderSettingsSet){
(function() {
                alert("Tiff output being setup, this should happen only once. It needs to close the current comp.");
     var comp = app.project.activeItem;
                opFile = new File(rootFolder+"base.aep");
    if (comp && comp instanceof CompItem) {
                if(opFile.exists){
        var selectedLayers = comp.selectedLayers;
                    app.open(opFile);
        if (selectedLayers.length > 1) {
                    app.project.renderQueue.items[1].outputModules[1].saveAsTemplate("TiffOutput");
            app.beginUndoGroup("Parent to Last Selected");
                    app.settings.saveSetting("dandybuildPrefs", "tiffsettings","is there");
            var parentLayer = selectedLayers[selectedLayers.length - 1];
                    app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
            for (var i = 0; i < selectedLayers.length - 1; i++) {
                    app.newProject();
                selectedLayers[i].parent = parentLayer;
                }else{
            }
                    alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
            app.endUndoGroup();
                }
        }else{
    }
alert("Requires at least a child an parent layer to be selected.");
}
    }else{
alert("Select a comp's layers and run the script again");
}
})();
</pre>
 
===Import pos from maya===
<pre>
function createUI(thisObj) {
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",
[100, 100, 300, 300]);
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
impButton.onClick = openfile;
return myPanel;
}
}
var myToolsPanel = createUI(this);
//myToolsPanel.show();


function checkOutFolder(tpath){
function openfile(){
    foldFile = new Folder(tpath);
            var myFile = File.openDialog ("Select track file","*.txt");  
    //$.writeln(tpath);
            var fileOK = myFile.open("r","TEXT","????");
    if(foldFile.exists){
//var fileD = OpenDlg ("Tracking Point File","*.txt",true);
        files = foldFile.getFiles(); 
//txt = fileD.readln ()
        if(files.length > 1){
alert("txt"+readTxt(myFile));
            return false;
        }else{
            return true;
        }
    }else{
      newF = foldFile.create();
        return newF;
    }
}
function getSetTake(force){
    show = app.settings.getSetting("dandybuildPrefs","show");// showDDL.selection.toString();
    shot = app.settings.getSetting("dandybuildPrefs","shot"); //shotDDL.selection.toString();
    projectTake = getTake(findMainShot().name);
//    paths  = rootFolder+show+"/"+shot+"/OUT/";
    paths  = "/n/01_OUT/"+show+"/"+shot+"/";
    lastFolder = getLastModified(paths);
    //alert(lastFolder.name+" -->"+paths);
    if(lastFolder){
        folderTake = getTake(lastFolder.name);
    }else{
        folderTake = false;
    }
    msgPt1 = (folderTake)? "The last take in OUT folder is take "+(folderTake)+".\n":"No take was found in the OUT folder.\n";
    msgPt2 = "The current project take is "+projectTake+". \n\nChoose the current take:";
    if(force || (folderTake != (projectTake-1))){
        recommended = (folderTake)?((folderTake)+1):projectTake;
        answer = prompt(msgPt1+msgPt2,recommended);
        //$.writeln("blabal "+answer);
        if(answer != null){
            return answer;
        }else{
            return false;
        }
    }
    return projectTake;
}
}
function readTxt(myFile){
var myText = myFile.read();
return myText;
}
</pre>
===Dandelion/Amazing World Of Gumball Shotbuilder===
http://imgur.com/xKJWB.png




For education purposes only, the script was run in production to helb build shots, it:
* created a list of shots & shows possible given a folder structure ("GB##_SHOWNAME_SC##_T##")
* opened the last .AEP file found in said shot folder
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the 'grab sources' button
* automatically sent files to the watchfolder to render on a small-ish farm.
Other buttons allowed to check for missing footage, open the comp's folder, open the current shows' latest animatic, etc


function setTake(value){
    comp = findMainShot();
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf("_T")+2))+value;
}


function getTake(normalizedName){
<pre>
    tmp = normalizedName.split("_T");
// 
    return  parseFloat(tmp[tmp.length-1]);
//  Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
}
//  last update 21/11/10
// Smart Import.jsx
//
//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases
// Known Bugs:
    //the only problem is that you can still not have 2 sequences in a single folder
//    
 
// -you can't have several sequences in a single folder and expect the script to pick up all the sequences
function SmartImport(targetFolder){
//  -this will not set color profiles
        writeLn("Importing files...");
// //TODO
        var importedFiles = 0;
//  >>> CHECK IF WF FOLDER ALREADY EXISTS
var scriptName = "Smart Import";
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
var sourcePaths = getSourcePathsArray();
//  -v0.7 fixes
// Ask the user for a folder whose contents are to be imported.
//        -new scene after WF works properly
//var targetFolder = Folder.selectDialog("Import items from folder...");
//       -changed output folder location to N:
if (targetFolder != null) {
//  -v0.6 fixes
// If no project open, create a new project to import the files into.
//       -cancel watchfolder cancels watchfolder
function processFile(theFile)
//        -fixed GB##_SC_###_T1
{
//      -added options
try {
//       -can work on a new location
// Create a variable containing ImportOptions.
//  -v0.5 fixes
var importOptions = new ImportOptions(theFile);
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)
                    //alert();
//      -will warn if there is missing footage before sending to WF
                    if(theFile.name.toString().toLowerCase().lastIndexOf(".psd") != -1){
//      -removed set take, added 'missing' dialog.
                        importOptions.importAs = ImportAsType.COMP;
//  -v0.4 fixes
                    }
//     -there shouldn't be a refresh problem on show change anymore
                      importSafeWithError(importOptions);
//  -v0.3 fixes
} catch (error) {
//      -changed watchfolder location to anthony's mac
// Ignore errors.
//      -changed save as dialog
}
//      -removed unused buttons
}
//     -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)
//  -v0.2 fixes
function testForSequences(files)
//     -sequences weren't getting imported.
{
//      -fixed UI/little problems
var searcher = new RegExp("[0-9]{3,}$");
//      -added folders, refresh button, save before watchfoldering
var movieFileSearcher = new RegExp("(mov|avi|mpg)$", "i");
var version = "0.7";
var parseResults = new Array;
 
                  var isFolder = new Array;
//default locations
var watchfolderLocation = "/c/__WATCHFOLDER__";
// Test that we have a sequence. Stop parsing after 10 files.
var rootFolder = "/c/";
for (x = 0; (x < files.length) & x < 10; x++) {
var forEditFolderLoaction = "//MAC0023DFDF5429/for%20edit";
var movieFileResult = movieFileSearcher.exec(files[x].name);
 
if (!movieFileResult) {
 
//******                          splitName = files[x].name.split('.')[0];
rootFolder = ((app.settings.haveSetting("dandybuildPrefs", "rootfolder")))?(app.settings.getSetting("dandybuildPrefs", "rootfolder")):rootFolder;
                            splitName=files[x].name.substring(0,files[x].name.length-4);
watchfolderLocation = ((app.settings.haveSetting("dandybuildPrefs", "watchfolderLocation")))?(app.settings.getSetting("dandybuildPrefs", "watchfolderLocation")):watchfolderLocation;
forEditFolderLoaction = ((app.settings.haveSetting("dandybuildPrefs", "forEditFolderLoaction")))?(app.settings.getSetting("dandybuildPrefs", "forEditFolderLoaction")):forEditFolderLoaction;


                          //splitName[splitNameA.length] =  
          //dirty hack
                          //alert(splitName);
            a = ((app.settings.haveSetting("dandybuildPrefs", "optMissingFootage")))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true;
                        //  alert(files[x].name+" >>> "+files[x].name.split('.')[0]);
            b = ((app.settings.haveSetting("dandybuildPrefs", "optNuComp")))?(app.settings.getSetting("dandybuildPrefs", "optNuComp")):false;
var currentResult = searcher.exec(splitName);
            c = ((app.settings.haveSetting("dandybuildPrefs", "optSaveComp")))?(app.settings.getSetting("dandybuildPrefs", "optSaveComp")):true;
// Regular expressions return null if no match was found.
            if(a=="false") a = false;
// Otherwise, they return an array with the following information:
            if(a=="true") a = true;
// array[0] = the matched string.
            if(b=="false") b = false;
// array[1..n] = the matched capturing parentheses.
            if(b=="true") b = true;
                 
            if(c=="false") c = false;
                    if (currentResult) { // We have a match -- the string contains numbers.
            if(c=="true") c = true;
                         
           
                            // The match of those numbers is stored in the array[1]
         
// Take that number and save it into parseResults.
 
parseResults[parseResults.length] = currentResult[0];
 
} else {
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_";
parseResults[parseResults.length] = null;
var loadFile = ((app.settings.haveSetting("dandybuildPrefs", "loadscene")))?(app.settings.getSetting("dandybuildPrefs", "loadscene")):"";
}
checkOutputSettings();
                          if(files[x].name == splitName){
 
                                isFolder[isFolder.length] = true;
Array.prototype.has = function(value) {
                              // alert("Folder "+files[x].name);
    var i;
                          }else{
    for (i=0;i<this.length;i++) {
                                isFolder[isFolder.length] = false;
        if (this[i] == value) {
                          }
            return true;
} else {
        }
parseResults[parseResults.length] = null;
    }
}
    return false;
}
}
 
// If all the files we just went through have a number in their file names,
 
// assume they are part of a sequence and return the first file.
function addShotDialog(sel){
    alert(sel);
var result = null;
    }
for (i = 0; i < parseResults.length; ++i) {
 
                 
function pathToWinPath(path){
                  if(!isFolder[i]){
    path = path.toString();
                    if (parseResults[i]) {
str = path.replace(/\//, "");
if (!result) {
str = str.replace(/\//, ":/");
                           
str = str.replace(/%20/g, " ");
result = files[i];
str = str.replace(/\//g, "\\");
}
return str;
} else {
}
                       
 
// In this case, a file name did not contain a number.
function returnFolderArray(location){
result = null;
    fold = new Folder(location.toString());
break;
    foldArray = fold.getFiles()
}
    folderOnly = new Array();
                  }
    for(i=0;i<foldArray.length;i++){
                }
        if(foldArray[i] instanceof Folder){
            folderOnly[folderOnly.length] = foldArray[i];
return result;
            }
}
        }
       
    return folderOnly;
}
function importSafeWithError(importOptions)
 
{
function sendToWatchFolder(watchfolderlocation,compname){
              if(!(sourcePaths.has(importOptions.file.toString()))){
    ocn = compname;
try {
    if(app.settings.getSetting("dandybuildPrefs", "optSaveComp") == "true"){
                  b = app.project.importFile(importOptions);
        c = confirm("Save "+app.project.file.name+" ?",false,"Save Project");
                    writeLn(importOptions.file.toString());
        if(c){
                sfi = sourcesFolderItem();
            app.project.save();
                  b.parentFolder = sfi;
        }
                    if(importOptions.importAs == ImportAsType.COMP){
    }
                        for (i = 1; i <= app.project.numItems ; i++) {
    curFile = app.project.file;
                            if(app.project.items[i].typeName == "Folder" && app.project.items[i].name.lastIndexOf(" Layers") != -1){
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
                        app.project.items[i].parentFolder = sfi;
    compname += "_WF";
                            }
    myFolder.create();
                        }
    mySaveFile = new File(myFolder.toString()+"/"+compname+".aep");      
                    }
    app.project.save(mySaveFile);
                        writeLn("Importing file ("+importedFiles+")");
    myTextFile = new File(myFolder.toString()+"/"+compname.substring(0,22)+"_RCF.txt");  
                        importedFiles++;
    myTextFile.open("w","TEXT","????");
} catch (error) {
    text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n" ;
alert(error.toString() + importOptions.file.fsName, scriptName);
    myTextFile.write(text);
}
    myTextFile.close();
                // }else{
    writeInfo(watchfolderlocation,ocn);
                //        alert("file already here");
    if(app.settings.getSetting("dandybuildPrefs", "optNuComp") == "false"){
                }
        opFile = new File(curFile);
}
        if(opFile){
            app.open(opFile);
        }
function processFolder(theFolder)
    }else{
{
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
// Get an array of files in the target folder.
        app.project.new();
var files = theFolder.getFiles();
//alert(files);
// Test whether theFolder contains a sequence.
var sequenceStartFile = testForSequences(files);
// If it does contain a sequence, import the sequence,
if (sequenceStartFile) {
try {
// Create a variable containing ImportOptions.
var importOptions = new ImportOptions(sequenceStartFile);
importOptions.sequence = true;
// importOptions.forceAlphabetical = true; // Un-comment this if you want to force alpha order by default.
importSafeWithError(importOptions);
} catch (error) {
}
}
// Otherwise, import the files and recurse.
for (index in files) { // Go through the array and set each element to singleFile, then run the following.
if (files[index] instanceof File) {
if (!sequenceStartFile) { // If file is already part of a sequence, don't import it individually.
processFile(files[index]); // Calls the processFile function above.
}
}
if (files[index] instanceof Folder) {
processFolder(files[index]); // recursion
}
}
}
// Recursively examine that folder.
processFolder(targetFolder);
}
    clearOutput();
     }
     }
 
function sourcesFolderItem(){
    for (i = 1; i <= app.project.numItems ; i++) {
      // alert(app.project.items[i].name);
    if(app.project.items[i].typeName == "Folder" && app.project.items[i].name == "Sources"){
          return app.project.items[i];
      }
  }
    return app.project.items.addFolder("Sources");
    //else
      
      
   
    //explore(watchfolderlocation);
}
}


function getSourcePathsArray(){
function writeInfo(watchfolderlocation,compname){
     var footageLocations = new Array();
     myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
    for (i = 1; i <= app.project.numItems ; i++) {
        if(app.project.item(i).typeName == "Footage"){
            footageLocations[footageLocations.length] = app.project.item(i).file;
        }   
    }
    return footageLocations;
}


function getLastModified(location,extension){
    mySaveFile = new File(myFolder.toString()+"/"+compname+"_DandyWatch.txt");  
      myFolder = new Folder(location.toString());
    mySaveFile.open("w","TEXT","????");
      if (!myFolder instanceof Folder){
    rq = app.project.renderQueue;
          return false;
    text ="";  
    }
    for(i=1;i<=rq.numItems;i++){  
    listFiles = myFolder.getFiles(extension);
        if(rq.items[i].render){
    listDates = new Array();
            text += rq.items[i].outputModules[1].file.path+"\n";
    maxId = 0;
            text += (Math.round(rq.items[i].timeSpanDuration*25)+"\n");
    maxValue = 0;
            text += system.callSystem("hostname");
    for(i=0;i<listFiles.length;i++){
        if (!(extension == "" && (listFiles[i] instanceof File))){
            d = listFiles[i].modified;
            formattedDate = d.getFullYear()+""+pad(d.getMonth()+1,2,0)+""+pad(d.getDate(),2,0)+""+pad(d.getHours(),2,0)+""+pad(d.getMinutes(),2,0)+""+pad(d.getSeconds(),2,0);
            listDates[i] = formattedDate;
            if(Math.max(maxValue,formattedDate) == formattedDate){
                maxValue = formattedDate;
                maxId = i;
            }
         }
         }
   
    }
    if(listFiles[maxId] == "" ||listFiles[maxId] == "undefined"||listFiles[maxId] == null){
        return false;
     }
     }
     return listFiles[maxId];     
     mySaveFile.write(text);
     mySaveFile.close();
}
}


function pad(number, length, character) {
 
    if (character == null) {
function explore(location){
        character = "0";
    fold = new Folder(location.toString());
    }
    return fold.execute();     
    var numberStr = "" + number;
    while (numberStr.length < length) {
        numberStr = character + numberStr;
     }
    return numberStr;
}
}
function grabAnimatic(folderLocation){
 
    newLocation = new Folder(folderLocation);
function checkForMissingFootage(dialogFlag){
    animatic = newLocation.getFiles("*.mov");
        var dialog = dialogFlag;
    sfi = sourcesFolderItem();
        var missing = false;
    if(animatic[0]){
        for(i=1;i<=app.project.numItems;i++){
        try {
             app.project.items[i].selected = false;
             var importOptions = new ImportOptions(animatic[0]);
             if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
             animatic = app.project.importFile(importOptions);
                            if(app.project.items[i].footageMissing){
            for (i = 1; i <= app.project.numItems ; i++) {
                                app.project.items[i].selected = true;
                    if(app.project.item(i).typeName == "Footage"){
                                missing = true;
                  app.project.item(i).parentFolder = sfi;
                                if(dialog){
                    }  
                                    dialog = prompt("Original folder of \""+(app.project.items[i].name)+"\" :",pathToWinPath(app.project.items[i].file),"Missing footage! (hit cancel to suppress further dialogs)");
                                }
                            }
             }
             }
            return animatic;
        }catch (error){
            return false;
         }
         }
    }
        return missing;
}
}
function figureOutShotName(filepath){
 
     folders = filepath.split("/");
function checkOutputSettings(){
     showFolder = folders[folders.length-2];
     renderSettingsSet = ((app.settings.haveSetting("dandybuildPrefs", "tiffsettings")))?true:false;
    showFolder = showFolder.split("_")[0];
  // renderSettingsSet = 1;
    shotFolder = folders[folders.length-1];
     if(!renderSettingsSet){
    var searcher = new RegExp("[0-9]{2,}$");
                alert("Tiff output being setup, this should happen only once. It needs to close the current comp.");
    var currentResult = searcher.exec(shotFolder);
                opFile = new File(rootFolder+"base.aep");
    if (currentResult){
                if(opFile.exists){
        shotFolder = currentResult[0];
                    app.open(opFile);
                    app.project.renderQueue.items[1].outputModules[1].saveAsTemplate("TiffOutput");
                    app.settings.saveSetting("dandybuildPrefs", "tiffsettings","is there");
                    app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
                    app.newProject();
                }else{
                    alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
                }
     }
     }
    return showFolder+"_SC"+shotFolder;
}
}


function createMainComp(path){
function checkOutFolder(tpath){
     grabAnimatic(path);
     foldFile = new Folder(tpath);
     shotName = figureOutShotName(path);
     //$.writeln(tpath);
     workComp = app.project.items.addComp(shotName+"_T1", 1920, 1080,1.0 ,animatic.duration,25.0);
     if(foldFile.exists){
     animaticLayer = workComp.layers.add(animatic);
        files = foldFile.getFiles(); 
    animaticLayer.guideLayer = true;
        if(files.length > 1){
     return workComp;
            return false;
        }else{
            return true;
        }
     }else{
      newF = foldFile.create();
        return newF;
     }
}
}
 
function getSetTake(force){
function returnList2(type){
    show = app.settings.getSetting("dandybuildPrefs","show");// showDDL.selection.toString();
    if(type == "show"){
    shot = app.settings.getSetting("dandybuildPrefs","shot"); //shotDDL.selection.toString();
        rArray = returnFolderArray(rootFolder);
    projectTake = getTake(findMainShot().name);
        for(i=0;i<rArray.length;i++){
//    paths  = rootFolder+show+"/"+shot+"/OUT/";
            rArray[i] = rArray[i].name;
    paths  = "/n/01_OUT/"+show+"/"+shot+"/";
            }
    lastFolder = getLastModified(paths);
     }else if(type == "scene"){
    //alert(lastFolder.name+" -->"+paths);
        rArray = ["a","b"];
    if(lastFolder){
         alert(dPanel.shotLoad.showDDL.selection.toString());
        folderTake = getTake(lastFolder.name);
 
    }else{
        folderTake = false;
     }
    msgPt1 = (folderTake)? "The last take in OUT folder is take "+(folderTake)+".\n":"No take was found in the OUT folder.\n";
    msgPt2 = "The current project take is "+projectTake+". \n\nChoose the current take:";
    if(force || (folderTake != (projectTake-1))){
        recommended = (folderTake)?((folderTake)+1):projectTake;
         answer = prompt(msgPt1+msgPt2,recommended);
        //$.writeln("blabal "+answer);
        if(answer != null){
            return answer;
        }else{
            return false;
        }
     }
     }
     return rArray;
     return projectTake;
}
}






function returnList(folder){
function setTake(value){
     rArray = returnFolderArray(folder);
     comp = findMainShot();
     for(i=0;i<rArray.length;i++){
     comp.name = comp.name.substring(0,(comp.name.lastIndexOf("_T")+2))+value;
            rArray[i] = rArray[i].name;
    }
    return rArray;
}
}


function findMainShot(){
function getTake(normalizedName){
     //fix GB##_SC_###_T1
     tmp = normalizedName.split("_T");
    var findShot = new RegExp("^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
    return  parseFloat(tmp[tmp.length-1]);
    for(i=1;i<=app.project.numItems;i++){
}
        if(app.project.items[i] instanceof CompItem){
// Smart Import.jsx
          var movieFileResult = findShot.exec(app.project.items[i].name);
//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases
          if(movieFileResult){
    //the only problem is that you can still not have 2 sequences in a single folder
              nuName = app.project.items[i].name;
 
              nuName = nuName.split("SC_");
function SmartImport(targetFolder){
              app.project.items[i].name = nuName[0]+"SC"+nuName[1];
        writeLn("Importing files...");
            }
        var importedFiles = 0;
        }      
var scriptName = "Smart Import";
    }
var sourcePaths = getSourcePathsArray();
   
// Ask the user for a folder whose contents are to be imported.
    var findShot = new RegExp("^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
//var targetFolder = Folder.selectDialog("Import items from folder...");
    var shot = null;
if (targetFolder != null) {
    for(i=1;i<=app.project.numItems;i++){
// If no project open, create a new project to import the files into.
        if(app.project.items[i] instanceof CompItem){
function processFile(theFile)
          var movieFileResult = findShot.exec(app.project.items[i].name);
{
          if(movieFileResult){
try {
              shot = app.project.items[i];
// Create a variable containing ImportOptions.
              break;
var importOptions = new ImportOptions(theFile);
              }
                    //alert();
        }
                    if(theFile.name.toString().toLowerCase().lastIndexOf(".psd") != -1){
    }
                        importOptions.importAs = ImportAsType.COMP;
    if(shot == null){
                    }
        alert("Could not find a comp named like \"GB##_SC###(a-z)_T#\"\n\nRename main comp accordingly, render again");
                      importSafeWithError(importOptions);
        return false;
} catch (error) {
    }
// Ignore errors.
    return shot;
}
}
}
function testForSequences(files)
{
var searcher = new RegExp("[0-9]{3,}$");
var movieFileSearcher = new RegExp("(mov|avi|mpg)$", "i");
var parseResults = new Array;
                  var isFolder = new Array;
// Test that we have a sequence. Stop parsing after 10 files.
for (x = 0; (x < files.length) & x < 10; x++) {
var movieFileResult = movieFileSearcher.exec(files[x].name);
if (!movieFileResult) {
//******                          splitName = files[x].name.split('.')[0];
                            splitName=files[x].name.substring(0,files[x].name.length-4);


 
                          //splitName[splitNameA.length] =
function dandyShotBuilderUI(thisObj){
                          //alert(splitName);
   
                        // alert(files[x].name+" >>> "+files[x].name.split('.')[0]);
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var currentResult = searcher.exec(splitName);
   
// Regular expressions return null if no match was found.
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window("palette", "DandyShotBuilderFix", [100, 100, 300, 300]);
// Otherwise, they return an array with the following information:
        var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
// array[0] = the matched string.
        if (securitySetting != 1) {
// array[1..n] = the matched capturing parentheses.
          var msg = "This script requires the scripting security preference to be set. \nGo the \"General\" panel of your application preferences and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.\n\n Restart AFX";
                 
          var shotInfo = dPanel.add("edittext",[10,5,235,305],msg,{multiline:true});  
                    if (currentResult) { // We have a match -- the string contains numbers.
        }else{
                         
      var shotLoad = dPanel.add("panel",[10,5,235,175],"Load Shot");
                            // The match of those numbers is stored in the array[1]
        var shotLoadTxt1 = shotLoad.add("statictext",[15,24,52,44],"Show:");
// Take that number and save it into parseResults.
        var showDDL = shotLoad.add ("dropdownlist", [56,19,210,44], returnList(rootFolder));
parseResults[parseResults.length] = currentResult[0];
            if (app.settings.haveSetting("dandybuildPrefs", "show")){
} else {
                showDDL.selection = showDDL.find(app.settings.getSetting("dandybuildPrefs","show"));
parseResults[parseResults.length] = null;
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
}
                }else{
                          if(files[x].name == splitName){
                showDDL.selection = 0;
                                isFolder[isFolder.length] = true;
                app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
                              // alert("Folder "+files[x].name);
                }
                          }else{
        var shotLoadTxt2 = shotLoad.add("statictext",[12,59,52,79],"Scene:");
                                isFolder[isFolder.length] = false;
        var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+app.settings.getSetting("dandybuildPrefs", "show")));
                          }
            if (app.settings.haveSetting("dandybuildPrefs", "shot")){
} else {
                shotDDL.selection = shotDDL.find(app.settings.getSetting("dandybuildPrefs","shot"));
parseResults[parseResults.length] = null;
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
}
                }else{
}
                shotDDL.selection = 0;
// If all the files we just went through have a number in their file names,  
// assume they are part of a sequence and return the first file.
var result = null;
for (i = 0; i < parseResults.length; ++i) {
                 
                  if(!isFolder[i]){
                    if (parseResults[i]) {
if (!result) {
                           
result = files[i];
}
} else {
                       
// In this case, a file name did not contain a number.
result = null;
break;
}
                  }
                 }
                 }
       
         
return result;
        var shotStatusTxt = shotLoad.add("statictext",[25,95,52,115],"File:");
}
        var shotStatusEditTxt = shotLoad.add("edittext",[56,92,210,115],"...");
       
            shotStatusEditTxt.active = false;
            shotStatusEditTxt.enabled = false;
function importSafeWithError(importOptions)
        var shotLoadBtn = shotLoad.add("button",[56,125,96,150],"Load");
{
            shotLoadBtn.enabled = false;
              if(!(sourcePaths.has(importOptions.file.toString()))){
        var shotBuildBtn = shotLoad.add("button",[101,125,141,150],"Build");
try {
            shotBuildBtn.enabled = false;
                  b = app.project.importFile(importOptions);
        var shotRefreshBtn = shotLoad.add("button",[146,125,168,150],"r");
                    writeLn(importOptions.file.toString());
        //var shotOptns = shotLoad.add("button",[174,125,210,150],"optns");
                sfi = sourcesFolderItem();
            //shotSetBtn.enabled = false;
                  b.parentFolder = sfi;
       
                    if(importOptions.importAs == ImportAsType.COMP){
      var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
                        for (i = 1; i <= app.project.numItems ; i++) {
            shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
                            if(app.project.items[i].typeName == "Folder" && app.project.items[i].name.lastIndexOf(" Layers") != -1){
            shotToolsBtn1Xtra = shotTools.add("button",[82,12,102,35],"N:");
                        app.project.items[i].parentFolder = sfi;
            //shotToolsBtn2 =  shotTools.add("button",[107,12,197,35],"Flick Last Take");
                            }
            //shotToolsBtn2.enabled = false;
                        }
            shotToolsBtnWF = shotTools.add("button",[107,12,135,35],"WF");
                    }
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};
                        writeLn("Importing file ("+importedFiles+")");
            shotToolsBtnFE = shotTools.add("button",[139,12,167,35],"FE");
                        importedFiles++;
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
} catch (error) {
            shotToolsBtnXLS =  shotTools.add("button",[172,12,197,35],"xls");
alert(error.toString() + importOptions.file.fsName, scriptName);
            //shotToolsBtnXLS.enabled = false;           
}
               
                // }else{
            shotToolsBtn3 =  shotTools.add("button",[12,40,102,63],"Grab Sources");
                //       alert("file already here");
            shotToolsBtn4 =  shotTools.add("button",[107,40,135,63],"Miss");
                }
            shotToolsBtnANI =  shotTools.add("button",[139,40,167,63],"ANI");
}
            shotToolsBtnOPT =  shotTools.add("button",[172,40,197,63],"Opt");
           
              // shotToolsBtn4.enabled = false;           
function processFolder(theFolder)
            shotToolsBtn5 =  shotTools.add("button",[12,68,102,91],"Renderqueue");
{
            shotToolsBtn6 =  shotTools.add("button",[107,68,197,91],"To Watchfolder");
// Get an array of files in the target folder.
                //shotToolsBtn6.enabled = false;
var files = theFolder.getFiles();
            }
//alert(files);
      var optnsGrp = dPanel.add("panel",[10,5,235,300],"Options");
// Test whether theFolder contains a sequence.
            optnsGrp.visible = false;
var sequenceStartFile = testForSequences(files);
            mgL = 20;
            mgT=15;
// If it does contain a sequence, import the sequence,
            mgBet=3;
if (sequenceStartFile) {
            i=0;
try {
            w=210;
// Create a variable containing ImportOptions.
            h=23;
var importOptions = new ImportOptions(sequenceStartFile);
            version = optnsGrp.add("statictext",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"version "+version);i++;
importOptions.sequence = true;
            optMissingFootage = optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Check for missing footage\rlonger, but safer");i++;
// importOptions.forceAlphabetical = true; // Un-comment this if you want to force alpha order by default.
            //$.writeln("load missing footage pref set to"+(app.settings.haveSetting("dandybuildPrefs", "optMissingFootage"))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true);
importSafeWithError(importOptions);
                optMissingFootage.value = a;
} catch (error) {
            optNuComp = optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"New comp after sending to WF");i++;
}
                optNuComp.value = b;
}
            optSaveComp =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Ask to save before sending to WF");i+=2;
                optSaveComp.value = c;
// Otherwise, import the files and recurse.
            rootFolderLoc =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Root folder location");i++;
            watchFolderLoc = optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Watchfolder location");i+=2;
for (index in files) { // Go through the array and set each element to singleFile, then run the following.
             okBtn = optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"OK");
if (files[index] instanceof File) {
           
if (!sequenceStartFile) { // If file is already part of a sequence, don't import it individually.
 
processFile(files[index]); // Calls the processFile function above.
      ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////
}
     
}
        showDDL.onChange = function(){
if (files[index] instanceof Folder) {
            app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
processFolder(files[index]); // recursion
          // shotLoad.remove(shotDDL);
}
           shotDDL.removeAll();
}
 
}
          items = returnList(rootFolder+"/"+showDDL.selection.toString());
          //  alert(items);
// Recursively examine that folder.
            for(i in items){
processFolder(targetFolder);
              shotDDL.add("item", items[i]);
}
            }
    clearOutput();
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
    }
            shotDDL.selection = 0;
            shotDDL.notify();
        }
        shotDDL.onChange = function(){
 
         
function sourcesFolderItem(){
            app.settings.saveSetting("dandybuildPrefs","shot",shotDDL.selection.toString());
    for (i = 1; i <= app.project.numItems ; i++) {
            //$.writeln("Changing");
      // alert(app.project.items[i].name);
            var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
    if(app.project.items[i].typeName == "Folder" && app.project.items[i].name == "Sources"){
            if(!lastAEP){
          return app.project.items[i];
                shotStatusEditTxt.text = "no shot found, build one!";
      }
                  shotLoadBtn.enabled = false;
  }
                  shotBuildBtn.enabled = true;
    return app.project.items.addFolder("Sources");
           
    //else
            }else{
   
                app.settings.saveSetting("dandybuildPrefs", "loadscene",lastAEP.toString());
}
                loadFile = lastAEP.toString();
 
                fullSplitPath = lastAEP.toString().split("/");
function getSourcePathsArray(){
                fileName = fullSplitPath[fullSplitPath.length-1];
    var footageLocations = new Array();
                if(fileName.length > 20){
    for (i = 1; i <= app.project.numItems ; i++) {
                    shotStatusEditTxt.text = fileName.substr(0,12)+"[...]"+fileName.substr(fileName.length-8);
        if(app.project.item(i).typeName == "Footage"){
                }else{
             footageLocations[footageLocations.length] = app.project.item(i).file;
                    shotStatusEditTxt.text = fileName;
        }   
                }
    }
                    shotLoadBtn.enabled = true;
    return footageLocations;
                    shotBuildBtn.enabled = false;
}
            }
 
        }
function getLastModified(location,extension){
        shotLoadBtn.onClick = function(){
      myFolder = new Folder(location.toString());
            opFile = new File(loadFile);
      if (!myFolder instanceof Folder){
            if(opFile){
           return false;
             app.open(opFile);
    }
            }else{
    listFiles = myFolder.getFiles(extension);
                alert("Can't open file");
    listDates = new Array();
            }      
    maxId = 0;
    maxValue = 0;
    for(i=0;i<listFiles.length;i++){
        if (!(extension == "" && (listFiles[i] instanceof File))){
            d = listFiles[i].modified;
            formattedDate = d.getFullYear()+""+pad(d.getMonth()+1,2,0)+""+pad(d.getDate(),2,0)+""+pad(d.getHours(),2,0)+""+pad(d.getMinutes(),2,0)+""+pad(d.getSeconds(),2,0);
            listDates[i] = formattedDate;
            if(Math.max(maxValue,formattedDate) == formattedDate){
                maxValue = formattedDate;
                maxId = i;
             }
         }
         }
         shotRefreshBtn.onClick = function(){
   
            shotDDL.notify();
    }
            writeLn("If it doesn't seem to refresh, close and launch again.");
    if(listFiles[maxId] == "" ||listFiles[maxId] == "undefined"||listFiles[maxId] == null){
            }
         return false;
        shotBuildBtn.onClick = function(){
    }
            opFile = new File(rootFolder+"base.aep");
    return listFiles[maxId];   
            if(opFile.exists){
}
                app.open(opFile);
 
                for(i=1;i<=app.project.numItems;i++){                    
function pad(number, length, character) {
                     app.project.items[i].remove();
    if (character == null) {
                }
        character = "0";
            }else{
    }
                alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
    var numberStr = "" + number;
    while (numberStr.length < length) {
        numberStr = character + numberStr;
    }
    return numberStr;
}
function grabAnimatic(folderLocation){
    newLocation = new Folder(folderLocation);
    animatic = newLocation.getFiles("*.mov");
    sfi = sourcesFolderItem();
    if(animatic[0]){
        try {
            var importOptions = new ImportOptions(animatic[0]);
            animatic = app.project.importFile(importOptions);
            for (i = 1; i <= app.project.numItems ; i++) {
                     if(app.project.item(i).typeName == "Footage"){
                  app.project.item(i).parentFolder = sfi;
                    }   
             }
             }
             targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
             return animatic;
            createMainComp(targetFolder);
        }catch (error){
            app.project.bitsPerChannel = 16;
             return false;
             app.project.items.addFolder("Precomps");
            importFolder = new Folder(targetFolder+"/Source");
            SmartImport(importFolder);
            saveConf = confirm("Save "+shotDDL.selection.toString()+" in the right folder ?",false,"Dandelion Shot Builder");
            if(saveConf){
                saveFile = new File(targetFolder+"/AEP/"+showDDL.selection.toString()+"_"+shotDDL.selection.toString()+"_comp01.aep");
                app.project.save(saveFile);
                shotDDL.notify();
                }
         }
         }
        shotToolsBtn1.onClick = function(){
    }
            explore(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString());
}
        }
function figureOutShotName(filepath){
        shotToolsBtn1Xtra.onClick = function(){
    folders = filepath.split("/");
            //$.writeln("button");
    showFolder = folders[folders.length-2];
            p = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
    showFolder = showFolder.split("_")[0];
            checkOutFolder(p);
    shotFolder = folders[folders.length-1];
            explore(p);
    var searcher = new RegExp("[0-9]{2,}$");
         }
    var currentResult = searcher.exec(shotFolder);
        shotToolsBtn3.onClick = function(){
    if (currentResult){
            app.project.bitsPerChannel = 16;
         shotFolder = currentResult[0];
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
    }
            importFolder = new Folder(targetFolder+"/Source");
    return showFolder+"_SC"+shotFolder;
            SmartImport(importFolder);
}
        }
 
        shotToolsBtnXLS.onClick = function(){
function createMainComp(path){
            loc =  rootFolder+"/"+showDDL.selection.toString();
    grabAnimatic(path);
            fold = new Folder(loc.toString());
    shotName = figureOutShotName(path);
            files = fold.getFiles("*.xls*");
    workComp = app.project.items.addComp(shotName+"_T1", 1920, 1080,1.0 ,animatic.duration,25.0);
            if(files.length > 0){
    animaticLayer = workComp.layers.add(animatic);
                files[0].execute();
    animaticLayer.guideLayer = true;
            }else{
    return workComp;
                writeLn("No .xls shortcut found in in:");
}
                writeLn(showDDL.selection.toString());
 
            }
function returnList2(type){
        }
    if(type == "show"){
      shotToolsBtnANI.onClick = function(){
        rArray = returnFolderArray(rootFolder);
            loc = rootFolder+"/"+showDDL.selection.toString();
        for(i=0;i<rArray.length;i++){
            fold = new Folder(loc.toString());
            rArray[i] = rArray[i].name;
            files = fold.getFiles("*.mov*");
            if(files.length > 0){
                files[0].execute();
            }else{
                writeLn("No .mov shortcut found in in: ");
                writeLn(showDDL.selection.toString());
             }
             }
        }
    }else if(type == "scene"){
        shotToolsBtnOPT.onClick = function(){
        rArray = ["a","b"];
            shotTools.visible = false;
        alert(dPanel.shotLoad.showDDL.selection.toString());
            shotLoad.visible = false;
 
            optnsGrp.visible = true;
    }
        }
     return rArray;
     okBtn.onClick = function(){
}
            shotTools.visible = true;
 
            shotLoad.visible = true;
 
            optnsGrp.visible = false;
 
         
function returnList(folder){
            app.settings.saveSetting("dandybuildPrefs", "optMissingFootage",optMissingFootage.value);
    rArray = returnFolderArray(folder);
            app.settings.saveSetting("dandybuildPrefs", "optNuComp",optNuComp.value);
    for(i=0;i<rArray.length;i++){
            app.settings.saveSetting("dandybuildPrefs", "optSaveComp",optSaveComp.value);
            rArray[i] = rArray[i].name;
          // $.writeln("new comp checked: "+app.settings.getSetting("dandybuildPrefs", "optNuComp"));
     }
     }
       
    return rArray;
        //send to renderqueue
}
        //uses the comp name to figure out folder


                 
function findMainShot(){
        shotToolsBtn5.onClick = function(){
    //fix GB##_SC_###_T1
        rflag = false;
    var findShot = new RegExp("^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
        //$.writeln("missing fottage "+app.settings.getSetting("dandybuildPrefs", "optMissingFootage"));
    for(i=1;i<=app.project.numItems;i++){
        if(app.settings.getSetting("dandybuildPrefs", "optMissingFootage") == "true"){
        if(app.project.items[i] instanceof CompItem){
            rflag = checkForMissingFootage(false);
          var movieFileResult = findShot.exec(app.project.items[i].name);
        }
          if(movieFileResult){
        if(!rflag){
              nuName = app.project.items[i].name;
            rcomp = findMainShot();
              nuName = nuName.split("SC_");
                if(rcomp){
              app.project.items[i].name = nuName[0]+"SC"+nuName[1];
                    shotNumber = getSetTake();
                    if(shotNumber != false){
                        //$.writeln(shotNumber);
                        setTake(shotNumber);
                        rq = app.project.renderQueue;
                        for(i=1;i<=rq.numItems;i++){  
                                rq.items[i].render = false;
                        }
                        rqitem = app.project.renderQueue.items.add(rcomp);
                        rqitem.outputModules[1].applyTemplate("TiffOutput");
                        rqitem.applyTemplate("Multi-Machine Settings");
                        //savePath = rootFolder+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/OUT/"+rcomp.name;
                        savePath = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/"+rcomp.name;
                       
                       
                        if(checkOutFolder(savePath)){
                          rqitem.outputModules[1].file = new File(savePath+"/"+rcomp.name+"_[#####].tif");
                        }else{
                          alert("Couldn't figure out an output folder for \""+(rcomp.name)+"\", select a folder manually.");
                          rqitem.outputModules[1].file = new File();
                        }
                    }
                }
            }else{
                alert("This project is missing source files and will not render. \nUse the 'miss' button to find out where the footage should be");
             }
             }
         }
         }      
       
    }
        shotToolsBtn4.onClick = function(){
   
            //shotNumber = getSetTake(true);
    var findShot = new RegExp("^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
            //setTake(shotNumber);
    var shot = null;
            checkForMissingFootage(true);
    for(i=1;i<=app.project.numItems;i++){
        }
         if(app.project.items[i] instanceof CompItem){
       
           var movieFileResult = findShot.exec(app.project.items[i].name);
         shotToolsBtn6.onClick = function(){
           if(movieFileResult){
           wf = new Folder(watchfolderLocation);
               shot = app.project.items[i];
           if(wf.exists){
               break;
               sendToWatchFolder(wf,findMainShot().name);
               }else{
                  alert("Watchfolder error, sorry!");
               }
               }
         }
         }
           
    }
      
     if(shot == null){
      
        alert("Could not find a comp named like \"GB##_SC###(a-z)_T#\"\n\nRename main comp accordingly, render again");
      
        return false;
      
     }
    return shot;
}
 
 
function dandyShotBuilderUI(thisObj){
      
     ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      
      
    rootFolderLoc.onClick = function(){
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window("palette", "DandyShotBuilderFix", [100, 100, 300, 300]);
         y = confirm("Root folder is currently set to \n\n\""+rootFolder+"\"\n\nChange it ?");
         var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
        if(y){
        if (securitySetting != 1) {
            //v = new Folder();
          var msg = "This script requires the scripting security preference to be set. \nGo the \"General\" panel of your application preferences and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.\n\n Restart AFX";
            v = Folder.selectDialog ("New root folder location").toString();
          var shotInfo = dPanel.add("edittext",[10,5,235,305],msg,{multiline:true});
             if(v!=null && v!="undefined"){
        }else{
                app.settings.saveSetting("dandybuildPrefs", "rootfolder",v);    
      var shotLoad = dPanel.add("panel",[10,5,235,175],"Load Shot");
                 rootFolder = v;
        var shotLoadTxt1 = shotLoad.add("statictext",[15,24,52,44],"Show:");
                          showDDL.removeAll();
        var showDDL = shotLoad.add ("dropdownlist", [56,19,210,44], returnList(rootFolder));
                          items = returnList(rootFolder);
             if (app.settings.haveSetting("dandybuildPrefs", "show")){
          // alert(items);
                showDDL.selection = showDDL.find(app.settings.getSetting("dandybuildPrefs","show"));
            for(i in items){
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
              showDDL.add("item", items[i]);
                 }else{
             }
                showDDL.selection = 0;
             //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
                app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
             shotDDL.selection = 0;
                }
             shotDDL.notify();
        var shotLoadTxt2 = shotLoad.add("statictext",[12,59,52,79],"Scene:");
                showDDL.notify();
        var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+app.settings.getSetting("dandybuildPrefs", "show")));
                shotDDL.notify();
            if (app.settings.haveSetting("dandybuildPrefs", "shot")){
                //$.writeln("notified");
                shotDDL.selection = shotDDL.find(app.settings.getSetting("dandybuildPrefs","shot"));
             }
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
        }
                }else{
    }
                shotDDL.selection = 0;
    watchFolderLoc.onClick = function(){
                }
        y = confirm("Watchfolder is currently set to \n\n\""+watchfolderLocation+"\"\n\nChange it ?");
       
        if(y){
         
             //v = new Folder();
        var shotStatusTxt = shotLoad.add("statictext",[25,95,52,115],"File:");
             v = Folder.selectDialog ("New watchfolder location").toString();
        var shotStatusEditTxt = shotLoad.add("edittext",[56,92,210,115],"...");
             if(v!=null && v!="undefined"){
             shotStatusEditTxt.active = false;
                app.settings.saveSetting("dandybuildPrefs", "watchfolderLocation",v);    
             shotStatusEditTxt.enabled = false;
                 watchfolderLocation = v;
        var shotLoadBtn = shotLoad.add("button",[56,125,96,150],"Load");
             }
            shotLoadBtn.enabled = false;
        var shotBuildBtn = shotLoad.add("button",[101,125,141,150],"Build");
            shotBuildBtn.enabled = false;
        var shotRefreshBtn = shotLoad.add("button",[146,125,168,150],"r");
        //var shotOptns = shotLoad.add("button",[174,125,210,150],"optns");
             //shotSetBtn.enabled = false;
       
      var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
             shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
            shotToolsBtn1Xtra =  shotTools.add("button",[82,12,102,35],"N:");
            //shotToolsBtn2 =  shotTools.add("button",[107,12,197,35],"Flick Last Take");
            //shotToolsBtn2.enabled = false;
            shotToolsBtnWF =  shotTools.add("button",[107,12,135,35],"WF");
             shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};
            shotToolsBtnFE =  shotTools.add("button",[139,12,167,35],"FE");
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
            shotToolsBtnXLS =  shotTools.add("button",[172,12,197,35],"xls");
            //shotToolsBtnXLS.enabled = false;           
               
            shotToolsBtn3 =  shotTools.add("button",[12,40,102,63],"Grab Sources");
            shotToolsBtn4 = shotTools.add("button",[107,40,135,63],"Miss");
            shotToolsBtnANI =  shotTools.add("button",[139,40,167,63],"ANI");
            shotToolsBtnOPT =  shotTools.add("button",[172,40,197,63],"Opt");
              
              // shotToolsBtn4.enabled = false;           
            shotToolsBtn5 = shotTools.add("button",[12,68,102,91],"Renderqueue");
             shotToolsBtn6 = shotTools.add("button",[107,68,197,91],"To Watchfolder");
                //shotToolsBtn6.enabled = false;
            }
      var optnsGrp = dPanel.add("panel",[10,5,235,300],"Options");
             optnsGrp.visible = false;
            mgL = 20;
            mgT=15;
            mgBet=3;
            i=0;
            w=210;
            h=23;
            version = optnsGrp.add("statictext",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"version "+version);i++;
            optMissingFootage = optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Check for missing footage\rlonger, but safer");i++;
            //$.writeln("load missing footage pref set to"+(app.settings.haveSetting("dandybuildPrefs", "optMissingFootage"))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true);
                optMissingFootage.value = a;
            optNuComp =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"New comp after sending to WF");i++;
                 optNuComp.value = b;
            optSaveComp =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Ask to save before sending to WF");i+=2;
                optSaveComp.value = c;
            rootFolderLoc =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Root folder location");i++;
            watchFolderLoc =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Watchfolder location");i+=2;
             okBtn =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"OK");
              
              
        }
    }
    shotDDL.notify();
    }
    dandyShotBuilderUI(this) ;
 
</pre>


      ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////
     
        showDDL.onChange = function(){
            app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
          // shotLoad.remove(shotDDL);
          shotDDL.removeAll();


===layer Position to .txt===
          items = returnList(rootFolder+"/"+showDDL.selection.toString());
https://i.imgur.com/G5DX1T0.gif
          // alert(items);
Old, might not work! (but should :)
            for(i in items){
<pre>
              shotDDL.add("item", items[i]);
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
            }
//
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
// The script will write to a text file the x & y values of the layer position for every frame comprised in
            shotDDL.selection = 0;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected
            shotDDL.notify();
//
        }
//
        shotDDL.onChange = function(){
function timeToFrameNum(myTime){
         
  return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
            app.settings.saveSetting("dandybuildPrefs","shot",shotDDL.selection.toString());
}
            //$.writeln("Changing");
function framesToTime(myTime){
            var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
  return myTime/app.project.activeItem.frameRate;
            if(!lastAEP){
}
                shotStatusEditTxt.text = "no shot found, build one!";
function timeToTimeCode(myTime){
                  shotLoadBtn.enabled = false;
  var framesN = myTime * app.project.activeItem.frameRate;
                  shotBuildBtn.enabled = true;
  fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));
           
  ho = addZero(Math.floor(myTime/3600));
            }else{
  mi = addZero(Math.floor(myTime/60)-ho*60);
                app.settings.saveSetting("dandybuildPrefs", "loadscene",lastAEP.toString());
  se = addZero(Math.floor(myTime)-mi*60-ho*3600);
                loadFile = lastAEP.toString();
  return ho+":"+mi+":"+se+":"+fr;
                fullSplitPath = lastAEP.toString().split("/");
}
                fileName = fullSplitPath[fullSplitPath.length-1];
function addZero(val){
                if(fileName.length > 20){
  if(val<10){
                    shotStatusEditTxt.text = fileName.substr(0,12)+"[...]"+fileName.substr(fileName.length-8);
      val = "0"+val;
                }else{
  }
                    shotStatusEditTxt.text = fileName;
  return val;
                }  
}
                    shotLoadBtn.enabled = true;
var myDisp = app.project.timecodeDisplayType;
                    shotBuildBtn.enabled = false;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;
             }
var pText = "Choose an output format using:\n%f (framenumber),%i (index, starts at 0) %t (SMTPE timecode), %x (x value), %y (y value), %l (linebreak) and any other character. Default output looks like '16: 230;22'";
        }
 
        shotLoadBtn.onClick = function(){
if(app.project.activeItem != "null" && app.project.activeItem != null && app.project.activeItem != 0){
             opFile = new File(loadFile);
  if(app.project.activeItem.selectedLayers.length != 0){
             if(opFile){
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
            app.open(opFile);
        curLayer = app.project.activeItem.selectedLayers[0];
        var textName = "AEcoordinates.txt";
        var myTextFile = filePutDialog("Select a location to save your .txt file", textName, "TEXT txt");
        if(myTextFile == null){
            alert("You must choose a place to save the file");
        }else{
            var formatString = prompt(pText,"%i: %x;%y%l");
             myTextFile.open("w","TEXT","????");
             myKeys = curLayer.property("position").selectedKeys;
             if(myKeys.length != 0){
              strTime = curLayer.property("position").keyTime(myKeys[0]);
              endTime = curLayer.property("position").keyTime(myKeys[myKeys.length-1]);
             }else{
             }else{
               strTime = app.project.activeItem.workAreaStart;
                alert("Can't open file");
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;
            }       
        }
        shotRefreshBtn.onClick = function(){
            shotDDL.notify();
            writeLn("If it doesn't seem to refresh, close and launch again.");
            }
        shotBuildBtn.onClick = function(){
            opFile = new File(rootFolder+"base.aep");
            if(opFile.exists){
                app.open(opFile);
                for(i=1;i<=app.project.numItems;i++){                   
                    app.project.items[i].remove();
                }
            }else{
                alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
            }
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
            createMainComp(targetFolder);
            app.project.bitsPerChannel = 16;
            app.project.items.addFolder("Precomps");
            importFolder = new Folder(targetFolder+"/Source");
            SmartImport(importFolder);
            saveConf = confirm("Save "+shotDDL.selection.toString()+" in the right folder ?",false,"Dandelion Shot Builder");
            if(saveConf){
                saveFile = new File(targetFolder+"/AEP/"+showDDL.selection.toString()+"_"+shotDDL.selection.toString()+"_comp01.aep");
                app.project.save(saveFile);
                shotDDL.notify();
                }
        }
        shotToolsBtn1.onClick = function(){
            explore(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString());
        }
        shotToolsBtn1Xtra.onClick = function(){
            //$.writeln("button");
            p = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
            checkOutFolder(p);
            explore(p);
        }
        shotToolsBtn3.onClick = function(){
            app.project.bitsPerChannel = 16;
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
            importFolder = new Folder(targetFolder+"/Source");
            SmartImport(importFolder);
        }
        shotToolsBtnXLS.onClick = function(){
            loc =  rootFolder+"/"+showDDL.selection.toString();
            fold = new Folder(loc.toString());
            files = fold.getFiles("*.xls*");
            if(files.length > 0){
                files[0].execute();
            }else{
                writeLn("No .xls shortcut found in in:");
                writeLn(showDDL.selection.toString());
            }
        }
      shotToolsBtnANI.onClick = function(){
            loc =  rootFolder+"/"+showDDL.selection.toString();
            fold = new Folder(loc.toString());
            files = fold.getFiles("*.mov*");
            if(files.length > 0){
                files[0].execute();
            }else{
                writeLn("No .mov shortcut found in in: ");
                writeLn(showDDL.selection.toString());
            }
        }
        shotToolsBtnOPT.onClick = function(){
            shotTools.visible = false;
            shotLoad.visible = false;
            optnsGrp.visible = true;
        }
    okBtn.onClick = function(){
            shotTools.visible = true;
            shotLoad.visible = true;
            optnsGrp.visible = false;
         
            app.settings.saveSetting("dandybuildPrefs", "optMissingFootage",optMissingFootage.value);
            app.settings.saveSetting("dandybuildPrefs", "optNuComp",optNuComp.value);
            app.settings.saveSetting("dandybuildPrefs", "optSaveComp",optSaveComp.value);
          // $.writeln("new comp checked: "+app.settings.getSetting("dandybuildPrefs", "optNuComp"));
    }
       
        //send to renderqueue
        //uses the comp name to figure out folder
 
                 
        shotToolsBtn5.onClick = function(){
        rflag = false;
        //$.writeln("missing fottage "+app.settings.getSetting("dandybuildPrefs", "optMissingFootage"));
        if(app.settings.getSetting("dandybuildPrefs", "optMissingFootage") == "true"){
            rflag = checkForMissingFootage(false);
        }
        if(!rflag){
            rcomp = findMainShot();
                if(rcomp){
                    shotNumber = getSetTake();
                    if(shotNumber != false){
                        //$.writeln(shotNumber);
                        setTake(shotNumber);
                        rq = app.project.renderQueue;
                        for(i=1;i<=rq.numItems;i++){   
                                rq.items[i].render = false;
                        }
                        rqitem = app.project.renderQueue.items.add(rcomp);
                        rqitem.outputModules[1].applyTemplate("TiffOutput");
                        rqitem.applyTemplate("Multi-Machine Settings");
                        //savePath = rootFolder+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/OUT/"+rcomp.name;
                        savePath = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/"+rcomp.name;
                       
                       
                        if(checkOutFolder(savePath)){
                          rqitem.outputModules[1].file = new File(savePath+"/"+rcomp.name+"_[#####].tif");
                        }else{
                          alert("Couldn't figure out an output folder for \""+(rcomp.name)+"\", select a folder manually.");
                          rqitem.outputModules[1].file = new File();
                        }
                    }
                }
            }else{
                alert("This project is missing source files and will not render. \nUse the 'miss' button to find out where the footage should be");
            }
        }
       
        shotToolsBtn4.onClick = function(){
            //shotNumber = getSetTake(true);
            //setTake(shotNumber);
            checkForMissingFootage(true);
        }
       
        shotToolsBtn6.onClick = function(){
          wf = new Folder(watchfolderLocation);
          if(wf.exists){
              sendToWatchFolder(wf,findMainShot().name);
              }else{
                  alert("Watchfolder error, sorry!");
              }
        }
           
   
   
   
   
   
    rootFolderLoc.onClick = function(){
        y = confirm("Root folder is currently set to \n\n\""+rootFolder+"\"\n\nChange it ?");
        if(y){
            //v = new Folder();
            v = Folder.selectDialog ("New root folder location").toString();
            if(v!=null && v!="undefined"){
                app.settings.saveSetting("dandybuildPrefs", "rootfolder",v);   
                rootFolder = v;
                          showDDL.removeAll();
                          items = returnList(rootFolder);
          //  alert(items);
            for(i in items){
              showDDL.add("item", items[i]);
            }
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
            shotDDL.selection = 0;
            shotDDL.notify();
                showDDL.notify();
                shotDDL.notify();
                //$.writeln("notified");
            }
        }
    }
    watchFolderLoc.onClick = function(){
        y = confirm("Watchfolder is currently set to \n\n\""+watchfolderLocation+"\"\n\nChange it ?");
        if(y){
            //v = new Folder();
            v = Folder.selectDialog ("New watchfolder location").toString();
            if(v!=null && v!="undefined"){
                app.settings.saveSetting("dandybuildPrefs", "watchfolderLocation",v);   
                watchfolderLocation = v;
            }
           
        }
    }
    shotDDL.notify();
    }
    dandyShotBuilderUI(this) ;
 
</pre>
 
 
 
===layer Position to .txt===
https://i.imgur.com/G5DX1T0.gif
Old, might not work! (but should :)
<pre>
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
//
// The script will write to a text file the x & y values of the layer position for every frame comprised in
// a selection of keyframes, or for the whole comp duration if no keyframes are selected
//
//
function timeToFrameNum(myTime){
  return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
}
function framesToTime(myTime){
  return myTime/app.project.activeItem.frameRate;
}
function timeToTimeCode(myTime){
  var framesN = myTime * app.project.activeItem.frameRate;
  fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));
  ho = addZero(Math.floor(myTime/3600));
  mi = addZero(Math.floor(myTime/60)-ho*60);
  se = addZero(Math.floor(myTime)-mi*60-ho*3600);
  return ho+":"+mi+":"+se+":"+fr;
}
function addZero(val){
  if(val<10){
      val = "0"+val;
  }
  return val;
}
var myDisp = app.project.timecodeDisplayType;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;
var pText = "Choose an output format using:\n%f (framenumber),%i (index, starts at 0) %t (SMTPE timecode), %x (x value), %y (y value), %l (linebreak) and any other character. Default output looks like '16: 230;22'";
 
if(app.project.activeItem != "null" && app.project.activeItem != null && app.project.activeItem != 0){
  if(app.project.activeItem.selectedLayers.length != 0){
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
        curLayer = app.project.activeItem.selectedLayers[0];
        var textName = "AEcoordinates.txt";
        var myTextFile = filePutDialog("Select a location to save your .txt file", textName, "TEXT txt");
        if(myTextFile == null){
            alert("You must choose a place to save the file");
        }else{
            var formatString = prompt(pText,"%i: %x;%y%l");
            myTextFile.open("w","TEXT","????");
            myKeys = curLayer.property("position").selectedKeys;
            if(myKeys.length != 0){
              strTime = curLayer.property("position").keyTime(myKeys[0]);
              endTime = curLayer.property("position").keyTime(myKeys[myKeys.length-1]);
            }else{
               strTime = app.project.activeItem.workAreaStart;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;
             }
             }
             var startLoop = timeToFrameNum(strTime);
             var startLoop = timeToFrameNum(strTime);
             var endLoop = timeToFrameNum(endTime);
             var endLoop = timeToFrameNum(endTime);
             for(i=startLoop;i<=endLoop;i++){
             for(i=startLoop;i<=endLoop;i++){
               var curTime = framesToTime(i);
               var curTime = framesToTime(i);
               out = formatString.replace('%x',curLayer.property("position").valueAtTime(curTime,true)[0]);
               out = formatString.replace('%x',curLayer.property("position").valueAtTime(curTime,true)[0]);
               out = out.replace('%y',curLayer.property("position").valueAtTime(curTime,true)[1]);
               out = out.replace('%y',curLayer.property("position").valueAtTime(curTime,true)[1]);
               out = out.replace('%f',i);timeToTimeCode
               out = out.replace('%f',i);timeToTimeCode
               out = out.replace('%t',timeToTimeCode(curTime));
               out = out.replace('%t',timeToTimeCode(curTime));
               out = out.replace('%i',i-startLoop);
               out = out.replace('%i',i-startLoop);
               out = out.replace('%l','\n');
               out = out.replace('%l','\n');
               myTextFile.write(out);
               myTextFile.write(out);
               clearOutput();
               clearOutput();
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+"% done...");
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+"% done...");
             }
             }
         clearOutput();
         clearOutput();
         writeLn(endLoop-startLoop+" positions saved to file");
         writeLn(endLoop-startLoop+" positions saved to file");
         writeLn("script written by mlk =)");
         writeLn("script written by mlk =)");
         myTextFile.close();
         myTextFile.close();
         }
         }
       } else {
       } else {
         alert ("This script requires the scripting security preference to be set.\n" +
         alert ("This script requires the scripting security preference to be set.\n" +
         "Go to the \"General\" panel of your application preferences,\n" +
         "Go to the \"General\" panel of your application preferences,\n" +
         "and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.");
         "and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.");
       }
       }
 
  }else{
      alert("Select a layer with 'position' keyframes");
  }
}else{
  alert("Select a composition and a layer with 'position' keyframes");
}
app.project.timecodeDisplayType = myDisp;
</pre>
 
==Other Scripts==
 
=== Work specific ===
 
==== Auto scale precomps ====
Script that precomposes animation layers (specifically that are in .swf):
* scales up the layer by X amount
* scales down the precomp by 1/x amount
* Toggles the continuously rasterize off on the precomp, on inside.
This specifically solves the problem that lines out of Animate/Flash are 'rough' (aliased) when imported at 100%, no matter which flags are toggled.
Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)
 
Usage: save as .jsx, run the script via File>Scripts (Ctrl-Alt-Shift-D will redo latest script to repeat the process).
If no layers are selected, the script will instead prompt which scale factor it should use (4 by default).
 
<pre>
// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how
(function afterEffectsSmartScale() {
    var SCRIPT_PREF_KEY = "SmartScaleFactor";
var SCRIPT_NAME = "SmartScaleTool";
    var defaultScale = 4;
    var comp = app.project.activeItem;
    var selectedLayers = comp.selectedLayers;
    var scaleFactorX;
 
    function getScaleFactor(set) {
var factor = defaultScale;
if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){
factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);
}
if(set){
var promptResult = prompt("Enter the scale factor (default is 400% -> 4)\nThen select layer(s) and run again.", factor);
if (promptResult === null) return null;
factor = parseFloat(promptResult);
app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);
}
 
        return factor;
    }
 
    if (selectedLayers.length === 0) {
        scaleFactorX = getScaleFactor(1);
        return;
    }
 
    scaleFactorX = getScaleFactor();
    if (scaleFactorX === null) return;
 
    var scaleMultiplier = scaleFactorX;
    var inverseMultiplier = 1 / scaleFactorX;
 
    app.beginUndoGroup("Smart Scale Precomposition");
 
    try {
        for (var i = 0; i < selectedLayers.length; i++) {
            var layer = selectedLayers[i];
           
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + "_Scale", false);
            var precompLayer = app.project.activeItem.selectedLayers[0];
precompLayer.collapseTransformation = false;
 
            // 2. Inside the precomposition, scale the layer and composition
            if (precomp.numLayers > 0) {
                var innerLayer = precomp.layer(1);
                innerLayer.collapseTransformation = true;
                // Scale the inner layer
                var currentScale = innerLayer.property("ADBE Transform Group").property("ADBE Scale").value;
                innerLayer.property("ADBE Transform Group").property("ADBE Scale").setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);
               
                // Adjust composition size accordingly
                precomp.width *= scaleMultiplier;
                precomp.height *= scaleMultiplier;
 
                // Adjust the position of the layer within the precomp to keep it centered
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];
                innerLayer.property("ADBE Transform Group").property("ADBE Position").setValue(newPos);
            }
 
            // 3. Back in the top composition, scale down the precomposed layer by 1/X
            var currentOuterScale = precompLayer.property("ADBE Transform Group").property("ADBE Scale").value;
            precompLayer.property("ADBE Transform Group").property("ADBE Scale").setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);
        }
    } catch (e) {
        alert("An error occurred: " + e.toString());
    } finally {
        app.endUndoGroup();
    }
})();
</pre>


  }else{
Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx
      alert("Select a layer with 'position' keyframes");
  }
}else{
  alert("Select a composition and a layer with 'position' keyframes");
}
app.project.timecodeDisplayType = myDisp;
</pre>


==Other Scripts==
===Shelf===
===Shelf===
[[AFX Shelf]]
[[AFX Shelf]]

Latest revision as of 14:19, 19 December 2025

Scripts

Copy footage to local folder as proxy

oZ24pac.png

/* 
 * simpleLocalProxy.jsx v1.0
 *
 * 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 on the network and you want file on your local SSD. Less black-box version of After Effects' file cache on scratch disks ('conformed media')
 *
 *
 * 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
 *  
 *
 *
 */



/*
 * 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){
    //uncomment to allow debugging
    //$.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
                    sourcePath = pathToLocalizedPath(dir);
                    destinationPath = pathToLocalizedPath(outputDir);
                    filename = fileName.replace(/%20/g, " ");
                    
                    batchFile += "\nrobocopy ";
                    batchFile += "\"";
                    batchFile += sourcePath;
                    batchFile += "\"";
                    batchFile += " ";
                    //robocopy filename requires a wildcard, even if it's a single file
                    batchFile += "\"";
                    batchFile += destinationPath ;
                    batchFile += "\"";                    
                    batchFile += " ";
                    batchFile += "\"";
                    batchFile += filename ;
                    batchFile += "*\"";
                    batchFile += ((useforcecopy)?" /XO":"")+" /FFT"+((debugcheck)?"":" /NJH /NJS");
                    e(batchFile);
                }
            }
        }
        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: 'Proxy folder setup'},\
                                saveDirOptions: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
                                    usemd5rbox: RadioButton {text: ' Simple',helpTip:'Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)',value:true},\
                                    usepathrbox: RadioButton {text: ' Copy Folder Structure',helpTip:'Copies the target folder structure inside the proxy folder; more folders'}}},\
                            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 files (otherwise skips existing files)'},\
                                    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 Proxy Folder ', helpTip:'choose folder to copy files to',preferredSize:[-1,30]} , \
                            browseBut: Button {text: ' Browse ' , preferredSize:[-1,30]}} , \
                        curentDirTxt: EditText {text: '" + localFolder +"',enabled:false},\
                        copyFootageBut: Button {text: ' Copy Footage(s) to Proxy Folder ' ,helpTip:'Launches a background batch copy of selected footage',preferredSize:[-1,30]} , \
                        switchproxyBut: Button {text: ' Switch 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);

Auto Expose Animation

Adds 'hold' keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD, also 2025 update vibecodin' an update to make it more consistent: https://berniebernie.fr/wiki/Afx_Javascript_Temp#Auto-expose_rewrite/debug .

MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.


//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui
{
    
var currentSlider = "none";


 function watchFolderUI(thisObj){
    pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Auto Expose", [100, 100, 300, 300]);
    var res = 
    "group { \
                alignment: ['fill','fill'], \
                alignChildren: ['fill','top'], \
                orientation: 'column', \
                    setupDetector: Button {text: 'Step 1: setup detector on layer' ,preferredSize:[-1,30]} , \
                    txt1: StaticText {text: 'Temporarily set project to 8bits (will debug later). Move to a frame where there is a change and increase resolution slider until the slider called 'Detector' picks up a change (value > 0)',properties:{multiline:true}} , \
                    bakeKeys: Button {text: 'Step 2: bake to keys' ,preferredSize:[-1,30],enabled:true} , \
                    txt2: StaticText {text: 'Bakes detected animation as time remapped keys to animation, this can be long, watch your "Info" panel. If some animation is not detected, change resoltion and run again',properties:{multiline:true}} , \
                    applyKeys: Button {text: 'Step 3: apply as time remapping on selected layers' ,preferredSize:[-1,30]} , \
                    txt3: StaticText {text: '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}} , \
            }";	
    pan.grp = pan.add(res);        
    pan.grp.setupDetector.onClick = function () {
        //pan.grp.bakeKeys.enabled = true;        
        setupDetector();
        
    }
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}
    pan.grp.applyKeys.onClick = function () {applyKeys();}

    pan.layout.layout(true);
    pan.layout.resize();
    pan.onResizing = pan.onResize = function () {this.layout.resize();}
    return pan;
    }
watchFolderUI(this) ;



function e(s){
    $.writeln(s);
}



function setupDetector(){
    app.beginUndoGroup("Auto expose setup Detector");
    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 allLayers = app.project.activeItem.layers;
    for(i=1;i<=allLayers.length;i++){
        if(allLayers[i].enabled){
            allLayers[i].solo = false;
        }
    }
    precomplayer.enabled = true;    
    precomplayer.solo = 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";
    var resolutionslider = sliderctrl.property("ADBE Slider Control-0001");
   
    resolutionslider.setValue(3);

    var detectorctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
    detectorctrl.name = "Detector";
    var detectorslider = detectorctrl.property("ADBE Slider Control-0001");
    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;\
    ";

    detectorslider.expression = detectorexpression;
    currentSlider = detectorslider;
    app.endUndoGroup();
 }

function bakeKeys(){
    
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to 'clean' expression
    bakeCommand = app.findMenuCommandId("Convert Expression to Keyframes");
    currentSlider.selected = true;
    app.executeCommand( bakeCommand );
    detectorexpression = "f = effect('Detector')('ADBE Slider Control-0001');\nf>0?1:0;";
    currentSlider.expression = detectorexpression;
    currentSlider.expressionEnabled = true;
    currentSlider.selected = true;
    app.executeCommand( bakeCommand );
    

    // 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(currentSlider.keyValue(i) > 0){
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
        }else{
            currentSlider.removeKey(i);
        }
    }

    // add '0' key on first frame
    
    currentSlider.addKey(0);
    currentSlider.setValueAtKey(1, 0);
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);

}
function applyKeys(){
    var layers = app.project.activeItem.selectedLayers;
    for(i = 0;i<layers.length;i++){
        layers[i].timeRemapEnabled = true;
        var remap = layers[i].property("Time Remap");
        for(j=1;j<=currentSlider.numKeys;j++){
            
            v = currentSlider.keyValue(j);
            t = currentSlider.keyTime(j);
            remap.addKey(t);
            remap.setValueAtKey(j, v);
            remap.setInterpolationTypeAtKey(j,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
        }
        remap.expression = "\
        //toggle this on to set sequential time remap\
        a = timeRemap;\
        nk = a.nearestKey(time);\
        curframe = 0;\
        if(nk.time > time){\
            curframe = nk.index-1;\
        }else{\
            curframe = nk.index;\
        }\
        curframe*thisComp.frameDuration;";
        remap.expressionEnabled = false;
    }
}


}

Create Null controllers on Puppet pins

HdFjaYZ.jpg

//nulls created from puppet pins can be parented like normal layers
{
    app.beginUndoGroup("Create Null Controls on Puppet Pinsv");

    function getLayerFromProperty(prop){
        return prop.propertyGroup(prop.propertyDepth)
    }


    var c = app.project.activeItem;
    if( c != null && c.selectedProperties != null){
        var props = c.selectedProperties;
        j = 0;
        for(i = 0;i<props.length;i++){
            if(props[i].matchName == "ADBE FreePin3 PosPin Atom"){
                j++;
                child = props[i].property("ADBE FreePin3 PosPin Position");
                pos = [child.value[0],child.value[1]];
                nullLayer = app.project.activeItem.layers.addNull();
                nullLayer.name = "puppetCtrl"+j;
                nullLayer.position.setValue(pos);
                var expr = "thisComp.layer(\""+nullLayer.name+"\").toWorld(thisComp.layer(\""+nullLayer.name+"\").transform.anchorPoint)";
                child.expression = expr;
            }
        }
    }
    app.endUndoGroup();
}

Batch replace file locations with text file

88QHvlQ.jpg

//now works with sequences or still images, windows only
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path
function URIToWinPath(path){
	str = path.replace(/\//, "");
	str = str.replace(/\//, ":/");
	str = str.replace(/%20/g, " ");
	str = str.replace(/\//g, "\\");
	return str;
}
function WinPathtoURI(path){
    //windows, for now, the only one available!
    str = "/"+path.replace(":\\", "/");
    str = str.replace(/\\/g, "/");
    str = str.replace(/ /g, "%20");
    //str = str.substring(0,str.lastIndexOf("/"));
    return str;
}
{
    app.beginUndoGroup("Change File Locations");
    
    var txtFile = new File("~/Desktop/tempAE.txt");
    txtFile.open("w","TEXT","????");
    txt = "";
    sel = app.project.selection;
    var isSequence = new Array();
    if(sel.length == 0){
       alert("Select footage items.");
    }else{
        for(i=0;i<app.project.selection.length;i++){
            isSequence[i] = !sel[i].mainSource.isStill;
            txt += URIToWinPath(sel[i].mainSource.file.toString())+"\n";
        }
        txtFile.write("*** Paths are written in Unix style, once edited simply _SAVE_ and press Yes in the AE prompt.Undo available if you screw up.***\n\n");
        txtFile.write(txt);
        txtFile.close();
        txtFile.execute();
        isOk = confirm("Change file paths ?\n\nReloading might take a while!");
        if(isOk){
           txtFile.open("r","TEXT","????"); 
           contents = txtFile.read();
           arrayContents = contents.split("\n");

           for(i=0;i<app.project.selection.length;i++){
                var tmpFile =  new File(WinPathtoURI(arrayContents[i+2]));
                //alert(tmpFile);
                if(isSequence[i]){
                    sel[i].replaceWithSequence(tmpFile,0);
                }else{
                    sel[i].replace(tmpFile);
                }
                
                writeLn(Math.round((i+1)/app.project.selection.length*100)+"%");
           }
        }
    }
    app.endUndoGroup();
}

Loop Selected Layers

82c30ae9dd47a979c727a3b4f30ad617.gif

No hassle looping

app.beginUndoGroup("Set loops");
var layersList = app.project.activeItem.selectedLayers;
var frameD = app.project.activeItem.frameDuration;
for (i=0;i<layersList.length;i++){
    if(!layersList[i].timeRemapEnabled){
        var outP = layersList[i].outPoint;
        layersList[i].timeRemapEnabled = true;
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);
        layersList[i].timeRemap.setValueAtTime(outP,0);
        layersList[i].timeRemap.expressionEnabled = true;
        layersList[i].timeRemap.expression = "loopOut()";
        layersList[i].outPoint = app.project.activeItem.workAreaStart+app.project.activeItem.workAreaDuration;
    }
}
app.endUndoGroup();

AFX Shelf

Find Next Text Layer

for(var i = 1; i <= app.project.numItems; i++){
    a = true;
                if(!a){
                break;
                }
    if(app.project.item(i) instanceof CompItem){
        var comp = app.project.item(i);
        for(j = 1; j <=  comp.layers.length;j++){
            a = true;
            comp.layer(j).selected = false;
            if(comp.layer(j) instanceof TextLayer){
                comp.layer(j).selected = true;
                $.writeln(comp.name);
                a = confirm("Continue selecting text layers",true);
            }
            if(!a){
                break;
            }else{
                comp.layer(j).selected = false;
            }
        }
    }
}
if(a){
    alert("No (more) text layers found");
}

Simple Watchfolder

poTWO.png wvtgDqE.png

I know a lot of people use media encoder but it's been honestly underwhelming for my uses, so this is what I use when network rendering multiple comps when there a couple users and a couple more machines

/*
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;
        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++;
                            }
            }
        }
	if(m!=0){
		alert("There are "+m+" missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button");
	}else{
        	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");
            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);
        	myFolder.execute();
                //watchfolderLocation
            }
            sendWF.onClick = function() {
                if (watchfolderLocation == "none") {
                    setWatchFolder();
                } else {
                    sendToWF(watchfolderLocation);
                }
            }

        }
    }
    watchFolderUI(this);
}

Show original source location (WIN only)

04O3r.png

{
    function pathToWinPath(path){
        var str = path.toString().replace(/\//, "");
        str = str.replace(/\//, ":/");
        str = str.replace(/%20/g, " ");
        str = str.replace(/\//g, "\\");
        return str;
    }

    sel = app.project.selection;
    if(sel.length == 0){
        alert("You need to select source(s) in the project panel");
    }else{
        for(i=0;i<app.project.selection.length;i++){
            a = prompt("'OK' continues, 'cancel' stops displaying original sources\n\n[ "+sel[i].name+" ]",pathToWinPath(sel[i].mainSource.file.path.toString()));    
            if(!a){break}
        }
    }
}



List selected layers effects and their properties matchNames

//windows only
if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
    var txtFile = new File("~/Desktop/effectList.txt");
    txtFile.open("w","TEXT","????");
    var col = app.project.activeItem.selectedLayers;
    for (i=0;i<col.length;i++){
        effs = col[i].property("ADBE Effect Parade");
            for(j=1;j<=effs.numProperties;j++){
                txtFile.write("\n( "+effs.property(j).matchName+" ) "+effs.property(j).name+"\n------------------------------------------\n");
                for(k=1;k<=effs.property(j).numProperties;k++){
                    txtFile.write(effs.property(j).property(k).matchName+" --> "+effs.property(j).property(k).name);
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE && effs.property(j).property(k).value != undefined){
                        txtFile.write(" [ "+effs.property(j).property(k).value.toString()+" ] ");
                    }
                    txtFile.write("\n")

                    }
            }
    }

    txtFile.write("\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n");
    txtFile.write("v = s.Effects.addProperty(\"CC RepeTile\");\n");
    txtFile.write("v.property(\"CC RepeTile-0001\").setValue(10);");    
    txtFile.close();
    txtFile.execute();
}else{
    alert("Set scripting Prefs to enable to write to disk");
}

IEUwe.gif

Disable effects

Disable/enables all the effects that are similar to the currently selected one throughout the project. This was fully GPT generated on first try. Impressed!

(function toggleEffectGlobally() {
    app.beginUndoGroup("Toggle Effect Globally");

    var sel = app.project.activeItem && app.project.activeItem.selectedProperties;
    if (!sel || sel.length === 0) {
        alert("Please select an effect first.");
        return;
    }

    var selectedEffect = null;

    // Find the selected effect property group
    for (var i = 0; i < sel.length; i++) {
        if (sel[i].matchName && sel[i].parentProperty && sel[i].parentProperty.matchName === "ADBE Effect Parade") {
            selectedEffect = sel[i];
            break;
        }
    }

    if (!selectedEffect) {
        alert("Please select an effect in the timeline.");
        return;
    }

    var effectName = selectedEffect.matchName;
    var effectEnabled = selectedEffect.enabled;

    // Loop through all comps in the project
    for (var p = 1; p <= app.project.numItems; p++) {
        var item = app.project.item(p);
        if (item instanceof CompItem) {
            for (var l = 1; l <= item.numLayers; l++) {
                var layer = item.layer(l);
                if (layer.property("ADBE Effect Parade")) {
                    var effects = layer.property("ADBE Effect Parade");
                    for (var e = 1; e <= effects.numProperties; e++) {
                        var eff = effects.property(e);
                        if (eff.matchName === effectName) {
                            eff.enabled = !effectEnabled;
                        }
                    }
                }
            }
        }
    }

    app.endUndoGroup();
})();

List effects

 function projEffects(){
 	var effects = new Array();
 	var effects2 = new Array();
 	for(var i = 1; i <= app.project.numItems; i++){
 		if(app.project.item(i) instanceof CompItem){
 			   var comp = app.project.item(i);
 				for(j = 1; j <=  comp.layers.length;j++){
 					effs = comp.layer(j).property("ADBE Effect Parade");
 					for(k=1;k<=effs.numProperties;k++){
 						keyName = effs.property(k).matchName;
 						effects[keyName]  = 1;
 						}
 					   
 					}
 			}
 	}
 	for(a in effects){
 		effects2[effects2.length] = a;
 	}
 	effects2.sort();
 	return effects2;
 }
 alert(projEffects().join("\n"))

hFNiQ.jpg

Multiple sequences import (Windows)

// Multiple sequences import (windows)
// ----------------------------------------------
//  bernie@berniebernie.fr
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file
//  Access to files network required --- no error checking.
//   > only works on windows so far
//   > only finds exrs/pngs/jpgs
//   > probably fails if your sequences dont have the same start frame
//   > will work with /path/image.####.jpg or similar



function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}


function e(s){
    $.writeln(s);
}
curScript = new File($.fileName);

startT = Date.now();

sourceFolder = app.project.selection[0].mainSource.file.parent;
tmpFolder = Folder.temp;
tmpBat = Folder.temp.toString()+"/ae_dirlist.bat";
tmpFilesList = Folder.temp.toString()+"/ae_imageslist.txt";
var listFile = new File(tmpFilesList);



var batFile = new File(tmpBat);
batFile.open("w","TEXT","????");
batFile.write("REM Auto generated by this after effects script file: "+curScript.fsName);
batFile.write("\npushd \""+sourceFolder.fsName+"\"");
batFile.write("\ndir /b /on *.exr *.png *.jpg *.jpeg > \""+listFile.fsName+"\"");
batFile.close();
system.callSystem(batFile.fsName);


listFile.open("r","TEXT","????"); 
contents = listFile.read();
arrayContents = contents.split("\n");
listFile.close();
timer = (Date.now()-startT)/1000;
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");


var myRe = new RegExp("[0-9]{2,}(\)){0,1}\.[a-z]+$");
var myArray = myRe.exec(arrayContents[0]);
endlength = myArray[0].length;

for(i = 0;i<arrayContents.length;i++){
    arrayContents[i] = arrayContents[i].slice(0, -endlength);
}
unique = uniq_fast(arrayContents);

dialog = confirm(unique.length+" sequences found. Load them ? \nIt might take a long time !",false,"Confirm Loading");

if(dialog){      
    for(i = 0;i<unique.length;i++){
        if(unique[i].length>0){
            sequenceStartFile = new File(sourceFolder.toString()+"/"+unique[i]+myArray[0]);
            
            if (sequenceStartFile) {
            writeLn("Loading "+(i+1)+"/"+unique.length);
                try {
                    // Create a variable containing ImportOptions.
                    var importOptions = new ImportOptions(sequenceStartFile);
                    importOptions.sequence = true;
                    try { 

                        app.project.importFile(importOptions);
                    } catch (error) {
                       e(error.toString());
                    }
                } catch (error) {
                    e(error.toString());
                }
            }
        }
    }
}else{
    writeLn("Canceled");
}
timer = (Date.now()-startT)/1000;
e("after regext: "+timer);

Split to Renderqueue

0f9dcb815b75fef03af6c16d340614b6.gif

// Split Layers to Renderqueue v2.0
// ----------------------------------------------
//
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp
//  using the in and out points. I use this along with 'Magnum' the edit detector to split & render sequences.
//
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the 'base' renderqueue item that we derive everything from

function pad(n, width, z) {
  z = z || '0';
  n = n + '';
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}

app.beginUndoGroup("Split Layers to Renderqueue");

if(app.project.renderQueue.items.length == 1){
    FE = app.project.renderQueue.items[1];
    ai = FE.comp;
    path = FE.outputModule(1).file.path;
    for(i=1;i<=ai.layers.length;i++){

        RI = app.project.renderQueue.items[1].duplicate();
        RI.timeSpanStart = ai.layers[i].inPoint;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;
        $.writeln(ai.workAreaDuration+" "+ai.layers[i].outPoint);
        RI.outputModule(1).file =  new File(path+"/"+pad(i,2,"0")+"_"+ai.layers[i].name);
    }
    app.project.renderQueue.items[1].render= false;
}else{
    alert("Only 1 element should be in renderqueue");
}
app.endUndoGroup;

Docked Panel SNIP

//script panel
{
	var nested_file = new File("U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx");
	nested_file.open("r");
	eval(nested_file.read());
	nested_file.close();
}
//called file
function createUI(thisObj) {
	var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",[100, 100, 300, 300]);
	impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
	//impButton.onClick = openfile;
	return myPanel;
}
var myToolsPanel = createUI(this);

Watchfolder watcher (WIP)

lhO3k.gif

Needs in the ScriptUI folder Nfw8h.png rgPGl.png N93S7.png VTGZl.png PjAkj.png (flag_orange.png, flag_green.png, flag_red.png,flag_graygreen.png)

//  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.

/*
todo: check if icons are here
*/

{
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__";
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;


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();
    }
  // $.writeln("file >>> "+txtfiles[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{
                //info = ["debug",logFolderLocation.toString(),logFolderLocation.name.toString()];
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];
               
                firstFile.open("r","TEXT","????");
                contents = firstFile.read();
                firstFile.close();
                lines = contents.split("\n");
              //  $.writeln(logFolderLocation.toString()+" <<< ")
               info[3] = logFolderLocation.toString();
               info[0] = logFolderLocation.name.toString();
                for(i=0;i<lines.length;i++){
                    //$.writeln("Doing shit");
                    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: ";
                   
                   // info[2] = "n/a+";
//                    info[1] = 1;
                    
                    if(info[1]==0 ||info[1]==2){
                       // $.writeln( lines[i].indexOf(phrase));
                            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("\\"));
                               //$.writeln(wfold);
                                f =  wfold.substring(wfold.lastIndexOf("\\")+1,wfold.length);
                                ///info[3] = pathToWinPath(wfold);
                                 info[3] =localToRessource(wfold);
                                 $.writeln(info[3])
                                info[0] = f;
                               // info[2] = " ";
                            }
                        }
                   // info[1] = ;
                
                }
              //  $.writeln(info);
               // $.writeln(contents);
        }
   
    }
  $.writeln(info);
    return info;
            
}
function hasStarted(watchedFolderLocation){
    fold = new Folder(watchedFolderLocation.toString());
    files = fold.getFiles(getLogonly);
    //$.writeln(files.length);
    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()];
            }
            //$.writeln(">>> "+state+" - "+out+" - "+folders[folder].name);
            
           // $.writeln(out);
            vArray[counter2] =out;
        }
      // $.writeln(vArray[counter2]);
        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];
                                      //  $.writeln("Debug");$.writeln(p);
                    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], 600);
            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){
                    // $.writeln(shotinfos.join("\n"));
                    sL=shotinfos.length;
                    //$.writeln(sL);
                    for(i=1;i<sL;i++){
                       a=shotinfos[i];
                      //$.writeln(a);
                        //$.writeln(shotinfos[i][1]+" "+list.selection.toString());
                        if(a[0] == list.selection.toString()){
                           // $.writeln("ww"+);
                       //  $.writeln(a);
                            //explore(a[3]+((a[1]==2)?"/"+a[0]:""));
                            explore(a[3]);
                            break;
                            }
                        }
                    //$.writeln(sel[0].subItems[0].text);
                }


            }        
            
            
            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;
                    }
                }
                
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;
                //refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
                //cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
            }
            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(reloadEditTxt.text >=10){
                }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 grow(palette,value){
    
}
function addItem(name,state,msg){
    //    alert(listItem.selection);
    var arrayV = new Array("green","red","orange","graygreen","gray");
    //var texted = new Array("...","error:","rendering","...","queued");
    var item = list.add ('item',name);
    //$.writeln(scriptFolder.toString()+"/flag_"+array[state]+".png");
    item.image = File(scriptFolder.toString()+"/flag_"+arrayV[state]+".png");
    //item.subItems[0].helpTip = texted(state);
    item.subItems[0].text =msg;

}
///////////////////////////////////////////
           /* if((logFolder =  getFilePath(watchfolderLocation+"/"+shot)) != undefined){
                $.writeln(logInfo(logFolder));
            }else{
                $.writeln("no log folder");
                }*/
           // $.writeln();
           // addItem(list,"G12_SC213_T1",2);
           
           
           /*
var item1 = list.add ('item', 'GB15_SC138_T1');
item1.image = File("~/Desktop/flag_gray.png");
item1.subItems[0].text = 'Queued...';
item1.enabled = false;
*/

            
            //alert(getPercentage(watchfolderLocation+"/"+shot));
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+"/"+shot)*100);
          
                       // alert("test");
              //$.writeln(files[file].path+"/"+files[file].name);
                            //  a+=  getFilePath(files[file].path+"/"+files[file].name).name+"\n";
                            
/*                            
                            
                            {
                                
        wfLocBtn.onClick = function(){
            app.scheduleTask("loop()",2000,1);

           
            fold = new Folder( watchfolderLocation.toString());
            files = fold.getFiles();
                a= "";
            for(file in files){

              $.writeln(getFilePath(files[file].path+"/"+files[file].name));

            }
           alert(a);

            writeLn(pBar.value);
        }
*/

Parent to last selected layer

Why isn't this by default? Bound to alt-P.

(function() {
    var comp = app.project.activeItem;
    if (comp && comp instanceof CompItem) {
        var selectedLayers = comp.selectedLayers;
        if (selectedLayers.length > 1) {
            app.beginUndoGroup("Parent to Last Selected");
            var parentLayer = selectedLayers[selectedLayers.length - 1];
            for (var i = 0; i < selectedLayers.length - 1; i++) {
                selectedLayers[i].parent = parentLayer;
            }
            app.endUndoGroup();
        }else{
			alert("Requires at least a child an parent layer to be selected.");
		}
    }else{
		alert("Select a comp's layers and run the script again");
	}
})();

Import pos from maya

function createUI(thisObj) {
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",
[100, 100, 300, 300]);
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
impButton.onClick = openfile;
return myPanel;
}
var myToolsPanel = createUI(this);
//myToolsPanel.show();


function openfile(){
	            var myFile = File.openDialog ("Select track file","*.txt"); 
            var fileOK = myFile.open("r","TEXT","????");
	//var fileD = OpenDlg ("Tracking Point File","*.txt",true);
	//txt = fileD.readln ()
	alert("txt"+readTxt(myFile));
}

function readTxt(myFile){
var myText = myFile.read();	
return myText;
	}

Dandelion/Amazing World Of Gumball Shotbuilder

xKJWB.png


For education purposes only, the script was run in production to helb build shots, it:

  • created a list of shots & shows possible given a folder structure ("GB##_SHOWNAME_SC##_T##")
  • opened the last .AEP file found in said shot folder
  • if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder
  • imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the 'grab sources' button
  • automatically sent files to the watchfolder to render on a small-ish farm.

Other buttons allowed to check for missing footage, open the comp's folder, open the current shows' latest animatic, etc


//  
//  Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
//   last update 21/11/10
//
//  Known Bugs:
//      
//  -you can't have several sequences in a single folder and expect the script to pick up all the sequences
//  -this will not set color profiles
// //TODO
//  >>> CHECK IF WF FOLDER ALREADY EXISTS
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
//  -v0.7 fixes
//        -new scene after WF works properly
//        -changed output folder location to N:
//  -v0.6 fixes
//       -cancel watchfolder cancels watchfolder
//        -fixed GB##_SC_###_T1
//       -added options
//       -can work on a new location
//  -v0.5 fixes
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)
//      -will warn if there is missing footage before sending to WF
//      -removed set take, added 'missing' dialog.
//  -v0.4 fixes
//      -there shouldn't be a refresh problem on show change anymore
//  -v0.3 fixes
//      -changed watchfolder location to anthony's mac
//      -changed save as dialog
//      -removed unused buttons
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)
//  -v0.2 fixes
//      -sequences weren't getting imported.
//      -fixed UI/little problems
//      -added folders, refresh button, save before watchfoldering
var version = "0.7";

//default locations
var watchfolderLocation = "/c/__WATCHFOLDER__";
var rootFolder = "/c/";
var forEditFolderLoaction = "//MAC0023DFDF5429/for%20edit";


rootFolder = ((app.settings.haveSetting("dandybuildPrefs", "rootfolder")))?(app.settings.getSetting("dandybuildPrefs", "rootfolder")):rootFolder;
watchfolderLocation = ((app.settings.haveSetting("dandybuildPrefs", "watchfolderLocation")))?(app.settings.getSetting("dandybuildPrefs", "watchfolderLocation")):watchfolderLocation;
forEditFolderLoaction = ((app.settings.haveSetting("dandybuildPrefs", "forEditFolderLoaction")))?(app.settings.getSetting("dandybuildPrefs", "forEditFolderLoaction")):forEditFolderLoaction;

          //dirty hack
            a = ((app.settings.haveSetting("dandybuildPrefs", "optMissingFootage")))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true;
            b = ((app.settings.haveSetting("dandybuildPrefs", "optNuComp")))?(app.settings.getSetting("dandybuildPrefs", "optNuComp")):false;
            c = ((app.settings.haveSetting("dandybuildPrefs", "optSaveComp")))?(app.settings.getSetting("dandybuildPrefs", "optSaveComp")):true;
            if(a=="false") a = false;
            if(a=="true") a = true;
            if(b=="false") b = false;
            if(b=="true") b = true;
            if(c=="false") c = false;
            if(c=="true") c = true;
            
          


//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_";
var loadFile = ((app.settings.haveSetting("dandybuildPrefs", "loadscene")))?(app.settings.getSetting("dandybuildPrefs", "loadscene")):"";
checkOutputSettings();

Array.prototype.has = function(value) {
    var i;
    for (i=0;i<this.length;i++) {
        if (this[i] == value) {
            return true;
        }
    }
    return false;
}


function addShotDialog(sel){
    alert(sel);
    }

function pathToWinPath(path){
    path = path.toString();
	str = path.replace(/\//, "");
	str = str.replace(/\//, ":/");
	str = str.replace(/%20/g, " ");
	str = str.replace(/\//g, "\\");
	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 sendToWatchFolder(watchfolderlocation,compname){
    ocn = compname;
    if(app.settings.getSetting("dandybuildPrefs", "optSaveComp") == "true"){
        c = confirm("Save "+app.project.file.name+" ?",false,"Save Project");
        if(c){
            app.project.save();
        }
    }
    curFile = app.project.file;
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
    compname += "_WF";
    myFolder.create();
    mySaveFile = new File(myFolder.toString()+"/"+compname+".aep");        
    app.project.save(mySaveFile);
    myTextFile = new File(myFolder.toString()+"/"+compname.substring(0,22)+"_RCF.txt");    
    myTextFile.open("w","TEXT","????");
    text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n" ;
    myTextFile.write(text);
    myTextFile.close();
    writeInfo(watchfolderlocation,ocn);
    if(app.settings.getSetting("dandybuildPrefs", "optNuComp") == "false"){
        opFile = new File(curFile);
        if(opFile){
            app.open(opFile);
        }
    }else{
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
        app.project.new();
    }
    
    
    //explore(watchfolderlocation);
}

function writeInfo(watchfolderlocation,compname){
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");

    mySaveFile = new File(myFolder.toString()+"/"+compname+"_DandyWatch.txt");    
    mySaveFile.open("w","TEXT","????");
    rq = app.project.renderQueue;
    text ="";    
    for(i=1;i<=rq.numItems;i++){    
        if(rq.items[i].render){
            text += rq.items[i].outputModules[1].file.path+"\n";
            text += (Math.round(rq.items[i].timeSpanDuration*25)+"\n");
            text += system.callSystem("hostname");
        }
    }
    mySaveFile.write(text);
    mySaveFile.close();
}


function explore(location){
     fold = new Folder(location.toString());
     return fold.execute();     
}

function checkForMissingFootage(dialogFlag){
        var dialog = dialogFlag;
        var missing = false;
        for(i=1;i<=app.project.numItems;i++){
            app.project.items[i].selected = false;
            if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
                            if(app.project.items[i].footageMissing){
                                app.project.items[i].selected = true;
                                missing = true;
                                if(dialog){
                                    dialog = prompt("Original folder of \""+(app.project.items[i].name)+"\" :",pathToWinPath(app.project.items[i].file),"Missing footage! (hit cancel to suppress further dialogs)");
                                }
                            }
            }
        }
        return missing;
}

function checkOutputSettings(){
    renderSettingsSet = ((app.settings.haveSetting("dandybuildPrefs", "tiffsettings")))?true:false;
   // renderSettingsSet = 1;
    if(!renderSettingsSet){
                alert("Tiff output being setup, this should happen only once. It needs to close the current comp.");
                opFile = new File(rootFolder+"base.aep");
                if(opFile.exists){
                     app.open(opFile);
                     app.project.renderQueue.items[1].outputModules[1].saveAsTemplate("TiffOutput");
                     app.settings.saveSetting("dandybuildPrefs", "tiffsettings","is there");
                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
                     app.newProject();
                 }else{
                     alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
                }
    }
}

function checkOutFolder(tpath){
    foldFile = new Folder(tpath);
    //$.writeln(tpath);
    if(foldFile.exists){
        files = foldFile.getFiles();  
        if(files.length > 1){
            return false;
        }else{
            return true;
        }
    }else{
      newF = foldFile.create();
        return newF;
    }
}
function getSetTake(force){
    show = app.settings.getSetting("dandybuildPrefs","show");// showDDL.selection.toString();
    shot = app.settings.getSetting("dandybuildPrefs","shot"); //shotDDL.selection.toString();
    projectTake = getTake(findMainShot().name);
//    paths  = rootFolder+show+"/"+shot+"/OUT/";
    paths  = "/n/01_OUT/"+show+"/"+shot+"/";
    lastFolder = getLastModified(paths);
    //alert(lastFolder.name+" -->"+paths);
    if(lastFolder){
        folderTake = getTake(lastFolder.name);
    }else{
        folderTake = false;
    }
    msgPt1 = (folderTake)? "The last take in OUT folder is take "+(folderTake)+".\n":"No take was found in the OUT folder.\n";
    msgPt2 = "The current project take is "+projectTake+". \n\nChoose the current take:";
    if(force || (folderTake != (projectTake-1))){
        recommended = (folderTake)?((folderTake)+1):projectTake;
        answer = prompt(msgPt1+msgPt2,recommended);
        //$.writeln("blabal "+answer);
        if(answer != null){
            return answer;
        }else{
             return false;
         }
    }
    return projectTake;
}



function setTake(value){
    comp = findMainShot();
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf("_T")+2))+value;
}

function getTake(normalizedName){
    tmp = normalizedName.split("_T");
    return  parseFloat(tmp[tmp.length-1]);
}
	// Smart Import.jsx
	//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases
    //the only problem is that you can still not have 2 sequences in a single folder
   
function SmartImport(targetFolder){
         writeLn("Importing files...");
         var importedFiles = 0;
		var scriptName = "Smart Import";
		var sourcePaths = getSourcePathsArray();
		// Ask the user for a folder whose contents are to be imported.
		//var targetFolder = Folder.selectDialog("Import items from folder...");
		if (targetFolder != null) {
			// If no project open, create a new project to import the files into.
			function processFile(theFile)
			{
				try {
					// Create a variable containing ImportOptions.
					var importOptions = new ImportOptions(theFile);
                    //alert();
                     if(theFile.name.toString().toLowerCase().lastIndexOf(".psd") != -1){
                        importOptions.importAs = ImportAsType.COMP;
                     }
                       importSafeWithError(importOptions);
				} catch (error) {
					// Ignore errors.
				}
			}
			
			function testForSequences(files)
			{
				var searcher = new RegExp("[0-9]{3,}$");
				var movieFileSearcher = new RegExp("(mov|avi|mpg)$", "i");
				var parseResults = new Array;
                  var isFolder = new Array;
				
				// Test that we have a sequence. Stop parsing after 10 files.
				for (x = 0; (x < files.length) & x < 10; x++) {
					var movieFileResult = movieFileSearcher.exec(files[x].name);
					if (!movieFileResult) {
//******                           splitName = files[x].name.split('.')[0];
                            splitName=files[x].name.substring(0,files[x].name.length-4);

                           //splitName[splitNameA.length] = 
                           //alert(splitName);	
                         //  alert(files[x].name+" >>> "+files[x].name.split('.')[0]);
						var currentResult = searcher.exec(splitName);
						// Regular expressions return null if no match was found.
						// Otherwise, they return an array with the following information:
						// array[0] = the matched string.
						// array[1..n] = the matched capturing parentheses.
                   				
                    if (currentResult) { // We have a match -- the string contains numbers.
                           
                            // The match of those numbers is stored in the array[1]
							// Take that number and save it into parseResults.
							parseResults[parseResults.length] = currentResult[0];
						} else {
							parseResults[parseResults.length] = null;
						}
                           if(files[x].name == splitName){
                                isFolder[isFolder.length] = true;
                               // alert("Folder "+files[x].name);
                           }else{
                                isFolder[isFolder.length] = false;
                           }
					} else {
						parseResults[parseResults.length] = null;
					}
				}
				
				// If all the files we just went through have a number in their file names, 
				// assume they are part of a sequence and return the first file.
				
				var result = null;
				for (i = 0; i < parseResults.length; ++i) {
                   				
                   if(!isFolder[i]){
                    if (parseResults[i]) {
						if (!result) {
                            
							result = files[i];		
						}
					} else {
                        
						// In this case, a file name did not contain a number.
						result = null;
						break;
					}
                   }
                }
				
				return result;
			}
        			
			
			function importSafeWithError(importOptions)
			{
              if(!(sourcePaths.has(importOptions.file.toString()))){
 				try { 
                  b = app.project.importFile(importOptions);
                    writeLn(importOptions.file.toString());
                 sfi = sourcesFolderItem();
                   b.parentFolder = sfi;
                    if(importOptions.importAs == ImportAsType.COMP){
                        for (i = 1; i <= app.project.numItems ; i++) {
                            if(app.project.items[i].typeName == "Folder" && app.project.items[i].name.lastIndexOf(" Layers") != -1){
                         app.project.items[i].parentFolder = sfi;
                            }
                        }
                    }
                        writeLn("Importing file ("+importedFiles+")");
                        importedFiles++;
				} catch (error) {
					alert(error.toString() + importOptions.file.fsName, scriptName);
				}
                // }else{
                //        alert("file already here");
                 }
			}
			
			
			function processFolder(theFolder)
			{
				// Get an array of files in the target folder.
				var files = theFolder.getFiles();
				//alert(files);
				// Test whether theFolder contains a sequence.
				var sequenceStartFile = testForSequences(files);
				
				// If it does contain a sequence, import the sequence,
				if (sequenceStartFile) {
					try {
						// Create a variable containing ImportOptions.
						var importOptions = new ImportOptions(sequenceStartFile);
						importOptions.sequence = true;
						// importOptions.forceAlphabetical = true;		// Un-comment this if you want to force alpha order by default.
						importSafeWithError(importOptions);
					} catch (error) {
					}
				}
				
				// Otherwise, import the files and recurse.
				
				for (index in files) { // Go through the array and set each element to singleFile, then run the following.
					if (files[index] instanceof File) {
						if (!sequenceStartFile) { // If file is already part of a sequence, don't import it individually.
							processFile(files[index]); // Calls the processFile function above.
						}
					}
					if (files[index] instanceof Folder) {
						processFolder(files[index]); // recursion
					}
				}
			}
			
			// Recursively examine that folder.
			processFolder(targetFolder);
		}
    clearOutput();	
    }
	
	
	
   
function sourcesFolderItem(){
    for (i = 1; i <= app.project.numItems ; i++) {
       // alert(app.project.items[i].name);
     if(app.project.items[i].typeName == "Folder" && app.project.items[i].name == "Sources"){
           return app.project.items[i];
      }
   }
    return app.project.items.addFolder("Sources");
    //else
    
}

function getSourcePathsArray(){
    var footageLocations = new Array();
    for (i = 1; i <= app.project.numItems ; i++) {
        if(app.project.item(i).typeName == "Footage"){
            footageLocations[footageLocations.length] = app.project.item(i).file;
        }    
    }
    return footageLocations;
}

function getLastModified(location,extension){
      myFolder = new Folder(location.toString());
      if (!myFolder instanceof Folder){
          return false;
     }
     listFiles = myFolder.getFiles(extension);
     listDates = new Array();
     maxId = 0;
     maxValue = 0;
     for(i=0;i<listFiles.length;i++){
         if (!(extension == "" && (listFiles[i] instanceof File))){
             d = listFiles[i].modified;
             formattedDate = d.getFullYear()+""+pad(d.getMonth()+1,2,0)+""+pad(d.getDate(),2,0)+""+pad(d.getHours(),2,0)+""+pad(d.getMinutes(),2,0)+""+pad(d.getSeconds(),2,0);
             listDates[i] = formattedDate;
             if(Math.max(maxValue,formattedDate) == formattedDate){
                 maxValue = formattedDate;
                 maxId = i;
             }
        }
     
     }
    if(listFiles[maxId] == "" ||listFiles[maxId] == "undefined"||listFiles[maxId] == null){
        return false;
    }
    return listFiles[maxId];     
}

function pad(number, length, character) {
    if (character == null) {
        character = "0";
    }
    var numberStr = "" + number;
    while (numberStr.length < length) {
        numberStr = character + numberStr;
    }
    return numberStr;
}
function grabAnimatic(folderLocation){
    newLocation = new Folder(folderLocation);
    animatic = newLocation.getFiles("*.mov");
    sfi = sourcesFolderItem();
    if(animatic[0]){
        try {
            var importOptions = new ImportOptions(animatic[0]);
            animatic = app.project.importFile(importOptions);
            for (i = 1; i <= app.project.numItems ; i++) {
                    if(app.project.item(i).typeName == "Footage"){
                   app.project.item(i).parentFolder = sfi;
                    }    
            }
            return animatic;
        }catch (error){
            return false;
        }
    }
}
function figureOutShotName(filepath){
    folders = filepath.split("/");
    showFolder = folders[folders.length-2];
    showFolder = showFolder.split("_")[0];
    shotFolder = folders[folders.length-1];
    var searcher = new RegExp("[0-9]{2,}$");
    var currentResult = searcher.exec(shotFolder);
    if (currentResult){
        shotFolder = currentResult[0];
    }
    return showFolder+"_SC"+shotFolder;
}

function createMainComp(path){
    grabAnimatic(path);
    shotName = figureOutShotName(path);
    workComp = app.project.items.addComp(shotName+"_T1", 1920, 1080,1.0 ,animatic.duration,25.0);
    animaticLayer = workComp.layers.add(animatic);
    animaticLayer.guideLayer = true;
    return workComp;
}

function returnList2(type){
     if(type == "show"){
        rArray = returnFolderArray(rootFolder);
        for(i=0;i<rArray.length;i++){
            rArray[i] = rArray[i].name;
            }
    }else if(type == "scene"){
        rArray = ["a","b"];
        alert(dPanel.shotLoad.showDDL.selection.toString());

    }
    return rArray;
}



function returnList(folder){
    rArray = returnFolderArray(folder);
    for(i=0;i<rArray.length;i++){
            rArray[i] = rArray[i].name;
    }
    return rArray;
}

function findMainShot(){
    //fix GB##_SC_###_T1
    var findShot = new RegExp("^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
    for(i=1;i<=app.project.numItems;i++){
        if(app.project.items[i] instanceof CompItem){
           var movieFileResult =  findShot.exec(app.project.items[i].name);
           if(movieFileResult){
               nuName = app.project.items[i].name;
               nuName = nuName.split("SC_");
               app.project.items[i].name = nuName[0]+"SC"+nuName[1];
            }
        }        
    }
    
    var findShot = new RegExp("^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
    var shot = null;
    for(i=1;i<=app.project.numItems;i++){
        if(app.project.items[i] instanceof CompItem){
           var movieFileResult =  findShot.exec(app.project.items[i].name);
           if(movieFileResult){
               shot = app.project.items[i];
               break;
               }
        }
    }
    if(shot == null){
        alert("Could not find a comp named like \"GB##_SC###(a-z)_T#\"\n\nRename main comp accordingly, render again");
        return false;
    }
    return shot;
}


function dandyShotBuilderUI(thisObj){
    
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window("palette", "DandyShotBuilderFix", [100, 100, 300, 300]);
        var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
        if (securitySetting != 1) {
           var msg = "This script requires the scripting security preference to be set. \nGo the \"General\" panel of your application preferences and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.\n\n Restart AFX";
           var shotInfo = dPanel.add("edittext",[10,5,235,305],msg,{multiline:true}); 
         }else{
      var shotLoad = dPanel.add("panel",[10,5,235,175],"Load Shot");
         var shotLoadTxt1 = shotLoad.add("statictext",[15,24,52,44],"Show:");
         var showDDL = shotLoad.add ("dropdownlist", [56,19,210,44], returnList(rootFolder));
            if (app.settings.haveSetting("dandybuildPrefs", "show")){
                 showDDL.selection = showDDL.find(app.settings.getSetting("dandybuildPrefs","show"));
                 //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
                }else{
                 showDDL.selection = 0;
                 app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
                }
         var shotLoadTxt2 = shotLoad.add("statictext",[12,59,52,79],"Scene:");
         var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+app.settings.getSetting("dandybuildPrefs", "show")));
            if (app.settings.haveSetting("dandybuildPrefs", "shot")){
                 shotDDL.selection = shotDDL.find(app.settings.getSetting("dandybuildPrefs","shot"));
                 //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
                }else{
                 shotDDL.selection = 0;
                }
         
          
         var shotStatusTxt = shotLoad.add("statictext",[25,95,52,115],"File:");
         var shotStatusEditTxt = shotLoad.add("edittext",[56,92,210,115],"...");
            shotStatusEditTxt.active = false;
            shotStatusEditTxt.enabled = false;
         var shotLoadBtn = shotLoad.add("button",[56,125,96,150],"Load");
            shotLoadBtn.enabled = false;
         var shotBuildBtn = shotLoad.add("button",[101,125,141,150],"Build");
            shotBuildBtn.enabled = false;
         var shotRefreshBtn = shotLoad.add("button",[146,125,168,150],"r");
         //var shotOptns = shotLoad.add("button",[174,125,210,150],"optns");
            //shotSetBtn.enabled = false;
        
       var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
            shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
            shotToolsBtn1Xtra =  shotTools.add("button",[82,12,102,35],"N:");
            //shotToolsBtn2 =  shotTools.add("button",[107,12,197,35],"Flick Last Take");
            //shotToolsBtn2.enabled = false; 
            shotToolsBtnWF =  shotTools.add("button",[107,12,135,35],"WF");
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};
             shotToolsBtnFE =  shotTools.add("button",[139,12,167,35],"FE");
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
            shotToolsBtnXLS =  shotTools.add("button",[172,12,197,35],"xls");
            //shotToolsBtnXLS.enabled = false;             
                
            shotToolsBtn3 =  shotTools.add("button",[12,40,102,63],"Grab Sources");
            shotToolsBtn4 =  shotTools.add("button",[107,40,135,63],"Miss");
            shotToolsBtnANI =  shotTools.add("button",[139,40,167,63],"ANI");
            shotToolsBtnOPT =  shotTools.add("button",[172,40,197,63],"Opt");
            
              //  shotToolsBtn4.enabled = false;            
            shotToolsBtn5 =  shotTools.add("button",[12,68,102,91],"Renderqueue");
            shotToolsBtn6 =  shotTools.add("button",[107,68,197,91],"To Watchfolder");
                //shotToolsBtn6.enabled = false; 
            }
       var optnsGrp = dPanel.add("panel",[10,5,235,300],"Options");
            optnsGrp.visible = false;
            mgL = 20;
            mgT=15;
            mgBet=3;
            i=0;
            w=210;
            h=23;
            version = optnsGrp.add("statictext",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"version "+version);i++;
            optMissingFootage =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Check for missing footage\rlonger, but safer");i++;
            //$.writeln("load missing footage pref set to"+(app.settings.haveSetting("dandybuildPrefs", "optMissingFootage"))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true);
                optMissingFootage.value = a;
            optNuComp =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"New comp after sending to WF");i++;
                optNuComp.value = b;
            optSaveComp =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Ask to save before sending to WF");i+=2;
                optSaveComp.value = c;
            rootFolderLoc =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Root folder location");i++;
            watchFolderLoc =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Watchfolder location");i+=2;
            okBtn =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"OK");
            

       ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////
       
        showDDL.onChange = function(){
            app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
           // shotLoad.remove(shotDDL);
          shotDDL.removeAll();

          items = returnList(rootFolder+"/"+showDDL.selection.toString());
          //  alert(items);
            for(i in items){
               shotDDL.add("item", items[i]);
            }
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
            shotDDL.selection = 0;
            shotDDL.notify();
        }
        shotDDL.onChange = function(){
           
            app.settings.saveSetting("dandybuildPrefs","shot",shotDDL.selection.toString());
            //$.writeln("Changing");
            var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
            if(!lastAEP){
                shotStatusEditTxt.text = "no shot found, build one!";
                   shotLoadBtn.enabled = false;
                   shotBuildBtn.enabled = true;
            
            }else{
                app.settings.saveSetting("dandybuildPrefs", "loadscene",lastAEP.toString());
                loadFile = lastAEP.toString();
                fullSplitPath = lastAEP.toString().split("/");
                fileName = fullSplitPath[fullSplitPath.length-1];
                if(fileName.length > 20){
                    shotStatusEditTxt.text = fileName.substr(0,12)+"[...]"+fileName.substr(fileName.length-8);
                }else{
                    shotStatusEditTxt.text = fileName;
                } 
                    shotLoadBtn.enabled = true;
                    shotBuildBtn.enabled = false;
            }
        }
        shotLoadBtn.onClick = function(){
            opFile = new File(loadFile);
            if(opFile){
             app.open(opFile);
            }else{
                alert("Can't open file");
            }        
        }
        shotRefreshBtn.onClick = function(){
            shotDDL.notify();
            writeLn("If it doesn't seem to refresh, close and launch again.");
            }
        shotBuildBtn.onClick = function(){
            opFile = new File(rootFolder+"base.aep");
            if(opFile.exists){
                app.open(opFile);
                for(i=1;i<=app.project.numItems;i++){                     
                    app.project.items[i].remove();
                }
            }else{
                alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
            }
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
            createMainComp(targetFolder);
            app.project.bitsPerChannel = 16;
            app.project.items.addFolder("Precomps");
            importFolder = new Folder(targetFolder+"/Source");
            SmartImport(importFolder);
            saveConf = confirm("Save "+shotDDL.selection.toString()+" in the right folder ?",false,"Dandelion Shot Builder");
            if(saveConf){
                saveFile = new File(targetFolder+"/AEP/"+showDDL.selection.toString()+"_"+shotDDL.selection.toString()+"_comp01.aep");
                app.project.save(saveFile);
                shotDDL.notify();
                }
        }
        shotToolsBtn1.onClick = function(){
            explore(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString());
        }
        shotToolsBtn1Xtra.onClick = function(){
            //$.writeln("button");
            p = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
            checkOutFolder(p);
            explore(p);
        }
        shotToolsBtn3.onClick = function(){
            app.project.bitsPerChannel = 16;
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
            importFolder = new Folder(targetFolder+"/Source");
            SmartImport(importFolder);
        }
        shotToolsBtnXLS.onClick = function(){
            loc =  rootFolder+"/"+showDDL.selection.toString();
            fold = new Folder(loc.toString());
            files = fold.getFiles("*.xls*");
            if(files.length > 0){
                files[0].execute();
            }else{
                writeLn("No .xls shortcut found in in:");
                writeLn(showDDL.selection.toString());
            }
        }
       shotToolsBtnANI.onClick = function(){
            loc =  rootFolder+"/"+showDDL.selection.toString();
            fold = new Folder(loc.toString());
            files = fold.getFiles("*.mov*");
            if(files.length > 0){
                files[0].execute();
            }else{
                writeLn("No .mov shortcut found in in: ");
                writeLn(showDDL.selection.toString());
            }
        }
        shotToolsBtnOPT.onClick = function(){
            shotTools.visible = false;
            shotLoad.visible = false;
            optnsGrp.visible = true;
        }
    okBtn.onClick = function(){
            shotTools.visible = true;
            shotLoad.visible = true;
            optnsGrp.visible = false;
           
            app.settings.saveSetting("dandybuildPrefs", "optMissingFootage",optMissingFootage.value);
            app.settings.saveSetting("dandybuildPrefs", "optNuComp",optNuComp.value);
            app.settings.saveSetting("dandybuildPrefs", "optSaveComp",optSaveComp.value);
           // $.writeln("new comp checked: "+app.settings.getSetting("dandybuildPrefs", "optNuComp"));
    }
        
        //send to renderqueue
        //uses the comp name to figure out folder

                   
        shotToolsBtn5.onClick = function(){
        rflag = false;
        //$.writeln("missing fottage "+app.settings.getSetting("dandybuildPrefs", "optMissingFootage"));
        if(app.settings.getSetting("dandybuildPrefs", "optMissingFootage") == "true"){
            rflag = checkForMissingFootage(false);
        }
        if(!rflag){
            rcomp = findMainShot();
                if(rcomp){
                    shotNumber = getSetTake();
                    if(shotNumber != false){
                        //$.writeln(shotNumber);
                        setTake(shotNumber);
                        rq = app.project.renderQueue;
                        for(i=1;i<=rq.numItems;i++){    
                                rq.items[i].render = false;
                         }
                        rqitem = app.project.renderQueue.items.add(rcomp);
                        rqitem.outputModules[1].applyTemplate("TiffOutput");
                        rqitem.applyTemplate("Multi-Machine Settings");
                        //savePath = rootFolder+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/OUT/"+rcomp.name;
                        savePath = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/"+rcomp.name;
                        
                        
                        if(checkOutFolder(savePath)){
                           rqitem.outputModules[1].file = new File(savePath+"/"+rcomp.name+"_[#####].tif");
                        }else{
                           alert("Couldn't figure out an output folder for \""+(rcomp.name)+"\", select a folder manually.");
                          rqitem.outputModules[1].file = new File();
                        }
                    }
                }
            }else{
                alert("This project is missing source files and will not render. \nUse the 'miss' button to find out where the footage should be");
            }
        }
        
        shotToolsBtn4.onClick = function(){
            //shotNumber = getSetTake(true);
            //setTake(shotNumber);
            checkForMissingFootage(true);
        }
        
        shotToolsBtn6.onClick = function(){
           wf = new Folder(watchfolderLocation);
           if(wf.exists){
               sendToWatchFolder(wf,findMainShot().name);
               }else{
                   alert("Watchfolder error, sorry!");
               }
        }
            
    
    
    
    
    
    rootFolderLoc.onClick = function(){
        y = confirm("Root folder is currently set to \n\n\""+rootFolder+"\"\n\nChange it ?");
        if(y){
            //v = new Folder();
            v = Folder.selectDialog ("New root folder location").toString();
            if(v!=null && v!="undefined"){
                app.settings.saveSetting("dandybuildPrefs", "rootfolder",v);     
                rootFolder = v;
                          showDDL.removeAll();
                          items = returnList(rootFolder);
          //  alert(items);
            for(i in items){
               showDDL.add("item", items[i]);
            }
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
            shotDDL.selection = 0;
            shotDDL.notify();
                showDDL.notify();
                shotDDL.notify();
                //$.writeln("notified");
            }
        }
    }
    watchFolderLoc.onClick = function(){
        y = confirm("Watchfolder is currently set to \n\n\""+watchfolderLocation+"\"\n\nChange it ?");
        if(y){
            //v = new Folder();
            v = Folder.selectDialog ("New watchfolder location").toString();
            if(v!=null && v!="undefined"){
                app.settings.saveSetting("dandybuildPrefs", "watchfolderLocation",v);     
                watchfolderLocation = v;
            }
            
        }
    }
    shotDDL.notify();
    }
    dandyShotBuilderUI(this) ;
   


layer Position to .txt

G5DX1T0.gif Old, might not work! (but should :)

// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
//
// The script will write to a text file the x & y values of the layer position for every frame comprised in
// a selection of keyframes, or for the whole comp duration if no keyframes are selected
//
//
function timeToFrameNum(myTime){
   return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
}
function framesToTime(myTime){
   return myTime/app.project.activeItem.frameRate;
}
function timeToTimeCode(myTime){
   var framesN = myTime * app.project.activeItem.frameRate;
   fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));
   ho = addZero(Math.floor(myTime/3600));
   mi = addZero(Math.floor(myTime/60)-ho*60);
   se = addZero(Math.floor(myTime)-mi*60-ho*3600);
   return ho+":"+mi+":"+se+":"+fr;
}
function addZero(val){
   if(val<10){
      val = "0"+val;
   }
   return val;
}
var myDisp = app.project.timecodeDisplayType;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;
var pText = "Choose an output format using:\n%f (framenumber),%i (index, starts at 0) %t (SMTPE timecode), %x (x value), %y (y value), %l (linebreak) and any other character. Default output looks like '16: 230;22'";

if(app.project.activeItem != "null" && app.project.activeItem != null && app.project.activeItem != 0){
   if(app.project.activeItem.selectedLayers.length != 0){
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
         curLayer = app.project.activeItem.selectedLayers[0];
         var textName = "AEcoordinates.txt";
         var myTextFile = filePutDialog("Select a location to save your .txt file", textName, "TEXT txt");
         if(myTextFile == null){
            alert("You must choose a place to save the file");
         }else{
            var formatString = prompt(pText,"%i: %x;%y%l");
            myTextFile.open("w","TEXT","????");
            myKeys = curLayer.property("position").selectedKeys;
            if(myKeys.length != 0){
               strTime = curLayer.property("position").keyTime(myKeys[0]);
               endTime = curLayer.property("position").keyTime(myKeys[myKeys.length-1]);
            }else{
               strTime = app.project.activeItem.workAreaStart;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;
            }
            var startLoop = timeToFrameNum(strTime);
            var endLoop = timeToFrameNum(endTime);
            for(i=startLoop;i<=endLoop;i++){
               var curTime = framesToTime(i);
               out = formatString.replace('%x',curLayer.property("position").valueAtTime(curTime,true)[0]);
               out = out.replace('%y',curLayer.property("position").valueAtTime(curTime,true)[1]);
               out = out.replace('%f',i);timeToTimeCode
               out = out.replace('%t',timeToTimeCode(curTime));
               out = out.replace('%i',i-startLoop);
               out = out.replace('%l','\n');
               myTextFile.write(out);
               clearOutput();
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+"% done...");
            }
         clearOutput();
         writeLn(endLoop-startLoop+" positions saved to file");
         writeLn("script written by mlk =)");
         myTextFile.close();
         }
      } else {
         alert ("This script requires the scripting security preference to be set.\n" +
         "Go to the \"General\" panel of your application preferences,\n" +
         "and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.");
      }

   }else{
      alert("Select a layer with 'position' keyframes");
   }
}else{
   alert("Select a composition and a layer with 'position' keyframes");
}
app.project.timecodeDisplayType = myDisp;

Other Scripts

Work specific

Auto scale precomps

Script that precomposes animation layers (specifically that are in .swf):

  • scales up the layer by X amount
  • scales down the precomp by 1/x amount
  • Toggles the continuously rasterize off on the precomp, on inside.

This specifically solves the problem that lines out of Animate/Flash are 'rough' (aliased) when imported at 100%, no matter which flags are toggled. Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)

Usage: save as .jsx, run the script via File>Scripts (Ctrl-Alt-Shift-D will redo latest script to repeat the process). If no layers are selected, the script will instead prompt which scale factor it should use (4 by default).

// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how
(function afterEffectsSmartScale() {
	
    var SCRIPT_PREF_KEY = "SmartScaleFactor";
	var SCRIPT_NAME = "SmartScaleTool";
    var defaultScale = 4; 
    var comp = app.project.activeItem;
    var selectedLayers = comp.selectedLayers;
    var scaleFactorX;

    function getScaleFactor(set) {
		var factor = defaultScale;
		if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){
			factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);
		}
		if(set){
			var promptResult = prompt("Enter the scale factor (default is 400% -> 4)\nThen select layer(s) and run again.", factor);
			if (promptResult === null) return null; 
			factor = parseFloat(promptResult);
			app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);
		}

        return factor;
    }

    if (selectedLayers.length === 0) {
        scaleFactorX = getScaleFactor(1);
        return;
    }

    scaleFactorX = getScaleFactor();
    if (scaleFactorX === null) return;

    var scaleMultiplier = scaleFactorX;
    var inverseMultiplier = 1 / scaleFactorX;

    app.beginUndoGroup("Smart Scale Precomposition");

    try {
        for (var i = 0; i < selectedLayers.length; i++) {
            var layer = selectedLayers[i];
            
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + "_Scale", false);
            var precompLayer = app.project.activeItem.selectedLayers[0];
			precompLayer.collapseTransformation = false;

            // 2. Inside the precomposition, scale the layer and composition
            if (precomp.numLayers > 0) {
                var innerLayer = precomp.layer(1);
                innerLayer.collapseTransformation = true;
                // Scale the inner layer
                var currentScale = innerLayer.property("ADBE Transform Group").property("ADBE Scale").value;
                innerLayer.property("ADBE Transform Group").property("ADBE Scale").setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);
                
                // Adjust composition size accordingly
                precomp.width *= scaleMultiplier;
                precomp.height *= scaleMultiplier;

                // Adjust the position of the layer within the precomp to keep it centered 
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];
                innerLayer.property("ADBE Transform Group").property("ADBE Position").setValue(newPos);
            }

            // 3. Back in the top composition, scale down the precomposed layer by 1/X
            var currentOuterScale = precompLayer.property("ADBE Transform Group").property("ADBE Scale").value;
            precompLayer.property("ADBE Transform Group").property("ADBE Scale").setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);
        }
    } catch (e) {
        alert("An error occurred: " + e.toString());
    } finally {
        app.endUndoGroup();
    }
})();

Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx

Shelf

AFX Shelf

//to hijack admin and load shelf from my documents/shelf.jsx, this needs to be in the scriptUI Panel folder
{
    var nested_file = new File("~/Documents/shelfBernie.jsx");
	nested_file.open("r");
	//alert(nested_file.read());
	eval(nested_file.read());
	nested_file.close();
}