Difference between revisions of "Afx Javascript"

From bernie's
Jump to navigation Jump to search
 
(4 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
*
{
*
*
*      simpleLocalProxy.jsx  
*
*
*
************************************************************************************************************/
 
 
function e(str){
    //uncomment to allow debugging
    //$.writeln(str);
      
      
}
var currentSlider = "none";


function pathToLocalizedPath(path){
        f = new File(path);
        return f.fsName.toString();
}


function sequenceFilesWildcard(path){  
function watchFolderUI(thisObj){
     //returns false or an array with everything before a sequence's image number, and the extension: /c/path/file.0555.exr > { /c/pathfile. ; .exr }
     pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Auto Expose", [100, 100, 300, 300]);
     var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g;  
     var res =  
     var match = myRegexp.exec(path);
    "group { \
     if(!match){
                alignment: ['fill','fill'], \
         return false;
                alignChildren: ['fill','top'], \
    }else{
                orientation: 'column', \
         return match;
                    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();}
function grabPaths(footage){
     pan.grp.applyKeys.onClick = function () {applyKeys();}
     //returns false or the path of the given footage, if it's a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr > /c/path/file.*.exr
 
    var f = footage;
     pan.layout.layout(true);
    returnpath = false;
    pan.layout.resize();
     if(f instanceof FootageItem && f.file != null){     
    pan.onResizing = pan.onResize = function () {this.layout.resize();}
        var source = f.mainSource.file.toString();
    return pan;
        if(!f.mainSource.isStill){
            var pathFromRegex = sequenceFilesWildcard(source);
            if(pathFromRegex){
                returnpath = pathFromRegex[1]+"*"+pathFromRegex[2];
            }else{
                returnpath = source;
            }
        }else{
            returnpath = source;
        }
     }
     }
    return returnpath;
watchFolderUI(this) ;
}


function grabFootagePathsAndCopyToLocal(){
    //main worker function
   
    var sel = app.project.selection;
    if(sel.length < 1 || !(sel[0] instanceof FootageItem)){
        alert("Select footage(s) and try again");   
    }else{
       
        var debugcheck = (getPref("debug","false")=="true")?true:false;
        var useforcecopy = (getPref("forcecopy","false")=="true")?true:false;
       
        var isMacintosh = ($.os.toLowerCase().indexOf("windows")==-1);
        var localSaveDir = getPref("localSaveDir",Folder.temp.toString());
       
        var batchFile = (isMacintosh)?"# bash file used to copy After Effects footage to local storage":"@echo off\nREM batch file to copy After Effects footage to local storage";
        for(i=0;i<app.project.selection.length;i++){
            if(!sel[i].useProxy){
                //grab path from current selection item
                var curPath = grabPaths(sel[i]);
                var fileName = curPath.split('/').pop();
                var dir = curPath.substring(0,curPath.lastIndexOf('/')+1);
               
                var outputDir = "";
                if(getPref("usemd5",true)=="true"){
                    outputDir = localSaveDir + "/"+ jsmd5(dir);
                }else{
                    outputDir = localSaveDir + dir;
                }
           
                if(isMacintosh){
                    //macos uses rsync to copy files
                   
                    batchFile += "\nrsync -v -a ";
                    batchFile += ((useforcecopy)?"-I ":"");
                    batchFile += dir+fileName;
                    batchFile += " "+outputDir;
                   
                }else{
                   
                    //windows uses robocopy
                    sourcePath = pathToLocalizedPath(dir);
                    destinationPath = pathToLocalizedPath(outputDir);
                    filename = fileName.replace(/%20/g, " ");
                   
                    batchFile += "\nrobocopy ";
                    batchFile += "\"";
                    batchFile += sourcePath;
                    batchFile += "\"";
                    batchFile += " ";
                    //robocopy filename requires a wildcard, even if it's a single file
                    batchFile += "\"";
                    batchFile += destinationPath ;
                    batchFile += "\"";                   
                    batchFile += " ";
                    batchFile += "\"";
                    batchFile += filename ;
                    batchFile += "*\"";
                    batchFile += ((useforcecopy)?" /XO":"")+" /FFT"+((debugcheck)?"":" /NJH /NJS");
                    e(batchFile);
                }
            }
        }
        batchFile += ((debugcheck)?(isMacintosh?"\nread a":"\npause"):""); //nested tertiary operators, sue me
       
        var txtFile;
        if(isMacintosh){
            txtFile = new File(localSaveDir+"/AFX_footage_copy.command");
        }else{
            txtFile = new File(localSaveDir+"/AFX_footage_copy.bat");
        }
        if(txtFile.open("w","TEXT","????") == true){


            txtFile.write(batchFile);
            txtFile.close();
    if(isMacintosh){


            system.callSystem("chmod +x " + txtFile.toString() +"");
function e(s){
system.callSystem(txtFile.toString());
    $.writeln(s);
}else{
            txtFile.execute();
}
}
        }else{
 
            alert("Write permission denied on\n"+localSaveDir);
 
 
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;
         }
         }
        //txtFile.remove();
     }
     }
}
     precomplayer.enabled = true;     
function grabFootagePathsAndSwitchProxy(){
     precomplayer.solo = true;
     var sel = app.project.selection;
 
    if(sel.length < 1){
    var toplayer = precomp.layers[1];
        alert("Select footage first");     
    var props = toplayer.property("ADBE Effect Parade");
     }else{
    while(props.numProperties>0){
        for(i=0;i<app.project.selection.length;i++){
        props.property(1).remove();
            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);
                    }
                }
            }
        }
     }
     }
}
    var newlayer = toplayer.duplicate();


function setPref(pref,value){
     newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;
     app.settings.saveSetting("simpleLocalProxy", pref, value);
     newlayer.startTime += app.project.activeItem.frameDuration;
}
    newlayer.timeRemapEnabled = true;
function getPref(pref,defaultValue){
     newlayer.inPoint -= app.project.activeItem.frameDuration;
     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 explainer = new MarkerValue("1 frame shift + difference blendmode = highlight pixel changes");
     pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Simple Local Proxy", [100, 100, 300, 300]);
     newlayer.property("Marker").setValueAtTime(.5, explainer);
   
    var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
     if (securitySetting != 1) {
        pan.add("statictext",[15,15,300,45],"Set prefs and re-launch");
        alert("You need to check \"Allow Scripts to Write Files and Access Network\" in your preferences for this script to work");
    }else{
        var localFolder = getPref("localSaveDir",Folder.temp.toString());
        localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,"\\\\");
       
        // UI DESCRIPTION
       
        res = "group { alignment: ['fill','fill'], alignChildren: ['fill','top'], orientation: 'column', \
                        cols: Group {orientation:'row',align:'left', alignChildren:['fill','top'],\
                            col1: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
                                saveDirText: StaticText {text: 'Proxy folder setup'},\
                                saveDirOptions: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
                                    usemd5rbox: RadioButton {text: ' Simple',helpTip:'Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)',value:true},\
                                    usepathrbox: RadioButton {text: ' Copy Folder Structure',helpTip:'Copies the target folder structure inside the proxy folder; more folders'}}},\
                            col2: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
                                optionsText: StaticText {text: 'Options: '},\
                                optionsGrp: Group {orientation:'column',align:'left', alignChildren:['fill','center'],\
                                    forcecopyChkbox: Checkbox {text: ' Force copy',helpTip:'Overwrite files (otherwise skips existing files)'},\
                                    debugChkbox: Checkbox {text: ' Debug',helpTip:'Show full batch process and pause at end of copies'}}}},\
                        cols2: Group {orientation:'row',align:'left', alignChildren:['fill','top'],\
                            localSaveDirBut: Button {text: ' Choose Proxy Folder ', helpTip:'choose folder to copy files to',preferredSize:[-1,30]} , \
                            browseBut: Button {text: ' Browse ' , preferredSize:[-1,30]}} , \
                        curentDirTxt: EditText {text: '" + localFolder +"',enabled:false},\
                        copyFootageBut: Button {text: ' Copy Footage(s) to Proxy Folder ' ,helpTip:'Launches a background batch copy of selected footage',preferredSize:[-1,30]} , \
                        switchproxyBut: Button {text: ' Switch Proxy/Original ',helpTip:'Switches from original to proxy path and back,  warning if no local copy has been found' ,preferredSize:[-1,30]} , \
                    }";


        //UI DRAW
    var blackSolid = precomp.layers.addSolid([0,0,0], "Black", precomp.width, precomp.height, 1);
       
    blackSolid.moveToEnd();
        pan.grp = pan.add(res);  
        pan.layout.layout(true);
       
        //radio buttons and checkboxes prefs
         


        var usemd5 = getPref("usemd5",true);
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5=="true")?true:false;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5=="true")?false:true;


        var useforcecopy = getPref("forcecopy","false");
    var sliderctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy=="true")?true:false;
    sliderctrl.name = "Resolution";
        var debugcheck = getPref("debug","false");
    var resolutionslider = sliderctrl.property("ADBE Slider Control-0001");
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck=="true")?true:false;
 
    resolutionslider.setValue(3);


        pan.layout.resize();
    var detectorctrl = precomplayer.Effects.addProperty("ADBE Slider Control");
        pan.onResizing = pan.onResize = function () {this.layout.resize();}
    detectorctrl.name = "Detector";
 
    var detectorslider = detectorctrl.property("ADBE Slider Control-0001");
        // UI ACTIONS
    detectorexpression = "\
          
    resolution = effect(\"Resolution\")(\"ADBE Slider Control-0001\");\
        //browse button
    resolution = (resolution<1)?1:resolution;\
        pan.grp.cols2.browseBut.onClick = function(){
    a = [0,0,0,0];\
                localSaveDir = new Folder(getPref("localSaveDir",Folder.temp.toString()));
    for(i=0;i<resolution;i++){\
                localSaveDir.execute();
         for(j=0;j<resolution;j++){\
        }
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\
        //choose local folder button
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\
        pan.grp.cols2.localSaveDirBut.onClick = function(){
            a+= sampleImage(center, sampledistance , postEffect = true, t = time);\
            localSaveDir = new Folder(getPref("localSaveDir",Folder.temp.toString()));
        }\
            o = localSaveDir.selectDlg("Choose folder to copy footage to");
    }\
            if(o!=null){
    \
                    setPref("localSaveDir",o.toString());
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\
                    pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,"\\\\");
    ";
            }
 
        }
    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 );
      
      
        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);
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.
    currentSlider.setValueAtKey(1, 0);
 
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);
<youtube width="1000" height="800" >9-3XVlME2tY</youtube>


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


