Difference between revisions of "Afx Javascript"

From bernie's
Jump to navigation Jump to search
 
(5 intermediate revisions by the same user not shown)
Line 1: Line 1:
==Scripts==
==Scripts==
=== Copy footage to local folder as proxy===
=== Contact Sheet from Selected Layers ===
https://i.imgur.com/oZ24pac.png
<pre>
/*
* 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
* 
*
*
*/


screenshot TBD


one-shot from Gemini :0


/*
<pre>
* js-md5 v0.1.2
(function createContactSheet() {
* https://github.com/emn178/js-md5
    var comp = app.project.activeItem;
*
    if (!comp || !(comp instanceof CompItem)) {
* Copyright 2014, emn178@gmail.com
        alert("Please select a composition.");
*
        return;
* Licensed under the MIT license:
    }
* http://www.opensource.org/licenses/MIT
*/


    var selectedLayers = comp.selectedLayers;
    var numLayers = selectedLayers.length;
    if (numLayers === 0) {
        alert("Please select at least one layer.");
        return;
    }


    var cols = Math.ceil(Math.sqrt(numLayers));
    var rows = Math.ceil(numLayers / cols);


/************************************************************************************************************
    var cellWidth = comp.width / cols;
*
    var cellHeight = comp.height / rows;
*
*
*      js-md5.js
*
*
*
************************************************************************************************************/


    app.beginUndoGroup("Create Contact Sheet");


//(function(root, undefined){
    for (var i = 0; i < numLayers; i++) {
  //'use strict';
        var layer = selectedLayers[i];
       
        var col = i % cols;
        var row = Math.floor(i / cols);


  var HEX_CHARS = "0123456789abcdef";
        var layerBounds = layer.sourceRectAtTime(comp.time, false);
  var HEX_TABLE = {
        var layerWidth = layerBounds.width;
    '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
        var layerHeight = layerBounds.height;
    '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,
        if (layerWidth === 0 || layerHeight === 0) continue;
          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,
        var scaleX = (cellWidth / layerWidth) * 100;
          0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,
        var scaleY = (cellHeight / layerHeight) * 100;
          0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,
        var finalScale = Math.min(scaleX, scaleY);
          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) {
        layer.property("Transform").property("Scale").setValue([finalScale, finalScale]);
    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 posX = (col + 0.5) * cellWidth;
    {
        var posY = (row + 0.5) * cellHeight;
      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)
         layer.property("Transform").property("Position").setValue([posX, posY]);
      {
        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) {
     app.endUndoGroup();
     var hex = "";
})();
    for(var i = 0; i < 4; i++)
</pre>
    {
      var offset = i << 3;
      hex += HEX_CHARS.charAt((num >> (offset + 4)) & 0x0F) + HEX_CHARS.charAt((num >> offset) & 0x0F);
    }
    return hex;
  };


  var hasUTF8 = function(message) {
=== Auto Expose Animation ===
    var i = message.length;
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 .
    while(i--)
      if(message.charCodeAt(i) > 127)
        return true;
    return false;
  };


  var ASCIItoBlocks = function(message) {
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.
    // 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) {
<youtube width="1000" height="800" >9-3XVlME2tY</youtube>
    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));


<pre>
//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 =
*      simpleLocalProxy.jsx
    "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(str){
    //uncomment to allow debugging
    //$.writeln(str);
   
}


function pathToLocalizedPath(path){
function e(s){
        f = new File(path);
    $.writeln(s);
        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;  
function setupDetector(){
     var match = myRegexp.exec(path);
     app.beginUndoGroup("Auto expose setup Detector");
     if(!match){
     var layers = app.project.activeItem.selectedLayers;
        return false;
    curlayer = layers[0];
     }else{
 
        return match;
     var duplicatelayer = curlayer.duplicate();
     }
     curlayer.moveBefore(duplicatelayer);
}
     var futureprecompindex = duplicatelayer.index;
function grabPaths(footage){
     var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+"_anim_detection",true);
    //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 precomplayer = app.project.activeItem.layer(futureprecompindex);
     var f = footage;
     precomplayer.guideLayer = true;
     returnpath = false;
 
     if(f instanceof FootageItem && f.file != null){     
     var allLayers = app.project.activeItem.layers;
        var source = f.mainSource.file.toString();
    for(i=1;i<=allLayers.length;i++){
        if(!f.mainSource.isStill){
        if(allLayers[i].enabled){
            var pathFromRegex = sequenceFilesWildcard(source);
            allLayers[i].solo = false;
            if(pathFromRegex){
                returnpath = pathFromRegex[1]+"*"+pathFromRegex[2];
            }else{
                returnpath = source;
            }
        }else{
            returnpath = source;
         }
         }
     }
     }
     return returnpath;
     precomplayer.enabled = true;  
}
    precomplayer.solo = true;


function grabFootagePathsAndCopyToLocal(){
     var toplayer = precomp.layers[1];
    //main worker function
     var props = toplayer.property("ADBE Effect Parade");
   
    while(props.numProperties>0){
     var sel = app.project.selection;
         props.property(1).remove();
    if(sel.length < 1 || !(sel[0] instanceof FootageItem)){
    }
        alert("Select footage(s) and try again");  
    var newlayer = toplayer.duplicate();  
     }else{
 
       
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;
        var debugcheck = (getPref("debug","false")=="true")?true:false;
    newlayer.startTime += app.project.activeItem.frameDuration;
        var useforcecopy = (getPref("forcecopy","false")=="true")?true:false;
    newlayer.timeRemapEnabled = true;
          
    newlayer.inPoint -= app.project.activeItem.frameDuration;
        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);
    var explainer = new MarkerValue("1 frame shift + difference blendmode = highlight pixel changes");
            txtFile.close();
    newlayer.property("Marker").setValueAtTime(.5, explainer);
    if(isMacintosh){


            system.callSystem("chmod +x " + txtFile.toString() +"");
     var blackSolid = precomp.layers.addSolid([0,0,0], "Black", precomp.width, precomp.height, 1);
system.callSystem(txtFile.toString());
    blackSolid.moveToEnd();
}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) {
     var sliderctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
    pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Simple Local Proxy", [100, 100, 300, 300]);
     sliderctrl.name = "Resolution";
   
     var resolutionslider = sliderctrl.property("ADBE Slider Control-0001");
     var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
 
     if (securitySetting != 1) {
    resolutionslider.setValue(3);
        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
    var detectorctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
       
    detectorctrl.name = "Detector";
        pan.grp = pan.add(res);  
    var detectorslider = detectorctrl.property("ADBE Slider Control-0001");
         pan.layout.layout(true);
    detectorexpression = "\
          
    resolution = effect(\"Resolution\")(\"ADBE Slider Control-0001\");\
        //radio buttons and checkboxes prefs
    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;\
    ";


        var usemd5 = getPref("usemd5",true);
    detectorslider.expression = detectorexpression;
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5=="true")?true:false;
    currentSlider = detectorslider;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5=="true")?false:true;
    app.endUndoGroup();
}


        var useforcecopy = getPref("forcecopy","false");
function bakeKeys(){
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy=="true")?true:false;
   
        var debugcheck = getPref("debug","false");
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to 'clean' expression
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck=="true")?true:false;
    bakeCommand = app.findMenuCommandId("Convert Expression to Keyframes");
 
    currentSlider.selected = true;
        pan.layout.resize();
    app.executeCommand( bakeCommand );
        pan.onResizing = pan.onResize = function () {this.layout.resize();}
    detectorexpression = "f = effect('Detector')('ADBE Slider Control-0001');\nf>0?1:0;";
 
    currentSlider.expression = detectorexpression;
        // UI ACTIONS
    currentSlider.expressionEnabled = true;
       
    currentSlider.selected = true;
        //browse button
    app.executeCommand( bakeCommand );
        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();
    // 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);
         }
         }
     }
     }
    if (pan instanceof Window) pan.show() ;
}
simpleLocalProxy(this);
</pre>


=== Auto Expose Animation ===
    // add '0' key on first frame
Adds 'hold' keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD.
   
    currentSlider.addKey(0);
    currentSlider.setValueAtKey(1, 0);
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);


MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.
}
 
function applyKeys(){
<youtube width="1000" height="800" >9-3XVlME2tY</youtube>
    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;
    }
}




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


</pre>


function watchFolderUI(thisObj){
=== Create Null controllers on Puppet pins ===
    pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Auto Expose", [100, 100, 300, 300]);
http://i.imgur.com/HdFjaYZ.jpg
    var res =  
<pre>
    "group { \
//nulls created from puppet pins can be parented like normal layers
                alignment: ['fill','fill'], \
{
                alignChildren: ['fill','top'], \
    app.beginUndoGroup("Create Null Controls on Puppet Pinsv");
                orientation: 'column', \
 
                    setupDetector: Button {text: 'Step 1: setup detector on layer' ,preferredSize:[-1,30]} , \
     function getLayerFromProperty(prop){
                    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}} , \
         return prop.propertyGroup(prop.propertyDepth)
                    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();
     var c = app.project.activeItem;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}
    if( c != null && c.selectedProperties != null){
    return pan;
        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;
            }
        }
     }
     }
watchFolderUI(this) ;
     app.endUndoGroup();
 
 
 
function e(s){
     $.writeln(s);
}
}
</pre>
=== Batch replace file locations with text file===
http://i.imgur.com/88QHvlQ.jpg


 
<pre>
 
