Afx Javascript Temp

From bernie's
Jump to navigation Jump to search

SimpleWatchfolder WIP

some improvements

/*
WatchFolder.js v1.1
--------------------
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.


Update:
- Works for AE 2024
- Added UI to open a WF worker
- Copies the current .aep to the watchfolder without prompting for a save

To be added:
- A check for missing footage
- Some way to parse logs to figure what has rendered and what hasn't
- Some UIs to turn AFX into a render slave & set some WF options
*/
{
    watchfolderLocation = "none";
    watchfolderLocation = (app.settings.haveSetting("watchfolderPrefs", "watchfolderLocation")) ? (app.settings.getSetting("watchfolderPrefs", "watchfolderLocation")) : watchfolderLocation;



    function sendToWF(wf) {
        /*var c = confirm(app.project.file.name + " needs to be saved first. Save ?", false, "Save Project");*/
        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...");
	/*
        if (c) {
            app.project.save();
            var curFile = app.project.file.name;
            curFile = curFile.substring(0, curFile.length - 4);
            var myFolder = new Folder(wf + "/" + curFile + "_watch/");
            myFolder.create();
            curFile += "_watchfolder";
            var mySaveFile = new File(myFolder.toString() + "/" + curFile + ".aep");
            app.project.save(mySaveFile);
            var myTextFile = new File(myFolder.toString() + "/" + curFile.substring(0, 22) + "_RCF.txt");
            myTextFile.open("w", "TEXT", "????");
            var text = "After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\"\"\n";
            myTextFile.write(text);
            myTextFile.close();
            app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
            var opFile = new File(saved);
            if (opFile) {
                app.open(opFile);
            }
            writeln("Sent to watchfolder...");
        }*/
    }

    function setWatchFolder() {




	var 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();
 	    txtFile.execute();
    	}else{
		alert("unable to launch the AfterEffects worker");
	}
	
    }
    function watchFolderUI(thisObj) {
        pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Watchfolder", [100, 100, 300, 300]);
        var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
        if (securitySetting != 1) {
            pan.add("statictext", [15, 15, 300, 45], "Set prefs and re-launch");
            alert("You need to check \"Allow Scripts to Write Files and Access Network\" in your preferences for this script to work");
        } else {
            var res = "group { \
                        alignment: ['fill','fill'], \
                        alignChildren: ['fill','top'], \
                        orientation: 'column', \
                            setWF: Button {text: 'Set watchfolder' ,preferredSize:[-1,30]} , \
			    curWF: StaticText {text: 'Folder: (none selected yet)',properties:{multiline:false}} , \
                            sendWF: Button {text: 'Send Queue To Watchfolder' ,preferredSize:[-1,30]} , \
                            startWF: Button {text: 'Launch a Worker' ,preferredSize:[-1,30]} , \
                    }";
            pan.grp = pan.add(res);

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

            pan.grp.setWF.onClick = function() {
                swf = setWatchFolder();
		if(swf){
			pan.grp.curWF.text = "Folder: "+ Folder(watchfolderLocation).fsName;
		}
            }
            pan.grp.startWF.onClick = function() {
                startWatchingFolder();
            }
            pan.grp.sendWF.onClick = function() {
                if (watchfolderLocation == "none") {
                    setWatchFolder();
                } else {
                    sendToWF(watchfolderLocation);
                }
            }

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

Keyframes and Layers counter

wip, doesn't work with animated shapes for now


layersCount = 0;
keyframesCount = 0;
collection = app.project.items;
for(i = 1;i<=collection.length;i++){
    curItem = collection[i];
    if(curItem instanceof CompItem){
        len = curItem.layers.length;
        layersCount += len;
        for(j = 1;j<=len;j++){
            curLayer = curItem.layers[j];
            for(k = 1;k <= curLayer.numProperties;k++){
                for(l = 1;l <= curLayer.property(k).numProperties; l++){
                    //$.writeln(keyframesCount);
                    if(curLayer.property(k).property(l).numProperties>0){
                         for(m = 1;m <= curLayer.property(k).property(l).numProperties; m++){
                            nKeys = curLayer.property(k).property(l).property(m).numKeys;
                            if(nKeys > 0){
                                 keyframesCount += nKeys;
                            }
                            //ugh i wish i was better at recursiveness
                            //$.writeln(curLayer.property(k).property(l).property(m).matchName);
                            //$.writeln(curLayer.property(k).property(l).property(m).numKeys);     
                        }
                    }else{
                            nkeys = curLayer.property(k).property(l).numKeys;
                            if(nKeys > 0){
                                keyframesCount += nKeys;
                            }
                            //keyframesCount += curLayer.property(k).property(l).numKeys;
                            //$.writeln(curLayer.property(k).property(l).matchName);
                            //$.writeln(curLayer.property(k).property(l).numKeys);     
                    }
                }  
            }
        }
    }
}
alert(keyframesCount+" keyframes found and\n"+layersCount+" layers counted in the project");
    
    
function iterateProperties(prop){
           // $.writeln(prop.numProperties);    
    if(prop.numProperties > 0){
        for(i = 1;i<=prop.numProperties;i++){
            iterateProperties(prop.property(i));
        }
    }else{
    //    $.writeln(prop.matchName);
    }
    //$.writeln(prop.matchName);
}
//alert(app.project.items[2])

Batch render project items

Select project footage and it will render them with the chosen rq preset

template = "QTJPG2000"; // chosen by hand, for now

selectedItems = app.project.selection;
rq = app.project.renderQueue;

for(i=0;i<selectedItems.length;i++){
    
    item = selectedItems[i];
    path = item.mainSource.file.path.toString()+'/'+item.name;
    p = app.project.items.addComp(item.name,item.width,item.height,1.0,item.duration,item.frameRate);
    p.layers.add(item);
    rqitem = rq.items.add(p);
	rqitem.outputModules[1].file = new File(path);
	rqitem.outputModules[1].applyTemplate(template);
    
}

Auto Comp to Renderqueue with Template

6iJkeH9.png

Very wip. Features and UI don't fully work.

I hate having to go and fill out the outputs of renderqueue items by hand. This would/will automate the creation of render outputs according to variables like the comp's name and folder, the name of the After Effects project, the current date etc...

It still needs work, which will happend if someone ever hires me for AE work again :)
//todo: be able to select existing RQ items instead of simply comps, and edit their existing paths

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

var win;

function renderAll() {
	//prefTemplate = (app.settings.haveSetting("renderCompWithTemplate", "template"))?(app.settings.getSetting("renderCompWithTemplate", "template")):false;
	sel = app.project.selection;
	if (sel.length > 0) {
		//needs a comp to create a render item to get templates
		getTemplateUI(sel[0]);
		//if ui worked fine, there should be a template setting string, if there's a bug or user cancelled, fuck it.
		prefTemplate = (app.settings.haveSetting("renderCompWithTemplate", "template")) ? (app.settings.getSetting("renderCompWithTemplate", "template")) : false;
		if (prefTemplate != false && prefTemplate != "null") {
			for (i = 0; i < sel.length; i++) {
				renderCompWithTemplate(sel[i], prefTemplate)
			}
		} else {
			writeLn("Send to renderqueue cancelled.");
		}
	} else {
		alert("Select at least one comp or render queue element");
	}
}

function getTemplateUI(nullComp) {
	// get previous render settings, so we can use it as default selection later
	prefTemplate = (app.settings.haveSetting("renderCompWithTemplate", "template")) ? (app.settings.getSetting("renderCompWithTemplate", "template")) : false;
	// void render settings if user clicks cancel, to be changed
	app.settings.saveSetting("renderCompWithTemplate", "template", "null");
	ai = app.project.activeItem;
	// must have at least one comp selected, and the project saved to know where to place the renders
	// eventually the user shouldn't have to save his AEP, but have it appear as a warning somewhere that he should
	if (nullComp != null && nullComp instanceof CompItem && app.project.file != null) {
		rq = app.project.renderQueue;
		//create null render comp to fetch templates, delete it right afterwards
		rqitem = rq.items.add(nullComp);
		rqtemplates = rqitem.outputModules[1].templates;
		rqitem.remove();
		// dockable window to do
		//res = "dialog { properties:{ resizeable:true }, preferredSize: [690, 20],  alignChildren: 'fill',orientation:'column',\
		res = "dialog {  \
				properties:{ resizeable:true }, alignChildren: 'fill',orientation:'column',\
				templatesPnl: Panel { \
					orientation:'column', alignChildren:['left', 'top'],text: 'Choose a rendering template',\
					templates: DropDownList {}, \
				}, \
				filePnl: Panel { \
					orientation:'column', alignChildren:['left', 'top'],text: 'Display Paths',\
					pathRbs: Group { \
						orientation:'row',  alignment: 'left', alignChildren:['left', 'bottom'] \
						txt: StaticText { alignment:'left', text:'File path style:' }, \
						unixStylePathRb: RadioButton { text:'Unix (mac)' }, \
						winStylePathRb: RadioButton { text:'Windows', value:true } \
					}, \
					showPathText: StaticText {alignment: 'fill'},  \
					edithPathGrp: Group { \
						orientation:'row', spacing:2, alignment: 'left', \
						editPathBox: EditText { alignment: ['fill','center'],  margins: 8, text:'', properties:{borderless:true}},  \
						testBtn: Button { alignement:[center','center'], text:'Test', properties:{name:'test'} } ,\
					}, \
					explainPathText: StaticText {alignment: 'fill', properties:{multiline: true } }, \
					explainPathTextGrp: Group { \
						orientation:'row',  alignment: 'left', alignChildren:['left', 'center'] \
						c1: StaticText {alignment: 'fill', properties:{multiline: true } },  \
						c2: StaticText {alignment: 'fill', properties:{multiline: true } } \
					}  \
				}, \
				buttons: Group { orientation: 'row', alignment: 'right', \
					okBtn: Button { text:'OK', properties:{name:'ok'} }, \
					cancelBtn: Button { text:'Cancel', properties:{name:'cancel'} }, \
				}, \
			}";
		// get/set the display path preference (it will always be unix style internally) 1=windows 0=mac/unix
		//input base text 
		explanationText = "Here are variables you can use:\n";
		explanationText += "\t{compname} {compfolder} {projpath} {compid}\n";
		explanationText += "\t{projname} {date} (using date:y/m/d)";
		explanationText += "\t[#], [##], [####] (padding)\n";
		explanationText += "You can write '/../' to go 'up' one directory ";
		// create window resource
		win = new Window(res);
		//set the preferred template
		preferredItem = 0;
		for (i = 0; i < rqtemplates.length; i++) {
			//skip hidden templates
			if (rqtemplates[i].indexOf('_HIDDEN') != 0) {
				item = win.templatesPnl.templates.add('item', rqtemplates[i]);
				if (rqtemplates[i] == prefTemplate) {
					preferredItem = i;
				}
			}
		}
		win.templatesPnl.templates.selection = win.templatesPnl.templates.items[preferredItem];
		// can't add newline \n character in res, so fill in text now
		win.filePnl.explainPathText.text = explanationText;
		// set os choice radio button, figure out if it's in prefs 
		// eventually users shouldn't see this, the script should be tailored for mac or windows
		os = $.os.toLowerCase().indexOf("windows") != -1;
		if (app.settings.haveSetting("renderCompWithTemplate", "oschoice")) {
			os = (app.settings.getSetting("renderCompWithTemplate", "oschoice") == 1) ? 1 : 0;
		} else {
			app.settings.saveSetting("renderCompWithTemplate", "oschoice", os);
		}
		win.filePnl.pathRbs.unixStylePathRb.value = !os;
		// set path template box, if it doesn't have one, use a default;
		inputBox = win.filePnl.edithPathGrp.editPathBox;
		outPutBox = win.filePnl.showPathText;
		var pathTemplate = "";
		if (app.settings.haveSetting("renderCompWithTemplate", "pathtemplate")) {
			pathTemplate = app.settings.getSetting("renderCompWithTemplate", "pathtemplate");
		} else {
			pathTemplate = "{projpath}/RENDER/{date:ymd}_{compname}/{compname}.[####]";
			app.settings.saveSetting("renderCompWithTemplate", "pathtemplate", pathTemplate);
		}
		inputBox.text = pathTemplate;
		//add ui callbacks
		//call once, to fill the result text box
		updatePath(inputBox, outPutBox);
		inputBox.onChange = inputBox.onChanging = function() {
			updatePath(inputBox, outPutBox)
		};
		/*-----TO BE CUT---------*/
		win.filePnl.pathRbs.unixStylePathRb.onClick = win.filePnl.pathRbs.winStylePathRb.onClick = function() {
			//save preferences
			app.settings.saveSetting("renderCompWithTemplate", "oschoice", (!win.filePnl.pathRbs.unixStylePathRb.value) ? 1 : 0);
			updatePath(inputBox, outPutBox);
		}
		/*-----END CUT-----------*/
		win.filePnl.edithPathGrp.testBtn.onClick = function() {
			fp = descriptionToFilePath("", 1, inputBox.text, 0);
			fpFolder = new Folder(fp);
			e(fpFolder.absoluteURI);
		}
		win.buttons.okBtn.onClick = function() {
			//save preferences
			prefTemplate = win.templatesPnl.templates.selection;
			app.settings.saveSetting("renderCompWithTemplate", "pathtemplate", inputBox.text);
			//app.settings.saveSetting("renderCompWithTemplate", "oschoice", (!win.filePnl.pathRbs.unixStylePathRb.value)?1:0);
			app.settings.saveSetting("renderCompWithTemplate", "template", prefTemplate);
			win.close();
		}
		win.layout.layout(true);
		win.onResizing = win.onResize = function() {
			this.layout.resize()
		};
		win.center();
		win.show();
	} else {
		//alert dialogs suck, but they are efficient
		alert("Select at least one comp to render, and make sure your After Effects file is saved to disk.");
	}
}

function resizeUI(windowObj) {
	//nasty function which creates a new textbox to figure out how big the editbox is, so no text is clipped
	var textbox = windowObj.filePnl.edithPathGrp.editPathBox;
	//var testbutton = windowObj.filePnl.edithPathGrp.testBtn;
	g = windowObj.filePnl.add("group", [0, 0, 0, 0]);
	g.enabled = g.visible = false;
	tmp = g.add("edittext", undefined, textbox.text, {
		enabled: false,
		visible: false
	});
	var preferredWidth = tmp.preferredSize[0];
	//e("newpref: "+r.preferredSize[0]);
	windowObj.filePnl.remove(g);
	textbox.size = [preferredWidth, textbox.preferredSize[1]];
	//testbutton.size = testbutton.preferredSize;
	windowObj.layout.layout(true);
}

function updatePath(inputRes, outputRes) {
	os = (app.settings.getSetting("renderCompWithTemplate", "oschoice") == 1) ? 1 : 0;
	input = inputRes.text;
	input = descriptionToFilePath("", 1, input, 1);
	input = setPathStyle(input, !os); //backslashes to slashes
	outputRes.text = input;
	resizeUI(inputRes.parent.parent.parent);
	//windowObj.layout.layout(true);
}

function descriptionToFilePath(comp, rqid, descriptionString, forDisplay) {
	// takes a description like {projpath}/{projname}_{compname}/{compname}_[#####] and transforms it into a usable unix path
	// if there is no comp fed, use the first in the selection.
	// 'forDisplay' int decides if it's for display (add frame count and turn %20s to spaces) or actual path to be used
	// 'rqid' is the number of the comp in the renderqueue, default is 1
	var str = descriptionString;
	if (comp == "" || comp == null) {
		comp = app.project.selection[0];
	}
	var projName = app.project.file.name.toString();
	//standard stuff, comp path name etc....
	str = str.replace(/\{projpath\}/gi, app.project.file.path.toString());
	projName = projName.substr(0, projName.lastIndexOf('.')) || projName;
	str = str.replace(/\{projname\}/gi, projName);
	str = str.replace(/\{compname\}/gi, comp.name);
	var parentFolder = (comp.parentFolder.name == "Root") ? "" : comp.parentFolder.name;
	str = str.replace(/\{compfolder\}/gi, parentFolder);
	//if we use {id}, make sure it's padded 
	var rq = app.project.renderQueue;
	str = str.replace(/\{id\}/gi, pad(rq.items.length.toString().length, rqid.toString(), "0"));
	//figure out padding, if it's forDisplay, show it to the user
	startPad = str.indexOf("[#");
	endPad = str.lastIndexOf("#]");
	if (startPad > 0 && endPad > 0 && endPad > startPad && forDisplay) {
		startFrame = comp.workAreaStart / comp.frameDuration;
		str = str.substring(0, startPad) + pad(endPad - startPad, startFrame.toString(), "0") + str.substring(endPad + 2, str.length);
	}
	//date
	var datesArray = str.split("{date:");
	var tempstr = "";
	if (datesArray.length > 0) {
		var today = new Date();
		var dd = today.getDate().toString();
		var mm = (today.getMonth() + 1).toString();
		var yyyy = today.getFullYear();
		tempstr = datesArray[0];
		for (i = 1; i < datesArray.length; i++) {
			endBracketPos = datesArray[i].indexOf("}");
			nextBracketPos = datesArray[i].indexOf("{");
			nextBracketPos = (nextBracketPos == -1) ? 9999 : nextBracketPos; //case if date is the last used tag
			if (endBracketPos > -1 && endBracketPos < nextBracketPos) {
				dateString = datesArray[i].substring(0, endBracketPos);
				dateString = dateString.replace(/y/gi, yyyy);
				dateString = dateString.replace(/d/gi, pad(2, dd, "0"));
				dateString = dateString.replace(/m/gi, pad(2, mm, "0"));
				tempstr += dateString + datesArray[i].substring(endBracketPos + 1, datesArray[i].length);
			} else {
				tempstr = str;
			}
		}
		str = tempstr;
	}
	if (forDisplay) {
		str += ".ext";
	}
	return str;
}

function renderCompWithTemplate(comp, template) {
	//sends the comp to the renderqueue with the selected file path, creating folders and subfolders if needed
	rq = app.project.renderQueue;
	rqitem = rq.items.add(comp);
	rqtemplates = rqitem.outputModules[1].templates;
	filename = app.project.file.name;
	filepath = app.project.file.toString();
	projpath = filepath.slice(0, filepath.length - filename.length);
	savePath = projpath + comp.name + "/" + filename.slice(0, filename.length - 4) + "/";
	saveFile = comp.name + ".[####].tif";
	saveFolder = new Folder(savePath);
	saveFolder.create();
	rqitem.outputModules[1].file = new File(savePath + "/" + saveFile);
	rqitem.outputModules[1].applyTemplate(template);
}

function setPathStyle(path, toUnixPath) {
	// takes a string and creates either a unix path like /c/my%20folder/ if toUnixPath is set to true
	// or a windows path like c:\my folder\. Returns false if there's a problem (tbd)
	if (!toUnixPath) {
		//unix -> win
		path = path.replace(/\//, "");
		path = path.replace(/\//, ":/");
		path = path.replace(/%20/g, " ");
		path = path.replace(/\//g, "\\");
		path = path.charAt(0).toUpperCase() + path.slice(1);
	} else {
		path = "/" + path.replace(":\\", "/");
		path = path.replace(/\\/g, "/");
		path = path.replace(" ", "%20");
		//path = path.substring(0,path.lastIndexOf("/"));
	}
	return path;
}

function pad(width, string, padding) {
	return (width <= string.length) ? string : pad(width, padding + string, padding);
}
renderAll();

create effect creator

ui

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

/*

var windowObj;
function ui(thisObj) {
	windowObj = (thisObj instanceof Panel) ? thisObj : new Window("palette", "ui_test", [100, 100, 300, 300]);
	addButton("flip",windowObj);
	return windowObj;
}*/


function addButton(buttonname,ui){
	//ui.add("button", [0, 0, 20, 20], But_01[0]);
	return ui.add("button", undefined, buttonname);
}





function ui(thisObj){
    pan = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Watchfolder", [100, 100, 300, 300]);
    var securitySetting = app.preferences.getPrefAsLong("Main Pref Section", "Pref_SCRIPTING_FILE_NETWORK_SECURITY");
    if (securitySetting != 1) {
        pan.add("statictext",[15,15,300,45],"Set prefs and re-launch");
        alert("You need to check \"Allow Scripts to Write Files and Access Network\" in your preferences for this script to work");
     }else{
                var res = 
            "group { \
                        alignment: ['fill','fill'], \
                        alignChildren: ['fill','top'], \
                        orientation: 'column', \
                            setWF: Button {text: 'add btn' ,preferredSize:[-1,30]} , \
                            sendWF: Button {text: 'Send To Watchfolder' ,preferredSize:[-1,30]} , \
                    }";	
            pan.grp = pan.add(res);        
            pan.grp.setWF.onClick = function () {
            	addButton("joe",pan.grp);
            	resfreshUI(pan);
                    }
            pan.grp.sendWF.onClick = function () {
                }
            resfreshUI(pan);    
            /*
            pan.layout.layout(true);
            pan.layout.resize();
            pan.onResizing = pan.onResize = function () {this.layout.resize();}*/
            return pan;
    }
}

function resfreshUI(src){
	src.layout.layout(true);
	src.layout.resize();
	src.onResizing = pan.onResize = function () {this.layout.resize();}

}

ui(this);


fn

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

// to be done:
// 		save keys
// 		save masks
//		save text if text layer, camera zoom if camera etc
//		use FFX as custom values

function grabEffects(layers){
	
	var buffer = "";
	var chosenLayerName = "curLayer";

	for (i=0;i<layers.length;i++){

		effs = layers[i].property("ADBE Effect Parade");

		for(j=1;j<=effs.numProperties;j++){

			//create the effects, no matter if properties below have values

			buffer += "\n\tprop = "+ chosenLayerName +".Effects.addProperty(\""+ effs.property(j).matchName +"\");\n";
			buffer += "\tprop.name = \""+ effs.property(j).name +"\";\n";
			buffer += "\tprop.enabled = "+ effs.property(j).enabled +";\n";

			for(k=1;k<=effs.property(j).numProperties;k++){

				curProp = effs.property(j).property(k);
				
				//only add value if it's non-default

				if(curProp.isModified){

					curPropValue = propType(curProp);

					// if it returns -1, it's (probably) a useless 'parent' property, skip

					if( curPropValue != -1){
						
						// /!\ if it'sa  custom value type, there's little to no way of using it in a script, so we can comment it out and warn the user -- there are also false positives, ^but they should be ignored with '-1' as curpropvalue

						buffer += "\t\t// "+curProp.name+((curPropValue===false)?" /!\\ Can't use this custom data! could be a false negative, but unlikely ":"")+"\n";                                        
						buffer += "\t\t"+((curPropValue===false)?"//":"") + "prop.property(\""+curProp.matchName+"\").setValue(" + curPropValue + ");\n";
						
						// if there is an expression and it's enabled, use it

						if(curProp.expression != "" && curProp.expressionEnabled){
							buffer += "\t\t"+((curPropValue===false)?"//":"") + "prop.property(\""+curProp.matchName+"\").expression = \""+curProp.expression+"\";\n";
						}
					}
				}
			}
		}
	}

	return buffer;
}

function outputCode(){
	
	effectsLayers = app.project.activeItem.selectedLayers; //use effects from all selected layers

	buffer = "";
	if(effectsLayers.length>0){
		buffer = "var selectedLayers = app.project.activeItem.selectedLayers;\n";
		buffer += "for (i=0;i<selectedLayers.length;i++){\n";
		buffer += "\n\tcurLayer = selectedLayers[i];\n"; 
		buffer += grabEffects(effectsLayers) + "\n}";
		//e(buffer);
	}
	return buffer;
}


e(outputCode());
e("\n------------------------------------------------------");


 function propType(property){

	returnvalue = false;
	
	e("--->"+property.name+" "+property.propertyValueType+"\n"); //debug
	//e("--->"+property.name+" "+property.canVaryOverTime+"\n"); //debug		
	
	switch(property.propertyValueType){

		case PropertyValueType.ThreeD_SPATIAL:
			returnvalue = "["+property.value[0]+","+property.value[1]+","+property.value[2]+"]";
			break;
		case PropertyValueType.ThreeD:
			returnvalue = "["+property.value[0]+","+property.value[1]+","+property.value[2]+"]";
			break;

		case PropertyValueType.TwoD_SPATIAL:
			returnvalue = "["+property.value[0]+","+property.value[1]+"]";
			break;
		case PropertyValueType.TwoD:
			returnvalue = "["+property.value[0]+","+property.value[1]+"]";
			break;			

		case PropertyValueType.OneD:
			returnvalue = property.value;
			break;		

		case PropertyValueType.COLOR:
			returnvalue = "["+property.value[0]+","+property.value[1]+","+property.value[2]+","+property.value[3]+"]";
			break;

		case PropertyValueType.LAYER_INDEX:
			returnvalue = property.value;
			break;

		case PropertyValueType.MASK_INDEX:
			returnvalue = property.value;
			break;			

		//no way to store custom value but FFX, tbd
		case PropertyValueType.CUSTOM_VALUE:
			returnvalue = false;
			break;

		// if we land on default, it is _most likely_ a property 'parent' with no real use
		default:
			//e("using default: --->"+property.name+" "+property.propertyValueType+"\n")
			returnvalue = -1;
			break;
	}
	return (returnvalue);
 }

left to right

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


function replaceLR(s){
    
    s = s.replace(/left/g, "right");
    s = s.replace(/Left/g, "Right");
    s = s.replace(/LEFT/, "RIGHT");
    return s;
}


function checkImportLeftRight(layer){

	// is it a footage layer (includes solids etc.. not Comps)
	if(layer.source instanceof FootageItem){
		
		//is it a footage item (.exr)
		if(layer.source.file != null){
			path = layer.source.mainSource.file.toString();
	
			 //check path, if it has 'left' in it, grab 'right' and import and replace footage
			if(path.toLowerCase().indexOf("left") > -1){

				rightPath = replaceLR(path);
				rightFile = new File(replaceLR(path));

				//if file exists, import right file, replace layer with it
				if(rightFile.exists){
					
					
					var io = new ImportOptions(rightFile);
					if(io.canImportAs(ImportAsType.FOOTAGE)){
					
						io.importAs = ImportAsType.FOOTAGE;
						io.sequence = true;
						src = app.project.importFile(io);
						
						return(layer.replaceSource(src,1));
						
					}else{
						return false
					}



				}else{
					alert("file not found! "+rightPath);
					return false
				}
			}
	
		}
	}
}

/*
a = app.project.activeItem;

for(i=1;i<=a.layers.length;i++){
 //e(a.layers[i].name);
 e(checkImportLeftRight(a.layers[i]));
    //if it's footage

}

for(i = 1;i<app.project.numItems;i++){
	e(i+" "+app.project.items[i].name); 
}
// 3 and 4
*/
function new3dComp(leftcomp,rightcomp){
	threedcomp = leftcomp.name.replace(/left/g,"3D");
	w = leftcomp.width;
	h = leftcomp.height;
	duration = leftcomp.duration;
	frameRate = leftcomp.frameRate;
	newComp = app.project.items.addComp(threedcomp, w , h , 1.0, duration, frameRate);


	
	newComp.layers.add(rightcomp);
	newComp.layers.add(leftcomp);
	
	threed = newComp.layers.addSolid([1,1,1],"3D Glasses",w,h,1.0);
	threed.adjustmentLayer = true;
	
	threedglasses = threed.Effects.addProperty("ADBE 3D Glasses2");
	threedglasses.property("Left View").setValue(2);
	threedglasses.property("Right View").setValue(3);
	threedglasses.property("3D View").setValue(12);

}
new3dComp(app.project.items[3],app.project.items[4]);



//e(app.project.activeItem.name+" --- ")

Output .SRT from layer markers

function pad10(n){
    return (n<10)?"0"+n:n;
}
function pad100(n){
    return (n<100)?"0"+pad10(n):n;
}

function formatTime(time){
    time =~~ (time*1000)/1000;

    var hrs = ~~(time / 3600);
    var mins = ~~((time % 3600) / 60);
    var secs =  ~~(time % 60);    
    var ms = ~~((time-Math.floor(time))*1000);
    
    time = pad10(hrs)+":"+pad10(mins)+":"+pad10(secs)+","+pad100(ms);
    return time;
}

data = "";
var ai = app.project.activeItem;
if ( ai instanceof CompItem && ai.selectedLayers.length == 1) {
    var m = ai.selectedLayers[0].marker;
    for(i=1;i<=m.numKeys;i++){
        data += i;
        data += "\n"+ formatTime(m.keyTime(i))+" --> "+ formatTime(m.keyTime(i)+m.keyValue(i).duration);
        data += "\n"+m.keyValue(i).comment;
        data += "\n";
       }
}


alert(data)

//result:
//
//1
//00:03:16,945 --> 00:03:17,364
//testing comment 1
//
//2
//01:59:47,228 --> 01:59:49,396
//second subtitle at 2 hours in
//

Shift keys

function e(str){
      $.writeln(str);
}
function getLayerFromProperty(prop){
    return prop.propertyGroup(prop.propertyDepth)
}
var c = app.project.activeItem;
if( c != null){
    var props = c.selectedProperties;

    //e(props[0].matchName);
    for(i = 0;i<props.length;i++){
        e("\n---------------------------\n"+props[i].matchName+" / "+getLayerFromProperty(props[i]).name);
        k = props[i].selectedKeys;
        for(keyVar in k){
            //k[keyVar]
            
            
           // e( ); 
           e(props[i].keyTime(k[keyVar])); 
           //e(k[keyVar]); 
        }
    //CANT FUCKING NUDGE KEYFRAMES
      //  e(props[i].selectedKeys);
    }
}



Selection tool palette

{
//tmpdir = $.getenv("tmp");
    function toolWindow(thisObj){
        function drawUI(){
            var my_palette = new Window("palette","Selection Tool");
            my_palette.bounds = [300,200,300,285];
            /*var button1 = addScriptButton(my_palette,[l_button_left,   5, l_button_right, 25], 
                    "Find and Replace Text",    demosDirectory, "Find and Replace Text.jsx");
            var button3 = addScriptButton(my_palette,[l_button_left,  30, l_button_right, 50], 
                    "Scale Composition", 		demosDirectory, "Scale Composition.jsx");
            var button4 = addScriptButton(my_palette,[l_button_left,  55, l_button_right, 75], 
                    "Scale Selected Layers", demosDirectory, "Scale Selected Layers.jsx");

            var button6 = addScriptButton(my_palette,[r_button_left,   5, r_button_right, 25], 
                    "Sort Layers by In Point",     demosDirectory, "Sort Layers by In Point.jsx");
            var button8 = addScriptButton(my_palette,[r_button_left,  30, r_button_right, 50], 
                    "Render and Email",    myDirectory,    "Render and Email.jsx");

            var button12 = addHelpButton(my_palette,[r_button_left,  55, r_button_right, 75]);*/

            my_palette.show();
        }
    drawUI();
    }
 toolWindow(this);
}

Make .bat file (WIP)

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


function createBat(){
    d = new Date();
    m = d.getMonth()+1;
    j = d.getDate();
    if(m<10){
        m="0"+m;
    }
    if(j<10){
        j="0"+j;
    }

    var txtFile = new File("~/Desktop/ae_render"+m+"_"+j+".bat");
    alert(pathToWinPath(app.project.file));
    txtFile.open("w","TEXT","????");
    txtFile.close();
}

res =
"dialog { \
    allGroups: Panel {\
        orientation:'column',\
        alignChildren:'fill',\
        text:'Options', \
        chckOff: Checkbox {text:'Shutdown PC when finished'}, \
        chckAppend: Checkbox {text:'Append (instead of overwriting)'},\
        aePath: Group {orientation:'row',align:'fill', alignChildren:['fill','center'],\
            aeLocBtn: Button {text:' aerender.exe location : '}, \
            aeLocTst: StaticText {text:'C:\\Program fil...'}} \
        okCancel: Group {orientation:'row',align:'fill', alignChildren:'center',\
            okBtn: Button { text:'OK', properties:{name:'ok'}} , \
            cancelBtn: Button { text:'Cancel', properties:{name:'cancel'}},\
        }\
    }\
}";

win = new Window (res);
win.allGroups.chckAppend.value = true;
win.center();
win.show();

Carnet De Voyage rangement

//rangement pour carnet de voyage
app.beginUndoGroup("CDV Rangement");
boolVal = false;
sel = app.project.selection;
if(sel.length == 1){
    shot = sel[0];
    allItems = app.project.items;
    sortItems = new Array();
    for(i=1;i<=allItems.length;i++){
        if(!(allItems[i] instanceof FolderItem)){
            if(allItems[i].parentFolder.name == "Root"){
                sortItems[sortItems.length] = allItems[i];
            }
        }else{
            if(allItems[i].parentFolder.name == "Root"){
                    sortItems[sortItems.length] = allItems[i];
            }        
        }
    }
    t = app.project.items.addFolder("ELMTS");
    for(i=0;i<=sortItems.length-1;i++){
        if(sortItems[i] != shot){
        sortItems[i].parentFolder = t;
        }
    }
}
if(app.project.renderQueue.item(1).outputModules.length == 2){ //copie d'un output module à la main nécéssaire
    fileP = app.project.renderQueue.item(1).outputModules[1].file.toString();
    fileP = fileP.replace("/m/EPISODE","/b/EPISODE");
    fileP = new File(fileP);
    app.project.renderQueue.item(1).outputModules[2].file = fileP;
    app.project.renderQueue.item(1).outputModules[2].applyTemplate("PNGSeq");
    var curFile = app.project.file.name;
    curFile = curFile.substring(0,curFile.length-4);
    var pth = app.project.file.path+"/"+curFile+"_MOV+PNG.aep";
    var mySaveFile = new File(pth);        
    app.project.save(mySaveFile);
}else{
    writeLn("Duplicate output modules first");
}
app.endUndoGroup();

Chat

JSX

var myPalette = buildUI(this);

    if (myPalette != null && myPalette instanceof Window) {
        myPalette.show()
        }

    function buildUI (thisObject) {

    if (thisObject instanceof Panel) {
        var myWindow = thisObject;
        } else { 
        var myWindow = new Window ("palette", "My Window");
        }

g = myWindow.add("group");
g.orientation = "row";
g.alignChildren = "left";
//g.alignement = "top";
bt1 = g.add("button",undefined,"test");
bt1.onClick = bob;
//myWindow.myPanel.titleText = myWindow.myPanel.add("staticText");
//myWindow.myPanel.titleText.text =  "Move After Render v1.0";  

myWindow.layout.layout(true);
//myWindow.layout = new AutoLayoutManager(myWindow);
//myWindow.layout.resize();
return myWindow;
} 
h = 0;
function bob(){
//    alert();
    w = this.parent.parent;
    p = this.parent;
    h++;
    bt2 = p.add("button",undefined,"test"+h);  
      bt2.alignement = ["left","top"];
    if(h%3==0){

       
        bt2.helpTip = "TTTTT";
        }
    bt2.onClick = flup;
   //w.update();
   w.layout.layout(1); 
  // w.layout.resize();

//    w.show();
    }

function flup(){
    f = new Folder($.fileName);
    f = new Folder(f.parent+"/shelves/");
   if(!f.exists){f.create()};
    cfg = new File(f.parent+"/shelves/shelves.cfg");
    cfg.open('e');
    cfg.write(this.onClick);
    cfg.close();
    cfg.execute();
}
function pop(){
    f = new Folder($.fileName);
    fi = new File(f.parent+"/shelves/testShelf.jsx");
    fi.open('e');
    eval(fi.read());
    }
----------------
function grab(){
    var reply = "";
    c = new Socket;
    if (c.open ("berniebernie.fr:80")) {
        if(c.writeln ("GET /dump/afx/chat.php  HTTP/1.0\nHost: berniebernie.fr\n")){
            reply = decodeURIComponent(c.read(1000));
            reply = reply.split("--afx chat file log--");
            reply = reply[1];
        }else{
             return 0;
        }
        c.close();
    }else{
             return 0;
    }
    return reply;
}
function talk(str){
    var reply = "";
    c = new Socket;
    if (c.open ("berniebernie.fr:80")) {
        if(c.writeln ("GET /dump/afx/chat.php?msg="+str+"  HTTP/1.0\nHost: berniebernie.fr\n")){
            reply = decodeURIComponent(c.read(1000));
            reply = reply.split("--afx chat file log--");
            reply = reply[1];
        }else{
             return 0;
        }
        c.close();
    }else{
             return 0;
    }
    return reply;
}
//alert(grab());
alert(talk("flipflap"));

////////////////
app.preferences.getPrefAsLong("Main Pref Section","Pref_SCRIPTING_FILE_NETWORK_SECURITY"

Php

<?php
$myFile = "testFile.txt";
if(isset($_GET['msg']) && $_GET['msg'] != ""){
    $fh = fopen($myFile, 'a') or die("can't open file");
    fwrite($fh, ($_GET['msg']."\n"));
    fclose($fh);    
}else{
    //$fh = fopen($myFile, 'r') or die("can't open file");
    //include$str = $str, true);
    //header("Content-Type: plain/text"); 
    echo encodeURIComponent(file_get_contents("testFile.txt"));
    //echo nl2br(htmlentities(file_get_contents("testFile.txt")));
}

?>