<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){
</pre>
    pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Auto Expose", [100, 100, 300, 300]);
 
    var res =  
=== Create Null controllers on Puppet pins ===
    "group { \
http://i.imgur.com/HdFjaYZ.jpg
                alignment: ['fill','fill'], \
<pre>
                alignChildren: ['fill','top'], \
//nulls created from puppet pins can be parented like normal layers
                orientation: 'column', \
{
                    setupDetector: Button {text: 'Step 1: setup detector on layer' ,preferredSize:[-1,30]} , \
    app.beginUndoGroup("Create Null Controls on Puppet Pinsv");
                    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} , \
     function getLayerFromProperty(prop){
                    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}} , \
         return prop.propertyGroup(prop.propertyDepth)
                    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");
            var launchWorker = group2.add("button", undefined, undefined, {name: "launchWorker"});
}else{
            launchWorker.helpTip = "Saves a .bat file to your desktop that launches a worker";
        var saved = app.project.file;
            launchWorker.text = "Launch a worker";
        var curFile = app.project.file.name;
    launchWorker.enabled = (watchfolderLocation=="none")?false:true;
        curFile = curFile.substring(0, curFile.length - 4);
 
        var myFolder = new Folder(wf + "/" + curFile + "_wf/");
 
        myFolder.create();
            var switchToWorker = group2.add("button", undefined, undefined, {name: "switchToWorker"});
        writeLn("Copying AEP to watchfolder.");
            switchToWorker.helpTip = "Turns the current After Effects into a watchfolder worker";
        var mySaveFile = new File(myFolder.toString() + "/" + curFile + ".aep");
            switchToWorker.text = "Set as worker";
        saved.copy(mySaveFile);
            switchToWorker.enabled = (watchfolderLocation=="none")?false:true;
        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;
        }


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


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


    }
    if (watchfolderLocation != "none") {
    function startAERender() {
                curWF.text = "Folder: " + Folder(watchfolderLocation).fsName;
//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) {
            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);
    function watchFolderUI(thisObj) {
        myFolder.execute();
        var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
                //watchfolderLocation
        if (securitySetting != 1) {
            }
            alert("You need to check 'Allow Scripts to Write Files and Access Network' in your preferences for this script to work");
            sendWF.onClick = function() {
        } else {
                if (watchfolderLocation == "none") {
                    setWatchFolder();
                } else {
                    sendToWF(watchfolderLocation);
                }
            }


        }
    }
    watchFolderUI(this);
}
</pre>


===Show original source location (WIN only)===
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;
    }


             var panelGlobal = thisObj;
    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}
        }
    }
}
</pre>


            /*
            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, {
===List selected layers effects and their properties matchNames ===
                name: "group0"
<pre>
            });
//windows only
            grp.alignement = ["fill", "fill"];
if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
             grp.alignChildren = ["fill", "top"];
    var txtFile = new File("~/Desktop/effectList.txt");
            grp.orientation = "column";
    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")


                    }
            }
    }


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


http://i.imgur.com/IEUwe.gif


==== Disable effects ====
Disable/enables all the effects that are similar to the currently selected one throughout the project. This was fully GPT generated on first try. Impressed!
<pre>
(function toggleEffectGlobally() {
    app.beginUndoGroup("Toggle Effect Globally");


            var group1 = grp.add("group", undefined, {name: "group1"});
    var sel = app.project.activeItem && app.project.activeItem.selectedProperties;
            group1.orientation = "row";
    if (!sel || sel.length === 0) {
            group1.alignChildren = ["fill", "top"];
        alert("Please select an effect first.");
            group1.spacing = 10;
        return;
            group1.margins = 0;
    }


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


            var openWF = group1.add("button", undefined, undefined, {name: "openWF"});
    // Find the selected effect property group
    openWF.enabled = (watchfolderLocation=="none")?false:true;  
    for (var i = 0; i < sel.length; i++) {
             openWF.text = "Open WF";
        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 curWF = grp.add("statictext", undefined, undefined, {name: "statictext2"});
    var effectName = selectedEffect.matchName;
            curWF.helpTip = "shows current watchfolder";
    var effectEnabled = selectedEffect.enabled;
            curWF.text = "(choose watchfolder)";


    // 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 sendWF = grp.add("button", undefined, undefined, {name: "sendWF"});
    app.endUndoGroup();
            sendWF.helpTip = "save file first!";
})();
            sendWF.text = "Send .aep to Watchfolder";
</pre>
 
            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"});
===List effects===
            group2.orientation = "row";
            group2.alignChildren = ["fill", "top"];
            group2.spacing = 10;
            group2.margins = 0;


            var launchWorker = group2.add("button", undefined, undefined, {name: "launchWorker"});
<pre>
            launchWorker.helpTip = "Saves a .bat file to your desktop that launches a worker";
function projEffects(){
            launchWorker.text = "Launch a worker";
var effects = new Array();
    launchWorker.enabled = (watchfolderLocation=="none")?false:true;
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>


            var switchToWorker = group2.add("button", undefined, undefined, {name: "switchToWorker"});
http://i.imgur.com/hFNiQ.jpg
            switchToWorker.helpTip = "Turns the current After Effects into a watchfolder worker";
            switchToWorker.text = "Set as worker";
            switchToWorker.enabled = (watchfolderLocation=="none")?false:true;


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




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


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


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


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


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


===Show original source location (WIN only)===
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;
listFile.open("r","TEXT","????");  
    if(sel.length == 0){
contents = listFile.read();
        alert("You need to select source(s) in the project panel");
arrayContents = contents.split("\n");
    }else{
listFile.close();
        for(i=0;i<app.project.selection.length;i++){
timer = (Date.now()-startT)/1000;
            a = prompt("'OK' continues, 'cancel' stops displaying original sources\n\n[ "+sel[i].name+" ]",pathToWinPath(sel[i].mainSource.file.path.toString()));  
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");
            if(!a){break}
        }
    }
}
</pre>




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


===List selected layers effects and their properties matchNames ===
if(dialog){    
<pre>
     for(i = 0;i<unique.length;i++){
//windows only
         if(unique[i].length>0){
if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
             sequenceStartFile = new File(sourceFolder.toString()+"/"+unique[i]+myArray[0]);
    var txtFile = new File("~/Desktop/effectList.txt");
           
    txtFile.open("w","TEXT","????");
            if (sequenceStartFile) {
    var col = app.project.activeItem.selectedLayers;
            writeLn("Loading "+(i+1)+"/"+unique.length);
     for (i=0;i<col.length;i++){
                try {
         effs = col[i].property("ADBE Effect Parade");
                     // Create a variable containing ImportOptions.
             for(j=1;j<=effs.numProperties;j++){
                    var importOptions = new ImportOptions(sequenceStartFile);
                txtFile.write("\n( "+effs.property(j).matchName+" ) "+effs.property(j).name+"\n------------------------------------------\n");
                    importOptions.sequence = true;
                for(k=1;k<=effs.property(j).numProperties;k++){
                     try {
                    txtFile.write(effs.property(j).property(k).matchName+" --> "+effs.property(j).property(k).name);
                     if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE && effs.property(j).property(k).value != undefined){
                        txtFile.write(" [ "+effs.property(j).property(k).value.toString()+" ] ");
                     }
                    txtFile.write("\n")


                        app.project.importFile(importOptions);
                    } catch (error) {
                      e(error.toString());
                     }
                     }
                } catch (error) {
                    e(error.toString());
                }
             }
             }
        }
     }
     }
    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{
}else{
     alert("Set scripting Prefs to enable to write to disk");
     writeLn("Canceled");
}
}
timer = (Date.now()-startT)/1000;
e("after regext: "+timer);
</pre>
</pre>


http://i.imgur.com/IEUwe.gif
=== Split to Renderqueue ===
 
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif
==== Disable effects ====
Disable/enables all the effects that are similar to the currently selected one throughout the project. This was fully GPT generated on first try. Impressed!
<pre>
<pre>
(function toggleEffectGlobally() {
// Split Layers to Renderqueue v2.0
    app.beginUndoGroup("Toggle Effect Globally");
// ----------------------------------------------
//
//  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


    var sel = app.project.activeItem && app.project.activeItem.selectedProperties;
function pad(n, width, z) {
    if (!sel || sel.length === 0) {
  z = z || '0';
        alert("Please select an effect first.");
  n = n + '';
        return;
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
    }
}


    var selectedEffect = null;
app.beginUndoGroup("Split Layers to Renderqueue");


    // Find the selected effect property group
if(app.project.renderQueue.items.length == 1){
    for (var i = 0; i < sel.length; i++) {
    FE = app.project.renderQueue.items[1];
        if (sel[i].matchName && sel[i].parentProperty && sel[i].parentProperty.matchName === "ADBE Effect Parade") {
    ai = FE.comp;
            selectedEffect = sel[i];
    path = FE.outputModule(1).file.path;
            break;
     for(i=1;i<=ai.layers.length;i++){
        }
     }


    if (!selectedEffect) {
        RI = app.project.renderQueue.items[1].duplicate();
         alert("Please select an effect in the timeline.");
         RI.timeSpanStart = ai.layers[i].inPoint;
         return;
        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;
</pre>


    var effectName = selectedEffect.matchName;
=== Docked Panel SNIP ===
    var effectEnabled = selectedEffect.enabled;
<pre>
 
//script panel
    // Loop through all comps in the project
{
    for (var p = 1; p <= app.project.numItems; p++) {
var nested_file = new File("U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx");
        var item = app.project.item(p);
nested_file.open("r");
        if (item instanceof CompItem) {
eval(nested_file.read());
            for (var l = 1; l <= item.numLayers; l++) {
nested_file.close();
                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();
})();
</pre>
</pre>
===List effects===
<pre>
<pre>
function projEffects(){
//called file
var effects = new Array();
function createUI(thisObj) {
var effects2 = new Array();
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",[100, 100, 300, 300]);
for(var i = 1; i <= app.project.numItems; i++){
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
if(app.project.item(i) instanceof CompItem){
//impButton.onClick = openfile;
  var comp = app.project.item(i);
return myPanel;
for(j = 1; j <=  comp.layers.length;j++){
}
effs = comp.layer(j).property("ADBE Effect Parade");
var myToolsPanel = createUI(this);
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>
</pre>


http://i.imgur.com/hFNiQ.jpg
===Watchfolder watcher (WIP)===
http://i.imgur.com/lhO3k.gif


===Multiple sequences import (Windows)===
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>
<pre>
// Multiple sequences import (windows)
// watchwatchfolder: a tool to look at what's in the watchfolder
// ----------------------------------------------
// v1.0 by bernie - mbernadat@gmail.com
// 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.
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License.
//   > only works on windows so far
//
//   > only finds exrs/pngs/jpgs
//  known limitations (v1.0), to be fixed:
//   > probably fails if your sequences dont have the same start frame
//  -windows only for now
//  > will work with /path/image.####.jpg or similar
// -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();


function uniq_fast(a) {
//global ui names
    var seen = {};
var refreshBtn;
    var out = [];
var list;
    var len = a.length;
var wfStText;
    var j = 0;
var wfPbar;
    for(var i = 0; i < len; i++) {
var chckBox0;
        var item = a[i];
var chckBox1;
        if(seen[item] !== 1) {
var chckBox2;
              seen[item] = 1;
var chckBox3;
              out[j++] = item;
        }
    }
    return out;
}




function e(s){
function getLogonly(file){
     $.writeln(s);
     if(file.name.indexOf("Logs)") != -1){
}
        return true;
curScript = new File($.fileName);
    }
 
        return false;
startT = Date.now();
}
 
function pathToWinPath(path){
sourceFolder = app.project.selection[0].mainSource.file.parent;
str = path.replace(/\//, "");
tmpFolder = Folder.temp;
str = str.replace(/\//, ":/");
tmpBat = Folder.temp.toString()+"/ae_dirlist.bat";
str = str.replace(/%20/g, " ");
tmpFilesList = Folder.temp.toString()+"/ae_imageslist.txt";
str = str.replace(/\//g, "\\");
var listFile = new File(tmpFilesList);
return str;
 
}
 
function localToRessource(path){
 
    //windows, for now, the only one available!
var batFile = new File(tmpBat);
    str = "/"+path.replace(":\\", "/");
batFile.open("w","TEXT","????");
    str = str.replace(/\\/g, "/");
batFile.write("REM Auto generated by this after effects script file: "+curScript.fsName);
    str = str.replace(" ", "%20");
batFile.write("\npushd \""+sourceFolder.fsName+"\"");
    str = str.substring(0,str.lastIndexOf("/"));
batFile.write("\ndir /b /on *.exr *.png *.jpg *.jpeg > \""+listFile.fsName+"\"");
    return str;
batFile.close();
}
system.callSystem(batFile.fsName);
function returnFolderArray(location){
 
    fold = new Folder(location.toString());
 
    foldArray = fold.getFiles()
listFile.open("r","TEXT","????");  
    folderOnly = new Array();
contents = listFile.read();
    for(i=0;i<foldArray.length;i++){
arrayContents = contents.split("\n");
        if(foldArray[i] instanceof Folder){
listFile.close();
            folderOnly[folderOnly.length] = foldArray[i];
timer = (Date.now()-startT)/1000;
            }
e("via batch: "+arrayContents.length+" files found in "+timer+"s ");
        }
 
    return folderOnly;
 
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);
function logInfo(logFolderLocation){
 
   
dialog = confirm(unique.length+" sequences found. Load them ? \nIt might take a long time !",false,"Confirm Loading");
    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,""];
            }


if(dialog){     
         }else{
    for(i = 0;i<unique.length;i++){
          // $.writeln("Buggy file");
         if(unique[i].length>0){
             info=[logFolderLocation.name.toString(),1,"--Bug--",logFolderLocation.toString()+"eeee"];
            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());
                }
            }
         }
         }
    }
        fold = new Folder(folds[0].toString());
}else{
        files = fold.getFiles("*.txt");  
    writeLn("Canceled");
        firstFile = files[files.length-1]; //used to be first file, works better with last text file
}
        if(firstFile == undefined || firstFile == null){
timer = (Date.now()-startT)/1000;
              info=[logFolderLocation.name.toString(),1,"Error",htmlfile];
e("after regext: "+timer);
        }else{
</pre>
                //info = ["debug",logFolderLocation.toString(),logFolderLocation.name.toString()];
 
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];
=== Split to Renderqueue ===
             
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif
                firstFile.open("r","TEXT","????");
<pre>
                contents = firstFile.read();
// Split Layers to Renderqueue v2.0
                firstFile.close();
// ----------------------------------------------
                lines = contents.split("\n");
//
              //  $.writeln(logFolderLocation.toString()+" <<< ")
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp
              info[3] = logFolderLocation.toString();
// using the in and out points. I use this along with 'Magnum' the edit detector to split & render sequences.
              info[0] = logFolderLocation.name.toString();
//
                for(i=0;i<lines.length;i++){
//  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
                    //$.writeln("Doing shit");
 
                    phrase ="Rendering started on";
function pad(n, width, z) {
                    info[4] = "n/a";
  z = z || '0';
                    if(s = lines[i].indexOf(phrase) != -1){
  n = n + '';
                        info[4] = lines[i].substring(s+phrase.length);       
  return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
                    }
}
                    phrase ="Output To: ";
 
                 
app.beginUndoGroup("Split Layers to Renderqueue");
                  // info[2] = "n/a+";
 
//                    info[1] = 1;
if(app.project.renderQueue.items.length == 1){
                   
    FE = app.project.renderQueue.items[1];
                    if(info[1]==0 ||info[1]==2){
    ai = FE.comp;
                      // $.writeln( lines[i].indexOf(phrase));
    path = FE.outputModule(1).file.path;
                            if(s = lines[i].indexOf(phrase) != -1){
    for(i=1;i<=ai.layers.length;i++){
                                s = lines[i].indexOf(phrase);
 
                                v = lines[i].substring(s+phrase.length);
        RI = app.project.renderQueue.items[1].duplicate();
                                wfold = v.substring(0,v.lastIndexOf("\\"));
        RI.timeSpanStart = ai.layers[i].inPoint;
                              //$.writeln(wfold);
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;
                                f =  wfold.substring(wfold.lastIndexOf("\\")+1,wfold.length);
        $.writeln(ai.workAreaDuration+" "+ai.layers[i].outPoint);
                                ///info[3] = pathToWinPath(wfold);
        RI.outputModule(1).file = new File(path+"/"+pad(i,2,"0")+"_"+ai.layers[i].name);
                                info[3] =localToRessource(wfold);
     }
                                $.writeln(info[3])
     app.project.renderQueue.items[1].render= false;
                                info[0] = f;
}else{
                              // info[2] = " ";
     alert("Only 1 element should be in renderqueue");
                            }
                        }
                  // 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;
     }
}
}
app.endUndoGroup;
</pre>


=== Docked Panel SNIP ===
function getFilePath(watchedFolderLocation){
<pre>
    fold = new Folder(watchedFolderLocation.toString());
//script panel
    files = fold.getFiles(getLogonly);
{
    alert(files[0].name);
var nested_file = new File("U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx");
    return files[0].name;
nested_file.open("r");
eval(nested_file.read());
nested_file.close();
}
}
</pre>
/*
<pre>
function getPercentage(folder){
//called file
    myFolder = new Folder(folder);
function createUI(thisObj) {
    files = myFolder.getFiles("*DandyWatch.txt");
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",[100, 100, 300, 300]);
    if(files.length != 1){
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
        return false;
//impButton.onClick = openfile;
    }
return myPanel;
    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();
}
}
var myToolsPanel = createUI(this);
//getPercentage(watchfolderLocation+"/"+shot);
</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.
 
/*
todo: check if icons are here
*/
*/