//now works with sequences or still images, windows only
function setupDetector(){
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path
    app.beginUndoGroup("Auto expose setup Detector");
function URIToWinPath(path){
    var layers = app.project.activeItem.selectedLayers;
str = path.replace(/\//, "");
    curlayer = layers[0];
str = str.replace(/\//, ":/");
 
str = str.replace(/%20/g, " ");
     var duplicatelayer = curlayer.duplicate();
str = str.replace(/\//g, "\\");
     curlayer.moveBefore(duplicatelayer);
return str;
     var futureprecompindex = duplicatelayer.index;
}
     var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+"_anim_detection",true);
function WinPathtoURI(path){
     var precomplayer = app.project.activeItem.layer(futureprecompindex);
    //windows, for now, the only one available!
     precomplayer.guideLayer = true;
     str = "/"+path.replace(":\\", "/");
 
     str = str.replace(/\\/g, "/");
     var allLayers = app.project.activeItem.layers;
     str = str.replace(/ /g, "%20");
     for(i=1;i<=allLayers.length;i++){
     //str = str.substring(0,str.lastIndexOf("/"));
        if(allLayers[i].enabled){
    return str;
             allLayers[i].solo = false;
}
{
    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");
    precomplayer.enabled = true;  
        txtFile.write(txt);
    precomplayer.solo = true;
        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");


    var toplayer = precomp.layers[1];
          for(i=0;i<app.project.selection.length;i++){
    var props = toplayer.property("ADBE Effect Parade");
                var tmpFile = new File(WinPathtoURI(arrayContents[i+2]));
    while(props.numProperties>0){
                //alert(tmpFile);
        props.property(1).remove();
                if(isSequence[i]){
                    sel[i].replaceWithSequence(tmpFile,0);
                }else{
                    sel[i].replace(tmpFile);
                }
               
                writeLn(Math.round((i+1)/app.project.selection.length*100)+"%");
          }
        }
     }
     }
     var newlayer = toplayer.duplicate();  
     app.endUndoGroup();
}
</pre>


    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;
===Loop Selected Layers===
    newlayer.startTime += app.project.activeItem.frameDuration;
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif
    newlayer.timeRemapEnabled = true;
    newlayer.inPoint -= app.project.activeItem.frameDuration;


    var explainer = new MarkerValue("1 frame shift + difference blendmode = highlight pixel changes");
No hassle looping
    newlayer.property("Marker").setValueAtTime(.5, explainer);


     var blackSolid = precomp.layers.addSolid([0,0,0], "Black", precomp.width, precomp.height, 1);
<pre>
    blackSolid.moveToEnd();
app.beginUndoGroup("Set loops");
 
var layersList = app.project.activeItem.selectedLayers;
 
var frameD = app.project.activeItem.frameDuration;
    var sliderctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
for (i=0;i<layersList.length;i++){
    sliderctrl.name = "Resolution";
     if(!layersList[i].timeRemapEnabled){
    var resolutionslider = sliderctrl.property("ADBE Slider Control-0001");
        var outP = layersList[i].outPoint;
 
        layersList[i].timeRemapEnabled = true;
    resolutionslider.setValue(3);
        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();
</pre>


    var detectorctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
[[AFX Shelf]]
    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;
===Find Next Text Layer===
     currentSlider = detectorslider;
<pre>
     app.endUndoGroup();
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");
}
</pre>


function bakeKeys(){
===Simple Watchfolder===
   
http://i.imgur.com/poTWO.png
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to 'clean' expression
https://i.imgur.com/wvtgDqE.png
    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
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
    // 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
<pre>
   
/*
    currentSlider.addKey(0);
WatchFolder.js v1.2
    currentSlider.setValueAtKey(1, 0);
--------------------
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
By bernie @ berniebernie.fr


}
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder
function applyKeys(){
In essence it's a simplified Collect Files > Project only.
    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;
    }
}


Windows only.


}


</pre>
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)


=== Create Null controllers on Puppet pins ===
*/
http://i.imgur.com/HdFjaYZ.jpg
<pre>
//nulls created from puppet pins can be parented like normal layers
{
{
     app.beginUndoGroup("Create Null Controls on Puppet Pinsv");
     watchfolderLocation = "none";
    watchfolderLocation = (app.settings.haveSetting("watchfolderPrefs", "watchfolderLocation")) ? (app.settings.getSetting("watchfolderPrefs", "watchfolderLocation")) : watchfolderLocation;


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




     var c = app.project.activeItem;
     function sendToWF(wf) {
    if( c != null && c.selectedProperties != null){
var m=0;
        var props = c.selectedProperties;
         for(i=1;i<=app.project.numItems;i++){
        j = 0;
             if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
         for(i = 0;i<props.length;i++){
                            if(app.project.items[i].footageMissing){
             if(props[i].matchName == "ADBE FreePin3 PosPin Atom"){
                                m++;
                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;
             }
             }
         }
         }
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...");
}
     }
     }
    app.endUndoGroup();
}
</pre>
=== Batch replace file locations with text file===
http://i.imgur.com/88QHvlQ.jpg


<pre>
    function setWatchFolder() {
//now works with sequences or still images, windows only
        var tmpfile = new File(String(Folder.desktop) + "/save this temp file with any name in the watchfolder");
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path
        var selectedFolder = tmpfile.saveDlg('Select Watchfolder Location');
function URIToWinPath(path){
        if (selectedFolder) {
str = path.replace(/\//, "");
 
str = str.replace(/\//, ":/");
            app.settings.saveSetting("watchfolderPrefs", "watchfolderLocation", selectedFolder.path);
str = str.replace(/%20/g, " ");
            watchfolderLocation = selectedFolder.path;
str = str.replace(/\//g, "\\");
            return true;
return str;
        } else {
}
            return false;
function WinPathtoURI(path){
        }
    //windows, for now, the only one available!
 
    str = "/"+path.replace(":\\", "/");
    }
    str = str.replace(/\\/g, "/");
 
    str = str.replace(/ /g, "%20");
    function startWatchingFolder() {
    //str = str.substring(0,str.lastIndexOf("/"));
        aerenderExe = '"' + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + '\\AfterFX.exe" -m -re -wf "' + Folder(watchfolderLocation).fsName + '"';
    return str;
        batch = new File(Folder.desktop.toString() + "/launch_WatchFolder.bat");
}
        if (batch.open("w", "TEXT", "????") == true) {
{
            batch.write("@echo off\n");
    app.beginUndoGroup("Change File Locations");
            batch.write(aerenderExe);
   
            batch.close();
    var txtFile = new File("~/Desktop/tempAE.txt");
            batch.execute();
    txtFile.open("w","TEXT","????");
        } else {
    txt = "";
             alert("unable to launch the AfterEffects worker");
    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]));
    function startAERender() {
                //alert(tmpFile);
//to add: multiframe rendering
                if(isSequence[i]){
        aerenderExe = '"' + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + '\\aerender.exe" -continueOnMissingFootage -project "' + File(app.project.file).fsName + '"';
                    sel[i].replaceWithSequence(tmpFile,0);
        batch = new File(Folder.temp.toString() + "/launch_aerender.bat");
                }else{
        if (batch.open("w", "TEXT", "????") == true) {
                    sel[i].replace(tmpFile);
            batch.write("@echo off\n");
                }
            batch.write(aerenderExe);
               
    batch.write("\nPAUSE");
                writeLn(Math.round((i+1)/app.project.selection.length*100)+"%");
            batch.close();
          }
            batch.execute();
        } else {
            alert("unable to launch the aerender.exe");
         }
         }
     }
     }
    app.endUndoGroup();
}
</pre>


===Loop Selected Layers===
    function updateUI(dialog) {
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif


No hassle looping
    }


<pre>
     function watchFolderUI(thisObj) {
app.beginUndoGroup("Set loops");
         var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
var layersList = app.project.activeItem.selectedLayers;
         if (securitySetting != 1) {
var frameD = app.project.activeItem.frameDuration;
            alert("You need to check 'Allow Scripts to Write Files and Access Network' in your preferences for this script to work");
for (i=0;i<layersList.length;i++){
         } else {
     if(!layersList[i].timeRemapEnabled){
 
         var outP = layersList[i].outPoint;
 
        layersList[i].timeRemapEnabled = true;
 
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);
            var panelGlobal = thisObj;
         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();
</pre>


[[AFX Shelf]]


===Find Next Text Layer===
             /*
<pre>
             Code for Import https://scriptui.joonas.me
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");
}
</pre>


===Simple Watchfolder===
            // DIALOG
http://i.imgur.com/poTWO.png
            // ======
https://i.imgur.com/wvtgDqE.png
            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";


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
            dialog.alignChildren = ["fill", "top"];
            dialog.spacing = 10;
            dialog.margins = 16;


<pre>
            var grp = dialog.add("group", undefined, {
/*
                name: "group0"
WatchFolder.js v1.2
            });
--------------------
            grp.alignement = ["fill", "fill"];
By bernie @ berniebernie.fr
            grp.alignChildren = ["fill", "top"];
            grp.orientation = "column";


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.
            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
            });




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
            var group1 = grp.add("group", undefined, {name: "group1"});
- 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.
            group1.orientation = "row";
- set some WF options (flags)
            group1.alignChildren = ["fill", "top"];
            group1.spacing = 10;
            group1.margins = 0;


*/
            var setWF = group1.add("button", undefined, undefined, {name: "setWF"});
{
            setWF.text = "Set WatchFolder";
    watchfolderLocation = "none";
 
    watchfolderLocation = (app.settings.haveSetting("watchfolderPrefs", "watchfolderLocation")) ? (app.settings.getSetting("watchfolderPrefs", "watchfolderLocation")) : watchfolderLocation;
            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";


    function sendToWF(wf) {
            var sendAER = grp.add("button", undefined, undefined, {name: "sendAER"});
var m=0;
            sendAER.helpTip = "local cmdline render";
        for(i=1;i<=app.project.numItems;i++){
            sendAER.text = "render local aerender.exe batch";
            if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
 
                            if(app.project.items[i].footageMissing){
            var group2 = grp.add("group", undefined, {name: "group2"});
                                m++;
            group2.orientation = "row";
                            }
            group2.alignChildren = ["fill", "top"];
            }
            group2.spacing = 10;
        }
            group2.margins = 0;
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 launchWorker = group2.add("button", undefined, undefined, {name: "launchWorker"});
        var tmpfile = new File(String(Folder.desktop) + "/save this temp file with any name in the watchfolder");
            launchWorker.helpTip = "Saves a .bat file to your desktop that launches a worker";
        var selectedFolder = tmpfile.saveDlg('Select Watchfolder Location');
            launchWorker.text = "Launch a worker";
        if (selectedFolder) {
    launchWorker.enabled = (watchfolderLocation=="none")?false:true;


            app.settings.saveSetting("watchfolderPrefs", "watchfolderLocation", selectedFolder.path);
            watchfolderLocation = selectedFolder.path;
            return true;
        } else {
            return false;
        }


    }
            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;


    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");
        }


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


    function updateUI(dialog) {


    }
    if (watchfolderLocation != "none") {
                curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
}


    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 {


            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);
                }
            }


            var panelGlobal = thisObj;
        }
 
    }
    watchFolderUI(this);
}
</pre>


            /*
===Show original source location (WIN only)===
            Code for Import https://scriptui.joonas.me
http://i.imgur.com/04O3r.png
            */
<pre>
{
    function pathToWinPath(path){
        var str = path.toString().replace(/\//, "");
        str = str.replace(/\//, ":/");
        str = str.replace(/%20/g, " ");
        str = str.replace(/\//g, "\\");
        return str;
    }


            // DIALOG
    sel = app.project.selection;
            // ======
    if(sel.length == 0){
             var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window("palette", "Simple Watchfolder", [100, 100, 300, 300]);
        alert("You need to select source(s) in the project panel");
             if (!(panelGlobal instanceof Panel)) dialog.text = "Simple Watchfolder";
    }else{
            dialog.orientation = "column";
        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>


            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", {
===List selected layers effects and their properties matchNames ===
                multiline: true
<pre>
            });
//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 group1 = grp.add("group", undefined, {name: "group1"});
    var col = app.project.activeItem.selectedLayers;
            group1.orientation = "row";
    for (i=0;i<col.length;i++){
            group1.alignChildren = ["fill", "top"];
        effs = col[i].property("ADBE Effect Parade");
            group1.spacing = 10;
             for(j=1;j<=effs.numProperties;j++){
            group1.margins = 0;
                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")


             var setWF = group1.add("button", undefined, undefined, {name: "setWF"});
                    }
            setWF.text = "Set WatchFolder";
             }
    }


            var openWF = group1.add("button", undefined, undefined, {name: "openWF"});
    txtFile.write("\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n");
    openWF.enabled = (watchfolderLocation=="none")?false:true;  
    txtFile.write("v = s.Effects.addProperty(\"CC RepeTile\");\n");
            openWF.text = "Open WF";
    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 curWF = grp.add("statictext", undefined, undefined, {name: "statictext2"});
==== Disable effects ====
            curWF.helpTip = "shows current watchfolder";
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!
            curWF.text = "(choose watchfolder)";
<pre>
(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 sendWF = grp.add("button", undefined, undefined, {name: "sendWF"});
    var selectedEffect = null;
            sendWF.helpTip = "save file first!";
            sendWF.text = "Send .aep to Watchfolder";


            var sendAER = grp.add("button", undefined, undefined, {name: "sendAER"});
    // Find the selected effect property group
             sendAER.helpTip = "local cmdline render";
    for (var i = 0; i < sel.length; i++) {
             sendAER.text = "render local aerender.exe batch";
        if (sel[i].matchName && sel[i].parentProperty && sel[i].parentProperty.matchName === "ADBE Effect Parade") {
             selectedEffect = sel[i];
             break;
        }
    }


            var group2 = grp.add("group", undefined, {name: "group2"});
    if (!selectedEffect) {
            group2.orientation = "row";
        alert("Please select an effect in the timeline.");
            group2.alignChildren = ["fill", "top"];
        return;
            group2.spacing = 10;
    }
            group2.margins = 0;


            var launchWorker = group2.add("button", undefined, undefined, {name: "launchWorker"});
    var effectName = selectedEffect.matchName;
            launchWorker.helpTip = "Saves a .bat file to your desktop that launches a worker";
    var effectEnabled = selectedEffect.enabled;
            launchWorker.text = "Launch a worker";
    launchWorker.enabled = (watchfolderLocation=="none")?false:true;


    // 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;
                        }
                    }
                }
            }
        }
    }


            var switchToWorker = group2.add("button", undefined, undefined, {name: "switchToWorker"});
    app.endUndoGroup();
            switchToWorker.helpTip = "Turns the current After Effects into a watchfolder worker";
})();
            switchToWorker.text = "Set as worker";