{
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 = "";
//global ui names
        if(folders[folder].name != "anonymous"){ //??
var refreshBtn;
            counter2++;
var list;
            if(hasStarted(folders[folder])){
var wfStText;
                state = 0;
var wfPbar;
                out = logInfo(folders[folder]);
var chckBox0;
                //$.writeln(out);
var chckBox1;
                //out[3] = localToRessource(out[3]);
var chckBox2;
                                 
var chckBox3;
            }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){
function getLogonly(file){
            pal.update();
    if(file.name.indexOf("Logs)") != -1){
        }else{
         return true;
            if(counter < Math.round(wfPbar.value/5,0)*5){
                counter +=5;
                clearOutput();
                writeLn(counter+"%");
            }else{
                write(".");
            }
         }
     }
     }
        return false;
    return vArray;
}
}
function pathToWinPath(path){
function gather(){
str = path.replace(/\//, "");
                refreshBtn.text = "refreshing...";
str = str.replace(/\//, ":/");
                refreshBtn.enabled = false;
str = str.replace(/%20/g, " ");
                wfStText.visible = false;
str = str.replace(/\//g, "\\");
                wfPbar.visible = true;
return str;
                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 localToRessource(path){
function watchWatchFolder(thisObj){
    //windows, for now, the only one available!
 
    str = "/"+path.replace(":\\", "/");
        //UI Design
    str = str.replace(/\\/g, "/");
        {
    str = str.replace(" ", "%20");
            pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", "WatchFolder", undefined, {resizeable:true});
    str = str.substring(0,str.lastIndexOf("/"));
            var winGfx = pal.graphics;
    return str;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);
}
             pal.bounds = [300,200,600,600];      
function returnFolderArray(location){
             var wfLocBtn = pal.add("button", [10,10,105,35],"Set Watchfolder");
    fold = new Folder(location.toString());
            wfStTextContents = ( wf != null)?app.settings.getSetting("watchwatchfolder", "wfloc"):"(not set...)";
    foldArray = fold.getFiles()
            wfStText = pal.add("edittext",[115,12,285,33], wfStTextContents);
    folderOnly = new Array();
                wfStText.active = false;
    for(i=0;i<foldArray.length;i++){
                wfStText.enabled = false;
        if(foldArray[i] instanceof Folder){
            wfPbar = pal.add("progressbar",[115,12,285,33],0,100);
             folderOnly[folderOnly.length] = foldArray[i];
                wfPbar.visible = false;
             }
             var sortStText = pal.add("statictext",[17,42,80,60], "Sort by: ");
        }
             var sortByName = pal.add('radiobutton',[62,40,120,57], 'name');
    return folderOnly;
             var sortByStatus = pal.add('radiobutton',[115,40,170,57], 'status');
}
 
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;
              
              
}
                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();   
           
}
            refreshBtn.onClick = function(){
function retrieveArray(){
                if((timer>0 && !firstTime) || !pal.visible){
    var state = 4; //queued, default state
                        timer = 0;
    var vArray = new Array();
                      // $.writeln("stopped");
    l =0;
                        refreshBtn.text = "Start";
    counter=0;
                        firstTime = true;
    counter2=0;
                        cancelTask(cancelTaskID);
    folders = returnFolderArray(wf);
                }else{
    for(folder in folders){
                    if(reloadEditTxt.text == 0 || firstTime){
    l++;
                            gather();
        var out = "";
                            firstTime = false;
        if(folders[folder].name != "anonymous"){ //??
                    }
            counter2++;
                }
            if(hasStarted(folders[folder])){
                  
                state = 0;
                 ///timer = parseInt(reloadEditTxt.text)/refreshRate;
                 out = logInfo(folders[folder]);
                 //refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
                 //$.writeln(out);
                 //cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
                 //out[3] = localToRessource(out[3]);
                                 
            }else{
                 state = 4;
                out = [folders[folder].name,4,"",folders[folder].toString()];
             }
             }
             //$.writeln(">>> "+state+" - "+out+" - "+folders[folder].name);
             reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};
              
             reloadEditTxt.onChange = function(){
          // $.writeln(out);
                if(reloadEditTxt.text < 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){
            vArray[counter2] =out;
                    timer = 0;
        }
                    reloadEditTxt.text = 0;
      // $.writeln(vArray[counter2]);
                    refreshBtn.text = "Refresh";
        wfPbar.value = l/folders.length*100;
                //}else if(reloadEditTxt.text >=10){
 
                }else{
        if (pal instanceof Window){
                    if(timer <= 0){
            pal.update();
                        refreshBtn.text = "Start (~ "+eggTimer(reloadEditTxt.text)+")";
        }else{
                    }
            if(counter < Math.round(wfPbar.value/5,0)*5){
                 }
                counter +=5;
            reloadEditTxt.enabled=false;              
                clearOutput();
                writeLn(counter+"%");
            }else{
                 write(".");
             }
             }
         }
         }
     }
      
     return vArray;
      
}
        //Initialize the whole shebang
function gather(){
            if (pal instanceof Window){
                 refreshBtn.text = "refreshing...";
                 pal.center();
                refreshBtn.enabled = false;
                 pal.show();
                wfStText.visible = false;
            }
                wfPbar.visible = true;
 
                shotinfos = retrieveArray();
       
                 list.removeAll();
      return pal;
                for(i=1;i<shotinfos.length;i++){
    }
                    var p = shotinfos[i];
//reloadEditTxt.notify("onChange");
                                      //  $.writeln("Debug");$.writeln(p);
ui = watchWatchFolder(this);
                    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
function eggTimer(time){
        {
    if(time<=59){
            pal = (thisObj instanceof Panel) ? thisObj : new Window("palette", "WatchFolder", undefined, {resizeable:true});
        time = time+"s";
            var winGfx = pal.graphics;
    }else if(time<=60*4){
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);
        time = Math.floor(time/60)+"m "+(time%60)+"s";
            pal.bounds = [300,200,600,600];       
    }else if(time<60*60){
            var wfLocBtn = pal.add("button", [10,10,105,35],"Set Watchfolder");
        time = Math.round(time/60,0)+"m";
            wfStTextContents = ( wf != null)?app.settings.getSetting("watchwatchfolder", "wfloc"):"(not set...)";
    }else{
            wfStText = pal.add("edittext",[115,12,285,33], wfStTextContents);
        time = Math.round(time/(60*60),0)+"h";
                wfStText.active = false;
    }
                wfStText.enabled = false;
    return time;
            wfPbar = pal.add("progressbar",[115,12,285,33],0,100);
    }
                wfPbar.visible = false;
function loop(){
            var sortStText = pal.add("statictext",[17,42,80,60], "Sort by: ");
    timer--;
            var sortByName = pal.add('radiobutton',[62,40,120,57], 'name');
  //  $.writeln(timer+" pal.visible:"+pal.visible);
            var sortByStatus = pal.add('radiobutton',[115,40,170,57], 'status');
    refreshBtn.text = "Click to stop ("+eggTimer(timer*5)+")";
    if(timer<=0 || !pal.visible){


 
        cancelTask(cancelTaskID);
           
        if(reloadEditTxt.value == 0 || !pal.visible){
                sortByName.value = true;
         
            var bottomElements = pal.add("panel",[10,310,260,360],undefined,{borderStyle:"none"});
        }else{
           
            gather();
            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");
function cancelTask(id){
            chckBox0 = bottomElements.add('checkbox',[22,bl,39,bl+16], '');
        app.cancelTask(id);
            chckBox0.value = true;
    }
            chckBox0.helpTip = "Show finished renders";
 
           
function grow(palette,value){
            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;
function addItem(name,state,msg){
            chckBox1.helpTip = "Show errors";
    //    alert(listItem.selection);
           
    var arrayV = new Array("green","red","orange","graygreen","gray");
            bottomElements.add('image',[85,bl,100,bl+14],scriptFolder.toString()+"/flag_"+arrayV[2]+".png");
    //var texted = new Array("...","error:","rendering","...","queued");
             chckBox2 = bottomElements.add('checkbox',[103,bl,120,bl+16], '');
    var item = list.add ('item',name);
            chckBox2.value = true;
    //$.writeln(scriptFolder.toString()+"/flag_"+array[state]+".png");
            chckBox2.helpTip = "Show rendering";
    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);
         
            
            
            bottomElements.add('image',[125,bl,140,bl+14],scriptFolder.toString()+"/flag_"+arrayV[4]+".png");
          /*
            chckBox3 = bottomElements.add('checkbox',[143,bl,160,bl+16], '');
var item1 = list.add ('item', 'GB15_SC138_T1');
            chckBox3.value = true;
item1.image = File("~/Desktop/flag_gray.png");
            chckBox3.helpTip = "Show queued";
item1.subItems[0].text = 'Queued...';
item1.enabled = false;
*/
 
              
              
             bl = 25;
             //alert(getPercentage(watchfolderLocation+"/"+shot));
            var reloadStTxt =  bottomElements.add("statictext",[5,bl+2,50,bl+21], "Update: ");
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+"/"+shot)*100);
            reloadEditTxt = bottomElements.add("edittext",[55,bl,90,bl+20], 600);
         
            reloadEditTxt.enabled=false;
                      // alert("test");
            refreshBtn =  bottomElements.add("button",[100,bl,260,bl+20], "Start");
              //$.writeln(files[file].path+"/"+files[file].name);
            reloadEditTxt.helpTip = "in seconds, 0 for manual refresh only.";
                            // a+= getFilePath(files[file].path+"/"+files[file].name).name+"\n";
            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
                            {
           
                               
        }
         wfLocBtn.onClick = function(){
   
            app.scheduleTask("loop()",2000,1);
   
 
        //UI Callbacks
         
         {
            fold = new Folder( watchfolderLocation.toString());
            wfLocBtn.onClick = function(){
            files = fold.getFiles();
                wfLoc = Folder.selectDialog("Select watchfolder");
                 a= "";
                if(wfLoc != null){
             for(file in files){
                    app.settings.saveSetting("watchwatchfolder", "wfloc",wfLoc.toString());
 
                    wf = new Folder(wfLoc.toString());
              $.writeln(getFilePath(files[file].path+"/"+files[file].name));
                    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
          alert(a);
                sel = list.selection;
 
             
            writeLn(pBar.value);
                if(list.selection != null){
        }
                    // $.writeln(shotinfos.join("\n"));
*/
                    sL=shotinfos.length;
</pre>
                    //$.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);
                }


===Parent to last selected layer===
Why isn't this by default? Bound to alt-P.
<pre>
(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");
}
})();
</pre>


            }       
===Import pos from maya===
           
<pre>
           
function createUI(thisObj) {
            refreshBtn.onClick = function(){
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window("palette", "Maya Track",
                if((timer>0 && !firstTime) || !pal.visible){
[100, 100, 300, 300]);
                        timer = 0;
impButton = myPanel.add("button", [10, 10, 100, 30], "Import");
                      // $.writeln("stopped");
impButton.onClick = openfile;
                        refreshBtn.text = "Start";
return myPanel;
                        firstTime = true;
}
                        cancelTask(cancelTaskID);
var myToolsPanel = createUI(this);
                }else{
//myToolsPanel.show();
                    if(reloadEditTxt.text == 0 || firstTime){
 
                            gather();
 
                            firstTime = false;
function openfile(){
                    }
            var myFile = File.openDialog ("Select track file","*.txt");  
                }
            var fileOK = myFile.open("r","TEXT","????");
               
//var fileD = OpenDlg ("Tracking Point File","*.txt",true);
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;
//txt = fileD.readln ()
                //refreshBtn.text = "Click to stop ("+(timer*5)+"s)";
alert("txt"+readTxt(myFile));
                //cancelTaskID = app.scheduleTask("loop()",refreshRate*1000,1);
}
            }
 
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};
function readTxt(myFile){
            reloadEditTxt.onChange = function(){
var myText = myFile.read();
                if(reloadEditTxt.text < 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){
return myText;
                    timer = 0;
}
                    reloadEditTxt.text = 0;
</pre>
                    refreshBtn.text = "Refresh";
 
                //}else if(reloadEditTxt.text >=10){
===Dandelion/Amazing World Of Gumball Shotbuilder===
                }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);
http://imgur.com/xKJWB.png
        if(reloadEditTxt.value == 0 || !pal.visible){
 
         
 
        }else{
For education purposes only, the script was run in production to helb build shots, it:
            gather();
* 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
function cancelTask(id){
* automatically sent files to the watchfolder to render on a small-ish farm.  
        app.cancelTask(id);
Other buttons allowed to check for missing footage, open the comp's folder, open the current shows' latest animatic, etc
    }


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;


}
<pre>
///////////////////////////////////////////
//
          /* if((logFolder = getFilePath(watchfolderLocation+"/"+shot)) != undefined){
// Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
                $.writeln(logInfo(logFolder));
//   last update 21/11/10
            }else{
//
                $.writeln("no log folder");
// Known Bugs:
                }*/
//    
          // $.writeln();
// -you can't have several sequences in a single folder and expect the script to pick up all the sequences
          // addItem(list,"G12_SC213_T1",2);
// -this will not set color profiles
         
// //TODO
         
// >>> CHECK IF WF FOLDER ALREADY EXISTS
          /*
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
var item1 = list.add ('item', 'GB15_SC138_T1');
// -v0.7 fixes
item1.image = File("~/Desktop/flag_gray.png");
//       -new scene after WF works properly
item1.subItems[0].text = 'Queued...';
//       -changed output folder location to N:
item1.enabled = false;
// -v0.6 fixes
*/
//       -cancel watchfolder cancels watchfolder
 
//       -fixed GB##_SC_###_T1
           
//       -added options
            //alert(getPercentage(watchfolderLocation+"/"+shot));
//       -can work on a new location
          // pBar.value = Math.round(getPercentage(watchfolderLocation+"/"+shot)*100);
//  -v0.5 fixes
         
//     -shows allow for a letter in the comp now (ie GB##_SC###a_T#)
                      // alert("test");
//     -will warn if there is missing footage before sending to WF
              //$.writeln(files[file].path+"/"+files[file].name);
//      -removed set take, added 'missing' dialog.
                            // a+=  getFilePath(files[file].path+"/"+files[file].name).name+"\n";
// -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
        wfLocBtn.onClick = function(){
//     -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)
            app.scheduleTask("loop()",2000,1);
// -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
            fold = new Folder( watchfolderLocation.toString());
var watchfolderLocation = "/c/__WATCHFOLDER__";
            files = fold.getFiles();
var rootFolder = "/c/";
                a= "";
var forEditFolderLoaction = "//MAC0023DFDF5429/for%20edit";
            for(file in files){


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


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


             writeLn(pBar.value);
          //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;
</pre>
             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;
           
         


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


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


function openfile(){
Array.prototype.has = function(value) {
            var myFile = File.openDialog ("Select track file","*.txt");
    var i;
            var fileOK = myFile.open("r","TEXT","????");
    for (i=0;i<this.length;i++) {
//var fileD = OpenDlg ("Tracking Point File","*.txt",true);
        if (this[i] == value) {
//txt = fileD.readln ()
            return true;
alert("txt"+readTxt(myFile));
        }
    }
    return false;
}
}


function readTxt(myFile){
var myText = myFile.read();
return myText;
}
</pre>
===Dandelion/Amazing World Of Gumball Shotbuilder===


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


http://imgur.com/xKJWB.png
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;
}


For education purposes only, the script was run in production to helb build shots, it:
function sendToWatchFolder(watchfolderlocation,compname){
* created a list of shots & shows possible given a folder structure ("GB##_SHOWNAME_SC##_T##")
    ocn = compname;
* opened the last .AEP file found in said shot folder
    if(app.settings.getSetting("dandybuildPrefs", "optSaveComp") == "true"){
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder
        c = confirm("Save "+app.project.file.name+" ?",false,"Save Project");
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the 'grab sources' button
        if(c){
* automatically sent files to the watchfolder to render on a small-ish farm.  
            app.project.save();
Other buttons allowed to check for missing footage, open the comp's folder, open the current shows' latest animatic, etc
        }
 
    }
 
    curFile = app.project.file;
<pre>
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
//
    compname += "_WF";
//  Dandelion Shot Builder for "the Amazing World Of Gumball" v0.7 by Bernie
    myFolder.create();
//  last update 21/11/10
    mySaveFile = new File(myFolder.toString()+"/"+compname+".aep");        
//
    app.project.save(mySaveFile);
//  Known Bugs:
    myTextFile = new File(myFolder.toString()+"/"+compname.substring(0,22)+"_RCF.txt");   
//     
    myTextFile.open("w","TEXT","????");
//  -you can't have several sequences in a single folder and expect the script to pick up all the sequences
    text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n" ;
//  -this will not set color profiles
    myTextFile.write(text);
// //TODO
    myTextFile.close();
//  >>> CHECK IF WF FOLDER ALREADY EXISTS
    writeInfo(watchfolderlocation,ocn);
// >>>> GET TAKE FROM N DRIVE, NOT OUT FOLDER
    if(app.settings.getSetting("dandybuildPrefs", "optNuComp") == "false"){
//  -v0.7 fixes
        opFile = new File(curFile);
//        -new scene after WF works properly
        if(opFile){
//        -changed output folder location to N:
            app.open(opFile);
//  -v0.6 fixes
        }
//      -cancel watchfolder cancels watchfolder
    }else{
//        -fixed GB##_SC_###_T1
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
//      -added options
        app.project.new();
//      -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
    //explore(watchfolderlocation);
//      -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
function writeInfo(watchfolderlocation,compname){
var watchfolderLocation = "/c/__WATCHFOLDER__";
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
var rootFolder = "/c/";
var forEditFolderLoaction = "//MAC0023DFDF5429/for%20edit";


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


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;
           
         


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


//var watchfolderLocation = "/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_";
function checkForMissingFootage(dialogFlag){
var loadFile = ((app.settings.haveSetting("dandybuildPrefs", "loadscene")))?(app.settings.getSetting("dandybuildPrefs", "loadscene")):"";
        var dialog = dialogFlag;
checkOutputSettings();
        var missing = false;
 
        for(i=1;i<=app.project.numItems;i++){
Array.prototype.has = function(value) {
            app.project.items[i].selected = false;
    var i;
            if(app.project.items[i] instanceof FootageItem && app.project.items[i].file != null){
    for (i=0;i<this.length;i++) {
                            if(app.project.items[i].footageMissing){
        if (this[i] == value) {
                                app.project.items[i].selected = true;
             return 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;
    return false;
}
}


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


function returnFolderArray(location){
function checkOutFolder(tpath){
    fold = new Folder(location.toString());
    foldFile = new Folder(tpath);
     foldArray = fold.getFiles()
     //$.writeln(tpath);
     folderOnly = new Array();
     if(foldFile.exists){
    for(i=0;i<foldArray.length;i++){
        files = foldFile.getFiles();
         if(foldArray[i] instanceof Folder){
         if(files.length > 1){
             folderOnly[folderOnly.length] = foldArray[i];
             return false;
             }
        }else{
             return true;
         }
         }
     return folderOnly;
     }else{
      newF = foldFile.create();
        return newF;
    }
}
}
 
function getSetTake(force){
function sendToWatchFolder(watchfolderlocation,compname){
     show = app.settings.getSetting("dandybuildPrefs","show");// showDDL.selection.toString();
     ocn = compname;
     shot = app.settings.getSetting("dandybuildPrefs","shot"); //shotDDL.selection.toString();
     if(app.settings.getSetting("dandybuildPrefs", "optSaveComp") == "true"){
    projectTake = getTake(findMainShot().name);
        c = confirm("Save "+app.project.file.name+" ?",false,"Save Project");
//    paths  = rootFolder+show+"/"+shot+"/OUT/";
        if(c){
    paths  = "/n/01_OUT/"+show+"/"+shot+"/";
            app.project.save();
    lastFolder = getLastModified(paths);
         }
    //alert(lastFolder.name+" -->"+paths);
    if(lastFolder){
        folderTake = getTake(lastFolder.name);
    }else{
         folderTake = false;
     }
     }
     curFile = app.project.file;
     msgPt1 = (folderTake)? "The last take in OUT folder is take "+(folderTake)+".\n":"No take was found in the OUT folder.\n";
    myFolder = new Folder(watchfolderlocation.toString()+"/"+compname+"_watch/");
     msgPt2 = "The current project take is "+projectTake+". \n\nChoose the current take:";
    compname += "_WF";
     if(force || (folderTake != (projectTake-1))){
     myFolder.create();
        recommended = (folderTake)?((folderTake)+1):projectTake;
    mySaveFile = new File(myFolder.toString()+"/"+compname+".aep");      
        answer = prompt(msgPt1+msgPt2,recommended);
     app.project.save(mySaveFile);
        //$.writeln("blabal "+answer);
    myTextFile = new File(myFolder.toString()+"/"+compname.substring(0,22)+"_RCF.txt");   
         if(answer != null){
    myTextFile.open("w","TEXT","????");
             return answer;
    text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n" ;
         }else{
    myTextFile.write(text);
            return false;
    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();
     }
     }
      
     return projectTake;
   
    //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","????");
function setTake(value){
     rq = app.project.renderQueue;
     comp = findMainShot();
    text ="";   
     comp.name = comp.name.substring(0,(comp.name.lastIndexOf("_T")+2))+value;
    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 getTake(normalizedName){
function explore(location){
     tmp = normalizedName.split("_T");
    fold = new Folder(location.toString());
    return parseFloat(tmp[tmp.length-1]);
    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"+"\"");
                }
    }
}
}
// 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);


function checkOutFolder(tpath){
                          //splitName[splitNameA.length] =  
    foldFile = new Folder(tpath);
                          //alert(splitName);
    //$.writeln(tpath);
                        //  alert(files[x].name+" >>> "+files[x].name.split('.')[0]);
    if(foldFile.exists){
var currentResult = searcher.exec(splitName);
        files = foldFile.getFiles(); 
// Regular expressions return null if no match was found.
        if(files.length > 1){
// Otherwise, they return an array with the following information:
            return false;
// array[0] = the matched string.
        }else{
// array[1..n] = the matched capturing parentheses.
            return true;
                 
        }
                    if (currentResult) { // We have a match -- the string contains numbers.
    }else{
                         
      newF = foldFile.create();
                            // The match of those numbers is stored in the array[1]
        return newF;
// Take that number and save it into parseResults.
    }
parseResults[parseResults.length] = currentResult[0];
}
} else {
function getSetTake(force){
parseResults[parseResults.length] = null;
    show = app.settings.getSetting("dandybuildPrefs","show");// showDDL.selection.toString();
}
    shot = app.settings.getSetting("dandybuildPrefs","shot"); //shotDDL.selection.toString();
                          if(files[x].name == splitName){
    projectTake = getTake(findMainShot().name);
                                isFolder[isFolder.length] = true;
//   paths  = rootFolder+show+"/"+shot+"/OUT/";
                              // alert("Folder "+files[x].name);
    paths  = "/n/01_OUT/"+show+"/"+shot+"/";
                          }else{
    lastFolder = getLastModified(paths);
                                isFolder[isFolder.length] = false;
    //alert(lastFolder.name+" -->"+paths);
                          }
    if(lastFolder){
} else {
        folderTake = getTake(lastFolder.name);
parseResults[parseResults.length] = null;
    }else{
}
        folderTake = false;
}
    }
    msgPt1 = (folderTake)? "The last take in OUT folder is take "+(folderTake)+".\n":"No take was found in the OUT folder.\n";
// If all the files we just went through have a number in their file names,
    msgPt2 = "The current project take is "+projectTake+". \n\nChoose the current take:";
// assume they are part of a sequence and return the first file.
    if(force || (folderTake != (projectTake-1))){
        recommended = (folderTake)?((folderTake)+1):projectTake;
var result = null;
        answer = prompt(msgPt1+msgPt2,recommended);
for (i = 0; i < parseResults.length; ++i) {
        //$.writeln("blabal "+answer);
                 
        if(answer != null){
                  if(!isFolder[i]){
            return answer;
                    if (parseResults[i]) {
        }else{
if (!result) {
            return false;
                           
        }
result = files[i];
    }
}
    return projectTake;
} else {
}
                       
 
// In this case, a file name did not contain a number.
 
result = null;
 
break;
function setTake(value){
}
    comp = findMainShot();
                  }
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf("_T")+2))+value;
                }
}
 
return result;
function getTake(normalizedName){
}
    tmp = normalizedName.split("_T");
       
    return  parseFloat(tmp[tmp.length-1]);
}
function importSafeWithError(importOptions)
// 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 {
              if(!(sourcePaths.has(importOptions.file.toString()))){
// Create a variable containing ImportOptions.
try {  
var importOptions = new ImportOptions(theFile);
                  b = app.project.importFile(importOptions);
                     //alert();
                    writeLn(importOptions.file.toString());
                    if(theFile.name.toString().toLowerCase().lastIndexOf(".psd") != -1){
                sfi = sourcesFolderItem();
                        importOptions.importAs = ImportAsType.COMP;
                  b.parentFolder = sfi;
                    }
                     if(importOptions.importAs == ImportAsType.COMP){
                      importSafeWithError(importOptions);
                        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) {
} catch (error) {
// Ignore errors.
alert(error.toString() + importOptions.file.fsName, scriptName);
}
}
                // }else{
                //        alert("file already here");
                }
}
}
function testForSequences(files)
function processFolder(theFolder)
{
{
var searcher = new RegExp("[0-9]{3,}$");
// Get an array of files in the target folder.
var movieFileSearcher = new RegExp("(mov|avi|mpg)$", "i");
var files = theFolder.getFiles();
var parseResults = new Array;
//alert(files);
                  var isFolder = new Array;
// Test whether theFolder contains a sequence.
var sequenceStartFile = testForSequences(files);
// Test that we have a sequence. Stop parsing after 10 files.
// If it does contain a sequence, import the sequence,
for (x = 0; (x < files.length) & x < 10; x++) {
if (sequenceStartFile) {
var movieFileResult = movieFileSearcher.exec(files[x].name);
try {
if (!movieFileResult) {
// Create a variable containing ImportOptions.
//******                          splitName = files[x].name.split('.')[0];
var importOptions = new ImportOptions(sequenceStartFile);
                            splitName=files[x].name.substring(0,files[x].name.length-4);
importOptions.sequence = true;
 
// importOptions.forceAlphabetical = true; // Un-comment this if you want to force alpha order by default.
                          //splitName[splitNameA.length] =  
importSafeWithError(importOptions);
                          //alert(splitName);
} catch (error) {
                        // 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:
// Otherwise, import the files and recurse.
// array[0] = the matched string.
// array[1..n] = the matched capturing parentheses.
for (index in files) { // Go through the array and set each element to singleFile, then run the following.
                 
if (files[index] instanceof File) {
                    if (currentResult) { // We have a match -- the string contains numbers.
if (!sequenceStartFile) { // If file is already part of a sequence, don't import it individually.
                         
processFile(files[index]); // Calls the processFile function above.
                            // The match of those numbers is stored in the array[1]
}
// Take that number and save it into parseResults.
}
parseResults[parseResults.length] = currentResult[0];
if (files[index] instanceof Folder) {
} else {
processFolder(files[index]); // recursion
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.
// Recursively examine that folder.
processFolder(targetFolder);
var result = null;
}
for (i = 0; i < parseResults.length; ++i) {
    clearOutput();
                 
    }
                  if(!isFolder[i]){
                    if (parseResults[i]) {
if (!result) {
                           
 
result = files[i];
function sourcesFolderItem(){
}
    for (i = 1; i <= app.project.numItems ; i++) {
} else {
      // alert(app.project.items[i].name);
                       
    if(app.project.items[i].typeName == "Folder" && app.project.items[i].name == "Sources"){
// In this case, a file name did not contain a number.
          return app.project.items[i];
result = null;
      }
break;
  }
}
    return app.project.items.addFolder("Sources");
                  }
    //else
                }
   
}
return result;
 
}
function getSourcePathsArray(){
       
    var footageLocations = new Array();
    for (i = 1; i <= app.project.numItems ; i++) {
function importSafeWithError(importOptions)
        if(app.project.item(i).typeName == "Footage"){
{
            footageLocations[footageLocations.length] = app.project.item(i).file;
              if(!(sourcePaths.has(importOptions.file.toString()))){
        }   
try {
    }
                  b = app.project.importFile(importOptions);
    return footageLocations;
                    writeLn(importOptions.file.toString());
}
                sfi = sourcesFolderItem();
 
                  b.parentFolder = sfi;
function getLastModified(location,extension){
                    if(importOptions.importAs == ImportAsType.COMP){
      myFolder = new Folder(location.toString());
                        for (i = 1; i <= app.project.numItems ; i++) {
      if (!myFolder instanceof Folder){
                            if(app.project.items[i].typeName == "Folder" && app.project.items[i].name.lastIndexOf(" Layers") != -1){
          return false;
                        app.project.items[i].parentFolder = sfi;
    }
                            }
    listFiles = myFolder.getFiles(extension);
                        }
    listDates = new Array();
                    }
    maxId = 0;
                        writeLn("Importing file ("+importedFiles+")");
    maxValue = 0;
                        importedFiles++;
    for(i=0;i<listFiles.length;i++){
} catch (error) {
        if (!(extension == "" && (listFiles[i] instanceof File))){
alert(error.toString() + importOptions.file.fsName, scriptName);
            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);
                // }else{
            listDates[i] = formattedDate;
                //        alert("file already here");
            if(Math.max(maxValue,formattedDate) == formattedDate){
                }
                maxValue = formattedDate;
}
                maxId = i;
            }
        }
function processFolder(theFolder)
   
{
    }
// Get an array of files in the target folder.
    if(listFiles[maxId] == "" ||listFiles[maxId] == "undefined"||listFiles[maxId] == null){
var files = theFolder.getFiles();
        return false;
//alert(files);
    }
// Test whether theFolder contains a sequence.
    return listFiles[maxId];    
var sequenceStartFile = testForSequences(files);
}
 
// If it does contain a sequence, import the sequence,
function pad(number, length, character) {
if (sequenceStartFile) {
    if (character == null) {
try {
        character = "0";
// Create a variable containing ImportOptions.
    }
var importOptions = new ImportOptions(sequenceStartFile);
    var numberStr = "" + number;
importOptions.sequence = true;
    while (numberStr.length < length) {
// importOptions.forceAlphabetical = true; // Un-comment this if you want to force alpha order by default.
        numberStr = character + numberStr;
importSafeWithError(importOptions);
    }
} catch (error) {
    return numberStr;
}
}
}
function grabAnimatic(folderLocation){
    newLocation = new Folder(folderLocation);
// Otherwise, import the files and recurse.
    animatic = newLocation.getFiles("*.mov");
    sfi = sourcesFolderItem();
for (index in files) { // Go through the array and set each element to singleFile, then run the following.
    if(animatic[0]){
if (files[index] instanceof File) {
        try {
if (!sequenceStartFile) { // If file is already part of a sequence, don't import it individually.
            var importOptions = new ImportOptions(animatic[0]);
processFile(files[index]); // Calls the processFile function above.
            animatic = app.project.importFile(importOptions);
}
            for (i = 1; i <= app.project.numItems ; i++) {
}
                    if(app.project.item(i).typeName == "Footage"){
if (files[index] instanceof Folder) {
                  app.project.item(i).parentFolder = sfi;
processFolder(files[index]); // recursion
                    }  
}
            }
}
            return animatic;
}
        }catch (error){
            return false;
// Recursively examine that folder.
        }
processFolder(targetFolder);
}
    clearOutput();
     }
     }
}
function figureOutShotName(filepath){
     folders = filepath.split("/");
 
    showFolder = folders[folders.length-2];
function sourcesFolderItem(){
     showFolder = showFolder.split("_")[0];
     for (i = 1; i <= app.project.numItems ; i++) {
     shotFolder = folders[folders.length-1];
      // alert(app.project.items[i].name);
     var searcher = new RegExp("[0-9]{2,}$");
    if(app.project.items[i].typeName == "Folder" && app.project.items[i].name == "Sources"){
     var currentResult = searcher.exec(shotFolder);
          return app.project.items[i];
     if (currentResult){
      }
         shotFolder = currentResult[0];
  }
     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;
     return showFolder+"_SC"+shotFolder;
}
}


function getLastModified(location,extension){
function createMainComp(path){
      myFolder = new Folder(location.toString());
    grabAnimatic(path);
      if (!myFolder instanceof Folder){
    shotName = figureOutShotName(path);
          return false;
    workComp = app.project.items.addComp(shotName+"_T1", 1920, 1080,1.0 ,animatic.duration,25.0);
    }
    animaticLayer = workComp.layers.add(animatic);
    listFiles = myFolder.getFiles(extension);
     animaticLayer.guideLayer = true;
    listDates = new Array();
     return workComp;
    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) {
function returnList2(type){
     if (character == null) {
    if(type == "show"){
         character = "0";
        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());
 
     }
     }
     var numberStr = "" + number;
     return rArray;
     while (numberStr.length < length) {
}
        numberStr = character + numberStr;
 
 
 
function returnList(folder){
    rArray = returnFolderArray(folder);
     for(i=0;i<rArray.length;i++){
            rArray[i] = rArray[i].name;
     }
     }
     return numberStr;
     return rArray;
}
}
function grabAnimatic(folderLocation){
 
     newLocation = new Folder(folderLocation);
function findMainShot(){
     animatic = newLocation.getFiles("*.mov");
     //fix GB##_SC_###_T1
    sfi = sourcesFolderItem();
     var findShot = new RegExp("^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
    if(animatic[0]){
    for(i=1;i<=app.project.numItems;i++){
        try {
        if(app.project.items[i] instanceof CompItem){
            var importOptions = new ImportOptions(animatic[0]);
          var movieFileResult =  findShot.exec(app.project.items[i].name);
            animatic = app.project.importFile(importOptions);
          if(movieFileResult){
            for (i = 1; i <= app.project.numItems ; i++) {
              nuName = app.project.items[i].name;
                    if(app.project.item(i).typeName == "Footage"){
              nuName = nuName.split("SC_");
                  app.project.item(i).parentFolder = sfi;
              app.project.items[i].name = nuName[0]+"SC"+nuName[1];
                    }   
             }
             }
            return animatic;
        }       
         }catch (error){
    }
            return false;
   
    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){
function figureOutShotName(filepath){
        alert("Could not find a comp named like \"GB##_SC###(a-z)_T#\"\n\nRename main comp accordingly, render again");
    folders = filepath.split("/");
         return false;
    showFolder = folders[folders.length-2];
     }
    showFolder = showFolder.split("_")[0];
     return shot;
    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){
function dandyShotBuilderUI(thisObj){
    if(type == "show"){
   
         rArray = returnFolderArray(rootFolder);
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
         for(i=0;i<rArray.length;i++){
   
            rArray[i] = rArray[i].name;
        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");
    }else if(type == "scene"){
         if (securitySetting != 1) {
        rArray = ["a","b"];
          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";
        alert(dPanel.shotLoad.showDDL.selection.toString());
          var shotInfo = dPanel.add("edittext",[10,5,235,305],msg,{multiline:true});  
 
        }else{
    }
      var shotLoad = dPanel.add("panel",[10,5,235,175],"Load Shot");
    return rArray;
        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"));
function returnList(folder){
                }else{
    rArray = returnFolderArray(folder);
                showDDL.selection = 0;
    for(i=0;i<rArray.length;i++){
                app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
             rArray[i] = rArray[i].name;
                }
    }
        var shotLoadTxt2 = shotLoad.add("statictext",[12,59,52,79],"Scene:");
    return rArray;
        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"));
function findMainShot(){
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
    //fix GB##_SC_###_T1
                }else{
    var findShot = new RegExp("^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
                shotDDL.selection = 0;
    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){
        var shotStatusTxt = shotLoad.add("statictext",[25,95,52,115],"File:");
              nuName = app.project.items[i].name;
        var shotStatusEditTxt = shotLoad.add("edittext",[56,92,210,115],"...");
              nuName = nuName.split("SC_");
            shotStatusEditTxt.active = false;
              app.project.items[i].name = nuName[0]+"SC"+nuName[1];
            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 findShot = new RegExp("^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$", "i");
        var shotRefreshBtn = shotLoad.add("button",[146,125,168,150],"r");
    var shot = null;
        //var shotOptns = shotLoad.add("button",[174,125,210,150],"optns");
    for(i=1;i<=app.project.numItems;i++){
            //shotSetBtn.enabled = false;
        if(app.project.items[i] instanceof CompItem){
       
          var movieFileResult =  findShot.exec(app.project.items[i].name);
      var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
          if(movieFileResult){
            shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
              shot = app.project.items[i];
            shotToolsBtn1Xtra =  shotTools.add("button",[82,12,102,35],"N:");
              break;
            //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)};
    if(shot == null){
            shotToolsBtnFE = shotTools.add("button",[139,12,167,35],"FE");
        alert("Could not find a comp named like \"GB##_SC###(a-z)_T#\"\n\nRename main comp accordingly, render again");
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
        return false;
            shotToolsBtnXLS =  shotTools.add("button",[172,12,197,35],"xls");
    }
            //shotToolsBtnXLS.enabled = false;           
    return shot;
               
}
            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();


function dandyShotBuilderUI(thisObj){
          items = returnList(rootFolder+"/"+showDDL.selection.toString());
   
          //  alert(items);
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
            for(i in items){
   
              shotDDL.add("item", items[i]);
        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");
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
        if (securitySetting != 1) {
            shotDDL.selection = 0;
          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";
            shotDDL.notify();
          var shotInfo = dPanel.add("edittext",[10,5,235,305],msg,{multiline:true});  
        }
        }else{
        shotDDL.onChange = function(){
      var shotLoad = dPanel.add("panel",[10,5,235,175],"Load Shot");
         
        var shotLoadTxt1 = shotLoad.add("statictext",[15,24,52,44],"Show:");
            app.settings.saveSetting("dandybuildPrefs","shot",shotDDL.selection.toString());
        var showDDL = shotLoad.add ("dropdownlist", [56,19,210,44], returnList(rootFolder));
            //$.writeln("Changing");
             if (app.settings.haveSetting("dandybuildPrefs", "show")){
             var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
                showDDL.selection = showDDL.find(app.settings.getSetting("dandybuildPrefs","show"));
            if(!lastAEP){
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
                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{
                 }else{
                showDDL.selection = 0;
                    shotStatusEditTxt.text = fileName;
                app.settings.saveSetting("dandybuildPrefs","show",showDDL.selection.toString());
                }
                }
                    shotLoadBtn.enabled = true;
        var shotLoadTxt2 = shotLoad.add("statictext",[12,59,52,79],"Scene:");
                    shotBuildBtn.enabled = false;
        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"));
        shotLoadBtn.onClick = function(){
                //parseFloat(app.settings.getSetting("dandybuildPrefs","show"));
            opFile = new File(loadFile);
                 }else{
            if(opFile){
                shotDDL.selection = 0;
            app.open(opFile);
                 }
            }else{
       
                alert("Can't open file");
         
            }       
        var shotStatusTxt = shotLoad.add("statictext",[25,95,52,115],"File:");
        }
        var shotStatusEditTxt = shotLoad.add("edittext",[56,92,210,115],"...");
        shotRefreshBtn.onClick = function(){
             shotStatusEditTxt.active = false;
            shotDDL.notify();
             shotStatusEditTxt.enabled = false;
            writeLn("If it doesn't seem to refresh, close and launch again.");
        var shotLoadBtn = shotLoad.add("button",[56,125,96,150],"Load");
            }
             shotLoadBtn.enabled = false;
        shotBuildBtn.onClick = function(){
        var shotBuildBtn = shotLoad.add("button",[101,125,141,150],"Build");
            opFile = new File(rootFolder+"base.aep");
             shotBuildBtn.enabled = false;
             if(opFile.exists){
        var shotRefreshBtn = shotLoad.add("button",[146,125,168,150],"r");
                app.open(opFile);
        //var shotOptns = shotLoad.add("button",[174,125,210,150],"optns");
                for(i=1;i<=app.project.numItems;i++){                   
            //shotSetBtn.enabled = false;
                    app.project.items[i].remove();
          
                 }
      var shotTools = dPanel.add("panel",[10,185,235,300],"Misc");
            }else{
             shotToolsBtn1 =  shotTools.add("button",[12,12,79,35],"Open Folder");
                 alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
            shotToolsBtn1Xtra = shotTools.add("button",[82,12,102,35],"N:");
            }
             //shotToolsBtn2 =  shotTools.add("button",[107,12,197,35],"Flick Last Take");
            targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
             //shotToolsBtn2.enabled = false;
             createMainComp(targetFolder);
            shotToolsBtnWF =  shotTools.add("button",[107,12,135,35],"WF");
             app.project.bitsPerChannel = 16;
             shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};
            app.project.items.addFolder("Precomps");
            shotToolsBtnFE = shotTools.add("button",[139,12,167,35],"FE");
            importFolder = new Folder(targetFolder+"/Source");
             shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};
             SmartImport(importFolder);
             shotToolsBtnXLS = shotTools.add("button",[172,12,197,35],"xls");
            saveConf = confirm("Save "+shotDDL.selection.toString()+" in the right folder ?",false,"Dandelion Shot Builder");
             //shotToolsBtnXLS.enabled = false;            
             if(saveConf){
               
                saveFile = new File(targetFolder+"/AEP/"+showDDL.selection.toString()+"_"+shotDDL.selection.toString()+"_comp01.aep");
            shotToolsBtn3 = shotTools.add("button",[12,40,102,63],"Grab Sources");
                app.project.save(saveFile);
             shotToolsBtn4 =  shotTools.add("button",[107,40,135,63],"Miss");
                shotDDL.notify();
             shotToolsBtnANI = shotTools.add("button",[139,40,167,63],"ANI");
                }
             shotToolsBtnOPT = shotTools.add("button",[172,40,197,63],"Opt");
         }
              
        shotToolsBtn1.onClick = function(){
              //  shotToolsBtn4.enabled = false;           
             explore(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString());
            shotToolsBtn5 =  shotTools.add("button",[12,68,102,91],"Renderqueue");
        }
             shotToolsBtn6 =  shotTools.add("button",[107,68,197,91],"To Watchfolder");
        shotToolsBtn1Xtra.onClick = function(){
                 //shotToolsBtn6.enabled = false;  
             //$.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());
             }
             }
       var optnsGrp = dPanel.add("panel",[10,5,235,300],"Options");
        }
            optnsGrp.visible = false;
       shotToolsBtnANI.onClick = function(){
            mgL = 20;
             loc =  rootFolder+"/"+showDDL.selection.toString();
            mgT=15;
             fold = new Folder(loc.toString());
            mgBet=3;
             files = fold.getFiles("*.mov*");
            i=0;
             if(files.length > 0){
            w=210;
                files[0].execute();
            h=23;
             }else{
            version = optnsGrp.add("statictext",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"version "+version);i++;
                writeLn("No .mov shortcut found in in: ");
             optMissingFootage =  optnsGrp.add("checkbox",[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],"Check for missing footage\rlonger, but safer");i++;
                writeLn(showDDL.selection.toString());
            //$.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(){
         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","shot",shotDDL.selection.toString());
             app.settings.saveSetting("dandybuildPrefs", "optMissingFootage",optMissingFootage.value);
             //$.writeln("Changing");
             app.settings.saveSetting("dandybuildPrefs", "optNuComp",optNuComp.value);
             var lastAEP = getLastModified(rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/AEP","*.aep");
             app.settings.saveSetting("dandybuildPrefs", "optSaveComp",optSaveComp.value);
            if(!lastAEP){
          // $.writeln("new comp checked: "+app.settings.getSetting("dandybuildPrefs", "optNuComp"));
                shotStatusEditTxt.text = "no shot found, build one!";
    }
                  shotLoadBtn.enabled = false;
       
                  shotBuildBtn.enabled = true;
        //send to renderqueue
           
        //uses the comp name to figure out folder
            }else{
 
                app.settings.saveSetting("dandybuildPrefs", "loadscene",lastAEP.toString());
                 
                loadFile = lastAEP.toString();
        shotToolsBtn5.onClick = function(){
                fullSplitPath = lastAEP.toString().split("/");
        rflag = false;
                fileName = fullSplitPath[fullSplitPath.length-1];
        //$.writeln("missing fottage "+app.settings.getSetting("dandybuildPrefs", "optMissingFootage"));
                if(fileName.length > 20){
        if(app.settings.getSetting("dandybuildPrefs", "optMissingFootage") == "true"){
                    shotStatusEditTxt.text = fileName.substr(0,12)+"[...]"+fileName.substr(fileName.length-8);
            rflag = checkForMissingFootage(false);
                }else{
                    shotStatusEditTxt.text = fileName;
                }
                    shotLoadBtn.enabled = true;
                    shotBuildBtn.enabled = false;
            }
         }
         }
         shotLoadBtn.onClick = function(){
         if(!rflag){
             opFile = new File(loadFile);
             rcomp = findMainShot();
            if(opFile){
                if(rcomp){
            app.open(opFile);
                    shotNumber = getSetTake();
            }else{
                    if(shotNumber != false){
                alert("Can't open file");
                        //$.writeln(shotNumber);
            }       
                        setTake(shotNumber);
        }
                        rq = app.project.renderQueue;
        shotRefreshBtn.onClick = function(){
                        for(i=1;i<=rq.numItems;i++){   
            shotDDL.notify();
                                rq.items[i].render = false;
            writeLn("If it doesn't seem to refresh, close and launch again.");
                        }
            }
                        rqitem = app.project.renderQueue.items.add(rcomp);
        shotBuildBtn.onClick = function(){
                        rqitem.outputModules[1].applyTemplate("TiffOutput");
            opFile = new File(rootFolder+"base.aep");
                        rqitem.applyTemplate("Multi-Machine Settings");
            if(opFile.exists){
                        //savePath = rootFolder+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/OUT/"+rcomp.name;
                app.open(opFile);
                        savePath = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString()+"/"+rcomp.name;
                for(i=1;i<=app.project.numItems;i++){                   
                       
                    app.project.items[i].remove();
                       
                        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{
             }else{
                 alert("Error:\n\nThere should be a file called\n\n\""+rootFolder+"base.aep"+"\"");
                 alert("This project is missing source files and will not render. \nUse the 'miss' button to find out where the footage should be");
             }
             }
            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());
        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!");
              }
         }
         }
        shotToolsBtn1Xtra.onClick = function(){
           
             //$.writeln("button");
   
             p = "/n/01_OUT/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
   
            checkOutFolder(p);
   
            explore(p);
   
        }
   
        shotToolsBtn3.onClick = function(){
    rootFolderLoc.onClick = function(){
             app.project.bitsPerChannel = 16;
        y = confirm("Root folder is currently set to \n\n\""+rootFolder+"\"\n\nChange it ?");
             targetFolder = rootFolder+"/"+showDDL.selection.toString()+"/"+shotDDL.selection.toString();
        if(y){
            importFolder = new Folder(targetFolder+"/Source");
             //v = new Folder();
             SmartImport(importFolder);
            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");
             }
         }
         }
        shotToolsBtnXLS.onClick = function(){
    }
            loc = rootFolder+"/"+showDDL.selection.toString();
    watchFolderLoc.onClick = function(){
             fold = new Folder(loc.toString());
        y = confirm("Watchfolder is currently set to \n\n\""+watchfolderLocation+"\"\n\nChange it ?");
             files = fold.getFiles("*.xls*");
        if(y){
             if(files.length > 0){
             //v = new Folder();
                 files[0].execute();
             v = Folder.selectDialog ("New watchfolder location").toString();
            }else{
             if(v!=null && v!="undefined"){
                writeLn("No .xls shortcut found in in:");
                 app.settings.saveSetting("dandybuildPrefs", "watchfolderLocation",v);    
                 writeLn(showDDL.selection.toString());
                 watchfolderLocation = v;
             }
             }
           
         }
         }
      shotToolsBtnANI.onClick = function(){
    }
            loc =  rootFolder+"/"+showDDL.selection.toString();
    shotDDL.notify();
            fold = new Folder(loc.toString());
    }
            files = fold.getFiles("*.mov*");
    dandyShotBuilderUI(this) ;
            if(files.length > 0){
 
                files[0].execute();
</pre>
            }else{
 
                writeLn("No .mov shortcut found in in: ");
 
                writeLn(showDDL.selection.toString());
 
            }
===layer Position to .txt===
        }
https://i.imgur.com/G5DX1T0.gif
        shotToolsBtnOPT.onClick = function(){
Old, might not work! (but should :)
            shotTools.visible = false;
<pre>
            shotLoad.visible = false;
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
            optnsGrp.visible = true;
//
        }
// The script will write to a text file the x & y values of the layer position for every frame comprised in
    okBtn.onClick = function(){
// a selection of keyframes, or for the whole comp duration if no keyframes are selected
            shotTools.visible = true;
//
            shotLoad.visible = true;
//
            optnsGrp.visible = false;
function timeToFrameNum(myTime){
         
  return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
            app.settings.saveSetting("dandybuildPrefs", "optMissingFootage",optMissingFootage.value);
}
            app.settings.saveSetting("dandybuildPrefs", "optNuComp",optNuComp.value);
function framesToTime(myTime){
            app.settings.saveSetting("dandybuildPrefs", "optSaveComp",optSaveComp.value);
  return myTime/app.project.activeItem.frameRate;
          // $.writeln("new comp checked: "+app.settings.getSetting("dandybuildPrefs", "optNuComp"));
}
    }
function timeToTimeCode(myTime){
       
  var framesN = myTime * app.project.activeItem.frameRate;
        //send to renderqueue
  fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));
        //uses the comp name to figure out folder
  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){
        shotToolsBtn5.onClick = function(){
  if(app.project.activeItem.selectedLayers.length != 0){
        rflag = false;
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
        //$.writeln("missing fottage "+app.settings.getSetting("dandybuildPrefs", "optMissingFootage"));
        curLayer = app.project.activeItem.selectedLayers[0];
        if(app.settings.getSetting("dandybuildPrefs", "optMissingFootage") == "true"){
        var textName = "AEcoordinates.txt";
            rflag = checkForMissingFootage(false);
        var myTextFile = filePutDialog("Select a location to save your .txt file", textName, "TEXT txt");
        }
        if(myTextFile == null){
        if(!rflag){
            alert("You must choose a place to save the file");
            rcomp = findMainShot();
        }else{
                if(rcomp){
            var formatString = prompt(pText,"%i: %x;%y%l");
                    shotNumber = getSetTake();
            myTextFile.open("w","TEXT","????");
                    if(shotNumber != false){
            myKeys = curLayer.property("position").selectedKeys;
                        //$.writeln(shotNumber);
            if(myKeys.length != 0){
                        setTake(shotNumber);
              strTime = curLayer.property("position").keyTime(myKeys[0]);
                        rq = app.project.renderQueue;
              endTime = curLayer.property("position").keyTime(myKeys[myKeys.length-1]);
                        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{
             }else{
                alert("This project is missing source files and will not render. \nUse the 'miss' button to find out where the footage should be");
              strTime = app.project.activeItem.workAreaStart;
              endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;
             }
             }
        }
             var startLoop = timeToFrameNum(strTime);
       
             var endLoop = timeToFrameNum(endTime);
        shotToolsBtn4.onClick = function(){
             for(i=startLoop;i<=endLoop;i++){
             //shotNumber = getSetTake(true);
              var curTime = framesToTime(i);
             //setTake(shotNumber);
              out = formatString.replace('%x',curLayer.property("position").valueAtTime(curTime,true)[0]);
             checkForMissingFootage(true);
               out = out.replace('%y',curLayer.property("position").valueAtTime(curTime,true)[1]);
        }
              out = out.replace('%f',i);timeToTimeCode
       
              out = out.replace('%t',timeToTimeCode(curTime));
        shotToolsBtn6.onClick = function(){
              out = out.replace('%i',i-startLoop);
          wf = new Folder(watchfolderLocation);
              out = out.replace('%l','\n');
          if(wf.exists){
              myTextFile.write(out);
              sendToWatchFolder(wf,findMainShot().name);
              clearOutput();
              }else{
              write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+"% done...");
                  alert("Watchfolder error, sorry!");
             }
               }
        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" +
    rootFolderLoc.onClick = function(){
        "and make sure that \"Allow Scripts to Write Files and Access Network\" is checked.");
        y = confirm("Root folder is currently set to \n\n\""+rootFolder+"\"\n\nChange it ?");
      }
        if(y){
 
            //v = new Folder();
  }else{
            v = Folder.selectDialog ("New root folder location").toString();
      alert("Select a layer with 'position' keyframes");
            if(v!=null && v!="undefined"){
  }
                app.settings.saveSetting("dandybuildPrefs", "rootfolder",v);    
}else{
                rootFolder = v;
  alert("Select a composition and a layer with 'position' keyframes");
                          showDDL.removeAll();
}
                          items = returnList(rootFolder);
app.project.timecodeDisplayType = myDisp;
          //  alert(items);
</pre>
            for(i in items){
 
              showDDL.add("item", items[i]);
==Other Scripts==
            }
            //var shotDDL = shotLoad.add ("dropdownlist", [56,54,210,79], returnList(rootFolder+"/"+showDDL.selection.toString()));
             shotDDL.selection = 0;
            shotDDL.notify();
                showDDL.notify();
                shotDDL.notify();
                //$.writeln("notified");
            }
        }
    }
    watchFolderLoc.onClick = function(){
        y = confirm("Watchfolder is currently set to \n\n\""+watchfolderLocation+"\"\n\nChange it ?");
        if(y){
            //v = new Folder();
            v = Folder.selectDialog ("New watchfolder location").toString();
            if(v!=null && v!="undefined"){
                app.settings.saveSetting("dandybuildPrefs", "watchfolderLocation",v);    
                watchfolderLocation = v;
            }
           
        }
    }
    shotDDL.notify();
    }
    dandyShotBuilderUI(this) ;
 
</pre>


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


===layer Position to .txt===
https://i.imgur.com/G5DX1T0.gif
Old, might not work! (but should :)
<pre>
<pre>
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)
// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how
//
(function afterEffectsSmartScale() {
// 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
    var SCRIPT_PREF_KEY = "SmartScaleFactor";
//
var SCRIPT_NAME = "SmartScaleTool";
//
    var defaultScale = 4;  
function timeToFrameNum(myTime){
    var comp = app.project.activeItem;
  return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);
    var selectedLayers = comp.selectedLayers;
}
    var scaleFactorX;
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){
    function getScaleFactor(set) {
  if(app.project.activeItem.selectedLayers.length != 0){
var factor = defaultScale;
      if(app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY")){
if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){
        curLayer = app.project.activeItem.selectedLayers[0];
factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);
        var textName = "AEcoordinates.txt";
}
        var myTextFile = filePutDialog("Select a location to save your .txt file", textName, "TEXT txt");
if(set){
        if(myTextFile == null){
var promptResult = prompt("Enter the scale factor (default is 400% -> 4)\nThen select layer(s) and run again.", factor);
            alert("You must choose a place to save the file");
if (promptResult === null) return null;
        }else{
factor = parseFloat(promptResult);
            var formatString = prompt(pText,"%i: %x;%y%l");
app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);
             myTextFile.open("w","TEXT","????");
}
             myKeys = curLayer.property("position").selectedKeys;
 
             if(myKeys.length != 0){
        return factor;
              strTime = curLayer.property("position").keyTime(myKeys[0]);
    }
              endTime = curLayer.property("position").keyTime(myKeys[myKeys.length-1]);
 
            }else{
    if (selectedLayers.length === 0) {
              strTime = app.project.activeItem.workAreaStart;
        scaleFactorX = getScaleFactor(1);
              endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;
        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);
             }
             }
            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();
}