</pre>
            switchToWorker.enabled = (watchfolderLocation=="none")?false:true;


===List effects===


<pre>
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"))
</pre>


            dialog.layout.layout(true);
http://i.imgur.com/hFNiQ.jpg
            dialog.layout.resize();
            dialog.onResizing = dialog.onResize = function() {
                this.layout.resize();
            }


===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


    if (watchfolderLocation != "none") {
                curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
}




            setWF.onClick = function() {
function uniq_fast(a) {
                swf = setWatchFolder();
    var seen = {};
                if (swf) {
    var out = [];
                    curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
    var len = a.length;
    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;
    var j = 0;
                }
    for(var i = 0; i < len; i++) {
            }
        var item = a[i];
            launchWorker.onClick = function() {
        if(seen[item] !== 1) {
                startWatchingFolder();
              seen[item] = 1;
            }
              out[j++] = item;
    switchToWorker.onClick = function() {
        }
app.watchFolder(watchfolderLocation);
    }
    }
    return out;
            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);
                }
            }


        }
function e(s){
    }
     $.writeln(s);
     watchFolderUI(this);
}
}
</pre>
curScript = new File($.fileName);


===Show original source location (WIN only)===
startT = Date.now();
http://i.imgur.com/04O3r.png
<pre>
{
    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;
sourceFolder = app.project.selection[0].mainSource.file.parent;
    if(sel.length == 0){
tmpFolder = Folder.temp;
        alert("You need to select source(s) in the project panel");
tmpBat = Folder.temp.toString()+"/ae_dirlist.bat";
    }else{
tmpFilesList = Folder.temp.toString()+"/ae_imageslist.txt";
        for(i=0;i<app.project.selection.length;i++){
var listFile = new File(tmpFilesList);
            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>






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


===List selected layers effects and their properties matchNames ===
<pre>
//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");
listFile.open("r","TEXT","????");
    txtFile.write("v = s.Effects.addProperty(\"CC RepeTile\");\n");
contents = listFile.read();
    txtFile.write("v.property(\"CC RepeTile-0001\").setValue(10);");  
arrayContents = contents.split("\n");
    txtFile.close();
listFile.close();
    txtFile.execute();
timer = (Date.now()-startT)/1000;
}else{
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");
     alert("Set scripting Prefs to enable to write to disk");
 
 
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);
}
}
</pre>
unique = uniq_fast(arrayContents);


http://i.imgur.com/IEUwe.gif
dialog = confirm(unique.length+" sequences found. Load them ? \nIt might take a long time !",false,"Confirm Loading");


===List effects===
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 {


<pre>
                        app.project.importFile(importOptions);
function projEffects(){
                    } catch (error) {
var effects = new Array();
                      e(error.toString());
var effects2 = new Array();
                    }
for(var i = 1; i <= app.project.numItems; i++){
                } catch (error) {
if(app.project.item(i) instanceof CompItem){
                    e(error.toString());
  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;
}else{
effects[keyName]  = 1;
    writeLn("Canceled");
}
}
 
timer = (Date.now()-startT)/1000;
}
e("after regext: "+timer);
}
</pre>
}
 
for(a in effects){
=== Split to Renderqueue ===
effects2[effects2.length] = a;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif
}
<pre>
effects2.sort();
// Split Layers to Renderqueue v2.0
return effects2;
}
alert(projEffects().join("\n"))
 
</pre>
 
http://i.imgur.com/hFNiQ.jpg
 
===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
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp
//  Access to files network required --- no error checking.
//  using the in and out points. I use this along with 'Magnum' the edit detector to split & render sequences.
//   > only works on windows so far
//
//   > only finds exrs/pngs/jpgs
// 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
//  > probably fails if your sequences dont have the same start frame
//  > will work with /path/image.####.jpg or similar


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");


function uniq_fast(a) {
if(app.project.renderQueue.items.length == 1){
     var seen = {};
     FE = app.project.renderQueue.items[1];
    var out = [];
     ai = FE.comp;
     var len = a.length;
     path = FE.outputModule(1).file.path;
     var j = 0;
     for(i=1;i<=ai.layers.length;i++){
     for(var i = 0; i < len; i++) {
 
        var item = a[i];
        RI = app.project.renderQueue.items[1].duplicate();
        if(seen[item] !== 1) {
        RI.timeSpanStart = ai.layers[i].inPoint;
              seen[item] = 1;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;
              out[j++] = item;
        $.writeln(ai.workAreaDuration+" "+ai.layers[i].outPoint);
        }
        RI.outputModule(1).file =  new File(path+"/"+pad(i,2,"0")+"_"+ai.layers[i].name);
     }
     }
     return out;
     app.project.renderQueue.items[1].render= false;
}else{
    alert("Only 1 element should be in renderqueue");
}
}
app.endUndoGroup;
</pre>


 
=== Docked Panel SNIP ===
function e(s){
<pre>
    $.writeln(s);
//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();
}
</pre>
<pre>
//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;
}
}
curScript = new File($.fileName);
var myToolsPanel = createUI(this);
</pre>


startT = Date.now();
===Watchfolder watcher (WIP)===
 
http://i.imgur.com/lhO3k.gif
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);


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)
<pre>
//  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 batFile = new File(tmpBat);
{
batFile.open("w","TEXT","????");
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__";
batFile.write("REM Auto generated by this after effects script file: "+curScript.fsName);
var wf = app.settings.haveSetting("watchwatchfolder", "wfloc")?new Folder(app.settings.getSetting("watchwatchfolder", "wfloc")):null;
batFile.write("\npushd \""+sourceFolder.fsName+"\"");
var scriptFile = new File($.fileName);
batFile.write("\ndir /b /on *.exr *.png *.jpg *.jpeg > \""+listFile.fsName+"\"");
var scriptFolder = scriptFile.parent; //png icons should be here
batFile.close();
b=0; //global string that will store the
system.callSystem(batFile.fsName);
var cancelTaskID;
var pal;
var timer = 0;
var refreshRate = 5;  //seconds
var reloadEditTxt;
var firstTime = true;
var shotinfos = new Array();


 
//global ui names
listFile.open("r","TEXT","????");  
var refreshBtn;
contents = listFile.read();
var list;
arrayContents = contents.split("\n");
var wfStText;
listFile.close();
var wfPbar;
timer = (Date.now()-startT)/1000;
var chckBox0;
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");
var chckBox1;
var chckBox2;
var chckBox3;




var myRe = new RegExp("[0-9]{2,}(\)){0,1}\.[a-z]+$");
function getLogonly(file){
var myArray = myRe.exec(arrayContents[0]);
    if(file.name.indexOf("Logs)") != -1){
endlength = myArray[0].length;
        return true;
 
    }
for(i = 0;i<arrayContents.length;i++){
        return false;
    arrayContents[i] = arrayContents[i].slice(0, -endlength);
}
function pathToWinPath(path){
str = path.replace(/\//, "");
str = str.replace(/\//, ":/");
str = str.replace(/%20/g, " ");
str = str.replace(/\//g, "\\");
return str;
}
}
unique = uniq_fast(arrayContents);
function localToRessource(path){
 
    //windows, for now, the only one available!
dialog = confirm(unique.length+" sequences found. Load them ? \nIt might take a long time !",false,"Confirm Loading");
    str = "/"+path.replace(":\\", "/");
 
    str = str.replace(/\\/g, "/");
if(dialog){    
    str = str.replace(" ", "%20");
     for(i = 0;i<unique.length;i++){
    str = str.substring(0,str.lastIndexOf("/"));
         if(unique[i].length>0){
    return str;
             sequenceStartFile = new File(sourceFolder.toString()+"/"+unique[i]+myArray[0]);
}
              
function returnFolderArray(location){
            if (sequenceStartFile) {
    fold = new Folder(location.toString());
            writeLn("Loading "+(i+1)+"/"+unique.length);
    foldArray = fold.getFiles()
                try {
    folderOnly = new Array();
                    // Create a variable containing ImportOptions.
     for(i=0;i<foldArray.length;i++){
                    var importOptions = new ImportOptions(sequenceStartFile);
         if(foldArray[i] instanceof Folder){
                    importOptions.sequence = true;
             folderOnly[folderOnly.length] = foldArray[i];
                    try {
             }
 
        }
                        app.project.importFile(importOptions);
    return folderOnly;
                    } catch (error) {
}
                      e(error.toString());
function logInfo(logFolderLocation){
                    }
   
                } catch (error) {
    curFold = new Folder(logFolderLocation.toString());
                    e(error.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();
     }
     }
}else{
  // $.writeln("file >>> "+txtfiles[0].toString());
    writeLn("Canceled");
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+"rrr"];
}
    var fcontents = "";
timer = (Date.now()-startT)/1000;
    if(txtfiles[0]){
e("after regext: "+timer);
        f = txtfiles[0].toString();
</pre>
        f = new File(f);
 
        f.open("r","TEXT","????");
=== Split to Renderqueue ===
        fcontents = f.read();
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif
        f.close();
<pre>
        var findItems = new RegExp("^(item)[0-9]{1,}(=\()(.*)(\))$","mi");
// Split Layers to Renderqueue v2.0
        v=findItems.exec(fcontents);
// ----------------------------------------------
        if(v){
//
            if(v[4].indexOf("Stopped") != -1){
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp
                info=["name",1,v[4].substring(1,v[4].indexOf(","))]; 
//  using the in and out points. I use this along with 'Magnum' the edit detector to split & render sequences.
            }else if(v[4].indexOf("In Progress") != -1){
//
                info=["name",2,"Rendering"];  
// 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
            }else if(v[4].indexOf("Finished") != -1){
                info=["name",0,""]; 
            }


function pad(n, width, z) {
        }else{
  z = z || '0';
          // $.writeln("Buggy file");
  n = n + '';
            info=[logFolderLocation.name.toString(),1,"--Bug--",logFolderLocation.toString()+"eeee"];
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
        }
}
        fold = new Folder(folds[0].toString());
 
        files = fold.getFiles("*.txt");  
app.beginUndoGroup("Split Layers to Renderqueue");
        firstFile = files[files.length-1]; //used to be first file, works better with last text file
 
        if(firstFile == undefined || firstFile == null){
if(app.project.renderQueue.items.length == 1){
              info=[logFolderLocation.name.toString(),1,"Error",htmlfile];  
    FE = app.project.renderQueue.items[1];
        }else{
    ai = FE.comp;
                //info = ["debug",logFolderLocation.toString(),logFolderLocation.name.toString()];
    path = FE.outputModule(1).file.path;
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];
    for(i=1;i<=ai.layers.length;i++){
             
 
                firstFile.open("r","TEXT","????");
        RI = app.project.renderQueue.items[1].duplicate();
                contents = firstFile.read();
        RI.timeSpanStart = ai.layers[i].inPoint;
                firstFile.close();
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;
                lines = contents.split("\n");
        $.writeln(ai.workAreaDuration+" "+ai.layers[i].outPoint);
              //  $.writeln(logFolderLocation.toString()+" <<< ")
        RI.outputModule(1).file =  new File(path+"/"+pad(i,2,"0")+"_"+ai.layers[i].name);
              info[3] = logFolderLocation.toString();
    }
              info[0] = logFolderLocation.name.toString();
    app.project.renderQueue.items[1].render= false;
                for(i=0;i<lines.length;i++){
}else{
                    //$.writeln("Doing shit");
    alert("Only 1 element should be in renderqueue");
                    phrase ="Rendering started on";
}
                    info[4] = "n/a";
app.endUndoGroup;
                    if(s = lines[i].indexOf(phrase) != -1){
</pre>
                        info[4] = lines[i].substring(s+phrase.length);       
 
                    }
=== Docked Panel SNIP ===
                    phrase ="Output To: ";
<pre>
                 
//script panel
                  // info[2] = "n/a+";
{
//                    info[1] = 1;
var nested_file = new File("U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx");
                   
nested_file.open("r");
                    if(info[1]==0 ||info[1]==2){
eval(nested_file.read());
                      // $.writeln( lines[i].indexOf(phrase));
nested_file.close();
                            if(s = lines[i].indexOf(phrase) != -1){
}
                                s = lines[i].indexOf(phrase);
</pre>
                                v = lines[i].substring(s+phrase.length);
<pre>
                                wfold = v.substring(0,v.lastIndexOf("\\"));
//called file
                              //$.writeln(wfold);
function createUI(thisObj) {
                                f =  wfold.substring(wfold.lastIndexOf("\\")+1,wfold.length);
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",[100, 100, 300, 300]);
                                ///info[3] = pathToWinPath(wfold);
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
                                info[3] =localToRessource(wfold);
//impButton.onClick = openfile;
                                $.writeln(info[3])
return myPanel;
                                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;
    }
}
}
var myToolsPanel = createUI(this);
</pre>
===Watchfolder watcher (WIP)===
http://i.imgur.com/lhO3k.gif
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)
<pre>
//  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.


function getFilePath(watchedFolderLocation){
    fold = new Folder(watchedFolderLocation.toString());
    files = fold.getFiles(getLogonly);
    alert(files[0].name);
    return files[0].name;
}
/*
/*
todo: check if icons are here
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){
//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__";
    fold = new Folder(location.toString());
var wf = app.settings.haveSetting("watchwatchfolder", "wfloc")?new Folder(app.settings.getSetting("watchwatchfolder", "wfloc")):null;
    return fold.execute();   
var scriptFile = new File($.fileName);
}
var scriptFolder = scriptFile.parent; //png icons should be here
function retrieveArray(){
b=0; //global string that will store the
    var state = 4; //queued, default state
var cancelTaskID;
    var vArray = new Array();
var pal;
    l =0;
var timer = 0;
    counter=0;
var refreshRate = 5; //seconds
    counter2=0;
var reloadEditTxt;
    folders = returnFolderArray(wf);
var firstTime = true;
    for(folder in folders){
var shotinfos = new Array();
    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;


//global ui names
        if (pal instanceof Window){
var refreshBtn;
            pal.update();
var list;
        }else{
var wfStText;
            if(counter < Math.round(wfPbar.value/5,0)*5){
var wfPbar;
                counter +=5;
var chckBox0;
                clearOutput();
var chckBox1;
                writeLn(counter+"%");
var chckBox2;
            }else{
var chckBox3;
                write(".");
 
            }
 
         }
function getLogonly(file){
    if(file.name.indexOf("Logs)") != -1){
         return true;
     }
     }
        return false;
    return vArray;
}
function pathToWinPath(path){
str = path.replace(/\//, "");
str = str.replace(/\//, ":/");
str = str.replace(/%20/g, " ");
str = str.replace(/\//g, "\\");
return str;
}
}
function localToRessource(path){
function gather(){
    //windows, for now, the only one available!
                refreshBtn.text = "refreshing...";
    str = "/"+path.replace(":\\", "/");
                refreshBtn.enabled = false;
    str = str.replace(/\\/g, "/");
                wfStText.visible = false;
    str = str.replace(" ", "%20");
                wfPbar.visible = true;
    str = str.substring(0,str.lastIndexOf("/"));
                shotinfos = retrieveArray();
    return str;
                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 returnFolderArray(location){
function watchWatchFolder(thisObj){
    fold = new Folder(location.toString());
 
    foldArray = fold.getFiles()
        //UI Design
    folderOnly = new Array();
         {
    for(i=0;i<foldArray.length;i++){
             pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", "WatchFolder", undefined, {resizeable:true});
         if(foldArray[i] instanceof Folder){
            var winGfx = pal.graphics;
             folderOnly[folderOnly.length] = foldArray[i];
            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");
    return folderOnly;
            wfStTextContents = ( wf != null)?app.settings.getSetting("watchwatchfolder", "wfloc"):"(not set...)";
}
            wfStText = pal.add("edittext",[115,12,285,33], wfStTextContents);
function logInfo(logFolderLocation){
                wfStText.active = false;
   
                wfStText.enabled = false;
    curFold = new Folder(logFolderLocation.toString());
            wfPbar = pal.add("progressbar",[115,12,285,33],0,100);
    folds = returnFolderArray(logFolderLocation);
                wfPbar.visible = false;
    txtfiles = fold.getFiles("*RCF.txt"); //there should only be one
             var sortStText = pal.add("statictext",[17,42,80,60], "Sort by: ");
    htmlfiles =  fold.getFiles("*.htm");
             var sortByName = pal.add('radiobutton',[62,40,120,57], 'name');
    var htmlfile;
             var sortByStatus = pal.add('radiobutton',[115,40,170,57], 'status');
    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;
              
              
}
                sortByName.value = true;
function hasStarted(watchedFolderLocation){
            var bottomElements = pal.add("panel",[10,310,260,360],undefined,{borderStyle:"none"});
    fold = new Folder(watchedFolderLocation.toString());
           
    files = fold.getFiles(getLogonly);
            bl = 0;
    //$.writeln(files.length);
            var arrayV = new Array("green","red","orange","graygreen","gray");     
    if(files.length>0){
           
        return true;
            bottomElements.add('image',[5,bl,20,bl+14],scriptFolder.toString()+"/flag_"+arrayV[0]+".png");
    }else{
            chckBox0 = bottomElements.add('checkbox',[22,bl,39,bl+16], '');
        return false;
            chckBox0.value = true;
    }
            chckBox0.helpTip = "Show finished renders";
}
           
 
            bottomElements.add('image',[45,bl,60,bl+14],scriptFolder.toString()+"/flag_"+arrayV[1]+".png");
function getFilePath(watchedFolderLocation){
            chckBox1 = bottomElements.add('checkbox',[62,bl,79,bl+16], '');
    fold = new Folder(watchedFolderLocation.toString());
            chckBox1.value = true;
    files = fold.getFiles(getLogonly);
            chckBox1.helpTip = "Show errors";
    alert(files[0].name);
           
    return files[0].name;
            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;
function getPercentage(folder){
            chckBox2.helpTip = "Show rendering";
    myFolder = new Folder(folder);
         
    files = myFolder.getFiles("*DandyWatch.txt");
            bottomElements.add('image',[125,bl,140,bl+14],scriptFolder.toString()+"/flag_"+arrayV[4]+".png");
    if(files.length != 1){
            chckBox3 = bottomElements.add('checkbox',[143,bl,160,bl+16], '');
        return false;
            chckBox3.value = true;
    }
            chckBox3.helpTip = "Show queued";
    myTextFile = files[0];  
           
    myTextFile.open("r","TEXT","????");
            bl = 25;
    contents = myTextFile.read();
            var reloadStTxt =  bottomElements.add("statictext",[5,bl+2,50,bl+21], "Update: ");
    myTextFile.close();
            reloadEditTxt =  bottomElements.add("edittext",[55,bl,90,bl+20], 600);
    lines = contents.split("\n");
            reloadEditTxt.enabled=false;
    outPutFolder = new Folder(lines[0]);
            refreshBtn =  bottomElements.add("button",[100,bl,260,bl+20], "Start");
    files = outPutFolder.getFiles();
            reloadEditTxt.helpTip = "in seconds, 0 for manual refresh only.";
    f = files.length;
            list = pal.add ("ListBox", [10, 65, 260,320], "desc",{numberOfColumns: 2,showHeaders: true});       
    writeLn(f+"/"+(lines[1]+1));
           
    return parseFloat(files.length/(lines[1]+1));
           
    //var percentage = new Array();
            //list.columnTitles = Array("First Name", "Last"); doesn't work in CS5 apparently
}
           
//getPercentage(watchfolderLocation+"/"+shot);
        }
*/
   
   
        //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);
                }
 


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;
            refreshBtn.onClick = function(){
        }
                if((timer>0 && !firstTime) || !pal.visible){
      // $.writeln(vArray[counter2]);
                        timer = 0;
        wfPbar.value = l/folders.length*100;
                      // $.writeln("stopped");
 
                        refreshBtn.text = "Start";
        if (pal instanceof Window){
                        firstTime = true;
             pal.update();
                        cancelTask(cancelTaskID);
        }else{
                }else{
            if(counter < Math.round(wfPbar.value/5,0)*5){
                    if(reloadEditTxt.text == 0 || firstTime){
                counter +=5;
                            gather();
                clearOutput();
                            firstTime = false;
                writeLn(counter+"%");
                    }
            }else{
                }
                 write(".");
               
                ///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 vArray;
   
        //Initialize the whole shebang
            if (pal instanceof Window){
                pal.center();
                pal.show();
            }
 
       
      return pal;
    }
//reloadEditTxt.notify("onChange");
ui = watchWatchFolder(this);
}
}
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]);


                }
function eggTimer(time){
                writeLn("Done");
    if(time<=59){
                wfStText.visible = true;
        time = time+"s";
                wfPbar.visible = false;
    }else if(time<=60*4){
               
        time = Math.floor(time/60)+"m "+(time%60)+"s";
                //set (or not) the timer
    }else if(time<60*60){
                refreshBtn.enabled = true;
        time = Math.round(time/60,0)+"m";
                if(parseInt(reloadEditTxt.text) > 0){
    }else{
                    timer = parseInt(reloadEditTxt.text)/refreshRate;
        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);
    }


                    refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
function grow(palette,value){
                    cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);               
   
                }else{
                    refreshBtn.text = "Refresh";
                }
}
}
function watchWatchFolder(thisObj){
function addItem(name,state,msg){
 
    //   alert(listItem.selection);
        //UI Design
    var arrayV = new Array("green","red","orange","graygreen","gray");
        {
    //var texted = new Array("...","error:","rendering","...","queued");
            pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", "WatchFolder", undefined, {resizeable:true});
    var item = list.add ('item',name);
            var winGfx = pal.graphics;
    //$.writeln(scriptFolder.toString()+"/flag_"+array[state]+".png");
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);
    item.image = File(scriptFolder.toString()+"/flag_"+arrayV[state]+".png");
            pal.bounds = [300,200,600,600];       
    //item.subItems[0].helpTip = texted(state);
            var wfLocBtn = pal.add("button", [10,10,105,35],"Set Watchfolder");
    item.subItems[0].text =msg;
            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');


}
///////////////////////////////////////////
          /* 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;
*/


              
              
                sortByName.value = true;
             //alert(getPercentage(watchfolderLocation+"/"+shot));
             var bottomElements = pal.add("panel",[10,310,260,360],undefined,{borderStyle:"none"});
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+"/"+shot)*100);
           
         
            bl = 0;
                      // alert("test");
            var arrayV = new Array("green","red","orange","graygreen","gray");     
              //$.writeln(files[file].path+"/"+files[file].name);
           
                            //  a+= getFilePath(files[file].path+"/"+files[file].name).name+"\n";
            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");
        wfLocBtn.onClick = function(){
            chckBox1 = bottomElements.add('checkbox',[62,bl,79,bl+16], '');
             app.scheduleTask("loop()",2000,1);
            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");
             fold = new Folder( watchfolderLocation.toString());
             chckBox3 = bottomElements.add('checkbox',[143,bl,160,bl+16], '');
             files = fold.getFiles();
            chckBox3.value = true;
                a= "";
            chckBox3.helpTip = "Show queued";
             for(file in files){
              
 
            bl = 25;
              $.writeln(getFilePath(files[file].path+"/"+files[file].name));
            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;
          alert(a);
            refreshBtn =  bottomElements.add("button",[100,bl,260,bl+20], "Start");
 
            reloadEditTxt.helpTip = "in seconds, 0 for manual refresh only.";
             writeLn(pBar.value);
             list = pal.add ("ListBox", [10, 65, 260,320], "desc",{numberOfColumns: 2,showHeaders: true});      
        }
           
*/
           
</pre>
            //list.columnTitles = Array("First Name", "Last"); doesn't work in CS5 apparently
 
           
===Parent to last selected layer===
        }
Why isn't this by default? Bound to alt-P.
   
<pre>
   
(function() {
        //UI Callbacks
    var comp = app.project.activeItem;
        {
    if (comp && comp instanceof CompItem) {
            wfLocBtn.onClick = function(){
        var selectedLayers = comp.selectedLayers;
                wfLoc = Folder.selectDialog("Select watchfolder");
        if (selectedLayers.length > 1) {
                if(wfLoc != null){
            app.beginUndoGroup("Parent to Last Selected");
                    app.settings.saveSetting("watchwatchfolder", "wfloc",wfLoc.toString());
             var parentLayer = selectedLayers[selectedLayers.length - 1];
                    wf = new Folder(wfLoc.toString());
            for (var i = 0; i < selectedLayers.length - 1; i++) {
                    if(wf){
                 selectedLayers[i].parent = parentLayer;
                        wfStText.text = wfLoc.toString();
             }
                    }
             app.endUndoGroup();
                }
        }else{
             }   
alert("Requires at least a child an parent layer to be selected.");
            pal.onResize = function(){
}
                //because using layouts is too confusing for me
    }else{
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];
alert("Select a comp's layers and run the script again");
                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];
</pre>
             }
 
             list.onDoubleClick = function(){//onChange
===Import pos from maya===
                sel = list.selection;
<pre>
             
function createUI(thisObj) {
                if(list.selection != null){
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",
                    // $.writeln(shotinfos.join("\n"));
[100, 100, 300, 300]);
                    sL=shotinfos.length;
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
                    //$.writeln(sL);
impButton.onClick = openfile;
                    for(i=1;i<sL;i++){
return myPanel;
                      a=shotinfos[i];
}
                      //$.writeln(a);
var myToolsPanel = createUI(this);
                        //$.writeln(shotinfos[i][1]+" "+list.selection.toString());
//myToolsPanel.show();
                        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);
                }


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;
            refreshBtn.onClick = function(){
}
                if((timer>0 && !firstTime) || !pal.visible){
</pre>
                        timer = 0;
 
                      // $.writeln("stopped");
===Dandelion/Amazing World Of Gumball Shotbuilder===
                        refreshBtn.text = "Start";
 
                        firstTime = true;
 
                        cancelTask(cancelTaskID);
http://imgur.com/xKJWB.png
                }else{
 
                    if(reloadEditTxt.text == 0 || firstTime){
 
                            gather();
For education purposes only, the script was run in production to helb build shots, it:
                            firstTime = false;
* 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
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;
* automatically sent files to the watchfolder to render on a small-ish farm.
                //refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
Other buttons allowed to check for missing footage, open the comp's folder, open the current shows' latest animatic, etc
                //cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
 
            }
 
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};
<pre>
            reloadEditTxt.onChange = function(){
// 
                if(reloadEditTxt.text < 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){
//  Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
                    timer = 0;
//  last update 21/11/10
                    reloadEditTxt.text = 0;
//
                    refreshBtn.text = "Refresh";
// Known Bugs:
                //}else if(reloadEditTxt.text >=10){
//    
                }else{
// -you can't have several sequences in a single folder and expect the script to pick up all the sequences
                    if(timer <= 0){
//  -this will not set color profiles
                        refreshBtn.text = "Start (~ "+eggTimer(reloadEditTxt.text)+")";
// //TODO
                    }
//  >>> CHECK IF WF FOLDER ALREADY EXISTS
                }
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
            reloadEditTxt.enabled=false;               
//  -v0.7 fixes
            }
//        -new scene after WF works properly
        }
//        -changed output folder location to N:
   
//  -v0.6 fixes
   
//      -cancel watchfolder cancels watchfolder
        //Initialize the whole shebang
//       -fixed GB##_SC_###_T1
            if (pal instanceof Window){
//      -added options
                pal.center();
//      -can work on a new location
                pal.show();
//  -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.
      return pal;
//  -v0.4 fixes
    }
//      -there shouldn't be a refresh problem on show change anymore
//reloadEditTxt.notify("onChange");
//  -v0.3 fixes
ui = watchWatchFolder(this);
//      -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";


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);
rootFolder = ((app.settings.haveSetting("dandybuildPrefs", "rootfolder")))?(app.settings.getSetting("dandybuildPrefs", "rootfolder")):rootFolder;
        if(reloadEditTxt.value == 0 || !pal.visible){
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;
           
            
            
         }else{
 
             gather();
 
//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 cancelTask(id){
        app.cancelTask(id);
    }


function grow(palette,value){
 
      
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 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;


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);
}
}
///////////////////////////////////////////
          /* 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);


         
function writeInfo(watchfolderlocation,compname){
            fold = new Folder( watchfolderLocation.toString());
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
            files = fold.getFiles();
                a= "";
            for(file in files){


              $.writeln(getFilePath(files[file].path+"/"+files[file].name));
    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();
}


            }
          alert(a);


            writeLn(pBar.value);
function explore(location){
        }
    fold = new Folder(location.toString());
*/
    return fold.execute();    
</pre>
}


===Import pos from maya===
function checkForMissingFootage(dialogFlag){
<pre>
        var dialog = dialogFlag;
function createUI(thisObj) {
        var missing = false;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",
        for(i=1;i<=app.project.numItems;i++){
[100, 100, 300, 300]);
            app.project.items[i].selected = false;
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
            if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
impButton.onClick = openfile;
                            if(app.project.items[i].footageMissing){
return myPanel;
                                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;
}
}
var myToolsPanel = createUI(this);
//myToolsPanel.show();


 
function checkOutputSettings(){
function openfile(){
    renderSettingsSet = ((app.settings.haveSetting("dandybuildPrefs", "tiffsettings")))?true:false;
            var myFile = File.openDialog ("Select track file","*.txt");  
  // renderSettingsSet = 1;
            var fileOK = myFile.open("r","TEXT","????");
    if(!renderSettingsSet){
//var fileD = OpenDlg ("Tracking Point File","*.txt",true);
                alert("Tiff output being setup, this should happen only once. It needs to close the current comp.");
//txt = fileD.readln ()
                opFile = new File(rootFolder+"base.aep");
alert("txt"+readTxt(myFile));
                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 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;
}


<pre>
function getTake(normalizedName){
// 
    tmp = normalizedName.split("_T");
// Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
    return parseFloat(tmp[tmp.length-1]);
//  last update 21/11/10
}
//
// Smart Import.jsx
// Known Bugs:
//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
//  -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
function SmartImport(targetFolder){
// //TODO
        writeLn("Importing files...");
//  >>> CHECK IF WF FOLDER ALREADY EXISTS
        var importedFiles = 0;
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
var scriptName = "Smart Import";
//  -v0.7 fixes
var sourcePaths = getSourcePathsArray();
//        -new scene after WF works properly
// Ask the user for a folder whose contents are to be imported.
//       -changed output folder location to N:
//var targetFolder = Folder.selectDialog("Import items from folder...");
// -v0.6 fixes
if (targetFolder != null) {
//       -cancel watchfolder cancels watchfolder
// If no project open, create a new project to import the files into.
//        -fixed GB##_SC_###_T1
function processFile(theFile)
//      -added options
{
//       -can work on a new location
try {
// -v0.5 fixes
// Create a variable containing ImportOptions.
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)
var importOptions = new ImportOptions(theFile);
//      -will warn if there is missing footage before sending to WF
                    //alert();
//     -removed set take, added 'missing' dialog.
                    if(theFile.name.toString().toLowerCase().lastIndexOf(".psd") != -1){
//  -v0.4 fixes
                        importOptions.importAs = ImportAsType.COMP;
//      -there shouldn't be a refresh problem on show change anymore
                    }
//  -v0.3 fixes
                      importSafeWithError(importOptions);
//      -changed watchfolder location to anthony's mac
} catch (error) {
//      -changed save as dialog
// Ignore errors.
//     -removed unused buttons
}
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)
}
// -v0.2 fixes
//      -sequences weren't getting imported.
function testForSequences(files)
//      -fixed UI/little problems
{
//      -added folders, refresh button, save before watchfoldering
var searcher = new RegExp("[0-9]{3,}$");
var version = "0.7";
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);


//default locations
                          //splitName[splitNameA.length] =  
var watchfolderLocation = "/c/__WATCHFOLDER__";
                          //alert(splitName);
var rootFolder = "/c/";
                        // alert(files[x].name+" >>> "+files[x].name.split('.')[0]);
var forEditFolderLoaction = "//MAC0023DFDF5429/for%20edit";
var currentResult = searcher.exec(splitName);
 
// Regular expressions return null if no match was found.
 
// Otherwise, they return an array with the following information:
rootFolder = ((app.settings.haveSetting("dandybuildPrefs", "rootfolder")))?(app.settings.getSetting("dandybuildPrefs", "rootfolder")):rootFolder;
// array[0] = the matched string.
watchfolderLocation = ((app.settings.haveSetting("dandybuildPrefs", "watchfolderLocation")))?(app.settings.getSetting("dandybuildPrefs", "watchfolderLocation")):watchfolderLocation;
// array[1..n] = the matched capturing parentheses.
forEditFolderLoaction = ((app.settings.haveSetting("dandybuildPrefs", "forEditFolderLoaction")))?(app.settings.getSetting("dandybuildPrefs", "forEditFolderLoaction")):forEditFolderLoaction;
                 
 
                    if (currentResult) { // We have a match -- the string contains numbers.
          //dirty hack
                         
            a = ((app.settings.haveSetting("dandybuildPrefs", "optMissingFootage")))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true;
                            // The match of those numbers is stored in the array[1]
            b = ((app.settings.haveSetting("dandybuildPrefs", "optNuComp")))?(app.settings.getSetting("dandybuildPrefs", "optNuComp")):false;
// Take that number and save it into parseResults.
            c = ((app.settings.haveSetting("dandybuildPrefs", "optSaveComp")))?(app.settings.getSetting("dandybuildPrefs", "optSaveComp")):true;
parseResults[parseResults.length] = currentResult[0];
            if(a=="false") a = false;
} else {
            if(a=="true") a = true;
parseResults[parseResults.length] = null;
            if(b=="false") b = false;
}
            if(b=="true") b = true;
                          if(files[x].name == splitName){
            if(c=="false") c = false;
                                isFolder[isFolder.length] = true;
            if(c=="true") c = true;
                              // alert("Folder "+files[x].name);
           
                          }else{
         
                                isFolder[isFolder.length] = false;
 
                          }
 
} 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();
}
 
Array.prototype.has = function(value) {
// If all the files we just went through have a number in their file names,
    var i;
// assume they are part of a sequence and return the first file.
    for (i=0;i<this.length;i++) {
        if (this[i] == value) {
var result = null;
            return true;
for (i = 0; i < parseResults.length; ++i) {
        }
                 
    }
                  if(!isFolder[i]){
    return false;
                    if (parseResults[i]) {
}
if (!result) {
 
                           
 
result = files[i];
function addShotDialog(sel){
}
    alert(sel);
} else {
    }
                       
 
// In this case, a file name did not contain a number.
function pathToWinPath(path){
result = null;
    path = path.toString();
break;
str = path.replace(/\//, "");
}
str = str.replace(/\//, ":/");
                  }
str = str.replace(/%20/g, " ");
                }
str = str.replace(/\//g, "\\");
return str;
return result;
}
}
 
       
function returnFolderArray(location){
    fold = new Folder(location.toString());
function importSafeWithError(importOptions)
    foldArray = fold.getFiles()
{
    folderOnly = new Array();
              if(!(sourcePaths.has(importOptions.file.toString()))){
    for(i=0;i<foldArray.length;i++){
try {
        if(foldArray[i] instanceof Folder){
                  b = app.project.importFile(importOptions);
            folderOnly[folderOnly.length] = foldArray[i];
                    writeLn(importOptions.file.toString());
            }
                sfi = sourcesFolderItem();
        }
                  b.parentFolder = sfi;
    return folderOnly;
                    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){
function sendToWatchFolder(watchfolderlocation,compname){
                        app.project.items[i].parentFolder = sfi;
    ocn = compname;
                            }
    if(app.settings.getSetting("dandybuildPrefs", "optSaveComp") == "true"){
                        }
        c = confirm("Save "+app.project.file.name+" ?",false,"Save Project");
                    }
        if(c){
                        writeLn("Importing file ("+importedFiles+")");
            app.project.save();
                        importedFiles++;
        }
} catch (error) {
    }
alert(error.toString() + importOptions.file.fsName, scriptName);
    curFile = app.project.file;
}
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
                // }else{
    compname += "_WF";
                //       alert("file already here");
    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","????");
function processFolder(theFolder)
    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);
// Get an array of files in the target folder.
    myTextFile.close();
var files = theFolder.getFiles();
    writeInfo(watchfolderlocation,ocn);
//alert(files);
    if(app.settings.getSetting("dandybuildPrefs", "optNuComp") == "false"){
// Test whether theFolder contains a sequence.
        opFile = new File(curFile);
var sequenceStartFile = testForSequences(files);
        if(opFile){
            app.open(opFile);
// If it does contain a sequence, import the sequence,
        }
if (sequenceStartFile) {
    }else{
try {
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
// Create a variable containing ImportOptions.
        app.project.new();
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);
    //explore(watchfolderlocation);
} catch (error) {
}
}
 
}
function writeInfo(watchfolderlocation,compname){
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
// Otherwise, import the files and recurse.
 
    mySaveFile = new File(myFolder.toString()+"/"+compname+"_DandyWatch.txt");   
for (index in files) { // Go through the array and set each element to singleFile, then run the following.
    mySaveFile.open("w","TEXT","????");
if (files[index] instanceof File) {
    rq = app.project.renderQueue;
if (!sequenceStartFile) { // If file is already part of a sequence, don't import it individually.
    text ="";   
processFile(files[index]); // Calls the processFile function above.
    for(i=1;i<=rq.numItems;i++){   
}
        if(rq.items[i].render){
}
            text += rq.items[i].outputModules[1].file.path+"\n";
if (files[index] instanceof Folder) {
            text += (Math.round(rq.items[i].timeSpanDuration*25)+"\n");
processFolder(files[index]); // recursion
            text += system.callSystem("hostname");
}
        }
}
}
// Recursively examine that folder.
processFolder(targetFolder);
}
    clearOutput();
     }
     }
     mySaveFile.write(text);
     mySaveFile.close();
 
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(){
function explore(location){
    var footageLocations = new Array();
    fold = new Folder(location.toString());
    for (i = 1; i <= app.project.numItems ; i++) {
    return fold.execute();     
        if(app.project.item(i).typeName == "Footage"){
            footageLocations[footageLocations.length] = app.project.item(i).file;
        }   
    }
     return footageLocations;
}
}


function checkForMissingFootage(dialogFlag){
function getLastModified(location,extension){
        var dialog = dialogFlag;
      myFolder = new Folder(location.toString());
        var missing = false;
      if (!myFolder instanceof Folder){
        for(i=1;i<=app.project.numItems;i++){
          return false;
            app.project.items[i].selected = false;
    }
            if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
    listFiles = myFolder.getFiles(extension);
                            if(app.project.items[i].footageMissing){
    listDates = new Array();
                                app.project.items[i].selected = true;
    maxId = 0;
                                missing = true;
    maxValue = 0;
                                if(dialog){
    for(i=0;i<listFiles.length;i++){
                                    dialog = prompt("Original folder of \""+(app.project.items[i].name)+"\" :",pathToWinPath(app.project.items[i].file),"Missing footage! (hit cancel to suppress further dialogs)");
        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;
            }
         }
         }
         return missing;
   
    }
    if(listFiles[maxId] == "" ||listFiles[maxId] == "undefined"||listFiles[maxId] == null){
         return false;
    }
    return listFiles[maxId];   
}
}


function checkOutputSettings(){
function pad(number, length, character) {
     renderSettingsSet = ((app.settings.haveSetting("dandybuildPrefs", "tiffsettings")))?true:false;
     if (character == null) {
  // renderSettingsSet = 1;
        character = "0";
    if(!renderSettingsSet){
    }
                alert("Tiff output being setup, this should happen only once. It needs to close the current comp.");
    var numberStr = "" + number;
                opFile = new File(rootFolder+"base.aep");
    while (numberStr.length < length) {
                if(opFile.exists){
        numberStr = character + numberStr;
                    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 numberStr;
}
}
 
function grabAnimatic(folderLocation){
function checkOutFolder(tpath){
     newLocation = new Folder(folderLocation);
     foldFile = new Folder(tpath);
     animatic = newLocation.getFiles("*.mov");
     //$.writeln(tpath);
    sfi = sourcesFolderItem();
     if(foldFile.exists){
     if(animatic[0]){
         files = foldFile.getFiles();
         try {
        if(files.length > 1){
            var importOptions = new ImportOptions(animatic[0]);
             return false;
            animatic = app.project.importFile(importOptions);
         }else{
            for (i = 1; i <= app.project.numItems ; i++) {
             return true;
                    if(app.project.item(i).typeName == "Footage"){
                  app.project.item(i).parentFolder = sfi;
                    }   
            }
             return animatic;
         }catch (error){
             return false;
         }
         }
    }else{
      newF = foldFile.create();
        return newF;
     }
     }
}
}
function getSetTake(force){
function figureOutShotName(filepath){
     show = app.settings.getSetting("dandybuildPrefs","show");// showDDL.selection.toString();
     folders = filepath.split("/");
     shot = app.settings.getSetting("dandybuildPrefs","shot"); //shotDDL.selection.toString();
    showFolder = folders[folders.length-2];
     projectTake = getTake(findMainShot().name);
     showFolder = showFolder.split("_")[0];
//    paths  = rootFolder+show+"/"+shot+"/OUT/";
     shotFolder = folders[folders.length-1];
     paths  = "/n/01_OUT/"+show+"/"+shot+"/";
     var searcher = new RegExp("[0-9]{2,}$");
     lastFolder = getLastModified(paths);
     var currentResult = searcher.exec(shotFolder);
    //alert(lastFolder.name+" -->"+paths);
     if (currentResult){
     if(lastFolder){
         shotFolder = currentResult[0];
        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";
     return showFolder+"_SC"+shotFolder;
     msgPt2 = "The current project take is "+projectTake+". \n\nChoose the current take:";
}
     if(force || (folderTake != (projectTake-1))){
 
        recommended = (folderTake)?((folderTake)+1):projectTake;
function createMainComp(path){
         answer = prompt(msgPt1+msgPt2,recommended);
    grabAnimatic(path);
         //$.writeln("blabal "+answer);
    shotName = figureOutShotName(path);
        if(answer != null){
     workComp = app.project.items.addComp(shotName+"_T1", 1920, 1080,1.0 ,animatic.duration,25.0);
            return answer;
     animaticLayer = workComp.layers.add(animatic);
         }else{
    animaticLayer.guideLayer = true;
            return false;
    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 projectTake;
     return rArray;
}
}






function setTake(value){
function returnList(folder){
     comp = findMainShot();
     rArray = returnFolderArray(folder);
     comp.name = comp.name.substring(0,(comp.name.lastIndexOf("_T")+2))+value;
     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 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] =
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;
function importSafeWithError(importOptions)
            shotStatusEditTxt.enabled = false;
{
        var shotLoadBtn = shotLoad.add("button",[56,125,96,150],"Load");
              if(!(sourcePaths.has(importOptions.file.toString()))){
            shotLoadBtn.enabled = false;
try {
        var shotBuildBtn = shotLoad.add("button",[101,125,141,150],"Build");
                  b = app.project.importFile(importOptions);
            shotBuildBtn.enabled = false;
                    writeLn(importOptions.file.toString());
        var shotRefreshBtn = shotLoad.add("button",[146,125,168,150],"r");
                sfi = sourcesFolderItem();
        //var shotOptns = shotLoad.add("button",[174,125,210,150],"optns");
                  b.parentFolder = sfi;
            //shotSetBtn.enabled = false;
                    if(importOptions.importAs == ImportAsType.COMP){
       
                        for (i = 1; i <= app.project.numItems ; i++) {
      var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
                            if(app.project.items[i].typeName == "Folder" && app.project.items[i].name.lastIndexOf(" Layers") != -1){
            shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
                        app.project.items[i].parentFolder = sfi;
            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");
                        writeLn("Importing file ("+importedFiles+")");
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};
                        importedFiles++;
            shotToolsBtnFE = shotTools.add("button",[139,12,167,35],"FE");
} catch (error) {
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
alert(error.toString() + importOptions.file.fsName, scriptName);
            shotToolsBtnXLS =  shotTools.add("button",[172,12,197,35],"xls");
}
            //shotToolsBtnXLS.enabled = false;           
                // }else{
               
                //        alert("file already here");
            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");
           
function processFolder(theFolder)
              // shotToolsBtn4.enabled = false;           
{
            shotToolsBtn5 =  shotTools.add("button",[12,68,102,91],"Renderqueue");
// Get an array of files in the target folder.
            shotToolsBtn6 =  shotTools.add("button",[107,68,197,91],"To Watchfolder");
var files = theFolder.getFiles();
                //shotToolsBtn6.enabled = false;
//alert(files);
            }
// Test whether theFolder contains a sequence.
      var optnsGrp = dPanel.add("panel",[10,5,235,300],"Options");
var sequenceStartFile = testForSequences(files);
            optnsGrp.visible = false;
            mgL = 20;
// If it does contain a sequence, import the sequence,
            mgT=15;
if (sequenceStartFile) {
            mgBet=3;
try {
            i=0;
// Create a variable containing ImportOptions.
            w=210;
var importOptions = new ImportOptions(sequenceStartFile);
            h=23;
importOptions.sequence = true;
            version = optnsGrp.add("statictext",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"version "+version);i++;
// importOptions.forceAlphabetical = true; // Un-comment this if you want to force alpha order by default.
            optMissingFootage = optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Check for missing footage\rlonger, but safer");i++;
importSafeWithError(importOptions);
            //$.writeln("load missing footage pref set to"+(app.settings.haveSetting("dandybuildPrefs", "optMissingFootage"))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true);
} catch (error) {
                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;
// Otherwise, import the files and recurse.
                optSaveComp.value = c;
            rootFolderLoc =  optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Root folder location");i++;
for (index in files) { // Go through the array and set each element to singleFile, then run the following.
            watchFolderLoc = optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Watchfolder location");i+=2;
if (files[index] instanceof File) {
             okBtn = optnsGrp.add("button",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"OK");
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 ///////////////////////////////////////////////////////////
}
     
if (files[index] instanceof Folder) {
        showDDL.onChange = function(){
processFolder(files[index]); // recursion
            app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
}
          // shotLoad.remove(shotDDL);
}
           shotDDL.removeAll();
}
 
          items = returnList(rootFolder+"/"+showDDL.selection.toString());
// Recursively examine that folder.
          //  alert(items);
processFolder(targetFolder);
            for(i in items){
}
              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(){
         
    for (i = 1; i <= app.project.numItems ; i++) {
            app.settings.saveSetting("dandybuildPrefs","shot",shotDDL.selection.toString());
      // alert(app.project.items[i].name);
            //$.writeln("Changing");
    if(app.project.items[i].typeName == "Folder" && app.project.items[i].name == "Sources"){
            var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
          return app.project.items[i];
            if(!lastAEP){
      }
                shotStatusEditTxt.text = "no shot found, build one!";
  }
                  shotLoadBtn.enabled = false;
    return app.project.items.addFolder("Sources");
                  shotBuildBtn.enabled = true;
    //else
           
   
            }else{
}
                app.settings.saveSetting("dandybuildPrefs", "loadscene",lastAEP.toString());
 
                loadFile = lastAEP.toString();
function getSourcePathsArray(){
                fullSplitPath = lastAEP.toString().split("/");
    var footageLocations = new Array();
                fileName = fullSplitPath[fullSplitPath.length-1];
    for (i = 1; i <= app.project.numItems ; i++) {
                if(fileName.length > 20){
        if(app.project.item(i).typeName == "Footage"){
                    shotStatusEditTxt.text = fileName.substr(0,12)+"[...]"+fileName.substr(fileName.length-8);
             footageLocations[footageLocations.length] = app.project.item(i).file;
                }else{
        }   
                    shotStatusEditTxt.text = fileName;
    }
                }  
    return footageLocations;
                    shotLoadBtn.enabled = true;
}
                    shotBuildBtn.enabled = false;
 
            }
function getLastModified(location,extension){
        }
      myFolder = new Folder(location.toString());
        shotLoadBtn.onClick = function(){
      if (!myFolder instanceof Folder){
            opFile = new File(loadFile);
           return false;
            if(opFile){
    }
            app.open(opFile);
    listFiles = myFolder.getFiles(extension);
            }else{
    listDates = new Array();
                alert("Can't open file");
    maxId = 0;
            }       
    maxValue = 0;
        }
    for(i=0;i<listFiles.length;i++){
        shotRefreshBtn.onClick = function(){
        if (!(extension == "" && (listFiles[i] instanceof File))){
            shotDDL.notify();
            d = listFiles[i].modified;
            writeLn("If it doesn't seem to refresh, close and launch again.");
            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;
         shotBuildBtn.onClick = function(){
            if(Math.max(maxValue,formattedDate) == formattedDate){
             opFile = new File(rootFolder+"base.aep");
                maxValue = formattedDate;
             if(opFile.exists){
                maxId = i;
                app.open(opFile);
            }
                for(i=1;i<=app.project.numItems;i++){                    
        }
                     app.project.items[i].remove();
   
                }
    }
            }else{
    if(listFiles[maxId] == "" ||listFiles[maxId] == "undefined"||listFiles[maxId] == null){
                alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
        return false;
            }
    }
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
    return listFiles[maxId];    
            createMainComp(targetFolder);
}
            app.project.bitsPerChannel = 16;
 
            app.project.items.addFolder("Precomps");
function pad(number, length, character) {
            importFolder = new Folder(targetFolder+"/Source");
    if (character == null) {
            SmartImport(importFolder);
        character = "0";
            saveConf = confirm("Save "+shotDDL.selection.toString()+" in the right folder ?",false,"Dandelion Shot Builder");
    }
            if(saveConf){
    var numberStr = "" + number;
                saveFile = new File(targetFolder+"/AEP/"+showDDL.selection.toString()+"_"+shotDDL.selection.toString()+"_comp01.aep");
    while (numberStr.length < length) {
                app.project.save(saveFile);
        numberStr = character + numberStr;
                shotDDL.notify();
    }
                }
    return numberStr;
        }
}
        shotToolsBtn1.onClick = function(){
function grabAnimatic(folderLocation){
            explore(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString());
    newLocation = new Folder(folderLocation);
        }
    animatic = newLocation.getFiles("*.mov");
        shotToolsBtn1Xtra.onClick = function(){
    sfi = sourcesFolderItem();
             //$.writeln("button");
    if(animatic[0]){
            p = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
         try {
            checkOutFolder(p);
             var importOptions = new ImportOptions(animatic[0]);
             explore(p);
             animatic = app.project.importFile(importOptions);
         }
            for (i = 1; i <= app.project.numItems ; i++) {
        shotToolsBtn3.onClick = function(){
                     if(app.project.item(i).typeName == "Footage"){
             app.project.bitsPerChannel = 16;
                  app.project.item(i).parentFolder = sfi;
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
                    }  
            importFolder = new Folder(targetFolder+"/Source");
             }
            SmartImport(importFolder);
             return animatic;
         }catch (error){
             return false;
         }
         }
    }
        shotToolsBtnXLS.onClick = function(){
}
            loc = rootFolder+"/"+showDDL.selection.toString();
function figureOutShotName(filepath){
            fold = new Folder(loc.toString());
    folders = filepath.split("/");
            files = fold.getFiles("*.xls*");
    showFolder = folders[folders.length-2];
            if(files.length > 0){
    showFolder = showFolder.split("_")[0];
                files[0].execute();
    shotFolder = folders[folders.length-1];
            }else{
    var searcher = new RegExp("[0-9]{2,}$");
                writeLn("No .xls shortcut found in in:");
    var currentResult = searcher.exec(shotFolder);
                writeLn(showDDL.selection.toString());
    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"];
      shotToolsBtnANI.onClick = function(){
        alert(dPanel.shotLoad.showDDL.selection.toString());
            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"));
     }
     }
    return rArray;
       
}
        //send to renderqueue
        //uses the comp name to figure out folder


 
                 
 
        shotToolsBtn5.onClick = function(){
function returnList(folder){
        rflag = false;
    rArray = returnFolderArray(folder);
        //$.writeln("missing fottage "+app.settings.getSetting("dandybuildPrefs", "optMissingFootage"));
    for(i=0;i<rArray.length;i++){
        if(app.settings.getSetting("dandybuildPrefs", "optMissingFootage") == "true"){
             rArray[i] = rArray[i].name;
             rflag = checkForMissingFootage(false);
    }
        }
    return rArray;
        if(!rflag){
}
            rcomp = findMainShot();
 
                if(rcomp){
function findMainShot(){
                    shotNumber = getSetTake();
    //fix GB##_SC_###_T1
                    if(shotNumber != false){
    var findShot = new RegExp("^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
                        //$.writeln(shotNumber);
    for(i=1;i<=app.project.numItems;i++){
                        setTake(shotNumber);
        if(app.project.items[i] instanceof CompItem){
                        rq = app.project.renderQueue;
          var movieFileResult = findShot.exec(app.project.items[i].name);
                        for(i=1;i<=rq.numItems;i++){  
          if(movieFileResult){
                                rq.items[i].render = false;
              nuName = app.project.items[i].name;
                        }
              nuName = nuName.split("SC_");
                        rqitem = app.project.renderQueue.items.add(rcomp);
              app.project.items[i].name = nuName[0]+"SC"+nuName[1];
                        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");
             }
             }
        }       
    }
   
    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){
        shotToolsBtn4.onClick = function(){
        alert("Could not find a comp named like \"GB##_SC###(a-z)_T#\"\n\nRename main comp accordingly, render again");
            //shotNumber = getSetTake(true);
         return false;
            //setTake(shotNumber);
     }
            checkForMissingFootage(true);
     return shot;
        }
}
       
 
        shotToolsBtn6.onClick = function(){
 
          wf = new Folder(watchfolderLocation);
function dandyShotBuilderUI(thisObj){
          if(wf.exists){
              sendToWatchFolder(wf,findMainShot().name);
              }else{
                  alert("Watchfolder error, sorry!");
              }
         }
           
      
      
   
      
      
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
      
      
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window("palette", "DandyShotBuilderFix", [100, 100, 300, 300]);
    rootFolderLoc.onClick = function(){
         var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
         y = confirm("Root folder is currently set to \n\n\""+rootFolder+"\"\n\nChange it ?");
        if (securitySetting != 1) {
        if(y){
          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 = new Folder();
          var shotInfo = dPanel.add("edittext",[10,5,235,305],msg,{multiline:true});
            v = Folder.selectDialog ("New root folder location").toString();
        }else{
             if(v!=null && v!="undefined"){
      var shotLoad = dPanel.add("panel",[10,5,235,175],"Load Shot");
                app.settings.saveSetting("dandybuildPrefs", "rootfolder",v);   
        var shotLoadTxt1 = shotLoad.add("statictext",[15,24,52,44],"Show:");
                rootFolder = v;
        var showDDL = shotLoad.add ("dropdownlist", [56,19,210,44], returnList(rootFolder));
                          showDDL.removeAll();
             if (app.settings.haveSetting("dandybuildPrefs", "show")){
                          items = returnList(rootFolder);
                showDDL.selection = showDDL.find(app.settings.getSetting("dandybuildPrefs","show"));
          //  alert(items);
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
            for(i in items){
                }else{
              showDDL.add("item", items[i]);
                showDDL.selection = 0;
            }
                app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
                }
             shotDDL.selection = 0;
        var shotLoadTxt2 = shotLoad.add("statictext",[12,59,52,79],"Scene:");
            shotDDL.notify();
        var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+app.settings.getSetting("dandybuildPrefs", "show")));
                showDDL.notify();
             if (app.settings.haveSetting("dandybuildPrefs", "shot")){
                 shotDDL.notify();
                shotDDL.selection = shotDDL.find(app.settings.getSetting("dandybuildPrefs","shot"));
                //$.writeln("notified");
                //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){
        var shotStatusTxt = shotLoad.add("statictext",[25,95,52,115],"File:");
            //v = new Folder();
        var shotStatusEditTxt = shotLoad.add("edittext",[56,92,210,115],"...");
             v = Folder.selectDialog ("New watchfolder location").toString();
             shotStatusEditTxt.active = false;
             if(v!=null && v!="undefined"){
            shotStatusEditTxt.enabled = false;
                app.settings.saveSetting("dandybuildPrefs", "watchfolderLocation",v);    
        var shotLoadBtn = shotLoad.add("button",[56,125,96,150],"Load");
                watchfolderLocation = v;
            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");
    shotDDL.notify();
             //shotSetBtn.enabled = false;
    }
       
    dandyShotBuilderUI(this) ;
      var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
 
            shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
</pre>
             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");
===layer Position to .txt===
             shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};
https://i.imgur.com/G5DX1T0.gif
            shotToolsBtnFE =  shotTools.add("button",[139,12,167,35],"FE");
Old, might not work! (but should :)
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
<pre>
            shotToolsBtnXLS =  shotTools.add("button",[172,12,197,35],"xls");
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
            //shotToolsBtnXLS.enabled = false;           
//
               
// The script will write to a text file the x & y values of the layer position for every frame comprised in
            shotToolsBtn3 = shotTools.add("button",[12,40,102,63],"Grab Sources");
// a selection of keyframes, or for the whole comp duration if no keyframes are selected
            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");
function timeToFrameNum(myTime){
           
  return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
              // shotToolsBtn4.enabled = false;           
}
            shotToolsBtn5 =  shotTools.add("button",[12,68,102,91],"Renderqueue");
function framesToTime(myTime){
            shotToolsBtn6 =  shotTools.add("button",[107,68,197,91],"To Watchfolder");
  return myTime/app.project.activeItem.frameRate;
                //shotToolsBtn6.enabled = false;  
}
            }
function timeToTimeCode(myTime){
      var optnsGrp = dPanel.add("panel",[10,5,235,300],"Options");
  var framesN = myTime * app.project.activeItem.frameRate;
            optnsGrp.visible = false;
  fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));
            mgL = 20;
  ho = addZero(Math.floor(myTime/3600));
            mgT=15;
  mi = addZero(Math.floor(myTime/60)-ho*60);
            mgBet=3;
  se = addZero(Math.floor(myTime)-mi*60-ho*3600);
            i=0;
  return ho+":"+mi+":"+se+":"+fr;
            w=210;
}
            h=23;
function addZero(val){
            version = optnsGrp.add("statictext",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"version "+version);i++;
  if(val<10){
            optMissingFootage = optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Check for missing footage\rlonger, but safer");i++;
      val = "0"+val;
            //$.writeln("load missing footage pref set to"+(app.settings.haveSetting("dandybuildPrefs", "optMissingFootage"))?(app.settings.getSetting("dandybuildPrefs", "optMissingFootage")):true);
  }
                optMissingFootage.value = a;
  return val;
            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;
var myDisp = app.project.timecodeDisplayType;
            optSaveComp =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Ask to save before sending to WF");i+=2;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;
                optSaveComp.value = c;
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'";
            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 ///////////////////////////////////////////////////////////
if(app.project.activeItem != "null" && app.project.activeItem != null && app.project.activeItem != 0){
     
  if(app.project.activeItem.selectedLayers.length != 0){
        showDDL.onChange = function(){
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
            app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
        curLayer = app.project.activeItem.selectedLayers[0];
          // shotLoad.remove(shotDDL);
        var textName = "AEcoordinates.txt";
          shotDDL.removeAll();
        var myTextFile = filePutDialog("Select a location to save your .txt file", textName, "TEXT txt");
 
        if(myTextFile == null){
          items = returnList(rootFolder+"/"+showDDL.selection.toString());
            alert("You must choose a place to save the file");
          //  alert(items);
        }else{
             for(i in items){
            var formatString = prompt(pText,"%i: %x;%y%l");
               shotDDL.add("item", items[i]);
            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 shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
             var startLoop = timeToFrameNum(strTime);
             shotDDL.selection = 0;
             var endLoop = timeToFrameNum(endTime);
             shotDDL.notify();
             for(i=startLoop;i<=endLoop;i++){
        }
              var curTime = framesToTime(i);
        shotDDL.onChange = function(){
              out = formatString.replace('%x',curLayer.property("position").valueAtTime(curTime,true)[0]);
         
              out = out.replace('%y',curLayer.property("position").valueAtTime(curTime,true)[1]);
            app.settings.saveSetting("dandybuildPrefs","shot",shotDDL.selection.toString());
              out = out.replace('%f',i);timeToTimeCode
            //$.writeln("Changing");
              out = out.replace('%t',timeToTimeCode(curTime));
            var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
              out = out.replace('%i',i-startLoop);
            if(!lastAEP){
              out = out.replace('%l','\n');
                shotStatusEditTxt.text = "no shot found, build one!";
              myTextFile.write(out);
                  shotLoadBtn.enabled = false;
              clearOutput();
                  shotBuildBtn.enabled = true;
              write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+"% done...");
           
            }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;
             }
             }
        }
        clearOutput();
        shotLoadBtn.onClick = function(){
        writeLn(endLoop-startLoop+" positions saved to file");
            opFile = new File(loadFile);
        writeLn("script written by mlk =)");
            if(opFile){
        myTextFile.close();
            app.open(opFile);
        }
            }else{
      } else {
                alert("Can't open file");
        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.");
        shotRefreshBtn.onClick = function(){
      }
            shotDDL.notify();
 
            writeLn("If it doesn't seem to refresh, close and launch again.");
  }else{
            }
      alert("Select a layer with 'position' keyframes");
        shotBuildBtn.onClick = function(){
  }
            opFile = new File(rootFolder+"base.aep");
}else{
            if(opFile.exists){
  alert("Select a composition and a layer with 'position' keyframes");
                app.open(opFile);
}
                for(i=1;i<=app.project.numItems;i++){                   
app.project.timecodeDisplayType = myDisp;
                    app.project.items[i].remove();
</pre>
                }
 
            }else{
==Other Scripts==
                alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
 
            }
=== Work specific ===
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
 
            createMainComp(targetFolder);
==== Auto scale precomps ====
            app.project.bitsPerChannel = 16;
Script that precomposes animation layers (specifically that are in .swf):
            app.project.items.addFolder("Precomps");
* scales up the layer by X amount
            importFolder = new Folder(targetFolder+"/Source");
* scales down the precomp by 1/x amount
            SmartImport(importFolder);
* Toggles the continuously rasterize off on the precomp, on inside.
            saveConf = confirm("Save "+shotDDL.selection.toString()+" in the right folder ?",false,"Dandelion Shot Builder");
This specifically solves the problem that lines out of Animate/Flash are 'rough' (aliased) when imported at 100%, no matter which flags are toggled.
            if(saveConf){
Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)
                saveFile = new File(targetFolder+"/AEP/"+showDDL.selection.toString()+"_"+shotDDL.selection.toString()+"_comp01.aep");
 
                app.project.save(saveFile);
Usage: save as .jsx, run the script via File>Scripts (Ctrl-Alt-Shift-D will redo latest script to repeat the process).
                shotDDL.notify();
If no layers are selected, the script will instead prompt which scale factor it should use (4 by default).
                }
 
        }
<pre>
        shotToolsBtn1.onClick = function(){
// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how
            explore(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString());
(function afterEffectsSmartScale() {
        }
        shotToolsBtn1Xtra.onClick = function(){
    var SCRIPT_PREF_KEY = "SmartScaleFactor";
            //$.writeln("button");
var SCRIPT_NAME = "SmartScaleTool";
            p = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
    var defaultScale = 4;  
            checkOutFolder(p);
    var comp = app.project.activeItem;
            explore(p);
    var selectedLayers = comp.selectedLayers;
        }
    var scaleFactorX;
        shotToolsBtn3.onClick = function(){
 
            app.project.bitsPerChannel = 16;
     function getScaleFactor(set) {
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
var factor = defaultScale;
            importFolder = new Folder(targetFolder+"/Source");
if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){
            SmartImport(importFolder);
factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);
        }
}
        shotToolsBtnXLS.onClick = function(){
if(set){
            loc = rootFolder+"/"+showDDL.selection.toString();
var promptResult = prompt("Enter the scale factor (default is 400% -> 4)\nThen select layer(s) and run again.", factor);
            fold = new Folder(loc.toString());
if (promptResult === null) return null;  
            files = fold.getFiles("*.xls*");
factor = parseFloat(promptResult);
            if(files.length > 0){
app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);
                files[0].execute();
}
            }else{
 
                writeLn("No .xls shortcut found in in:");
         return factor;
                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();
 
     if (selectedLayers.length === 0) {
        scaleFactorX = getScaleFactor(1);
        return;
     }
     }
    dandyShotBuilderUI(this) ;
 
</pre>


    scaleFactorX = getScaleFactor();
    if (scaleFactorX === null) return;
    var scaleMultiplier = scaleFactorX;
    var inverseMultiplier = 1 / scaleFactorX;


    app.beginUndoGroup("Smart Scale Precomposition");


===layer Position to .txt===
    try {
https://i.imgur.com/G5DX1T0.gif
        for (var i = 0; i < selectedLayers.length; i++) {
Old, might not work! (but should :)
            var layer = selectedLayers[i];
<pre>
           
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + "_Scale", false);
//
            var precompLayer = app.project.activeItem.selectedLayers[0];
// The script will write to a text file the x & y values of the layer position for every frame comprised in
precompLayer.collapseTransformation = false;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected
 
//
            // 2. Inside the precomposition, scale the layer and composition
//
            if (precomp.numLayers > 0) {
function timeToFrameNum(myTime){
                var innerLayer = precomp.layer(1);
  return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
                innerLayer.collapseTransformation = true;
}
                // Scale the inner layer
function framesToTime(myTime){
                var currentScale = innerLayer.property("ADBE Transform Group").property("ADBE Scale").value;
  return myTime/app.project.activeItem.frameRate;
                innerLayer.property("ADBE Transform Group").property("ADBE Scale").setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);
}
               
function timeToTimeCode(myTime){
                // Adjust composition size accordingly
  var framesN = myTime * app.project.activeItem.frameRate;
                precomp.width *= scaleMultiplier;
  fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));
                precomp.height *= scaleMultiplier;
  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){
                // Adjust the position of the layer within the precomp to keep it centered
  if(app.project.activeItem.selectedLayers.length != 0){
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
                innerLayer.property("ADBE Transform Group").property("ADBE Position").setValue(newPos);
        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);
             // 3. Back in the top composition, scale down the precomposed layer by 1/X
             for(i=startLoop;i<=endLoop;i++){
             var currentOuterScale = precompLayer.property("ADBE Transform Group").property("ADBE Scale").value;
              var curTime = framesToTime(i);
            precompLayer.property("ADBE Transform Group").property("ADBE Scale").setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);
              out = formatString.replace('%x',curLayer.property("position").valueAtTime(curTime,true)[0]);
        }
              out = out.replace('%y',curLayer.property("position").valueAtTime(curTime,true)[1]);
    } catch (e) {
              out = out.replace('%f',i);timeToTimeCode
        alert("An error occurred: " + e.toString());
              out = out.replace('%t',timeToTimeCode(curTime));
    } finally {
              out = out.replace('%i',i-startLoop);
        app.endUndoGroup();
              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;
</pre>
</pre>
==Other Scripts==


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

Latest revision as of 13:06, 19 October 2026

Scripts

Contact Sheet from Selected Layers

screenshot TBD

one-shot from Gemini :0

(function createContactSheet() {
    var comp = app.project.activeItem;
    if (!comp || !(comp instanceof CompItem)) {
        alert("Please select a composition.");
        return;
    }

    var selectedLayers = comp.selectedLayers;
    var numLayers = selectedLayers.length;
    if (numLayers === 0) {
        alert("Please select at least one layer.");
        return;
    }

    var cols = Math.ceil(Math.sqrt(numLayers));
    var rows = Math.ceil(numLayers / cols);

    var cellWidth = comp.width / cols;
    var cellHeight = comp.height / rows;

    app.beginUndoGroup("Create Contact Sheet");

    for (var i = 0; i < numLayers; i++) {
        var layer = selectedLayers[i];
        
        var col = i % cols;
        var row = Math.floor(i / cols);

        var layerBounds = layer.sourceRectAtTime(comp.time, false);
        var layerWidth = layerBounds.width;
        var layerHeight = layerBounds.height;

        if (layerWidth === 0 || layerHeight === 0) continue;

        var scaleX = (cellWidth / layerWidth) * 100;
        var scaleY = (cellHeight / layerHeight) * 100;
        var finalScale = Math.min(scaleX, scaleY);

        layer.property("Transform").property("Scale").setValue([finalScale, finalScale]);

        var posX = (col + 0.5) * cellWidth;
        var posY = (row + 0.5) * cellHeight;

        layer.property("Transform").property("Position").setValue([posX, posY]);
    }

    app.endUndoGroup();
})();

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();
}