<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://berniebernie.fr/mediawiki-1.37.1/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Bernie</id>
	<title>bernie&#039;s - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://berniebernie.fr/mediawiki-1.37.1/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Bernie"/>
	<link rel="alternate" type="text/html" href="https://berniebernie.fr/wiki/Special:Contributions/Bernie"/>
	<updated>2026-04-30T13:07:35Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=909</id>
		<title>Expressions and Tips</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=909"/>
		<updated>2026-04-13T10:14:55Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Somewhat automatic Contact Sheet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[azerty AFX]] keyboard shortcuts &lt;br /&gt;
=== Random footage with sample image WIP ===&lt;br /&gt;
poor man&amp;#039;s trapcode, images and explanation tbd&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
anims = 4;&lt;br /&gt;
flength = 150;&lt;br /&gt;
seedRandom(index,true);&lt;br /&gt;
s = thisComp.layer(&amp;quot;map&amp;quot;).sampleImage(position);&lt;br /&gt;
rlen = 24 * s[2];&lt;br /&gt;
choice =  Math.floor( s[0]*anims );&lt;br /&gt;
rand = Math.floor( ( random() * anims * 2  - anims) *  s[1] );&lt;br /&gt;
idx = ( choice + rand ) % 4;&lt;br /&gt;
idx * 150 * thisComp.frameDuration + time + random() * rlen * thisComp.frameDuration;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Somewhat automatic Contact Sheet ===&lt;br /&gt;
WIP. It will autolayout footage/comps and corresponding text layers (you&amp;#039;ll still need to ctrl c ctrl v position+scale expressions).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = &amp;#039;&amp;#039;;&lt;br /&gt;
list = [];&lt;br /&gt;
for(i=1;i&amp;lt;thisComp.numLayers;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	try{cur = L.source.name}catch(err){cur = false}&lt;br /&gt;
	list.push(cur?cur:null);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
list.join(&amp;#039;\n&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Converting RGB to CMYK in After Effects ===&lt;br /&gt;
Is a real pain in the buttocks. I know I&amp;#039;m not following any nifty color conversion algorithm but I just needed a way to use substractive colors on a project (fake print with halftone pattern). This was such a pain in the buttock that I&amp;#039;ll probably code a better plugin if I ever get the chance to.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/eL2vlqv.gif&lt;br /&gt;
&lt;br /&gt;
The way I went from RGB to C,M,Y. The settings are not all necessary (and also no K, I still need to fiddle with this to find the exact RGB match! I&amp;#039;ll reupload if I find a better solution).&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/iD9BZ24.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Afx Path Coordinates===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
path = thisComp.layer(&amp;quot;Shape Layer 1&amp;quot;).content(&amp;quot;Shape 1&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path;&lt;br /&gt;
p = path.points();&lt;br /&gt;
&lt;br /&gt;
txt = &amp;quot;[&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
for(i=0;i&amp;lt;p.length;i=i++){&lt;br /&gt;
txt += &amp;#039;{&amp;quot;x&amp;quot;:&amp;#039;+p[i][0]+&amp;#039;,&amp;quot;y&amp;quot;:&amp;#039;+p[i][1]+&amp;#039;}&amp;#039;;&lt;br /&gt;
	if(i&amp;lt;p.length-1){&lt;br /&gt;
		txt += &amp;quot;,&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
txt += &amp;quot;],&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Position from mask path ===&lt;br /&gt;
image tbd&lt;br /&gt;
&lt;br /&gt;
Takes the first and second frame of an effect to drive a position along a path. You can use this to drive the &amp;#039;write-on&amp;#039; effect if you don&amp;#039;t have 3d stroke.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = (time-key(1).time)/(key(2).time-key(1).time);&lt;br /&gt;
t = linear(t,0,1,0,1);&lt;br /&gt;
mask(1).maskPath.pointOnPath(t)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Loop animation before and after first and last keyframes===&lt;br /&gt;
&lt;br /&gt;
Should work with mask keyframes too, as opposed to loopOut/loopIn&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6adNfdW.png&lt;br /&gt;
&lt;br /&gt;
Keep in mind that just like the normal loopOut, the last keyframe will always be replaced by the value of the first one, so you should have a sacrificial keyframe. Tried to have a one-liner but haven&amp;#039;t wrapped my head around it quite fully&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var offset = time&amp;lt;key(1).time?key(numKeys).time:key(1).time;&lt;br /&gt;
valueAtTime( (time - offset ) % ( key(numKeys).time - key(1).time ) + offset );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Display current layer name ===&lt;br /&gt;
screenshot tbd this is pretty specific&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pct =&amp;#039; ░▒▓█&amp;#039;;&lt;br /&gt;
startIndex = 1;//what layer do I start with ? or use thisComp.layer(&amp;#039;Null 1&amp;#039;).index;&lt;br /&gt;
lastCompIndex = thisComp.numLayers;&lt;br /&gt;
outputText = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=startIndex+1;i&amp;lt;=lastCompIndex;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	inP = L.inPoint;&lt;br /&gt;
	outP = L.outPoint;&lt;br /&gt;
	if(time &amp;gt;= inP &amp;amp;&amp;amp; time &amp;lt; outP){&lt;br /&gt;
		curT = time - inP;&lt;br /&gt;
		maxT = outP - inP;&lt;br /&gt;
		per = curT/maxT;&lt;br /&gt;
		outputText += pct.substr(Math.floor(per*4),1) + &amp;#039; &amp;#039;;  //comment out if you dont want visual progress&lt;br /&gt;
		outputText += L.source.name + &amp;#039;\n&amp;#039;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
outputText;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simple one liner if you put a text object above each layer (you have to do the in/out yourself)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
thisComp.layer(thisLayer.index+1).source.name&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spread items along a path ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ideOrrs.gif&lt;br /&gt;
&lt;br /&gt;
Change path to fit your need, apply expresson to the position of your item layer, duplicate and you&amp;#039;re set&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// pointonpath uses a range from 0..1 to grab a position so let&amp;#039;s figure our where we are&lt;br /&gt;
// by counting the number of layers in the comp (and omitting a &amp;#039;paths&amp;#039; layer at the bottom of the comp)&lt;br /&gt;
&lt;br /&gt;
// [0,1] [0,.5,1] [0,.333...,.666...,1] [0,.25,.5,.75,1] etc&lt;br /&gt;
var percentpos = ( thisLayer.index - 1 ) / (thisComp.numLayers - 2)&lt;br /&gt;
&lt;br /&gt;
// prevents a divide by 0 error if there is only one object halfway through the path&lt;br /&gt;
percentpos = isNaN(percentpos)? 0.5 : percentpos&lt;br /&gt;
&lt;br /&gt;
//grab path layer and path ugh, &amp;#039;path&amp;#039;&lt;br /&gt;
var pathLayer = thisComp.layer(&amp;quot;path&amp;quot;)&lt;br /&gt;
var path = pathLayer.content(&amp;quot;Shape 2&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path&lt;br /&gt;
&lt;br /&gt;
//grab position on path and use toComp to place it in the right spot&lt;br /&gt;
var pos = path.pointOnPath( percentpos )&lt;br /&gt;
pathLayer.toComp(pos)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Retime (varispeed) any value ===&lt;br /&gt;
&lt;br /&gt;
Allows time remapping of animation without having to time remap (and precomp). This way you can link different animations. It&amp;#039;s using valueAtTime and a slider. You&amp;#039;ll have to edit it for n-dimensional values (like position).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;video width=&amp;quot;640&amp;quot; controls&amp;gt;&lt;br /&gt;
&amp;lt;source src=&amp;quot;https://i.imgur.com/KQhIIJi.mp4&amp;quot; type=&amp;quot;video/mp4&amp;quot; &amp;gt;&lt;br /&gt;
Your browser does not support the video tag.&lt;br /&gt;
&amp;lt;/video&amp;gt; &lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add a slider control that you call &amp;#039;speed&amp;#039;, and then add this expression to remap it according to the animation as seen in the video above&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
spd = effect(&amp;quot;speed&amp;quot;)(&amp;quot;Slider&amp;quot;);      //slider varies between 0 and 1, allows precise slow ups and downs in a single slider&lt;br /&gt;
p = thisProperty;                 &lt;br /&gt;
firstT = p.key(1).time;&lt;br /&gt;
lastT = p.key(p.numKeys).time;&lt;br /&gt;
p = p.valueAtTime(spd*(lastT-firstT)+firstT); &lt;br /&gt;
//might have to add [p[0],p[1]];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Padded timer text===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/pcRzMkB.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = effect(&amp;quot;Slider Control&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
sign = (t&amp;lt;0)?&amp;#039;-&amp;#039;:&amp;#039;&amp;#039;;&lt;br /&gt;
t = Math.abs(t);&lt;br /&gt;
&lt;br /&gt;
s = t%60;&lt;br /&gt;
m = Math.floor(t/60)%60;&lt;br /&gt;
h =  Math.floor(t/ (60*60));&lt;br /&gt;
s =  (s&amp;lt;10)?&amp;#039;0&amp;#039;+s:s;  		// pads&lt;br /&gt;
m = (m&amp;lt;10)?&amp;#039;0&amp;#039;+m:m;&lt;br /&gt;
sign+h+&amp;#039;:&amp;#039;+m+&amp;#039;:&amp;#039;+s;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Rand characters===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7hll8l1.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//seedRandom(1,true); //remove line to have it randomize each frame&lt;br /&gt;
chars = &amp;quot;GATC&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
characters = 6;&lt;br /&gt;
blocks = 4;&lt;br /&gt;
lines = 10;&lt;br /&gt;
&lt;br /&gt;
textValue = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=0;i&amp;lt;lines;i++){&lt;br /&gt;
	for(j=0;j&amp;lt;blocks;j++){&lt;br /&gt;
		for(k=0;k&amp;lt;characters;k++){&lt;br /&gt;
			randVar = random() * chars.length;&lt;br /&gt;
			characterVar = chars.charAt(randVar);&lt;br /&gt;
			textValue = textValue + characterVar;&lt;br /&gt;
		}&lt;br /&gt;
		textValue += &amp;quot; &amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	textValue += &amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
textValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Current Keyframe Index===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//gobelins&lt;br /&gt;
a = timeRemap;&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
curframe = 0;&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	curframe = nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	curframe = nk.index;&lt;br /&gt;
}&lt;br /&gt;
curframe*thisComp.frameDuration;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
https://gyazo.com/fa2262be189547b5381aa50041cdc32b.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = position;  //or whatever animated property&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	nk.index;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== sample image pr les gobz ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = sampleImage([thisComp.width/2,thisComp.height/2], [thisComp.width/2,thisComp.height/2], postEffect = true, t = time);&lt;br /&gt;
a[0]+a[1]+a[2]+a[3];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
////bake&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;)-effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;).valueAtTime(time-1/25)==0?0:1&lt;br /&gt;
&lt;br /&gt;
//////bake?&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;hold&amp;quot;)(&amp;quot;Curseur&amp;quot;);&lt;br /&gt;
a = 0;&lt;br /&gt;
for(i=0;i&amp;lt;=time*25;i++){&lt;br /&gt;
	if(v.valueAtTime(i/25)==1){&lt;br /&gt;
		a = i/25;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
a;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== spread thingy ===&lt;br /&gt;
http://i.imgur.com/dlJJcXt.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
p = thisComp.layer(&amp;quot;Null 1&amp;quot;).transform.position;&lt;br /&gt;
delta = transform.position - p;&lt;br /&gt;
amplitude = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;amplitude&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
reach = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;reach&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
exponent = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;exponent&amp;quot;)(&amp;quot;Slider&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
length = (length(delta));&lt;br /&gt;
if(length&amp;gt;0){&lt;br /&gt;
	transform.position += normalize( delta ) *  ( reach / Math.pow(length, exponent)  ) * amplitude;&lt;br /&gt;
}else{&lt;br /&gt;
	transform.position = [-2000,0]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Sprites ===&lt;br /&gt;
Duplicate layers by hand, add CtrlNul, offsetLeftRight, offsetDepth, spread sliders&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//POSITION&lt;br /&gt;
seedRandom(index,true)&lt;br /&gt;
ctrl = thisComp.layer(&amp;quot;CtrlNul&amp;quot;);&lt;br /&gt;
dist = ctrl.effect(&amp;quot;dist&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
offset = [ctrl.effect(&amp;quot;offsetLeftRight&amp;quot;)(&amp;quot;Slider&amp;quot;),0,ctrl.effect(&amp;quot;offsetDepth&amp;quot;)(&amp;quot;Slider&amp;quot;)];&lt;br /&gt;
&lt;br /&gt;
depthjitter = ctrl.effect(&amp;quot;depthjitter&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
depthmult = ctrl.effect(&amp;quot;depthmult&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
spread = ctrl.effect(&amp;quot;spread&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
dist = dist+random()*spread;&lt;br /&gt;
&lt;br /&gt;
transform.position+[(index%2)?-dist:dist,0,depthmult*index+depthjitter*random()]+offset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//ORIENTATION&lt;br /&gt;
lookAt(transform.position,thisComp.layer(&amp;quot;Camera 1&amp;quot;).transform.position)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Write on effect ===&lt;br /&gt;
https://i.gyazo.com/091522309c14c61f0cc7d63672e78900.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
cursorSpeed = 3;&lt;br /&gt;
text.sourceText.slice(0,time*fps)+((Math.floor(time*cursorSpeed)%2)?&amp;quot;|&amp;quot;:&amp;quot; &amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Unscramble wip ===&lt;br /&gt;
http://i.imgur.com/k8Gf1wC.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wordspeed = time/1; //or a slider, or whatever&lt;br /&gt;
letterspeed = time*2;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
txtVar = &amp;quot;&amp;quot;;&lt;br /&gt;
chars = &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
maxLines = Math.min(splitxt.length-1, Math.floor(wordspeed));&lt;br /&gt;
for(i=0;i&amp;lt;=maxLines;i++){&lt;br /&gt;
	txtVar += (i&amp;gt;0)?splitxt[i-1]+&amp;quot;\r&amp;quot;:&amp;quot;&amp;quot;;&lt;br /&gt;
	len = splitxt[i].length;&lt;br /&gt;
	if(i == maxLines){&lt;br /&gt;
		for(j=0;j&amp;lt;=len-1;j++){&lt;br /&gt;
			txtVar += (j/len&amp;gt;=wordspeed-Math.floor(wordspeed))?chars.charAt(random()*chars.length):splitxt[i].charAt(j);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
txtVar&lt;br /&gt;
;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Poor man&amp;#039;s padding ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//for lack of a cleaner version, up to 4 digits&lt;br /&gt;
a = time*25;&lt;br /&gt;
a=(a&amp;lt;10)?&amp;quot;000&amp;quot;+a:((a&amp;lt;100)?&amp;quot;00&amp;quot;+a:((a&amp;lt;1000)?&amp;quot;0&amp;quot;+a:a));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== 2d lookat ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LookAt = &amp;quot;ball&amp;quot;&lt;br /&gt;
offset = 0&lt;br /&gt;
diffx = position[0] - this_comp.layer(LookAt).position[0];&lt;br /&gt;
diffy = position[1] - this_comp.layer(LookAt).position[1];&lt;br /&gt;
if (diffx == 0) {&lt;br /&gt;
diffx = 1 }&lt;br /&gt;
sign = 1 + (-1 * (diffx / Math.abs(diffx))) * 90;&lt;br /&gt;
radians_to_degrees(Math.atan(diffy/diffx)) + sign + offset&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//with parenting&lt;br /&gt;
lookAt = &amp;quot;a&amp;quot;;&lt;br /&gt;
L = thisComp.layer(lookAt);&lt;br /&gt;
p = L.toComp(L.anchorPoint);&lt;br /&gt;
d = thisLayer.toComp(anchorPoint) - p&lt;br /&gt;
d[0] = (d[0]==0)?1:d[0];&lt;br /&gt;
rotation + radians_to_degrees(Math.atan(d[1]/d[0])) - 90*d[0]/ Math.abs(d[0])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pixilation (slow footage) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
numImages = 15;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
framesToWait = 3;Math.floor(time*fps/framesToWait%numImages)/fps;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Text scroller (DOS!)===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/JlLNh1Z.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//add your text, three slider control effects and rename them &amp;#039;line width&amp;#039;, &amp;#039;line&amp;#039;, &amp;#039;line animation&amp;#039;&lt;br /&gt;
//then this expression on the source text&lt;br /&gt;
charlen = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line width&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //20&lt;br /&gt;
numlines = Math.ceil(Math.abs(Math. round(effect(&amp;quot;lines&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //5&lt;br /&gt;
step = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line animation&amp;quot;)(&amp;quot;Slider&amp;quot;),0)));&lt;br /&gt;
&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;numlines+step;i++){&lt;br /&gt;
	output += (i &amp;gt; splitxt.length - numlines)?&amp;quot;.\r&amp;quot;:splitxt[i].substring(0,charlen)+&amp;quot;\r&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//single line&lt;br /&gt;
src = text.sourceText;&lt;br /&gt;
len = src.length;&lt;br /&gt;
scroll = time*25;&lt;br /&gt;
src.slice(scroll%len)+src.slice(0,scroll%len)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/////////////&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
charlen =200;&lt;br /&gt;
numlines = 15;&lt;br /&gt;
step = time/thisComp.frameDuration;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;(step+numlines);i++){&lt;br /&gt;
	output += (splitxt.length&amp;lt;=i)?&amp;quot;\r&amp;quot;:splitxt[i-1].substring(0,charlen)+&amp;quot;\r&amp;quot;;          &lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Open two simultaneous After Effects===&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\AfterFX.exe&amp;quot; -m&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Command-Line===&lt;br /&gt;
&amp;lt;pre&amp;gt;@echo off&lt;br /&gt;
&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\aerender.exe&amp;quot; -mp -project &amp;quot;W:\&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript_Temp&amp;diff=908</id>
		<title>Afx Javascript Temp</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript_Temp&amp;diff=908"/>
		<updated>2026-03-31T09:43:03Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Auto-expose rewrite/debug */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
=== Mask animation to json/text file===&lt;br /&gt;
chatgpt vibecoded, works, can&amp;#039;t use JSON standard library though.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function () {&lt;br /&gt;
    function exportMaskToGeoJSON() {&lt;br /&gt;
        var PRECISION = 3; // 👈 change this (number of decimal places)&lt;br /&gt;
&lt;br /&gt;
        function round(value) {&lt;br /&gt;
            var p = Math.pow(10, PRECISION);&lt;br /&gt;
            return Math.round(value * p) / p;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var comp = app.project.activeItem;&lt;br /&gt;
        if (!(comp instanceof CompItem)) {&lt;br /&gt;
            alert(&amp;quot;Please select a composition.&amp;quot;);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var layer = comp.selectedLayers[0];&lt;br /&gt;
        if (!layer) {&lt;br /&gt;
            alert(&amp;quot;Please select a layer.&amp;quot;);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var masks = layer.property(&amp;quot;Masks&amp;quot;);&lt;br /&gt;
        if (!masks || masks.numProperties === 0) {&lt;br /&gt;
            alert(&amp;quot;No masks found.&amp;quot;);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var mask = masks.property(1);&lt;br /&gt;
&lt;br /&gt;
        // Prefer selected mask&lt;br /&gt;
        for (var i = 1; i &amp;lt;= masks.numProperties; i++) {&lt;br /&gt;
            if (masks.property(i).selected) {&lt;br /&gt;
                mask = masks.property(i);&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var maskPath = mask.property(&amp;quot;Mask Path&amp;quot;);&lt;br /&gt;
        if (!maskPath || maskPath.numKeys === 0) {&lt;br /&gt;
            alert(&amp;quot;Mask has no keyframes.&amp;quot;);&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var fps = comp.frameRate;&lt;br /&gt;
        var json = &amp;#039;{\n&amp;quot;type&amp;quot;: &amp;quot;FeatureCollection&amp;quot;,\n&amp;quot;features&amp;quot;: [\n&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        for (var k = 1; k &amp;lt;= maskPath.numKeys; k++) {&lt;br /&gt;
            var time = maskPath.keyTime(k);&lt;br /&gt;
            var frame = Math.round(time * fps);&lt;br /&gt;
&lt;br /&gt;
            var shape = maskPath.keyValue(k);&lt;br /&gt;
            var verts = shape.vertices;&lt;br /&gt;
&lt;br /&gt;
            json += &amp;#039;  {\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;    &amp;quot;type&amp;quot;: &amp;quot;Feature&amp;quot;,\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;    &amp;quot;properties&amp;quot;: {\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;      &amp;quot;time&amp;quot;: &amp;#039; + round(time) + &amp;#039;,\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;      &amp;quot;frame&amp;quot;: &amp;#039; + frame + &amp;#039;\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;    },\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;    &amp;quot;geometry&amp;quot;: {\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;      &amp;quot;type&amp;quot;: &amp;quot;Polygon&amp;quot;,\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;      &amp;quot;coordinates&amp;quot;: [\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;        [&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
            // vertices&lt;br /&gt;
            for (var v = 0; v &amp;lt; verts.length; v++) {&lt;br /&gt;
                var x = round(verts[v][0]);&lt;br /&gt;
                var y = round(verts[v][1]);&lt;br /&gt;
&lt;br /&gt;
                json += &amp;#039;[&amp;#039; + x + &amp;#039;,&amp;#039; + y + &amp;#039;]&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
                if (v &amp;lt; verts.length - 1) {&lt;br /&gt;
                    json += &amp;#039;,&amp;#039;;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // close polygon (GeoJSON requirement)&lt;br /&gt;
            if (verts.length &amp;gt; 0) {&lt;br /&gt;
                var firstX = round(verts[0][0]);&lt;br /&gt;
                var firstY = round(verts[0][1]);&lt;br /&gt;
                json += &amp;#039;,[&amp;#039; + firstX + &amp;#039;,&amp;#039; + firstY + &amp;#039;]&amp;#039;;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            json += &amp;#039;]\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;      ]\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;    }\n&amp;#039;;&lt;br /&gt;
            json += &amp;#039;  }&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
            if (k &amp;lt; maskPath.numKeys) {&lt;br /&gt;
                json += &amp;#039;,&amp;#039;;&lt;br /&gt;
            }&lt;br /&gt;
            json += &amp;#039;\n&amp;#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        json += &amp;#039;]\n}&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
        var file = File.saveDialog(&amp;quot;Save GeoJSON&amp;quot;, &amp;quot;*.json&amp;quot;);&lt;br /&gt;
        if (!file) return;&lt;br /&gt;
&lt;br /&gt;
        file.open(&amp;quot;w&amp;quot;);&lt;br /&gt;
        file.write(json);&lt;br /&gt;
        file.close();&lt;br /&gt;
&lt;br /&gt;
        alert(&amp;quot;GeoJSON export complete!&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Export Mask as GeoJSON&amp;quot;);&lt;br /&gt;
    exportMaskToGeoJSON();&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Auto-expose rewrite/debug===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
// v1.1 - made the 8bit switch w/o user input, fixed bugs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
// currentSlider holds a reference to the Detector property after setupDetector runs,&lt;br /&gt;
// making it accessible to bakeKeys and applyKeys.&lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
var savedBitDepth = app.project.bitsPerChannel;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    // Determine if the script is running as a dockable panel or a floating window&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 100]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;Step1: Select a footage or layer in a comp and move to a frame where there is a change in animation.  Click setup button and increase resolution slider until the (red) slider value called \\&amp;#039;Detector\\&amp;#039; picks up a change (a value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Setup detector guide layer&amp;#039; } , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Step2: Bake the detected animation as time remapped keys, this can take some time, keep an eye on your \\\&amp;quot;Info\\\&amp;quot; panel. If some animation is not detected, change resolution slider and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Bake to Keys&amp;#039;} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Step3: Select layers on which to apply time remapping (\\\&amp;quot;exposed\\\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
					applyKeys: Button {text: &amp;#039;Apply as time remapping&amp;#039;} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    &lt;br /&gt;
    // Assign click handlers&lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        setupDetector();&lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    // Layout and resizing management for the UI&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
// Execute the UI function&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Helper function for writing debug messages&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    // Check if a composition is active&lt;br /&gt;
    if (!(app.project.activeItem instanceof CompItem)) {&lt;br /&gt;
        alert(&amp;quot;Please select a Composition and one layer.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Check if one layer is selected&lt;br /&gt;
    var selectedLayers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    if (selectedLayers.length !== 1) {&lt;br /&gt;
        alert(&amp;quot;Please select exactly one layer to set up the detector on.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
	app.project.bitsPerChannel = 8;&lt;br /&gt;
	&lt;br /&gt;
	//move playhead to frame 1 instead of 0 if user hasn&amp;#039;t moved, so that the script shows a change (should be a fully black frame if no animation is detected)&lt;br /&gt;
	if(app.project.activeItem.time == 0){&lt;br /&gt;
		app.project.activeItem.time = app.project.activeItem.frameDuration;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
    var curlayer = selectedLayers[0];&lt;br /&gt;
&lt;br /&gt;
    // 1. Duplicate the layer and precompose the duplicate&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    // Move the original layer before the duplicate for consistent indexing&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer); &lt;br /&gt;
    &lt;br /&gt;
    // Get the index of the duplicate layer before precomposing&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index; &lt;br /&gt;
    &lt;br /&gt;
    // Precompose the duplicate layer&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index], duplicatelayer.name + &amp;quot;_anim_detection&amp;quot;, true);&lt;br /&gt;
    &lt;br /&gt;
    // Get the layer *in the active comp* that references the new precomp&lt;br /&gt;
    var precomplayer = app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    // 2. Solo the detector precomp layer&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled &amp;amp;&amp;amp; allLayers[i].index !== precomplayer.index){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    // 3. Inside the Precomposition (precomp): setup the difference layer&lt;br /&gt;
    var toplayer = precomp.layers[1]; // The duplicated layer that was precomposed&lt;br /&gt;
&lt;br /&gt;
    // Remove existing effects from the top layer inside the precomp&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Duplicate the top layer inside the precomp&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    // 1 frame shift + difference blendmode = highlight pixel changes&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.timeRemapEnabled = true; // Enable Time Remapping&lt;br /&gt;
    // Shift the duplicated layer&amp;#039;s content by one frame&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration; &lt;br /&gt;
    // Shift the layer&amp;#039;s inPoint to compensate&lt;br /&gt;
    newlayer.inPoint = precomp.frameDuration; &lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;ADBE Marker&amp;quot;).setValueAtTime(.5, explainer); // Use match name &amp;quot;ADBE Marker&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    // Add black solid at the bottom to ensure correct difference result&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // 4. Add Slider Controls to the Precomp Layer in the main comp (precomplayer)&lt;br /&gt;
    var effectsGroup = precomplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Resolution Slider&lt;br /&gt;
    var sliderctrl = effectsGroup.addProperty(&amp;quot;ADBE Slider Control&amp;quot;); &lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;; &lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    // Detector Slider&lt;br /&gt;
    var detectorctrl = effectsGroup.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Expression to sample the difference precomp. It samples the previous frame&amp;#039;s result &lt;br /&gt;
    // because the precomp content itself is already a difference calculation between the current &lt;br /&gt;
    // and previous frame (due to the -1 frame shift on the layer inside the precomp).&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            // Sample image at time-1 frameDuration because the comp content itself is the difference.\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time-thisComp.frameDuration);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    // Normalize to 0-1, scale up for sensitivity, then subtract to baseline 0 for no change\&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
	detectorexpression = &amp;quot;\&lt;br /&gt;
	var resolution = effect(&amp;#039;Resolution&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\&lt;br /&gt;
resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
var cw = thisComp.width/resolution;\&lt;br /&gt;
var ch = thisComp.height/resolution;\&lt;br /&gt;
var a = [0,0,0,0];\&lt;br /&gt;
for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
	for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
		center = [cw/resolution/2+cw * j ,ch/resolution/2+ch/resolution * i ];\&lt;br /&gt;
		sampledistance = [cw/2,ch/2];\&lt;br /&gt;
		a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
	}\&lt;br /&gt;
}\&lt;br /&gt;
// show accumulated delta\&lt;br /&gt;
(a[0]+a[1]+a[2])*1000;\&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider; // Assign to the global variable&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // Guardrail: check if setupDetector has been run&lt;br /&gt;
    if (currentSlider === &amp;quot;none&amp;quot; || !(currentSlider instanceof Property)) {&lt;br /&gt;
        alert(&amp;quot;Please run &amp;#039;Step 1: setup detector on layer&amp;#039; first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose bake to keys&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    // 1. Bake slider keys (initial movement detection)&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
    // 2. Apply secondary expression to convert movement value (&amp;gt;0) to 1, or 0&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    &lt;br /&gt;
    // 3. Bake the 0 or 1 expression (clean expression)&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
    // 4. Clean up keys and set the correct key values&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        // If the value is 1 (movement detected)&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            // Set the value to the key&amp;#039;s time, this is the desired exposed time&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            // Remove keys where no movement was detected (value == 0)&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 5. Add &amp;#039;0&amp;#039; key on first frame (Time 0)&lt;br /&gt;
    var compStartTime = app.project.activeItem.time; &lt;br /&gt;
    &lt;br /&gt;
    // Add key at the comp&amp;#039;s start time (usually 0)&lt;br /&gt;
    currentSlider.addKey(compStartTime); &lt;br /&gt;
    &lt;br /&gt;
    var keyIndex = 1; &lt;br /&gt;
    &lt;br /&gt;
    if (currentSlider.numKeys &amp;gt; 0) {&lt;br /&gt;
        // If the first key is not at compStartTime, insert the new key at index 1&lt;br /&gt;
        if (currentSlider.keyTime(1) !== compStartTime) { &lt;br /&gt;
            currentSlider.setValueAtTime(compStartTime, 0); // Adds key, returns index&lt;br /&gt;
            keyIndex = currentSlider.nearestKeyIndex(compStartTime);&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        // No keys left, add the first key&lt;br /&gt;
        currentSlider.setValueAtTime(compStartTime, 0);&lt;br /&gt;
        keyIndex = 1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.setValueAtKey(keyIndex, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(keyIndex,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
    app.project.bitsPerChannel = savedBitDepth;&lt;br /&gt;
	app.endUndoGroup();&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // Guardrail: check if detection and baking has been run&lt;br /&gt;
    if (currentSlider === &amp;quot;none&amp;quot; || !(currentSlider instanceof Property)) {&lt;br /&gt;
        alert(&amp;quot;Please run &amp;#039;Step 1: setup detector on layer&amp;#039; first, then &amp;#039;Step 2: bake to keys&amp;#039;.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    if (!(comp instanceof CompItem)) {&lt;br /&gt;
        alert(&amp;quot;Please select a Composition and layers to apply keys to.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var layers = comp.selectedLayers;&lt;br /&gt;
    if (layers.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select layers to apply keys to.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose apply keys&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0; i &amp;lt; layers.length; i++){ &lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        // Use the match name for Time Remap property&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;ADBE Time Remapping&amp;quot;); &lt;br /&gt;
        &lt;br /&gt;
        // Remove existing keys first for a clean application&lt;br /&gt;
        while (remap.numKeys &amp;gt; 0) {&lt;br /&gt;
            remap.removeKey(1);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Copy the baked keys from the detector slider to the layer&amp;#039;s Time Remap&lt;br /&gt;
        for(var j=1;j&amp;lt;=currentSlider.numKeys;j++){ &lt;br /&gt;
            &lt;br /&gt;
            var v = currentSlider.keyValue(j); // Value (the frame time)&lt;br /&gt;
            var t = currentSlider.keyTime(j); // Time (the moment of exposure)&lt;br /&gt;
            &lt;br /&gt;
            remap.addKey(t); &lt;br /&gt;
            // Setting value at time is the robust way to set keys&lt;br /&gt;
            remap.setValueAtTime(t, v); &lt;br /&gt;
            &lt;br /&gt;
            // Find the index of the key we just added&lt;br /&gt;
            var keyIndex = remap.nearestKeyIndex(t); &lt;br /&gt;
&lt;br /&gt;
            remap.setInterpolationTypeAtKey(keyIndex,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Expression for sequential exposure (if enabled by user):&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\n\&lt;br /&gt;
        a = timeRemap;\n\&lt;br /&gt;
        nk = a.nearestKey(time);\n\&lt;br /&gt;
        curframe = 0;\n\&lt;br /&gt;
        if(nk.time &amp;gt; time){\n\&lt;br /&gt;
            // nearest key is in the future, use the previous key index\n\&lt;br /&gt;
            curframe = nk.index-1;\n\&lt;br /&gt;
        }else{\n\&lt;br /&gt;
            // nearest key is now or in the past, use its index\n\&lt;br /&gt;
            curframe = nk.index;\n\&lt;br /&gt;
        }\n\&lt;br /&gt;
        // Get the value of the key (which is the actual time) and hold it\n\&lt;br /&gt;
        if (curframe &amp;gt; 0) {\n\&lt;br /&gt;
            a.keyValue(curframe);\n\&lt;br /&gt;
        } else {\n\&lt;br /&gt;
            // Before the first key, hold the time 0.\n\&lt;br /&gt;
            0;\n\&lt;br /&gt;
        }\n\&lt;br /&gt;
        &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        remap.expressionEnabled = false; // Start with keyframes active, expression disabled&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PNG to image source ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var path = &amp;#039;/c/temp/temp2/&amp;#039;;&lt;br /&gt;
items = [&amp;#039;blue&amp;#039;,&amp;#039;red&amp;#039;,&amp;#039;red_o&amp;#039;,&amp;#039;green&amp;#039;,&amp;#039;green_o&amp;#039;,&amp;#039;gray&amp;#039;,&amp;#039;gray_o&amp;#039;];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myFile = new File(&amp;quot;/c/temp/temp2/pngs.txt&amp;quot;);&lt;br /&gt;
myFile.open(&amp;quot;w&amp;quot;);&lt;br /&gt;
myFile.encoding = &amp;quot;UTF-8&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var text = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
for (var i = 0, len = items.length; i &amp;lt; len; i++) {&lt;br /&gt;
item = items[i];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var f = File(path+item+&amp;#039;.png&amp;#039;);&lt;br /&gt;
    f.encoding = &amp;#039;BINARY&amp;#039;;&lt;br /&gt;
    f.open(&amp;#039;e&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
    var binary;&lt;br /&gt;
    binary = f.read().toSource();&lt;br /&gt;
&lt;br /&gt;
    //var myFile = new File(&amp;quot;/c/temp/temp2/&amp;quot;+item+&amp;quot;.txt&amp;quot;);&lt;br /&gt;
//            myFile.open(&amp;quot;w&amp;quot;);&lt;br /&gt;
//            myFile.encoding = &amp;quot;UTF-8&amp;quot;;&lt;br /&gt;
text += item + &amp;quot; = &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
temp = binary.toString();&lt;br /&gt;
temp = temp.replace(&amp;#039;(new String(&amp;#039;,&amp;#039;&amp;#039;);&lt;br /&gt;
temp = temp.replace(&amp;#039;))&amp;#039;,&amp;#039;;&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
text += temp;&lt;br /&gt;
text += &amp;#039;\n&amp;#039;;&lt;br /&gt;
            //myFile.write(binary.toString());&lt;br /&gt;
            //myFile.write(&amp;#039;\n\n&amp;#039;);&lt;br /&gt;
//            myFile.close();&lt;br /&gt;
&lt;br /&gt;
    //$.writeln(binary);&lt;br /&gt;
&lt;br /&gt;
    f.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myFile.write(text);&lt;br /&gt;
&lt;br /&gt;
myFile.close();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Watchfolder watcher ===&lt;br /&gt;
tb integrated&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
// v1.1 updated this 15 year old script for a job, surprised that it still worked-ish!&lt;br /&gt;
// &lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//states&lt;br /&gt;
// 0 - queued, paused           -&amp;gt; gray_o&lt;br /&gt;
// 1 - queued, ready to start   -&amp;gt; green_o&lt;br /&gt;
// 1 - done, no errors          -&amp;gt; green&lt;br /&gt;
// 2 - canceled, errors         -&amp;gt; red&lt;br /&gt;
// 3 - paused (?)               -&amp;gt; gray&lt;br /&gt;
// 4 - in progress              -&amp;gt; blue&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//ui pngs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
blue = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00DCIDAT8\u008D\u00A5\u0093\u00B1N\x02A\x14E\u00CF{\u00BB\u0089T\x04$F\u00D8\u0086\u00CE\u00BF\u00D0\u00DE\u00D2X\u0091\u00F8!\u00FE\x0B\u008D\u0095%\r\u00FE\u0085\u00DD~\x01\x1D\x18-\u008C\x15[8\u00D7\u008250\u0082\u00D9\u00D9\u00EC\u00ADf\u0092w\u00CF\u00DC\u00FB\u00921It\u0091wr\x03\u00F9\u00E1\u00C5\x16\u00EF7`s\u00CC\u00F3\u00FF\f;iK\u00F0\u0099\u00EE\u0087e&amp;lt;\u0098e\x0F\x04]AS-Ux\u00B8\x06\u00CA\u00B8\u00824M\u00CAmV!{\u0083\u00BF;\u0090M\u0092\x00\u00B2@\u00F6}\x02\u0080.\u0092\x00(#\u009CL\u00C0 \u00CD\u00CF\x19=\u00DFD\x00{Z\u00F50\x1A\u00B6_\u00CBq\u00DD\u009E\x7F\u00C5\t\u00FA\u0083K\u00D4\u00B8\u00FE\u009D\x02\x1F\u00BF\u00C7\u0083\x17\u00C3\x04c\x03&amp;lt;7\u00D8\x05\u00FEr\f0\x15\u00C0\u00AB\u00EEF\u008FI)j\u00ED+\x18\x05\u00B0nc\u008E\x01b\\Wh\u00A5}\x05g\u0089\u00E9\u00B3-\u00C0\u00BA~\u00E7\x1F\u0090\u00E3&amp;lt;q\u00B2\\\x19\u00D4\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
red = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00E5IDAT8\u008D\u00A5\u0093;N\x031\x14E\u00CF\u00F5D\u0090\u0092\x06\u0085\u00A4\u00A1c\x01\x14tD\u00CA&amp;amp;\u0090\&amp;quot;X\b{\u00A1\u00A1\u00A2\u00A0%\u008B\x18\th\u00B2\t\x06)\x05P\u0091(\u00E3KE\x14gD\u00F0h^eK&amp;gt;\u00E7}l\u00CB6]\&amp;quot;t\u00A2\u0081\u00DE\u00F6\u00E6\u00F1\u00E2|l\u00B8\u00B3\u00D4\u00FB\x0B\x00\x10|G1\u009D\u0096\u00AF\u00F3\u00F4`\b7\u00D8g\u00FA&amp;#039;\u00AB\u00F0R\u00E8\x12\u0098&amp;#039;-\u00D8\u009C\u00E6\u0094m\u00C2\x12\u00FC\x0E\u00BB3\u00B0\u00879\x02\u00E1\u0088\u00D5\x14H:\u00CE\x11\u0080\nQ7\x05\u00C6G9\u00B8\u00F1\u00E1Z\u00FD*\x11\u00DCO&amp;amp;}vneO\u0084\u00EB\u00B2\u00FCJ\x04\x07\u00AB\u00CF\x01\u0090\u00FB\u00AA\x16\u00BF\u008BM\u00C6\u00A2\u00D6\x10\u00A8\u0084\x1E\u00F6\u0091V\u00B4\u00EAb\u00D6\x10\x10\x19\x11x\u00BE*_n3\u00AB\x00\u00B6\u0087(F\u008A\u00BC\u00B5\u0081S\x01\u009C\u0080\u00AB\u00B6\u0082M\x0B\x0Ez\n\u00EB\u00F0\u00D1V\u00A0\u00AE\u00DF\u00F9\x07\x1F\u0090C\u00FA\u00D1U\r\x03\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
red_o = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00FEIDAT8\u008D\u00A5\u00931N\x02A\x18\u0085\u00BF7\x18C,L\u00AC\x168\u00C6\u00C6@\u00A5\u00E1\x10\u00C6hg\u00EB\t\u00F0\f^\u00C2\x16\u00B0\u00E4\x02\u0094\u009B(\x16\u009E\u00C1D\u00E9\u00A4\u00D1\u00A8\u00EC\u00FC6\u00EE\u00C2\u00AC\u00BA,\u00D9\u0097L\u00F16\u00EF\u00FFf\u00E7MFfF\x1D\u00B9Z\u00D3\u00C0\u00CE\u00BA\x19w\u00E3c\u0093\u00AE\u008B\u00DF\u00FF\u00D0\u0087\x17\u0097\u00E7\u00C9\u00EC1\b\u009A\u00DC\u0089`*\u00D9m\u00D9\u00B47\x06\u0082# \x04\b\u00DA^~t\u0096&amp;lt;\u00CC\u00CA\x00\u00E3\u00DE\u00A1\f?\u0087B\x07\u0086E\u0098\u00E6\x1B~?\u00C8\x15Kl\u0089t#`=W\x04DK5_*\x00V93\u00C3\u00CC\u00B8\u00E9\u00F7\u009B\u00C3n\u00FC\u0096\u00F9\u00FF\u00D6O\u00EE=\u00F3y\u0089\u00BB\u009F\u008B\b\u00F45\u00EC\u00C5\u0083\u00B2\u00AD\u00F7\u00D0\u00BE\u00C1S\u00E6s@#U\x1BX8\u00DCA\x19\u00C0\u00E4Mi\u00E3\u00E2\x17\x00O\x07\u00C7\u00DDir\x7FU\u00A1\u0083\\\u00AB\x12EG\u009E\u00E7m\u0086C\x00\u00B4\u00C0\u00AA\u00DC@\u00A0\u00FC\b\u00E64qK\u00F7\u00BA-@u\u009F\u00F37\u0085Ir\x0E\u00B9\u008B\u00D4\u0082\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
green = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00EFIDAT8\u008D\u00A5\u00931J\x03Q\x14E\u00CF}3hJ\x1B\u00D1\u00A4\u00B1s\x01\u00F6\n\u00AEA\x10\u00826\u00EE\&amp;quot;{\u00B1\u00B1\u00B2\u0090t\u008Ak\x10\u00D4&amp;amp;\u009Bp\x04\x0BM\u0095\u0080\u00F97\u00C5\u00A0f\u008C&amp;amp;3\u00CC\u00AB\u00FE\u0087\x7F\u00CF\u00BD\u00EF=\u00BEl\u00D3\u00A6\u00A2\u0095\x1A\u00C8\x17/\x077&amp;#039;G\u00D8\u0097\u00B2\u00F2\u00FF\x04\x00\u00C8\x13R\u00F4\u009F\u00FB\u00C3Q\u00E5a\u00A0s\u00C3&amp;gt;Z\u00EDji*\u00A5C`Tm\u00C1\u00EC\u00D5\u0089\x1D\u00F6\u00D4\u00E8\u00B54\u00AD\u00E8S\u00B7\x0E\u00C0R\x12\u00B1\f\u0090\u00B4]\x07 \u00C8f\u009A\u00FD\u0091\u00C0l\u00C1\u00FA\u00B5\u00DAlv&amp;gt;&amp;#039;E\x05p|u\u00D1\x01r\u00D6M\u00B0D\u00C4\u00C3\u00D9\u00FD\u00B8\x02\u00F8\u00D8\x18\u00EFP\u00C7\x1E\x00\u00BD}\u009D\u00BE\u00D7\u00A8\u008C.PH\u00BA^%MN\u00CE\u00A4\u00BB%\x00\u00B8\x07\u00F1\u00F8t:\x1C\u00D4KQ\u00D6\u00C2\x10\u00A3\u0087xi\&amp;quot;\u00FE\x05H\u00BB\u00C6ES\u00C0\u00CF\f\x1C\u00B7\x11\u00F1\u00DE\x14\u00A0\u00B6\u00DFy\x0E\u00F4\x1CG\u00E7\u00C3S\u00DC\u00E7\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
green_o = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00FFIDAT8\u008D\u00A5\u0093\u00B1J\x03a\x10\u0084g\u00F6b\b\x16\u0082\u00D5%y\u008C`\u00AB\u00E0+\x18\x11\u00C5FK\u009F &amp;gt;\u0083Oa\u00A91`!X[\n\x1A\x0B\u009FA\u00D0t&amp;amp;\u0095\u00A2\u00B7c\u00A1\u00B9\u00DC\u00FD\u00E2\x1F\u00C3M\u00B70\u00F3\u00B1\u00CC\u00B2\u0094\u0084*\u00B2Ji\x00\u00B5\u00E2\u00D0\u00E9w7(\u009C\x00\u00AC\u00FD\x15\u00F8\u0096\u00DE\u00E1&amp;lt;\x1A\u00EE\r\x1EKF\x13\u00B6A\u00DE\u0088\u00D9E4\u00EFI\x0F\u00F4u\x00e\x00\u0088\u0096\u00BB\u00CE\x1Fv/\u0087\u00B1\u00FCZ\u00BFK\x17G@\u00D0\u0081\u00A4\u0094\u00B0Q|\u00FD\u00B2/(\u0091\u00CD\u008C\u00D9\\@\u00D1\x17\x00\u00946&amp;gt;\u00DF^\u00E6\x03f\u00BE\x1C\u00B0yz\u00D8\x00\u00B8t\u00BB\x7F=\u0089E\x7F|\u00F5\u00A9//q\\\u009F\u00A4\u0084&amp;gt;:g[\u00BD\u00E8\u00F2\u00CB\\\u0081\u00F04\u009Ds\x00\x13\u00B4\x00\u008E\u00CD\u00B8\x1A\x03\u00B8\\\ty\u00F0\x0B\x00\u00A8\r\u00D8\u00DD\u00FD\u00CE\u00E08\x06\bU(\u00D1\u00DA \u009E\x17\t\x07\x00o\n\u00FA\u00C7\x05\u00CA\u009Au \u00BB2\u00B3\u00D7E\x01\u00AC\u00FA\u00CE_\u00A28Y\x00\x13L\u00B1)\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
gray = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00EEIDAT8\u008D\u00A5\u00931N\u00C3@\x10E\u00DF\x1F[@\x15\u00D1 H\x1A:n\x01\u0097\b\r2\u00CA9\u00B8\x0B\u008Di((\u00D2\u00C0-\u00E8r\t\x1C\u0089\x02\u00D1@\u0080\u00EC\u00A7!\x01;\&amp;quot;\u00B1\u0095\u00A9f\u00A5\u00FDo\u00FF\x1F\u00ED\u00C86\u00DBTl\u00A5\x06\u00F2\u00BF\u0087\u00F1\u00DD\u00CD\x19\u00E8\x1A)\u00FFO\x00 \u00D2{\n]\f\u0087\u00A3I\u00ED\u00A2B\u00976&amp;#039;\u00B0&amp;gt;\u0096a\x16s\u009D\x02\u0093\u00E6K\u00C7ml\u00DB\u009A!O\u00A19\x03\u00BB\u00DF\x06\x10rJ\u008AU\u0080\u00D1A+\x07(\u00CB\u00E6i\n\u008D!\x1A\u00F6\x01\u00B4\x11\u00C0\u00EE\u00DBWV\u00D5\x1C\u0094e\u00B9&amp;#039;\u00C87\u0089\x7FDQ\x14\u00C5k\r\u00D0\u00EB\u00ED\x1C\u009Av\u00BF\u00CA\u00F0\u00BC\u00E8\x7F#|~\u00F4\x15Q!n\u00D7\u0089el\u00FBa\x05\u00A0,\x1B$\u00FBqx&amp;gt;\u00BAj\u00E3bQ\u00CB\b\u00C9\x1E ?u\x11\u00D7\x00\u0081\u008F\x04UW\u00C02B\u008A\u00B8OI/]\x01\u00DAv\u009D\u00BF\x01\u00E7\u008FN\x13\u00B9\u00E9m\u0087\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
gray_o = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00FFIDAT8\u008D\u00A5\u00931N\u00C3@\x14Dg\u00BEC\u0094\x02Y\u00A2r\u0092\u00BB\u00903 9T\u0096\x02-&amp;#039;\bg\u00E0\x14&amp;amp;\x1D\u0089\x14\n.@\u008Fh8\x03R\u0092\x0Eh\x10$\u00DE\u00A1!N\u00BC\u00A0\u00B5\&amp;quot;O\u00F7wg\u009E\u00FE\u00FF\u00AB\u00A5$4\u00915J\x03h\u00ED\x17\u00F3\u00E9\u00ED\x00\u00E4\u008D\x7F\u00FE\u008F\u00BEd\u00B8J\u00D3\u00D1\u008Bg\u00E4\x10\u00C4\u00A3I\u00B3P\u00DA\u0081c+x\n\u00C0\x03\x10=\bwg\u00E7\x17\u00CF!\u00C0|6\u00A1\u00A8\x15\u00E0\u00ED\u0080P\u00E2h\u00AB\u009A\u00F6+\u00BE\n@`7*\\-`\u00DF\u00D7\u00AA^ \u00F9\u00DCD\u00CBz\u00C0\u00CEWv\u0090\u00E7y\u0087\u00C0Q\u0096e\x1F\u00A1\u00F0\u00AF\u00AF\u00BD\u00F5\u0095\x1D\u00C4q;Q\u00B1Y\u00DFO&amp;#039;\u00E3\x10 &amp;gt;\u00B6\x18\u00C2\u00EB\u00B6\u00DE\u008D\u00B0\u00FE\u00EE\u00D1\u00EC]\u0086\u0093\x10\u0080\u0082$]\u00FE\x010\u008A\u00FANzJ\u0087\u00A3\u00EB\x10\u00C0W\u00B9\x03&amp;#039;\u00F5A-\x0E\tW\x00\x06u\t\u00D4\u00BE\u0080\u00AFr\x04g\u00F6\u00E0\x1C\u00DF\x0E\x05\u00B0\u00E9w\u00FE\x01\u00C6z]\u00D4\u0097\x17j\u00EE\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var states = [gray_o,green_o,green, red, gray, blue];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
          &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 20);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
&lt;br /&gt;
    //var arrayV = new Array(green,green_o,gray,gray_o,red,red_o);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    item.image =  File.decode(states[state]);&lt;br /&gt;
    //palette.add(&amp;quot;image&amp;quot;, undefined, File.decode(gray), {name: &amp;quot;image1&amp;quot;}, [10,10]);&lt;br /&gt;
    //item.image =  File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SimpleWatchfolder WIP ===&lt;br /&gt;
some improvements&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
	var errorList=&amp;quot;&amp;quot;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
				errorList += app.project.items[i].name + &amp;quot;\n&amp;quot;; // + &amp;quot; &amp;gt; &amp;quot; + app.project.items[i].mainSource.file.path.toString + &amp;quot;\n&amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footage(s) in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button\n\n&amp;quot;+errorList);&lt;br /&gt;
	}else{&lt;br /&gt;
		// the important part of this script. Creates the RCF (render control file), the undocumented file that will launch the watchfolder process.&lt;br /&gt;
		// the way it is created is kind of black-boxy, but this setup works for me.&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
	    // todo add start &amp;quot;&amp;quot; &amp;quot;C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\AfterFX.exe&amp;quot; -noui -m -re -wf .......&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
		if (ScriptUI.environment.keyboardState.shiftKey === true) {&lt;br /&gt;
		        alert(&amp;quot;lol&amp;quot;);&lt;br /&gt;
    		}&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Keyframes and Layers counter ===&lt;br /&gt;
wip, doesn&amp;#039;t work with animated shapes for now&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
layersCount = 0;&lt;br /&gt;
keyframesCount = 0;&lt;br /&gt;
collection = app.project.items;&lt;br /&gt;
for(i = 1;i&amp;lt;=collection.length;i++){&lt;br /&gt;
    curItem = collection[i];&lt;br /&gt;
    if(curItem instanceof CompItem){&lt;br /&gt;
        len = curItem.layers.length;&lt;br /&gt;
        layersCount += len;&lt;br /&gt;
        for(j = 1;j&amp;lt;=len;j++){&lt;br /&gt;
            curLayer = curItem.layers[j];&lt;br /&gt;
            for(k = 1;k &amp;lt;= curLayer.numProperties;k++){&lt;br /&gt;
                for(l = 1;l &amp;lt;= curLayer.property(k).numProperties; l++){&lt;br /&gt;
                    //$.writeln(keyframesCount);&lt;br /&gt;
                    if(curLayer.property(k).property(l).numProperties&amp;gt;0){&lt;br /&gt;
                         for(m = 1;m &amp;lt;= curLayer.property(k).property(l).numProperties; m++){&lt;br /&gt;
                            nKeys = curLayer.property(k).property(l).property(m).numKeys;&lt;br /&gt;
                            if(nKeys &amp;gt; 0){&lt;br /&gt;
                                 keyframesCount += nKeys;&lt;br /&gt;
                            }&lt;br /&gt;
                            //ugh i wish i was better at recursiveness&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).property(m).matchName);&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).property(m).numKeys);     &lt;br /&gt;
                        }&lt;br /&gt;
                    }else{&lt;br /&gt;
                            nkeys = curLayer.property(k).property(l).numKeys;&lt;br /&gt;
                            if(nKeys &amp;gt; 0){&lt;br /&gt;
                                keyframesCount += nKeys;&lt;br /&gt;
                            }&lt;br /&gt;
                            //keyframesCount += curLayer.property(k).property(l).numKeys;&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).matchName);&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).numKeys);     &lt;br /&gt;
                    }&lt;br /&gt;
                }  &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
alert(keyframesCount+&amp;quot; keyframes found and\n&amp;quot;+layersCount+&amp;quot; layers counted in the project&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
function iterateProperties(prop){&lt;br /&gt;
           // $.writeln(prop.numProperties);    &lt;br /&gt;
    if(prop.numProperties &amp;gt; 0){&lt;br /&gt;
        for(i = 1;i&amp;lt;=prop.numProperties;i++){&lt;br /&gt;
            iterateProperties(prop.property(i));&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
    //    $.writeln(prop.matchName);&lt;br /&gt;
    }&lt;br /&gt;
    //$.writeln(prop.matchName);&lt;br /&gt;
}&lt;br /&gt;
//alert(app.project.items[2])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Batch render project items ===&lt;br /&gt;
Select project footage and it will render them with the chosen rq preset&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template = &amp;quot;QTJPG2000&amp;quot;; // chosen by hand, for now&lt;br /&gt;
&lt;br /&gt;
selectedItems = app.project.selection;&lt;br /&gt;
rq = app.project.renderQueue;&lt;br /&gt;
&lt;br /&gt;
for(i=0;i&amp;lt;selectedItems.length;i++){&lt;br /&gt;
    &lt;br /&gt;
    item = selectedItems[i];&lt;br /&gt;
    path = item.mainSource.file.path.toString()+&amp;#039;/&amp;#039;+item.name;&lt;br /&gt;
    p = app.project.items.addComp(item.name,item.width,item.height,1.0,item.duration,item.frameRate);&lt;br /&gt;
    p.layers.add(item);&lt;br /&gt;
    rqitem = rq.items.add(p);&lt;br /&gt;
	rqitem.outputModules[1].file = new File(path);&lt;br /&gt;
	rqitem.outputModules[1].applyTemplate(template);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Comp to Renderqueue with Template ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6iJkeH9.png&lt;br /&gt;
&lt;br /&gt;
Very wip. Features and UI don&amp;#039;t fully work.&lt;br /&gt;
&lt;br /&gt;
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&amp;#039;s name and folder, the name of the After Effects project, the current date etc...&lt;br /&gt;
&lt;br /&gt;
 It still needs work, which will happend if someone ever hires me for AE work again :)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//todo: be able to select existing RQ items instead of simply comps, and edit their existing paths&lt;br /&gt;
&lt;br /&gt;
function e(s) {&lt;br /&gt;
	$.writeln(s)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var win;&lt;br /&gt;
&lt;br /&gt;
function renderAll() {&lt;br /&gt;
	//prefTemplate = (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;))?(app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)):false;&lt;br /&gt;
	sel = app.project.selection;&lt;br /&gt;
	if (sel.length &amp;gt; 0) {&lt;br /&gt;
		//needs a comp to create a render item to get templates&lt;br /&gt;
		getTemplateUI(sel[0]);&lt;br /&gt;
		//if ui worked fine, there should be a template setting string, if there&amp;#039;s a bug or user cancelled, fuck it.&lt;br /&gt;
		prefTemplate = (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) ? (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) : false;&lt;br /&gt;
		if (prefTemplate != false &amp;amp;&amp;amp; prefTemplate != &amp;quot;null&amp;quot;) {&lt;br /&gt;
			for (i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
				renderCompWithTemplate(sel[i], prefTemplate)&lt;br /&gt;
			}&lt;br /&gt;
		} else {&lt;br /&gt;
			writeLn(&amp;quot;Send to renderqueue cancelled.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	} else {&lt;br /&gt;
		alert(&amp;quot;Select at least one comp or render queue element&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTemplateUI(nullComp) {&lt;br /&gt;
	// get previous render settings, so we can use it as default selection later&lt;br /&gt;
	prefTemplate = (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) ? (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) : false;&lt;br /&gt;
	// void render settings if user clicks cancel, to be changed&lt;br /&gt;
	app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;, &amp;quot;null&amp;quot;);&lt;br /&gt;
	ai = app.project.activeItem;&lt;br /&gt;
	// must have at least one comp selected, and the project saved to know where to place the renders&lt;br /&gt;
	// eventually the user shouldn&amp;#039;t have to save his AEP, but have it appear as a warning somewhere that he should&lt;br /&gt;
	if (nullComp != null &amp;amp;&amp;amp; nullComp instanceof CompItem &amp;amp;&amp;amp; app.project.file != null) {&lt;br /&gt;
		rq = app.project.renderQueue;&lt;br /&gt;
		//create null render comp to fetch templates, delete it right afterwards&lt;br /&gt;
		rqitem = rq.items.add(nullComp);&lt;br /&gt;
		rqtemplates = rqitem.outputModules[1].templates;&lt;br /&gt;
		rqitem.remove();&lt;br /&gt;
		// dockable window to do&lt;br /&gt;
		//res = &amp;quot;dialog { properties:{ resizeable:true }, preferredSize: [690, 20],  alignChildren: &amp;#039;fill&amp;#039;,orientation:&amp;#039;column&amp;#039;,\&lt;br /&gt;
		res = &amp;quot;dialog {  \&lt;br /&gt;
				properties:{ resizeable:true }, alignChildren: &amp;#039;fill&amp;#039;,orientation:&amp;#039;column&amp;#039;,\&lt;br /&gt;
				templatesPnl: Panel { \&lt;br /&gt;
					orientation:&amp;#039;column&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;top&amp;#039;],text: &amp;#039;Choose a rendering template&amp;#039;,\&lt;br /&gt;
					templates: DropDownList {}, \&lt;br /&gt;
				}, \&lt;br /&gt;
				filePnl: Panel { \&lt;br /&gt;
					orientation:&amp;#039;column&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;top&amp;#039;],text: &amp;#039;Display Paths&amp;#039;,\&lt;br /&gt;
					pathRbs: Group { \&lt;br /&gt;
						orientation:&amp;#039;row&amp;#039;,  alignment: &amp;#039;left&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;bottom&amp;#039;] \&lt;br /&gt;
						txt: StaticText { alignment:&amp;#039;left&amp;#039;, text:&amp;#039;File path style:&amp;#039; }, \&lt;br /&gt;
						unixStylePathRb: RadioButton { text:&amp;#039;Unix (mac)&amp;#039; }, \&lt;br /&gt;
						winStylePathRb: RadioButton { text:&amp;#039;Windows&amp;#039;, value:true } \&lt;br /&gt;
					}, \&lt;br /&gt;
					showPathText: StaticText {alignment: &amp;#039;fill&amp;#039;},  \&lt;br /&gt;
					edithPathGrp: Group { \&lt;br /&gt;
						orientation:&amp;#039;row&amp;#039;, spacing:2, alignment: &amp;#039;left&amp;#039;, \&lt;br /&gt;
						editPathBox: EditText { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],  margins: 8, text:&amp;#039;&amp;#039;, properties:{borderless:true}},  \&lt;br /&gt;
						testBtn: Button { alignement:[center&amp;#039;,&amp;#039;center&amp;#039;], text:&amp;#039;Test&amp;#039;, properties:{name:&amp;#039;test&amp;#039;} } ,\&lt;br /&gt;
					}, \&lt;br /&gt;
					explainPathText: StaticText {alignment: &amp;#039;fill&amp;#039;, properties:{multiline: true } }, \&lt;br /&gt;
					explainPathTextGrp: Group { \&lt;br /&gt;
						orientation:&amp;#039;row&amp;#039;,  alignment: &amp;#039;left&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;center&amp;#039;] \&lt;br /&gt;
						c1: StaticText {alignment: &amp;#039;fill&amp;#039;, properties:{multiline: true } },  \&lt;br /&gt;
						c2: StaticText {alignment: &amp;#039;fill&amp;#039;, properties:{multiline: true } } \&lt;br /&gt;
					}  \&lt;br /&gt;
				}, \&lt;br /&gt;
				buttons: Group { orientation: &amp;#039;row&amp;#039;, alignment: &amp;#039;right&amp;#039;, \&lt;br /&gt;
					okBtn: Button { text:&amp;#039;OK&amp;#039;, properties:{name:&amp;#039;ok&amp;#039;} }, \&lt;br /&gt;
					cancelBtn: Button { text:&amp;#039;Cancel&amp;#039;, properties:{name:&amp;#039;cancel&amp;#039;} }, \&lt;br /&gt;
				}, \&lt;br /&gt;
			}&amp;quot;;&lt;br /&gt;
		// get/set the display path preference (it will always be unix style internally) 1=windows 0=mac/unix&lt;br /&gt;
		//input base text &lt;br /&gt;
		explanationText = &amp;quot;Here are variables you can use:\n&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;\t{compname} {compfolder} {projpath} {compid}\n&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;\t{projname} {date} (using date:y/m/d)&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;\t[#], [##], [####] (padding)\n&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;You can write &amp;#039;/../&amp;#039; to go &amp;#039;up&amp;#039; one directory &amp;quot;;&lt;br /&gt;
		// create window resource&lt;br /&gt;
		win = new Window(res);&lt;br /&gt;
		//set the preferred template&lt;br /&gt;
		preferredItem = 0;&lt;br /&gt;
		for (i = 0; i &amp;lt; rqtemplates.length; i++) {&lt;br /&gt;
			//skip hidden templates&lt;br /&gt;
			if (rqtemplates[i].indexOf(&amp;#039;_HIDDEN&amp;#039;) != 0) {&lt;br /&gt;
				item = win.templatesPnl.templates.add(&amp;#039;item&amp;#039;, rqtemplates[i]);&lt;br /&gt;
				if (rqtemplates[i] == prefTemplate) {&lt;br /&gt;
					preferredItem = i;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		win.templatesPnl.templates.selection = win.templatesPnl.templates.items[preferredItem];&lt;br /&gt;
		// can&amp;#039;t add newline \n character in res, so fill in text now&lt;br /&gt;
		win.filePnl.explainPathText.text = explanationText;&lt;br /&gt;
		// set os choice radio button, figure out if it&amp;#039;s in prefs &lt;br /&gt;
		// eventually users shouldn&amp;#039;t see this, the script should be tailored for mac or windows&lt;br /&gt;
		os = $.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;) != -1;&lt;br /&gt;
		if (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;)) {&lt;br /&gt;
			os = (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;) == 1) ? 1 : 0;&lt;br /&gt;
		} else {&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;, os);&lt;br /&gt;
		}&lt;br /&gt;
		win.filePnl.pathRbs.unixStylePathRb.value = !os;&lt;br /&gt;
		// set path template box, if it doesn&amp;#039;t have one, use a default;&lt;br /&gt;
		inputBox = win.filePnl.edithPathGrp.editPathBox;&lt;br /&gt;
		outPutBox = win.filePnl.showPathText;&lt;br /&gt;
		var pathTemplate = &amp;quot;&amp;quot;;&lt;br /&gt;
		if (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;)) {&lt;br /&gt;
			pathTemplate = app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;);&lt;br /&gt;
		} else {&lt;br /&gt;
			pathTemplate = &amp;quot;{projpath}/RENDER/{date:ymd}_{compname}/{compname}.[####]&amp;quot;;&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;, pathTemplate);&lt;br /&gt;
		}&lt;br /&gt;
		inputBox.text = pathTemplate;&lt;br /&gt;
		//add ui callbacks&lt;br /&gt;
		//call once, to fill the result text box&lt;br /&gt;
		updatePath(inputBox, outPutBox);&lt;br /&gt;
		inputBox.onChange = inputBox.onChanging = function() {&lt;br /&gt;
			updatePath(inputBox, outPutBox)&lt;br /&gt;
		};&lt;br /&gt;
		/*-----TO BE CUT---------*/&lt;br /&gt;
		win.filePnl.pathRbs.unixStylePathRb.onClick = win.filePnl.pathRbs.winStylePathRb.onClick = function() {&lt;br /&gt;
			//save preferences&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;, (!win.filePnl.pathRbs.unixStylePathRb.value) ? 1 : 0);&lt;br /&gt;
			updatePath(inputBox, outPutBox);&lt;br /&gt;
		}&lt;br /&gt;
		/*-----END CUT-----------*/&lt;br /&gt;
		win.filePnl.edithPathGrp.testBtn.onClick = function() {&lt;br /&gt;
			fp = descriptionToFilePath(&amp;quot;&amp;quot;, 1, inputBox.text, 0);&lt;br /&gt;
			fpFolder = new Folder(fp);&lt;br /&gt;
			e(fpFolder.absoluteURI);&lt;br /&gt;
		}&lt;br /&gt;
		win.buttons.okBtn.onClick = function() {&lt;br /&gt;
			//save preferences&lt;br /&gt;
			prefTemplate = win.templatesPnl.templates.selection;&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;, inputBox.text);&lt;br /&gt;
			//app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;, (!win.filePnl.pathRbs.unixStylePathRb.value)?1:0);&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;, prefTemplate);&lt;br /&gt;
			win.close();&lt;br /&gt;
		}&lt;br /&gt;
		win.layout.layout(true);&lt;br /&gt;
		win.onResizing = win.onResize = function() {&lt;br /&gt;
			this.layout.resize()&lt;br /&gt;
		};&lt;br /&gt;
		win.center();&lt;br /&gt;
		win.show();&lt;br /&gt;
	} else {&lt;br /&gt;
		//alert dialogs suck, but they are efficient&lt;br /&gt;
		alert(&amp;quot;Select at least one comp to render, and make sure your After Effects file is saved to disk.&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function resizeUI(windowObj) {&lt;br /&gt;
	//nasty function which creates a new textbox to figure out how big the editbox is, so no text is clipped&lt;br /&gt;
	var textbox = windowObj.filePnl.edithPathGrp.editPathBox;&lt;br /&gt;
	//var testbutton = windowObj.filePnl.edithPathGrp.testBtn;&lt;br /&gt;
	g = windowObj.filePnl.add(&amp;quot;group&amp;quot;, [0, 0, 0, 0]);&lt;br /&gt;
	g.enabled = g.visible = false;&lt;br /&gt;
	tmp = g.add(&amp;quot;edittext&amp;quot;, undefined, textbox.text, {&lt;br /&gt;
		enabled: false,&lt;br /&gt;
		visible: false&lt;br /&gt;
	});&lt;br /&gt;
	var preferredWidth = tmp.preferredSize[0];&lt;br /&gt;
	//e(&amp;quot;newpref: &amp;quot;+r.preferredSize[0]);&lt;br /&gt;
	windowObj.filePnl.remove(g);&lt;br /&gt;
	textbox.size = [preferredWidth, textbox.preferredSize[1]];&lt;br /&gt;
	//testbutton.size = testbutton.preferredSize;&lt;br /&gt;
	windowObj.layout.layout(true);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function updatePath(inputRes, outputRes) {&lt;br /&gt;
	os = (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;) == 1) ? 1 : 0;&lt;br /&gt;
	input = inputRes.text;&lt;br /&gt;
	input = descriptionToFilePath(&amp;quot;&amp;quot;, 1, input, 1);&lt;br /&gt;
	input = setPathStyle(input, !os); //backslashes to slashes&lt;br /&gt;
	outputRes.text = input;&lt;br /&gt;
	resizeUI(inputRes.parent.parent.parent);&lt;br /&gt;
	//windowObj.layout.layout(true);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function descriptionToFilePath(comp, rqid, descriptionString, forDisplay) {&lt;br /&gt;
	// takes a description like {projpath}/{projname}_{compname}/{compname}_[#####] and transforms it into a usable unix path&lt;br /&gt;
	// if there is no comp fed, use the first in the selection.&lt;br /&gt;
	// &amp;#039;forDisplay&amp;#039; int decides if it&amp;#039;s for display (add frame count and turn %20s to spaces) or actual path to be used&lt;br /&gt;
	// &amp;#039;rqid&amp;#039; is the number of the comp in the renderqueue, default is 1&lt;br /&gt;
	var str = descriptionString;&lt;br /&gt;
	if (comp == &amp;quot;&amp;quot; || comp == null) {&lt;br /&gt;
		comp = app.project.selection[0];&lt;br /&gt;
	}&lt;br /&gt;
	var projName = app.project.file.name.toString();&lt;br /&gt;
	//standard stuff, comp path name etc....&lt;br /&gt;
	str = str.replace(/\{projpath\}/gi, app.project.file.path.toString());&lt;br /&gt;
	projName = projName.substr(0, projName.lastIndexOf(&amp;#039;.&amp;#039;)) || projName;&lt;br /&gt;
	str = str.replace(/\{projname\}/gi, projName);&lt;br /&gt;
	str = str.replace(/\{compname\}/gi, comp.name);&lt;br /&gt;
	var parentFolder = (comp.parentFolder.name == &amp;quot;Root&amp;quot;) ? &amp;quot;&amp;quot; : comp.parentFolder.name;&lt;br /&gt;
	str = str.replace(/\{compfolder\}/gi, parentFolder);&lt;br /&gt;
	//if we use {id}, make sure it&amp;#039;s padded &lt;br /&gt;
	var rq = app.project.renderQueue;&lt;br /&gt;
	str = str.replace(/\{id\}/gi, pad(rq.items.length.toString().length, rqid.toString(), &amp;quot;0&amp;quot;));&lt;br /&gt;
	//figure out padding, if it&amp;#039;s forDisplay, show it to the user&lt;br /&gt;
	startPad = str.indexOf(&amp;quot;[#&amp;quot;);&lt;br /&gt;
	endPad = str.lastIndexOf(&amp;quot;#]&amp;quot;);&lt;br /&gt;
	if (startPad &amp;gt; 0 &amp;amp;&amp;amp; endPad &amp;gt; 0 &amp;amp;&amp;amp; endPad &amp;gt; startPad &amp;amp;&amp;amp; forDisplay) {&lt;br /&gt;
		startFrame = comp.workAreaStart / comp.frameDuration;&lt;br /&gt;
		str = str.substring(0, startPad) + pad(endPad - startPad, startFrame.toString(), &amp;quot;0&amp;quot;) + str.substring(endPad + 2, str.length);&lt;br /&gt;
	}&lt;br /&gt;
	//date&lt;br /&gt;
	var datesArray = str.split(&amp;quot;{date:&amp;quot;);&lt;br /&gt;
	var tempstr = &amp;quot;&amp;quot;;&lt;br /&gt;
	if (datesArray.length &amp;gt; 0) {&lt;br /&gt;
		var today = new Date();&lt;br /&gt;
		var dd = today.getDate().toString();&lt;br /&gt;
		var mm = (today.getMonth() + 1).toString();&lt;br /&gt;
		var yyyy = today.getFullYear();&lt;br /&gt;
		tempstr = datesArray[0];&lt;br /&gt;
		for (i = 1; i &amp;lt; datesArray.length; i++) {&lt;br /&gt;
			endBracketPos = datesArray[i].indexOf(&amp;quot;}&amp;quot;);&lt;br /&gt;
			nextBracketPos = datesArray[i].indexOf(&amp;quot;{&amp;quot;);&lt;br /&gt;
			nextBracketPos = (nextBracketPos == -1) ? 9999 : nextBracketPos; //case if date is the last used tag&lt;br /&gt;
			if (endBracketPos &amp;gt; -1 &amp;amp;&amp;amp; endBracketPos &amp;lt; nextBracketPos) {&lt;br /&gt;
				dateString = datesArray[i].substring(0, endBracketPos);&lt;br /&gt;
				dateString = dateString.replace(/y/gi, yyyy);&lt;br /&gt;
				dateString = dateString.replace(/d/gi, pad(2, dd, &amp;quot;0&amp;quot;));&lt;br /&gt;
				dateString = dateString.replace(/m/gi, pad(2, mm, &amp;quot;0&amp;quot;));&lt;br /&gt;
				tempstr += dateString + datesArray[i].substring(endBracketPos + 1, datesArray[i].length);&lt;br /&gt;
			} else {&lt;br /&gt;
				tempstr = str;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		str = tempstr;&lt;br /&gt;
	}&lt;br /&gt;
	if (forDisplay) {&lt;br /&gt;
		str += &amp;quot;.ext&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function renderCompWithTemplate(comp, template) {&lt;br /&gt;
	//sends the comp to the renderqueue with the selected file path, creating folders and subfolders if needed&lt;br /&gt;
	rq = app.project.renderQueue;&lt;br /&gt;
	rqitem = rq.items.add(comp);&lt;br /&gt;
	rqtemplates = rqitem.outputModules[1].templates;&lt;br /&gt;
	filename = app.project.file.name;&lt;br /&gt;
	filepath = app.project.file.toString();&lt;br /&gt;
	projpath = filepath.slice(0, filepath.length - filename.length);&lt;br /&gt;
	savePath = projpath + comp.name + &amp;quot;/&amp;quot; + filename.slice(0, filename.length - 4) + &amp;quot;/&amp;quot;;&lt;br /&gt;
	saveFile = comp.name + &amp;quot;.[####].tif&amp;quot;;&lt;br /&gt;
	saveFolder = new Folder(savePath);&lt;br /&gt;
	saveFolder.create();&lt;br /&gt;
	rqitem.outputModules[1].file = new File(savePath + &amp;quot;/&amp;quot; + saveFile);&lt;br /&gt;
	rqitem.outputModules[1].applyTemplate(template);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPathStyle(path, toUnixPath) {&lt;br /&gt;
	// takes a string and creates either a unix path like /c/my%20folder/ if toUnixPath is set to true&lt;br /&gt;
	// or a windows path like c:\my folder\. Returns false if there&amp;#039;s a problem (tbd)&lt;br /&gt;
	if (!toUnixPath) {&lt;br /&gt;
		//unix -&amp;gt; win&lt;br /&gt;
		path = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
		path = path.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
		path = path.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
		path = path.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
		path = path.charAt(0).toUpperCase() + path.slice(1);&lt;br /&gt;
	} else {&lt;br /&gt;
		path = &amp;quot;/&amp;quot; + path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
		path = path.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
		path = path.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
		//path = path.substring(0,path.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
	}&lt;br /&gt;
	return path;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(width, string, padding) {&lt;br /&gt;
	return (width &amp;lt;= string.length) ? string : pad(width, padding + string, padding);&lt;br /&gt;
}&lt;br /&gt;
renderAll();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== create effect creator ===&lt;br /&gt;
==== ui ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(s){&lt;br /&gt;
	$.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
var windowObj;&lt;br /&gt;
function ui(thisObj) {&lt;br /&gt;
	windowObj = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;ui_test&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
	addButton(&amp;quot;flip&amp;quot;,windowObj);&lt;br /&gt;
	return windowObj;&lt;br /&gt;
}*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addButton(buttonname,ui){&lt;br /&gt;
	//ui.add(&amp;quot;button&amp;quot;, [0, 0, 20, 20], But_01[0]);&lt;br /&gt;
	return ui.add(&amp;quot;button&amp;quot;, undefined, buttonname);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function ui(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
     }else{&lt;br /&gt;
                var res = &lt;br /&gt;
            &amp;quot;group { \&lt;br /&gt;
                        alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                        alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                        orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                            setWF: Button {text: &amp;#039;add btn&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                            sendWF: Button {text: &amp;#039;Send To Watchfolder&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;	&lt;br /&gt;
            pan.grp = pan.add(res);        &lt;br /&gt;
            pan.grp.setWF.onClick = function () {&lt;br /&gt;
            	addButton(&amp;quot;joe&amp;quot;,pan.grp);&lt;br /&gt;
            	resfreshUI(pan);&lt;br /&gt;
                    }&lt;br /&gt;
            pan.grp.sendWF.onClick = function () {&lt;br /&gt;
                }&lt;br /&gt;
            resfreshUI(pan);    &lt;br /&gt;
            /*&lt;br /&gt;
            pan.layout.layout(true);&lt;br /&gt;
            pan.layout.resize();&lt;br /&gt;
            pan.onResizing = pan.onResize = function () {this.layout.resize();}*/&lt;br /&gt;
            return pan;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function resfreshUI(src){&lt;br /&gt;
	src.layout.layout(true);&lt;br /&gt;
	src.layout.resize();&lt;br /&gt;
	src.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ui(this);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== fn ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(s){&lt;br /&gt;
	$.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// to be done:&lt;br /&gt;
// 		save keys&lt;br /&gt;
// 		save masks&lt;br /&gt;
//		save text if text layer, camera zoom if camera etc&lt;br /&gt;
//		use FFX as custom values&lt;br /&gt;
&lt;br /&gt;
function grabEffects(layers){&lt;br /&gt;
	&lt;br /&gt;
	var buffer = &amp;quot;&amp;quot;;&lt;br /&gt;
	var chosenLayerName = &amp;quot;curLayer&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	for (i=0;i&amp;lt;layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
		effs = layers[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
&lt;br /&gt;
			//create the effects, no matter if properties below have values&lt;br /&gt;
&lt;br /&gt;
			buffer += &amp;quot;\n\tprop = &amp;quot;+ chosenLayerName +&amp;quot;.Effects.addProperty(\&amp;quot;&amp;quot;+ effs.property(j).matchName +&amp;quot;\&amp;quot;);\n&amp;quot;;&lt;br /&gt;
			buffer += &amp;quot;\tprop.name = \&amp;quot;&amp;quot;+ effs.property(j).name +&amp;quot;\&amp;quot;;\n&amp;quot;;&lt;br /&gt;
			buffer += &amp;quot;\tprop.enabled = &amp;quot;+ effs.property(j).enabled +&amp;quot;;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
			for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
&lt;br /&gt;
				curProp = effs.property(j).property(k);&lt;br /&gt;
				&lt;br /&gt;
				//only add value if it&amp;#039;s non-default&lt;br /&gt;
&lt;br /&gt;
				if(curProp.isModified){&lt;br /&gt;
&lt;br /&gt;
					curPropValue = propType(curProp);&lt;br /&gt;
&lt;br /&gt;
					// if it returns -1, it&amp;#039;s (probably) a useless &amp;#039;parent&amp;#039; property, skip&lt;br /&gt;
&lt;br /&gt;
					if( curPropValue != -1){&lt;br /&gt;
						&lt;br /&gt;
						// /!\ if it&amp;#039;sa  custom value type, there&amp;#039;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 &amp;#039;-1&amp;#039; as curpropvalue&lt;br /&gt;
&lt;br /&gt;
						buffer += &amp;quot;\t\t// &amp;quot;+curProp.name+((curPropValue===false)?&amp;quot; /!\\ Can&amp;#039;t use this custom data! could be a false negative, but unlikely &amp;quot;:&amp;quot;&amp;quot;)+&amp;quot;\n&amp;quot;;                                        &lt;br /&gt;
						buffer += &amp;quot;\t\t&amp;quot;+((curPropValue===false)?&amp;quot;//&amp;quot;:&amp;quot;&amp;quot;) + &amp;quot;prop.property(\&amp;quot;&amp;quot;+curProp.matchName+&amp;quot;\&amp;quot;).setValue(&amp;quot; + curPropValue + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
						&lt;br /&gt;
						// if there is an expression and it&amp;#039;s enabled, use it&lt;br /&gt;
&lt;br /&gt;
						if(curProp.expression != &amp;quot;&amp;quot; &amp;amp;&amp;amp; curProp.expressionEnabled){&lt;br /&gt;
							buffer += &amp;quot;\t\t&amp;quot;+((curPropValue===false)?&amp;quot;//&amp;quot;:&amp;quot;&amp;quot;) + &amp;quot;prop.property(\&amp;quot;&amp;quot;+curProp.matchName+&amp;quot;\&amp;quot;).expression = \&amp;quot;&amp;quot;+curProp.expression+&amp;quot;\&amp;quot;;\n&amp;quot;;&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return buffer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function outputCode(){&lt;br /&gt;
	&lt;br /&gt;
	effectsLayers = app.project.activeItem.selectedLayers; //use effects from all selected layers&lt;br /&gt;
&lt;br /&gt;
	buffer = &amp;quot;&amp;quot;;&lt;br /&gt;
	if(effectsLayers.length&amp;gt;0){&lt;br /&gt;
		buffer = &amp;quot;var selectedLayers = app.project.activeItem.selectedLayers;\n&amp;quot;;&lt;br /&gt;
		buffer += &amp;quot;for (i=0;i&amp;lt;selectedLayers.length;i++){\n&amp;quot;;&lt;br /&gt;
		buffer += &amp;quot;\n\tcurLayer = selectedLayers[i];\n&amp;quot;; &lt;br /&gt;
		buffer += grabEffects(effectsLayers) + &amp;quot;\n}&amp;quot;;&lt;br /&gt;
		//e(buffer);&lt;br /&gt;
	}&lt;br /&gt;
	return buffer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
e(outputCode());&lt;br /&gt;
e(&amp;quot;\n------------------------------------------------------&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function propType(property){&lt;br /&gt;
&lt;br /&gt;
	returnvalue = false;&lt;br /&gt;
	&lt;br /&gt;
	e(&amp;quot;---&amp;gt;&amp;quot;+property.name+&amp;quot; &amp;quot;+property.propertyValueType+&amp;quot;\n&amp;quot;); //debug&lt;br /&gt;
	//e(&amp;quot;---&amp;gt;&amp;quot;+property.name+&amp;quot; &amp;quot;+property.canVaryOverTime+&amp;quot;\n&amp;quot;); //debug		&lt;br /&gt;
	&lt;br /&gt;
	switch(property.propertyValueType){&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.ThreeD_SPATIAL:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;,&amp;quot;+property.value[2]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
		case PropertyValueType.ThreeD:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;,&amp;quot;+property.value[2]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.TwoD_SPATIAL:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
		case PropertyValueType.TwoD:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;			&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.OneD:&lt;br /&gt;
			returnvalue = property.value;&lt;br /&gt;
			break;		&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.COLOR:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;,&amp;quot;+property.value[2]+&amp;quot;,&amp;quot;+property.value[3]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.LAYER_INDEX:&lt;br /&gt;
			returnvalue = property.value;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.MASK_INDEX:&lt;br /&gt;
			returnvalue = property.value;&lt;br /&gt;
			break;			&lt;br /&gt;
&lt;br /&gt;
		//no way to store custom value but FFX, tbd&lt;br /&gt;
		case PropertyValueType.CUSTOM_VALUE:&lt;br /&gt;
			returnvalue = false;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		// if we land on default, it is _most likely_ a property &amp;#039;parent&amp;#039; with no real use&lt;br /&gt;
		default:&lt;br /&gt;
			//e(&amp;quot;using default: ---&amp;gt;&amp;quot;+property.name+&amp;quot; &amp;quot;+property.propertyValueType+&amp;quot;\n&amp;quot;)&lt;br /&gt;
			returnvalue = -1;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
	return (returnvalue);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== left to right ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(s){&lt;br /&gt;
	//debug&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function replaceLR(s){&lt;br /&gt;
    &lt;br /&gt;
    s = s.replace(/left/g, &amp;quot;right&amp;quot;);&lt;br /&gt;
    s = s.replace(/Left/g, &amp;quot;Right&amp;quot;);&lt;br /&gt;
    s = s.replace(/LEFT/, &amp;quot;RIGHT&amp;quot;);&lt;br /&gt;
    return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function checkImportLeftRight(layer){&lt;br /&gt;
&lt;br /&gt;
	// is it a footage layer (includes solids etc.. not Comps)&lt;br /&gt;
	if(layer.source instanceof FootageItem){&lt;br /&gt;
		&lt;br /&gt;
		//is it a footage item (.exr)&lt;br /&gt;
		if(layer.source.file != null){&lt;br /&gt;
			path = layer.source.mainSource.file.toString();&lt;br /&gt;
	&lt;br /&gt;
			 //check path, if it has &amp;#039;left&amp;#039; in it, grab &amp;#039;right&amp;#039; and import and replace footage&lt;br /&gt;
			if(path.toLowerCase().indexOf(&amp;quot;left&amp;quot;) &amp;gt; -1){&lt;br /&gt;
&lt;br /&gt;
				rightPath = replaceLR(path);&lt;br /&gt;
				rightFile = new File(replaceLR(path));&lt;br /&gt;
&lt;br /&gt;
				//if file exists, import right file, replace layer with it&lt;br /&gt;
				if(rightFile.exists){&lt;br /&gt;
					&lt;br /&gt;
					&lt;br /&gt;
					var io = new ImportOptions(rightFile);&lt;br /&gt;
					if(io.canImportAs(ImportAsType.FOOTAGE)){&lt;br /&gt;
					&lt;br /&gt;
						io.importAs = ImportAsType.FOOTAGE;&lt;br /&gt;
						io.sequence = true;&lt;br /&gt;
						src = app.project.importFile(io);&lt;br /&gt;
						&lt;br /&gt;
						return(layer.replaceSource(src,1));&lt;br /&gt;
						&lt;br /&gt;
					}else{&lt;br /&gt;
						return false&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
				}else{&lt;br /&gt;
					alert(&amp;quot;file not found! &amp;quot;+rightPath);&lt;br /&gt;
					return false&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
	&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
a = app.project.activeItem;&lt;br /&gt;
&lt;br /&gt;
for(i=1;i&amp;lt;=a.layers.length;i++){&lt;br /&gt;
 //e(a.layers[i].name);&lt;br /&gt;
 e(checkImportLeftRight(a.layers[i]));&lt;br /&gt;
    //if it&amp;#039;s footage&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
for(i = 1;i&amp;lt;app.project.numItems;i++){&lt;br /&gt;
	e(i+&amp;quot; &amp;quot;+app.project.items[i].name); &lt;br /&gt;
}&lt;br /&gt;
// 3 and 4&lt;br /&gt;
*/&lt;br /&gt;
function new3dComp(leftcomp,rightcomp){&lt;br /&gt;
	threedcomp = leftcomp.name.replace(/left/g,&amp;quot;3D&amp;quot;);&lt;br /&gt;
	w = leftcomp.width;&lt;br /&gt;
	h = leftcomp.height;&lt;br /&gt;
	duration = leftcomp.duration;&lt;br /&gt;
	frameRate = leftcomp.frameRate;&lt;br /&gt;
	newComp = app.project.items.addComp(threedcomp, w , h , 1.0, duration, frameRate);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	newComp.layers.add(rightcomp);&lt;br /&gt;
	newComp.layers.add(leftcomp);&lt;br /&gt;
	&lt;br /&gt;
	threed = newComp.layers.addSolid([1,1,1],&amp;quot;3D Glasses&amp;quot;,w,h,1.0);&lt;br /&gt;
	threed.adjustmentLayer = true;&lt;br /&gt;
	&lt;br /&gt;
	threedglasses = threed.Effects.addProperty(&amp;quot;ADBE 3D Glasses2&amp;quot;);&lt;br /&gt;
	threedglasses.property(&amp;quot;Left View&amp;quot;).setValue(2);&lt;br /&gt;
	threedglasses.property(&amp;quot;Right View&amp;quot;).setValue(3);&lt;br /&gt;
	threedglasses.property(&amp;quot;3D View&amp;quot;).setValue(12);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
new3dComp(app.project.items[3],app.project.items[4]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//e(app.project.activeItem.name+&amp;quot; --- &amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Output .SRT from layer markers ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function pad10(n){&lt;br /&gt;
    return (n&amp;lt;10)?&amp;quot;0&amp;quot;+n:n;&lt;br /&gt;
}&lt;br /&gt;
function pad100(n){&lt;br /&gt;
    return (n&amp;lt;100)?&amp;quot;0&amp;quot;+pad10(n):n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function formatTime(time){&lt;br /&gt;
    time =~~ (time*1000)/1000;&lt;br /&gt;
&lt;br /&gt;
    var hrs = ~~(time / 3600);&lt;br /&gt;
    var mins = ~~((time % 3600) / 60);&lt;br /&gt;
    var secs =  ~~(time % 60);    &lt;br /&gt;
    var ms = ~~((time-Math.floor(time))*1000);&lt;br /&gt;
    &lt;br /&gt;
    time = pad10(hrs)+&amp;quot;:&amp;quot;+pad10(mins)+&amp;quot;:&amp;quot;+pad10(secs)+&amp;quot;,&amp;quot;+pad100(ms);&lt;br /&gt;
    return time;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
data = &amp;quot;&amp;quot;;&lt;br /&gt;
var ai = app.project.activeItem;&lt;br /&gt;
if ( ai instanceof CompItem &amp;amp;&amp;amp; ai.selectedLayers.length == 1) {&lt;br /&gt;
    var m = ai.selectedLayers[0].marker;&lt;br /&gt;
    for(i=1;i&amp;lt;=m.numKeys;i++){&lt;br /&gt;
        data += i;&lt;br /&gt;
        data += &amp;quot;\n&amp;quot;+ formatTime(m.keyTime(i))+&amp;quot; --&amp;gt; &amp;quot;+ formatTime(m.keyTime(i)+m.keyValue(i).duration);&lt;br /&gt;
        data += &amp;quot;\n&amp;quot;+m.keyValue(i).comment;&lt;br /&gt;
        data += &amp;quot;\n&amp;quot;;&lt;br /&gt;
       }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
alert(data)&lt;br /&gt;
&lt;br /&gt;
//result:&lt;br /&gt;
//&lt;br /&gt;
//1&lt;br /&gt;
//00:03:16,945 --&amp;gt; 00:03:17,364&lt;br /&gt;
//testing comment 1&lt;br /&gt;
//&lt;br /&gt;
//2&lt;br /&gt;
//01:59:47,228 --&amp;gt; 01:59:49,396&lt;br /&gt;
//second subtitle at 2 hours in&lt;br /&gt;
//&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shift keys ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(str){&lt;br /&gt;
      $.writeln(str);&lt;br /&gt;
}&lt;br /&gt;
function getLayerFromProperty(prop){&lt;br /&gt;
    return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
}&lt;br /&gt;
var c = app.project.activeItem;&lt;br /&gt;
if( c != null){&lt;br /&gt;
    var props = c.selectedProperties;&lt;br /&gt;
&lt;br /&gt;
    //e(props[0].matchName);&lt;br /&gt;
    for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
        e(&amp;quot;\n---------------------------\n&amp;quot;+props[i].matchName+&amp;quot; / &amp;quot;+getLayerFromProperty(props[i]).name);&lt;br /&gt;
        k = props[i].selectedKeys;&lt;br /&gt;
        for(keyVar in k){&lt;br /&gt;
            //k[keyVar]&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
           // e( ); &lt;br /&gt;
           e(props[i].keyTime(k[keyVar])); &lt;br /&gt;
           //e(k[keyVar]); &lt;br /&gt;
        }&lt;br /&gt;
    //CANT FUCKING NUDGE KEYFRAMES&lt;br /&gt;
      //  e(props[i].selectedKeys);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selection tool palette ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
//tmpdir = $.getenv(&amp;quot;tmp&amp;quot;);&lt;br /&gt;
    function toolWindow(thisObj){&lt;br /&gt;
        function drawUI(){&lt;br /&gt;
            var my_palette = new Window(&amp;quot;palette&amp;quot;,&amp;quot;Selection Tool&amp;quot;);&lt;br /&gt;
            my_palette.bounds = [300,200,300,285];&lt;br /&gt;
            /*var button1 = addScriptButton(my_palette,[l_button_left,   5, l_button_right, 25], &lt;br /&gt;
                    &amp;quot;Find and Replace Text&amp;quot;,    demosDirectory, &amp;quot;Find and Replace Text.jsx&amp;quot;);&lt;br /&gt;
            var button3 = addScriptButton(my_palette,[l_button_left,  30, l_button_right, 50], &lt;br /&gt;
                    &amp;quot;Scale Composition&amp;quot;, 		demosDirectory, &amp;quot;Scale Composition.jsx&amp;quot;);&lt;br /&gt;
            var button4 = addScriptButton(my_palette,[l_button_left,  55, l_button_right, 75], &lt;br /&gt;
                    &amp;quot;Scale Selected Layers&amp;quot;, demosDirectory, &amp;quot;Scale Selected Layers.jsx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            var button6 = addScriptButton(my_palette,[r_button_left,   5, r_button_right, 25], &lt;br /&gt;
                    &amp;quot;Sort Layers by In Point&amp;quot;,     demosDirectory, &amp;quot;Sort Layers by In Point.jsx&amp;quot;);&lt;br /&gt;
            var button8 = addScriptButton(my_palette,[r_button_left,  30, r_button_right, 50], &lt;br /&gt;
                    &amp;quot;Render and Email&amp;quot;,    myDirectory,    &amp;quot;Render and Email.jsx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            var button12 = addHelpButton(my_palette,[r_button_left,  55, r_button_right, 75]);*/&lt;br /&gt;
&lt;br /&gt;
            my_palette.show();&lt;br /&gt;
        }&lt;br /&gt;
    drawUI();&lt;br /&gt;
    }&lt;br /&gt;
 toolWindow(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Make .bat file (WIP)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
    str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
    str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
    str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function createBat(){&lt;br /&gt;
    d = new Date();&lt;br /&gt;
    m = d.getMonth()+1;&lt;br /&gt;
    j = d.getDate();&lt;br /&gt;
    if(m&amp;lt;10){&lt;br /&gt;
        m=&amp;quot;0&amp;quot;+m;&lt;br /&gt;
    }&lt;br /&gt;
    if(j&amp;lt;10){&lt;br /&gt;
        j=&amp;quot;0&amp;quot;+j;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/ae_render&amp;quot;+m+&amp;quot;_&amp;quot;+j+&amp;quot;.bat&amp;quot;);&lt;br /&gt;
    alert(pathToWinPath(app.project.file));&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txtFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
res =&lt;br /&gt;
&amp;quot;dialog { \&lt;br /&gt;
    allGroups: Panel {\&lt;br /&gt;
        orientation:&amp;#039;column&amp;#039;,\&lt;br /&gt;
        alignChildren:&amp;#039;fill&amp;#039;,\&lt;br /&gt;
        text:&amp;#039;Options&amp;#039;, \&lt;br /&gt;
        chckOff: Checkbox {text:&amp;#039;Shutdown PC when finished&amp;#039;}, \&lt;br /&gt;
        chckAppend: Checkbox {text:&amp;#039;Append (instead of overwriting)&amp;#039;},\&lt;br /&gt;
        aePath: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;fill&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
            aeLocBtn: Button {text:&amp;#039; aerender.exe location : &amp;#039;}, \&lt;br /&gt;
            aeLocTst: StaticText {text:&amp;#039;C:\\Program fil...&amp;#039;}} \&lt;br /&gt;
        okCancel: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;fill&amp;#039;, alignChildren:&amp;#039;center&amp;#039;,\&lt;br /&gt;
            okBtn: Button { text:&amp;#039;OK&amp;#039;, properties:{name:&amp;#039;ok&amp;#039;}} , \&lt;br /&gt;
            cancelBtn: Button { text:&amp;#039;Cancel&amp;#039;, properties:{name:&amp;#039;cancel&amp;#039;}},\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
win = new Window (res);&lt;br /&gt;
win.allGroups.chckAppend.value = true;&lt;br /&gt;
win.center();&lt;br /&gt;
win.show();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Carnet De Voyage rangement===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//rangement pour carnet de voyage&lt;br /&gt;
app.beginUndoGroup(&amp;quot;CDV Rangement&amp;quot;);&lt;br /&gt;
boolVal = false;&lt;br /&gt;
sel = app.project.selection;&lt;br /&gt;
if(sel.length == 1){&lt;br /&gt;
    shot = sel[0];&lt;br /&gt;
    allItems = app.project.items;&lt;br /&gt;
    sortItems = new Array();&lt;br /&gt;
    for(i=1;i&amp;lt;=allItems.length;i++){&lt;br /&gt;
        if(!(allItems[i] instanceof FolderItem)){&lt;br /&gt;
            if(allItems[i].parentFolder.name == &amp;quot;Root&amp;quot;){&lt;br /&gt;
                sortItems[sortItems.length] = allItems[i];&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            if(allItems[i].parentFolder.name == &amp;quot;Root&amp;quot;){&lt;br /&gt;
                    sortItems[sortItems.length] = allItems[i];&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    t = app.project.items.addFolder(&amp;quot;ELMTS&amp;quot;);&lt;br /&gt;
    for(i=0;i&amp;lt;=sortItems.length-1;i++){&lt;br /&gt;
        if(sortItems[i] != shot){&lt;br /&gt;
        sortItems[i].parentFolder = t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(app.project.renderQueue.item(1).outputModules.length == 2){ //copie d&amp;#039;un output module à la main nécéssaire&lt;br /&gt;
    fileP = app.project.renderQueue.item(1).outputModules[1].file.toString();&lt;br /&gt;
    fileP = fileP.replace(&amp;quot;/m/EPISODE&amp;quot;,&amp;quot;/b/EPISODE&amp;quot;);&lt;br /&gt;
    fileP = new File(fileP);&lt;br /&gt;
    app.project.renderQueue.item(1).outputModules[2].file = fileP;&lt;br /&gt;
    app.project.renderQueue.item(1).outputModules[2].applyTemplate(&amp;quot;PNGSeq&amp;quot;);&lt;br /&gt;
    var curFile = app.project.file.name;&lt;br /&gt;
    curFile = curFile.substring(0,curFile.length-4);&lt;br /&gt;
    var pth = app.project.file.path+&amp;quot;/&amp;quot;+curFile+&amp;quot;_MOV+PNG.aep&amp;quot;;&lt;br /&gt;
    var mySaveFile = new File(pth);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Duplicate output modules first&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Chat==&lt;br /&gt;
&amp;#039;&amp;#039;JSX&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var myPalette = buildUI(this);&lt;br /&gt;
&lt;br /&gt;
    if (myPalette != null &amp;amp;&amp;amp; myPalette instanceof Window) {&lt;br /&gt;
        myPalette.show()&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    function buildUI (thisObject) {&lt;br /&gt;
&lt;br /&gt;
    if (thisObject instanceof Panel) {&lt;br /&gt;
        var myWindow = thisObject;&lt;br /&gt;
        } else { &lt;br /&gt;
        var myWindow = new Window (&amp;quot;palette&amp;quot;, &amp;quot;My Window&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
g = myWindow.add(&amp;quot;group&amp;quot;);&lt;br /&gt;
g.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
g.alignChildren = &amp;quot;left&amp;quot;;&lt;br /&gt;
//g.alignement = &amp;quot;top&amp;quot;;&lt;br /&gt;
bt1 = g.add(&amp;quot;button&amp;quot;,undefined,&amp;quot;test&amp;quot;);&lt;br /&gt;
bt1.onClick = bob;&lt;br /&gt;
//myWindow.myPanel.titleText = myWindow.myPanel.add(&amp;quot;staticText&amp;quot;);&lt;br /&gt;
//myWindow.myPanel.titleText.text =  &amp;quot;Move After Render v1.0&amp;quot;;  &lt;br /&gt;
&lt;br /&gt;
myWindow.layout.layout(true);&lt;br /&gt;
//myWindow.layout = new AutoLayoutManager(myWindow);&lt;br /&gt;
//myWindow.layout.resize();&lt;br /&gt;
return myWindow;&lt;br /&gt;
} &lt;br /&gt;
h = 0;&lt;br /&gt;
function bob(){&lt;br /&gt;
//    alert();&lt;br /&gt;
    w = this.parent.parent;&lt;br /&gt;
    p = this.parent;&lt;br /&gt;
    h++;&lt;br /&gt;
    bt2 = p.add(&amp;quot;button&amp;quot;,undefined,&amp;quot;test&amp;quot;+h);  &lt;br /&gt;
      bt2.alignement = [&amp;quot;left&amp;quot;,&amp;quot;top&amp;quot;];&lt;br /&gt;
    if(h%3==0){&lt;br /&gt;
&lt;br /&gt;
       &lt;br /&gt;
        bt2.helpTip = &amp;quot;TTTTT&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    bt2.onClick = flup;&lt;br /&gt;
   //w.update();&lt;br /&gt;
   w.layout.layout(1); &lt;br /&gt;
  // w.layout.resize();&lt;br /&gt;
&lt;br /&gt;
//    w.show();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function flup(){&lt;br /&gt;
    f = new Folder($.fileName);&lt;br /&gt;
    f = new Folder(f.parent+&amp;quot;/shelves/&amp;quot;);&lt;br /&gt;
   if(!f.exists){f.create()};&lt;br /&gt;
    cfg = new File(f.parent+&amp;quot;/shelves/shelves.cfg&amp;quot;);&lt;br /&gt;
    cfg.open(&amp;#039;e&amp;#039;);&lt;br /&gt;
    cfg.write(this.onClick);&lt;br /&gt;
    cfg.close();&lt;br /&gt;
    cfg.execute();&lt;br /&gt;
}&lt;br /&gt;
function pop(){&lt;br /&gt;
    f = new Folder($.fileName);&lt;br /&gt;
    fi = new File(f.parent+&amp;quot;/shelves/testShelf.jsx&amp;quot;);&lt;br /&gt;
    fi.open(&amp;#039;e&amp;#039;);&lt;br /&gt;
    eval(fi.read());&lt;br /&gt;
    }&lt;br /&gt;
----------------&lt;br /&gt;
function grab(){&lt;br /&gt;
    var reply = &amp;quot;&amp;quot;;&lt;br /&gt;
    c = new Socket;&lt;br /&gt;
    if (c.open (&amp;quot;berniebernie.fr:80&amp;quot;)) {&lt;br /&gt;
        if(c.writeln (&amp;quot;GET /dump/afx/chat.php  HTTP/1.0\nHost: berniebernie.fr\n&amp;quot;)){&lt;br /&gt;
            reply = decodeURIComponent(c.read(1000));&lt;br /&gt;
            reply = reply.split(&amp;quot;--afx chat file log--&amp;quot;);&lt;br /&gt;
            reply = reply[1];&lt;br /&gt;
        }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
        }&lt;br /&gt;
        c.close();&lt;br /&gt;
    }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
    }&lt;br /&gt;
    return reply;&lt;br /&gt;
}&lt;br /&gt;
function talk(str){&lt;br /&gt;
    var reply = &amp;quot;&amp;quot;;&lt;br /&gt;
    c = new Socket;&lt;br /&gt;
    if (c.open (&amp;quot;berniebernie.fr:80&amp;quot;)) {&lt;br /&gt;
        if(c.writeln (&amp;quot;GET /dump/afx/chat.php?msg=&amp;quot;+str+&amp;quot;  HTTP/1.0\nHost: berniebernie.fr\n&amp;quot;)){&lt;br /&gt;
            reply = decodeURIComponent(c.read(1000));&lt;br /&gt;
            reply = reply.split(&amp;quot;--afx chat file log--&amp;quot;);&lt;br /&gt;
            reply = reply[1];&lt;br /&gt;
        }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
        }&lt;br /&gt;
        c.close();&lt;br /&gt;
    }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
    }&lt;br /&gt;
    return reply;&lt;br /&gt;
}&lt;br /&gt;
//alert(grab());&lt;br /&gt;
alert(talk(&amp;quot;flipflap&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
////////////////&lt;br /&gt;
app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;Php&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$myFile = &amp;quot;testFile.txt&amp;quot;;&lt;br /&gt;
if(isset($_GET[&amp;#039;msg&amp;#039;]) &amp;amp;&amp;amp; $_GET[&amp;#039;msg&amp;#039;] != &amp;quot;&amp;quot;){&lt;br /&gt;
    $fh = fopen($myFile, &amp;#039;a&amp;#039;) or die(&amp;quot;can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
    fwrite($fh, ($_GET[&amp;#039;msg&amp;#039;].&amp;quot;\n&amp;quot;));&lt;br /&gt;
    fclose($fh);    &lt;br /&gt;
}else{&lt;br /&gt;
    //$fh = fopen($myFile, &amp;#039;r&amp;#039;) or die(&amp;quot;can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
    //include$str = $str, true);&lt;br /&gt;
    //header(&amp;quot;Content-Type: plain/text&amp;quot;); &lt;br /&gt;
    echo encodeURIComponent(file_get_contents(&amp;quot;testFile.txt&amp;quot;));&lt;br /&gt;
    //echo nl2br(htmlentities(file_get_contents(&amp;quot;testFile.txt&amp;quot;)));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=907</id>
		<title>Expressions and Tips</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=907"/>
		<updated>2026-03-30T15:08:27Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Afx Path Coordinates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[azerty AFX]] keyboard shortcuts &lt;br /&gt;
=== Somewhat automatic Contact Sheet ===&lt;br /&gt;
WIP. It will autolayout footage/comps and corresponding text layers (you&amp;#039;ll still need to ctrl c ctrl v position+scale expressions).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = &amp;#039;&amp;#039;;&lt;br /&gt;
list = [];&lt;br /&gt;
for(i=1;i&amp;lt;thisComp.numLayers;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	try{cur = L.source.name}catch(err){cur = false}&lt;br /&gt;
	list.push(cur?cur:null);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
list.join(&amp;#039;\n&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Converting RGB to CMYK in After Effects ===&lt;br /&gt;
Is a real pain in the buttocks. I know I&amp;#039;m not following any nifty color conversion algorithm but I just needed a way to use substractive colors on a project (fake print with halftone pattern). This was such a pain in the buttock that I&amp;#039;ll probably code a better plugin if I ever get the chance to.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/eL2vlqv.gif&lt;br /&gt;
&lt;br /&gt;
The way I went from RGB to C,M,Y. The settings are not all necessary (and also no K, I still need to fiddle with this to find the exact RGB match! I&amp;#039;ll reupload if I find a better solution).&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/iD9BZ24.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Afx Path Coordinates===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
path = thisComp.layer(&amp;quot;Shape Layer 1&amp;quot;).content(&amp;quot;Shape 1&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path;&lt;br /&gt;
p = path.points();&lt;br /&gt;
&lt;br /&gt;
txt = &amp;quot;[&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
for(i=0;i&amp;lt;p.length;i=i++){&lt;br /&gt;
txt += &amp;#039;{&amp;quot;x&amp;quot;:&amp;#039;+p[i][0]+&amp;#039;,&amp;quot;y&amp;quot;:&amp;#039;+p[i][1]+&amp;#039;}&amp;#039;;&lt;br /&gt;
	if(i&amp;lt;p.length-1){&lt;br /&gt;
		txt += &amp;quot;,&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
txt += &amp;quot;],&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Position from mask path ===&lt;br /&gt;
image tbd&lt;br /&gt;
&lt;br /&gt;
Takes the first and second frame of an effect to drive a position along a path. You can use this to drive the &amp;#039;write-on&amp;#039; effect if you don&amp;#039;t have 3d stroke.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = (time-key(1).time)/(key(2).time-key(1).time);&lt;br /&gt;
t = linear(t,0,1,0,1);&lt;br /&gt;
mask(1).maskPath.pointOnPath(t)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Loop animation before and after first and last keyframes===&lt;br /&gt;
&lt;br /&gt;
Should work with mask keyframes too, as opposed to loopOut/loopIn&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6adNfdW.png&lt;br /&gt;
&lt;br /&gt;
Keep in mind that just like the normal loopOut, the last keyframe will always be replaced by the value of the first one, so you should have a sacrificial keyframe. Tried to have a one-liner but haven&amp;#039;t wrapped my head around it quite fully&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var offset = time&amp;lt;key(1).time?key(numKeys).time:key(1).time;&lt;br /&gt;
valueAtTime( (time - offset ) % ( key(numKeys).time - key(1).time ) + offset );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Display current layer name ===&lt;br /&gt;
screenshot tbd this is pretty specific&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pct =&amp;#039; ░▒▓█&amp;#039;;&lt;br /&gt;
startIndex = 1;//what layer do I start with ? or use thisComp.layer(&amp;#039;Null 1&amp;#039;).index;&lt;br /&gt;
lastCompIndex = thisComp.numLayers;&lt;br /&gt;
outputText = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=startIndex+1;i&amp;lt;=lastCompIndex;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	inP = L.inPoint;&lt;br /&gt;
	outP = L.outPoint;&lt;br /&gt;
	if(time &amp;gt;= inP &amp;amp;&amp;amp; time &amp;lt; outP){&lt;br /&gt;
		curT = time - inP;&lt;br /&gt;
		maxT = outP - inP;&lt;br /&gt;
		per = curT/maxT;&lt;br /&gt;
		outputText += pct.substr(Math.floor(per*4),1) + &amp;#039; &amp;#039;;  //comment out if you dont want visual progress&lt;br /&gt;
		outputText += L.source.name + &amp;#039;\n&amp;#039;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
outputText;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simple one liner if you put a text object above each layer (you have to do the in/out yourself)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
thisComp.layer(thisLayer.index+1).source.name&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spread items along a path ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ideOrrs.gif&lt;br /&gt;
&lt;br /&gt;
Change path to fit your need, apply expresson to the position of your item layer, duplicate and you&amp;#039;re set&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// pointonpath uses a range from 0..1 to grab a position so let&amp;#039;s figure our where we are&lt;br /&gt;
// by counting the number of layers in the comp (and omitting a &amp;#039;paths&amp;#039; layer at the bottom of the comp)&lt;br /&gt;
&lt;br /&gt;
// [0,1] [0,.5,1] [0,.333...,.666...,1] [0,.25,.5,.75,1] etc&lt;br /&gt;
var percentpos = ( thisLayer.index - 1 ) / (thisComp.numLayers - 2)&lt;br /&gt;
&lt;br /&gt;
// prevents a divide by 0 error if there is only one object halfway through the path&lt;br /&gt;
percentpos = isNaN(percentpos)? 0.5 : percentpos&lt;br /&gt;
&lt;br /&gt;
//grab path layer and path ugh, &amp;#039;path&amp;#039;&lt;br /&gt;
var pathLayer = thisComp.layer(&amp;quot;path&amp;quot;)&lt;br /&gt;
var path = pathLayer.content(&amp;quot;Shape 2&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path&lt;br /&gt;
&lt;br /&gt;
//grab position on path and use toComp to place it in the right spot&lt;br /&gt;
var pos = path.pointOnPath( percentpos )&lt;br /&gt;
pathLayer.toComp(pos)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Retime (varispeed) any value ===&lt;br /&gt;
&lt;br /&gt;
Allows time remapping of animation without having to time remap (and precomp). This way you can link different animations. It&amp;#039;s using valueAtTime and a slider. You&amp;#039;ll have to edit it for n-dimensional values (like position).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;video width=&amp;quot;640&amp;quot; controls&amp;gt;&lt;br /&gt;
&amp;lt;source src=&amp;quot;https://i.imgur.com/KQhIIJi.mp4&amp;quot; type=&amp;quot;video/mp4&amp;quot; &amp;gt;&lt;br /&gt;
Your browser does not support the video tag.&lt;br /&gt;
&amp;lt;/video&amp;gt; &lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add a slider control that you call &amp;#039;speed&amp;#039;, and then add this expression to remap it according to the animation as seen in the video above&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
spd = effect(&amp;quot;speed&amp;quot;)(&amp;quot;Slider&amp;quot;);      //slider varies between 0 and 1, allows precise slow ups and downs in a single slider&lt;br /&gt;
p = thisProperty;                 &lt;br /&gt;
firstT = p.key(1).time;&lt;br /&gt;
lastT = p.key(p.numKeys).time;&lt;br /&gt;
p = p.valueAtTime(spd*(lastT-firstT)+firstT); &lt;br /&gt;
//might have to add [p[0],p[1]];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Padded timer text===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/pcRzMkB.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = effect(&amp;quot;Slider Control&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
sign = (t&amp;lt;0)?&amp;#039;-&amp;#039;:&amp;#039;&amp;#039;;&lt;br /&gt;
t = Math.abs(t);&lt;br /&gt;
&lt;br /&gt;
s = t%60;&lt;br /&gt;
m = Math.floor(t/60)%60;&lt;br /&gt;
h =  Math.floor(t/ (60*60));&lt;br /&gt;
s =  (s&amp;lt;10)?&amp;#039;0&amp;#039;+s:s;  		// pads&lt;br /&gt;
m = (m&amp;lt;10)?&amp;#039;0&amp;#039;+m:m;&lt;br /&gt;
sign+h+&amp;#039;:&amp;#039;+m+&amp;#039;:&amp;#039;+s;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Rand characters===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7hll8l1.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//seedRandom(1,true); //remove line to have it randomize each frame&lt;br /&gt;
chars = &amp;quot;GATC&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
characters = 6;&lt;br /&gt;
blocks = 4;&lt;br /&gt;
lines = 10;&lt;br /&gt;
&lt;br /&gt;
textValue = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=0;i&amp;lt;lines;i++){&lt;br /&gt;
	for(j=0;j&amp;lt;blocks;j++){&lt;br /&gt;
		for(k=0;k&amp;lt;characters;k++){&lt;br /&gt;
			randVar = random() * chars.length;&lt;br /&gt;
			characterVar = chars.charAt(randVar);&lt;br /&gt;
			textValue = textValue + characterVar;&lt;br /&gt;
		}&lt;br /&gt;
		textValue += &amp;quot; &amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	textValue += &amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
textValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Current Keyframe Index===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//gobelins&lt;br /&gt;
a = timeRemap;&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
curframe = 0;&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	curframe = nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	curframe = nk.index;&lt;br /&gt;
}&lt;br /&gt;
curframe*thisComp.frameDuration;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
https://gyazo.com/fa2262be189547b5381aa50041cdc32b.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = position;  //or whatever animated property&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	nk.index;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== sample image pr les gobz ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = sampleImage([thisComp.width/2,thisComp.height/2], [thisComp.width/2,thisComp.height/2], postEffect = true, t = time);&lt;br /&gt;
a[0]+a[1]+a[2]+a[3];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
////bake&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;)-effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;).valueAtTime(time-1/25)==0?0:1&lt;br /&gt;
&lt;br /&gt;
//////bake?&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;hold&amp;quot;)(&amp;quot;Curseur&amp;quot;);&lt;br /&gt;
a = 0;&lt;br /&gt;
for(i=0;i&amp;lt;=time*25;i++){&lt;br /&gt;
	if(v.valueAtTime(i/25)==1){&lt;br /&gt;
		a = i/25;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
a;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== spread thingy ===&lt;br /&gt;
http://i.imgur.com/dlJJcXt.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
p = thisComp.layer(&amp;quot;Null 1&amp;quot;).transform.position;&lt;br /&gt;
delta = transform.position - p;&lt;br /&gt;
amplitude = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;amplitude&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
reach = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;reach&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
exponent = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;exponent&amp;quot;)(&amp;quot;Slider&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
length = (length(delta));&lt;br /&gt;
if(length&amp;gt;0){&lt;br /&gt;
	transform.position += normalize( delta ) *  ( reach / Math.pow(length, exponent)  ) * amplitude;&lt;br /&gt;
}else{&lt;br /&gt;
	transform.position = [-2000,0]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Sprites ===&lt;br /&gt;
Duplicate layers by hand, add CtrlNul, offsetLeftRight, offsetDepth, spread sliders&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//POSITION&lt;br /&gt;
seedRandom(index,true)&lt;br /&gt;
ctrl = thisComp.layer(&amp;quot;CtrlNul&amp;quot;);&lt;br /&gt;
dist = ctrl.effect(&amp;quot;dist&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
offset = [ctrl.effect(&amp;quot;offsetLeftRight&amp;quot;)(&amp;quot;Slider&amp;quot;),0,ctrl.effect(&amp;quot;offsetDepth&amp;quot;)(&amp;quot;Slider&amp;quot;)];&lt;br /&gt;
&lt;br /&gt;
depthjitter = ctrl.effect(&amp;quot;depthjitter&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
depthmult = ctrl.effect(&amp;quot;depthmult&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
spread = ctrl.effect(&amp;quot;spread&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
dist = dist+random()*spread;&lt;br /&gt;
&lt;br /&gt;
transform.position+[(index%2)?-dist:dist,0,depthmult*index+depthjitter*random()]+offset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//ORIENTATION&lt;br /&gt;
lookAt(transform.position,thisComp.layer(&amp;quot;Camera 1&amp;quot;).transform.position)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Write on effect ===&lt;br /&gt;
https://i.gyazo.com/091522309c14c61f0cc7d63672e78900.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
cursorSpeed = 3;&lt;br /&gt;
text.sourceText.slice(0,time*fps)+((Math.floor(time*cursorSpeed)%2)?&amp;quot;|&amp;quot;:&amp;quot; &amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Unscramble wip ===&lt;br /&gt;
http://i.imgur.com/k8Gf1wC.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wordspeed = time/1; //or a slider, or whatever&lt;br /&gt;
letterspeed = time*2;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
txtVar = &amp;quot;&amp;quot;;&lt;br /&gt;
chars = &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
maxLines = Math.min(splitxt.length-1, Math.floor(wordspeed));&lt;br /&gt;
for(i=0;i&amp;lt;=maxLines;i++){&lt;br /&gt;
	txtVar += (i&amp;gt;0)?splitxt[i-1]+&amp;quot;\r&amp;quot;:&amp;quot;&amp;quot;;&lt;br /&gt;
	len = splitxt[i].length;&lt;br /&gt;
	if(i == maxLines){&lt;br /&gt;
		for(j=0;j&amp;lt;=len-1;j++){&lt;br /&gt;
			txtVar += (j/len&amp;gt;=wordspeed-Math.floor(wordspeed))?chars.charAt(random()*chars.length):splitxt[i].charAt(j);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
txtVar&lt;br /&gt;
;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Poor man&amp;#039;s padding ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//for lack of a cleaner version, up to 4 digits&lt;br /&gt;
a = time*25;&lt;br /&gt;
a=(a&amp;lt;10)?&amp;quot;000&amp;quot;+a:((a&amp;lt;100)?&amp;quot;00&amp;quot;+a:((a&amp;lt;1000)?&amp;quot;0&amp;quot;+a:a));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== 2d lookat ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LookAt = &amp;quot;ball&amp;quot;&lt;br /&gt;
offset = 0&lt;br /&gt;
diffx = position[0] - this_comp.layer(LookAt).position[0];&lt;br /&gt;
diffy = position[1] - this_comp.layer(LookAt).position[1];&lt;br /&gt;
if (diffx == 0) {&lt;br /&gt;
diffx = 1 }&lt;br /&gt;
sign = 1 + (-1 * (diffx / Math.abs(diffx))) * 90;&lt;br /&gt;
radians_to_degrees(Math.atan(diffy/diffx)) + sign + offset&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//with parenting&lt;br /&gt;
lookAt = &amp;quot;a&amp;quot;;&lt;br /&gt;
L = thisComp.layer(lookAt);&lt;br /&gt;
p = L.toComp(L.anchorPoint);&lt;br /&gt;
d = thisLayer.toComp(anchorPoint) - p&lt;br /&gt;
d[0] = (d[0]==0)?1:d[0];&lt;br /&gt;
rotation + radians_to_degrees(Math.atan(d[1]/d[0])) - 90*d[0]/ Math.abs(d[0])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pixilation (slow footage) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
numImages = 15;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
framesToWait = 3;Math.floor(time*fps/framesToWait%numImages)/fps;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Text scroller (DOS!)===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/JlLNh1Z.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//add your text, three slider control effects and rename them &amp;#039;line width&amp;#039;, &amp;#039;line&amp;#039;, &amp;#039;line animation&amp;#039;&lt;br /&gt;
//then this expression on the source text&lt;br /&gt;
charlen = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line width&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //20&lt;br /&gt;
numlines = Math.ceil(Math.abs(Math. round(effect(&amp;quot;lines&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //5&lt;br /&gt;
step = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line animation&amp;quot;)(&amp;quot;Slider&amp;quot;),0)));&lt;br /&gt;
&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;numlines+step;i++){&lt;br /&gt;
	output += (i &amp;gt; splitxt.length - numlines)?&amp;quot;.\r&amp;quot;:splitxt[i].substring(0,charlen)+&amp;quot;\r&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//single line&lt;br /&gt;
src = text.sourceText;&lt;br /&gt;
len = src.length;&lt;br /&gt;
scroll = time*25;&lt;br /&gt;
src.slice(scroll%len)+src.slice(0,scroll%len)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/////////////&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
charlen =200;&lt;br /&gt;
numlines = 15;&lt;br /&gt;
step = time/thisComp.frameDuration;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;(step+numlines);i++){&lt;br /&gt;
	output += (splitxt.length&amp;lt;=i)?&amp;quot;\r&amp;quot;:splitxt[i-1].substring(0,charlen)+&amp;quot;\r&amp;quot;;          &lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Open two simultaneous After Effects===&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\AfterFX.exe&amp;quot; -m&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Command-Line===&lt;br /&gt;
&amp;lt;pre&amp;gt;@echo off&lt;br /&gt;
&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\aerender.exe&amp;quot; -mp -project &amp;quot;W:\&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=906</id>
		<title>Expressions and Tips</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=906"/>
		<updated>2026-03-30T15:04:32Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Position from mask path */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[azerty AFX]] keyboard shortcuts &lt;br /&gt;
=== Somewhat automatic Contact Sheet ===&lt;br /&gt;
WIP. It will autolayout footage/comps and corresponding text layers (you&amp;#039;ll still need to ctrl c ctrl v position+scale expressions).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = &amp;#039;&amp;#039;;&lt;br /&gt;
list = [];&lt;br /&gt;
for(i=1;i&amp;lt;thisComp.numLayers;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	try{cur = L.source.name}catch(err){cur = false}&lt;br /&gt;
	list.push(cur?cur:null);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
list.join(&amp;#039;\n&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Converting RGB to CMYK in After Effects ===&lt;br /&gt;
Is a real pain in the buttocks. I know I&amp;#039;m not following any nifty color conversion algorithm but I just needed a way to use substractive colors on a project (fake print with halftone pattern). This was such a pain in the buttock that I&amp;#039;ll probably code a better plugin if I ever get the chance to.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/eL2vlqv.gif&lt;br /&gt;
&lt;br /&gt;
The way I went from RGB to C,M,Y. The settings are not all necessary (and also no K, I still need to fiddle with this to find the exact RGB match! I&amp;#039;ll reupload if I find a better solution).&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/iD9BZ24.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Afx Path Coordinates===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
path = thisComp.layer(&amp;quot;Shape Layer 1&amp;quot;).content(&amp;quot;Shape 1&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path;&lt;br /&gt;
p = path.points();&lt;br /&gt;
&lt;br /&gt;
txt = &amp;quot;[&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
for(i=0;i&amp;lt;p.length;i=i+2){&lt;br /&gt;
txt += &amp;#039;{&amp;quot;x&amp;quot;:&amp;#039;+p[i][0]+&amp;#039;,&amp;quot;y&amp;quot;:&amp;#039;+p[i][1]+&amp;#039;}&amp;#039;;&lt;br /&gt;
	if(i&amp;lt;p.length-1){&lt;br /&gt;
		txt += &amp;quot;,&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
txt += &amp;quot;],&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Position from mask path ===&lt;br /&gt;
image tbd&lt;br /&gt;
&lt;br /&gt;
Takes the first and second frame of an effect to drive a position along a path. You can use this to drive the &amp;#039;write-on&amp;#039; effect if you don&amp;#039;t have 3d stroke.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = (time-key(1).time)/(key(2).time-key(1).time);&lt;br /&gt;
t = linear(t,0,1,0,1);&lt;br /&gt;
mask(1).maskPath.pointOnPath(t)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Loop animation before and after first and last keyframes===&lt;br /&gt;
&lt;br /&gt;
Should work with mask keyframes too, as opposed to loopOut/loopIn&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6adNfdW.png&lt;br /&gt;
&lt;br /&gt;
Keep in mind that just like the normal loopOut, the last keyframe will always be replaced by the value of the first one, so you should have a sacrificial keyframe. Tried to have a one-liner but haven&amp;#039;t wrapped my head around it quite fully&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var offset = time&amp;lt;key(1).time?key(numKeys).time:key(1).time;&lt;br /&gt;
valueAtTime( (time - offset ) % ( key(numKeys).time - key(1).time ) + offset );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Display current layer name ===&lt;br /&gt;
screenshot tbd this is pretty specific&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pct =&amp;#039; ░▒▓█&amp;#039;;&lt;br /&gt;
startIndex = 1;//what layer do I start with ? or use thisComp.layer(&amp;#039;Null 1&amp;#039;).index;&lt;br /&gt;
lastCompIndex = thisComp.numLayers;&lt;br /&gt;
outputText = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=startIndex+1;i&amp;lt;=lastCompIndex;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	inP = L.inPoint;&lt;br /&gt;
	outP = L.outPoint;&lt;br /&gt;
	if(time &amp;gt;= inP &amp;amp;&amp;amp; time &amp;lt; outP){&lt;br /&gt;
		curT = time - inP;&lt;br /&gt;
		maxT = outP - inP;&lt;br /&gt;
		per = curT/maxT;&lt;br /&gt;
		outputText += pct.substr(Math.floor(per*4),1) + &amp;#039; &amp;#039;;  //comment out if you dont want visual progress&lt;br /&gt;
		outputText += L.source.name + &amp;#039;\n&amp;#039;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
outputText;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simple one liner if you put a text object above each layer (you have to do the in/out yourself)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
thisComp.layer(thisLayer.index+1).source.name&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spread items along a path ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ideOrrs.gif&lt;br /&gt;
&lt;br /&gt;
Change path to fit your need, apply expresson to the position of your item layer, duplicate and you&amp;#039;re set&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// pointonpath uses a range from 0..1 to grab a position so let&amp;#039;s figure our where we are&lt;br /&gt;
// by counting the number of layers in the comp (and omitting a &amp;#039;paths&amp;#039; layer at the bottom of the comp)&lt;br /&gt;
&lt;br /&gt;
// [0,1] [0,.5,1] [0,.333...,.666...,1] [0,.25,.5,.75,1] etc&lt;br /&gt;
var percentpos = ( thisLayer.index - 1 ) / (thisComp.numLayers - 2)&lt;br /&gt;
&lt;br /&gt;
// prevents a divide by 0 error if there is only one object halfway through the path&lt;br /&gt;
percentpos = isNaN(percentpos)? 0.5 : percentpos&lt;br /&gt;
&lt;br /&gt;
//grab path layer and path ugh, &amp;#039;path&amp;#039;&lt;br /&gt;
var pathLayer = thisComp.layer(&amp;quot;path&amp;quot;)&lt;br /&gt;
var path = pathLayer.content(&amp;quot;Shape 2&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path&lt;br /&gt;
&lt;br /&gt;
//grab position on path and use toComp to place it in the right spot&lt;br /&gt;
var pos = path.pointOnPath( percentpos )&lt;br /&gt;
pathLayer.toComp(pos)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Retime (varispeed) any value ===&lt;br /&gt;
&lt;br /&gt;
Allows time remapping of animation without having to time remap (and precomp). This way you can link different animations. It&amp;#039;s using valueAtTime and a slider. You&amp;#039;ll have to edit it for n-dimensional values (like position).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;video width=&amp;quot;640&amp;quot; controls&amp;gt;&lt;br /&gt;
&amp;lt;source src=&amp;quot;https://i.imgur.com/KQhIIJi.mp4&amp;quot; type=&amp;quot;video/mp4&amp;quot; &amp;gt;&lt;br /&gt;
Your browser does not support the video tag.&lt;br /&gt;
&amp;lt;/video&amp;gt; &lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add a slider control that you call &amp;#039;speed&amp;#039;, and then add this expression to remap it according to the animation as seen in the video above&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
spd = effect(&amp;quot;speed&amp;quot;)(&amp;quot;Slider&amp;quot;);      //slider varies between 0 and 1, allows precise slow ups and downs in a single slider&lt;br /&gt;
p = thisProperty;                 &lt;br /&gt;
firstT = p.key(1).time;&lt;br /&gt;
lastT = p.key(p.numKeys).time;&lt;br /&gt;
p = p.valueAtTime(spd*(lastT-firstT)+firstT); &lt;br /&gt;
//might have to add [p[0],p[1]];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Padded timer text===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/pcRzMkB.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = effect(&amp;quot;Slider Control&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
sign = (t&amp;lt;0)?&amp;#039;-&amp;#039;:&amp;#039;&amp;#039;;&lt;br /&gt;
t = Math.abs(t);&lt;br /&gt;
&lt;br /&gt;
s = t%60;&lt;br /&gt;
m = Math.floor(t/60)%60;&lt;br /&gt;
h =  Math.floor(t/ (60*60));&lt;br /&gt;
s =  (s&amp;lt;10)?&amp;#039;0&amp;#039;+s:s;  		// pads&lt;br /&gt;
m = (m&amp;lt;10)?&amp;#039;0&amp;#039;+m:m;&lt;br /&gt;
sign+h+&amp;#039;:&amp;#039;+m+&amp;#039;:&amp;#039;+s;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Rand characters===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7hll8l1.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//seedRandom(1,true); //remove line to have it randomize each frame&lt;br /&gt;
chars = &amp;quot;GATC&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
characters = 6;&lt;br /&gt;
blocks = 4;&lt;br /&gt;
lines = 10;&lt;br /&gt;
&lt;br /&gt;
textValue = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=0;i&amp;lt;lines;i++){&lt;br /&gt;
	for(j=0;j&amp;lt;blocks;j++){&lt;br /&gt;
		for(k=0;k&amp;lt;characters;k++){&lt;br /&gt;
			randVar = random() * chars.length;&lt;br /&gt;
			characterVar = chars.charAt(randVar);&lt;br /&gt;
			textValue = textValue + characterVar;&lt;br /&gt;
		}&lt;br /&gt;
		textValue += &amp;quot; &amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	textValue += &amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
textValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Current Keyframe Index===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//gobelins&lt;br /&gt;
a = timeRemap;&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
curframe = 0;&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	curframe = nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	curframe = nk.index;&lt;br /&gt;
}&lt;br /&gt;
curframe*thisComp.frameDuration;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
https://gyazo.com/fa2262be189547b5381aa50041cdc32b.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = position;  //or whatever animated property&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	nk.index;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== sample image pr les gobz ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = sampleImage([thisComp.width/2,thisComp.height/2], [thisComp.width/2,thisComp.height/2], postEffect = true, t = time);&lt;br /&gt;
a[0]+a[1]+a[2]+a[3];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
////bake&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;)-effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;).valueAtTime(time-1/25)==0?0:1&lt;br /&gt;
&lt;br /&gt;
//////bake?&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;hold&amp;quot;)(&amp;quot;Curseur&amp;quot;);&lt;br /&gt;
a = 0;&lt;br /&gt;
for(i=0;i&amp;lt;=time*25;i++){&lt;br /&gt;
	if(v.valueAtTime(i/25)==1){&lt;br /&gt;
		a = i/25;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
a;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== spread thingy ===&lt;br /&gt;
http://i.imgur.com/dlJJcXt.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
p = thisComp.layer(&amp;quot;Null 1&amp;quot;).transform.position;&lt;br /&gt;
delta = transform.position - p;&lt;br /&gt;
amplitude = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;amplitude&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
reach = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;reach&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
exponent = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;exponent&amp;quot;)(&amp;quot;Slider&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
length = (length(delta));&lt;br /&gt;
if(length&amp;gt;0){&lt;br /&gt;
	transform.position += normalize( delta ) *  ( reach / Math.pow(length, exponent)  ) * amplitude;&lt;br /&gt;
}else{&lt;br /&gt;
	transform.position = [-2000,0]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Sprites ===&lt;br /&gt;
Duplicate layers by hand, add CtrlNul, offsetLeftRight, offsetDepth, spread sliders&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//POSITION&lt;br /&gt;
seedRandom(index,true)&lt;br /&gt;
ctrl = thisComp.layer(&amp;quot;CtrlNul&amp;quot;);&lt;br /&gt;
dist = ctrl.effect(&amp;quot;dist&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
offset = [ctrl.effect(&amp;quot;offsetLeftRight&amp;quot;)(&amp;quot;Slider&amp;quot;),0,ctrl.effect(&amp;quot;offsetDepth&amp;quot;)(&amp;quot;Slider&amp;quot;)];&lt;br /&gt;
&lt;br /&gt;
depthjitter = ctrl.effect(&amp;quot;depthjitter&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
depthmult = ctrl.effect(&amp;quot;depthmult&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
spread = ctrl.effect(&amp;quot;spread&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
dist = dist+random()*spread;&lt;br /&gt;
&lt;br /&gt;
transform.position+[(index%2)?-dist:dist,0,depthmult*index+depthjitter*random()]+offset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//ORIENTATION&lt;br /&gt;
lookAt(transform.position,thisComp.layer(&amp;quot;Camera 1&amp;quot;).transform.position)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Write on effect ===&lt;br /&gt;
https://i.gyazo.com/091522309c14c61f0cc7d63672e78900.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
cursorSpeed = 3;&lt;br /&gt;
text.sourceText.slice(0,time*fps)+((Math.floor(time*cursorSpeed)%2)?&amp;quot;|&amp;quot;:&amp;quot; &amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Unscramble wip ===&lt;br /&gt;
http://i.imgur.com/k8Gf1wC.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wordspeed = time/1; //or a slider, or whatever&lt;br /&gt;
letterspeed = time*2;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
txtVar = &amp;quot;&amp;quot;;&lt;br /&gt;
chars = &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
maxLines = Math.min(splitxt.length-1, Math.floor(wordspeed));&lt;br /&gt;
for(i=0;i&amp;lt;=maxLines;i++){&lt;br /&gt;
	txtVar += (i&amp;gt;0)?splitxt[i-1]+&amp;quot;\r&amp;quot;:&amp;quot;&amp;quot;;&lt;br /&gt;
	len = splitxt[i].length;&lt;br /&gt;
	if(i == maxLines){&lt;br /&gt;
		for(j=0;j&amp;lt;=len-1;j++){&lt;br /&gt;
			txtVar += (j/len&amp;gt;=wordspeed-Math.floor(wordspeed))?chars.charAt(random()*chars.length):splitxt[i].charAt(j);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
txtVar&lt;br /&gt;
;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Poor man&amp;#039;s padding ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//for lack of a cleaner version, up to 4 digits&lt;br /&gt;
a = time*25;&lt;br /&gt;
a=(a&amp;lt;10)?&amp;quot;000&amp;quot;+a:((a&amp;lt;100)?&amp;quot;00&amp;quot;+a:((a&amp;lt;1000)?&amp;quot;0&amp;quot;+a:a));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== 2d lookat ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LookAt = &amp;quot;ball&amp;quot;&lt;br /&gt;
offset = 0&lt;br /&gt;
diffx = position[0] - this_comp.layer(LookAt).position[0];&lt;br /&gt;
diffy = position[1] - this_comp.layer(LookAt).position[1];&lt;br /&gt;
if (diffx == 0) {&lt;br /&gt;
diffx = 1 }&lt;br /&gt;
sign = 1 + (-1 * (diffx / Math.abs(diffx))) * 90;&lt;br /&gt;
radians_to_degrees(Math.atan(diffy/diffx)) + sign + offset&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//with parenting&lt;br /&gt;
lookAt = &amp;quot;a&amp;quot;;&lt;br /&gt;
L = thisComp.layer(lookAt);&lt;br /&gt;
p = L.toComp(L.anchorPoint);&lt;br /&gt;
d = thisLayer.toComp(anchorPoint) - p&lt;br /&gt;
d[0] = (d[0]==0)?1:d[0];&lt;br /&gt;
rotation + radians_to_degrees(Math.atan(d[1]/d[0])) - 90*d[0]/ Math.abs(d[0])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pixilation (slow footage) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
numImages = 15;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
framesToWait = 3;Math.floor(time*fps/framesToWait%numImages)/fps;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Text scroller (DOS!)===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/JlLNh1Z.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//add your text, three slider control effects and rename them &amp;#039;line width&amp;#039;, &amp;#039;line&amp;#039;, &amp;#039;line animation&amp;#039;&lt;br /&gt;
//then this expression on the source text&lt;br /&gt;
charlen = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line width&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //20&lt;br /&gt;
numlines = Math.ceil(Math.abs(Math. round(effect(&amp;quot;lines&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //5&lt;br /&gt;
step = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line animation&amp;quot;)(&amp;quot;Slider&amp;quot;),0)));&lt;br /&gt;
&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;numlines+step;i++){&lt;br /&gt;
	output += (i &amp;gt; splitxt.length - numlines)?&amp;quot;.\r&amp;quot;:splitxt[i].substring(0,charlen)+&amp;quot;\r&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//single line&lt;br /&gt;
src = text.sourceText;&lt;br /&gt;
len = src.length;&lt;br /&gt;
scroll = time*25;&lt;br /&gt;
src.slice(scroll%len)+src.slice(0,scroll%len)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/////////////&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
charlen =200;&lt;br /&gt;
numlines = 15;&lt;br /&gt;
step = time/thisComp.frameDuration;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;(step+numlines);i++){&lt;br /&gt;
	output += (splitxt.length&amp;lt;=i)?&amp;quot;\r&amp;quot;:splitxt[i-1].substring(0,charlen)+&amp;quot;\r&amp;quot;;          &lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Open two simultaneous After Effects===&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\AfterFX.exe&amp;quot; -m&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Command-Line===&lt;br /&gt;
&amp;lt;pre&amp;gt;@echo off&lt;br /&gt;
&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\aerender.exe&amp;quot; -mp -project &amp;quot;W:\&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_Tutorials&amp;diff=905</id>
		<title>Blender Tutorials</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_Tutorials&amp;diff=905"/>
		<updated>2026-03-23T17:50:12Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Geometry Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Geometry Nodes ==&lt;br /&gt;
=== Delaunay Triangulation ===&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/HyW54cI8Qdo&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://www.youtube.com/watch?v=HyW54cI8Qdo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom curve deformer ===&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/uv0E_FQLQYI&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://www.youtube.com/watch?v=uv0E_FQLQYI&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Particle Simulator From Scratch ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/kUWwC6wudZE&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://www.youtube.com/watch?v=kUWwC6wudZE&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=904</id>
		<title>Blender 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=904"/>
		<updated>2026-03-18T16:16:02Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Geometry Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I&amp;#039;m slowly catching up and trying to learn blender so I&amp;#039;m just writing down a few things that frustrate me as an old maya/houdini user, and answer it as i go.&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
&lt;br /&gt;
* Cool cards/non-video tutorials. Would be great to have them in a normal scrollable page! https://www.3dbestie.com/&lt;br /&gt;
* In the same spirit, a Github readme page for the standard shortcuts w/ gifs: https://hollisbrown.github.io/blendershortcuts/&lt;br /&gt;
* French Studio Les Fées Speciales&amp;#039; blender tech blog: https://lacuisine.tech/&lt;br /&gt;
* Maya to blender in 30 minutes: https://www.youtube.com/watch?v=0C0njghXPAI&lt;br /&gt;
&lt;br /&gt;
=== Some shortcuts ===&lt;br /&gt;
Will sort! Trying to be a GoodBoy and learning the blender standard. God it&amp;#039;s hard not to hit the Alt key for navigation and the marking menus are so baked in muscle memory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;function&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;key&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;equivalent to&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| Add objects&lt;br /&gt;
| shift-a&lt;br /&gt;
| Tab-drop node (houdini)&lt;br /&gt;
|-&lt;br /&gt;
| Frame/focus object&lt;br /&gt;
| . (numpad)&lt;br /&gt;
| f (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Make active camera&lt;br /&gt;
| ctrl-num0 (?)&lt;br /&gt;
| mmb drag and drop cam from outliner to VP (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Toggle object/faces edges/vertices&lt;br /&gt;
| tab 1 2 3&lt;br /&gt;
| Maya F8 F9 etc...&lt;br /&gt;
|-&lt;br /&gt;
| Search for function&lt;br /&gt;
| F3&lt;br /&gt;
| Maya&amp;#039;s Find Menu but actually looks nice&lt;br /&gt;
|-&lt;br /&gt;
| Change sudbdiv&lt;br /&gt;
| ctrl+0,1,2...(top row keyboard digits)&lt;br /&gt;
| Maya&amp;#039;s numpad 1 2 3&lt;br /&gt;
|-&lt;br /&gt;
| Select Linked&lt;br /&gt;
| ctrl-L&lt;br /&gt;
| double click in maya to select shell&lt;br /&gt;
|-&lt;br /&gt;
| Select Edge Loops/Rings&lt;br /&gt;
| alt-LMB, ctrl-alt-LMB&lt;br /&gt;
| double click in maya to select loops, marking menus&lt;br /&gt;
|-&lt;br /&gt;
| Select More/Less&lt;br /&gt;
| ctrl-Num+&lt;br /&gt;
| Maya&amp;#039;s grow/shrink selection &amp;lt;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Proportional Editing&lt;br /&gt;
| o / scrollwheel (while using it)&lt;br /&gt;
| Soft selection in maya (B/shift MMB)&lt;br /&gt;
|-&lt;br /&gt;
| Isolate Selection&lt;br /&gt;
| Numpad-/ or Alt/Shift-H&lt;br /&gt;
| Shift-i in Maya&lt;br /&gt;
|-&lt;br /&gt;
| Add Edge Loop&lt;br /&gt;
| Ctrl-R + scrollwheel &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Wire/shaded&lt;br /&gt;
| Shift-Z&lt;br /&gt;
| 1 2 3 in Maya&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Some Blender Concepts ===&lt;br /&gt;
&lt;br /&gt;
==== Collections ====&lt;br /&gt;
&lt;br /&gt;
Is &amp;#039;&amp;#039;kind&amp;#039;&amp;#039; of like maya&amp;#039;s set+renderlayer on steroids. You can have an item in several collections (ctrl drag).&lt;br /&gt;
&lt;br /&gt;
==== Groups ====&lt;br /&gt;
&lt;br /&gt;
No such thing, but use a null object&lt;br /&gt;
&lt;br /&gt;
==== Asset/References  ====&lt;br /&gt;
&lt;br /&gt;
So this one is super weird. When you import an asset, or link it (like a reference in maya or .hda in Hou), you&amp;#039;re note linking a .blend file but a subset of the file, could be a mesh/object/material. Kind of tedious TBH, but the blenderheads seem fine with it. It helps if you work with collections, so you just append/link a specific collection.&lt;br /&gt;
&lt;br /&gt;
==== Help &amp;amp; Community ====&lt;br /&gt;
&lt;br /&gt;
Amazing how many people have created tutorials. Make sure to search for your version of Blender as I feel it&amp;#039;s a &amp;#039;move fast, break things&amp;#039; software so shortcuts and things might work differently, unlike maya (you can still safely import files from maya 5.0).&lt;br /&gt;
&lt;br /&gt;
Pretty cool that people can vote for what they wish to see: https://blender.community/c/rightclickselect/?sorting=hot (not in the community enough to know if it has some say into newer versions though).&lt;br /&gt;
&lt;br /&gt;
https://docs.blender.org/manual/en/latest/ kind of terse, but works well. Big up to most software packages that take care of their documentations. (&amp;lt;3 Houdini)&lt;br /&gt;
&lt;br /&gt;
== Specific Tips ==&lt;br /&gt;
&lt;br /&gt;
to be sorted once I get a few&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where can I edit the pivot point, what&amp;#039;s with the weird cursor that looks like a lifebuoy ? ===&lt;br /&gt;
&lt;br /&gt;
The cursor which is a bit jarring is actually quite useful. &amp;#039;&amp;#039;Shift + right click&amp;#039;&amp;#039; to move it around, &amp;#039;&amp;#039;Shift + s&amp;#039;&amp;#039; to send it places (like the origin)&lt;br /&gt;
&lt;br /&gt;
For pivots, top of the viewport, shortcut is &amp;#039;.&amp;#039; dot but apparently not on a french keyboard. Still don&amp;#039;t know what the first box does, I&amp;#039;ll remove this line once I know ;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7dXmNgq.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
* Material Override per render layer https://blender.stackexchange.com/questions/154321/material-overrides-for-view-layers-per-object&lt;br /&gt;
* /!\ &amp;#039;&amp;#039;&amp;#039;Alpha in render&amp;#039;&amp;#039;&amp;#039; needs to be toggled on in &amp;#039;Film&amp;gt;Transparent&amp;#039;. Why isn&amp;#039;t this on by default ???&lt;br /&gt;
* You can&amp;#039;t use maya&amp;#039;s &amp;lt;code&amp;gt;&amp;lt;scene&amp;gt;/%s&amp;lt;/code&amp;gt; or houdini&amp;#039;s &amp;lt;code&amp;gt;$HIPNAME&amp;lt;/code&amp;gt; or write custom python directy in a string field. See the bottom for the Loom extension.&lt;br /&gt;
* To render multilayer EXRs w/o having to connect layer&amp;gt;outputs by hand &amp;#039;&amp;#039;&amp;#039;untoggle&amp;#039;&amp;#039;&amp;#039; use nodes in compositor.&lt;br /&gt;
&lt;br /&gt;
=== Modeling ===&lt;br /&gt;
* Bake transforms: Ctrl-A (also work when over modifiers to bake them&lt;br /&gt;
* Soft selection (&amp;#039;Proportional Editing&amp;#039;) has some nice options, but its UI kinda sucks compared to maya. Most of the time the radius of your brush will be outside the viewport window, and you have to scroll the mousewheel to change radius. In maya, you get the big yellowy-orangy-red-black mesh that shows the falloff. Also I &amp;#039;&amp;#039;think&amp;#039;&amp;#039; blender doesn&amp;#039;t have a &amp;#039;surface&amp;#039; vs volume falloff.&lt;br /&gt;
==== Snapping ====&lt;br /&gt;
* Ctrl-Shift-Tab &amp;gt; Include Active&lt;br /&gt;
&lt;br /&gt;
==== Extract a Curve from an edgeloop ====&lt;br /&gt;
* Select your edge loop, duplicate(Shift-D)&lt;br /&gt;
* Extract using P (separate)&lt;br /&gt;
* Go back to object mode, RMB convert mesh to curve. Done&lt;br /&gt;
&lt;br /&gt;
==== UVs ====&lt;br /&gt;
* You can mark seams in the 3d view for the unwrap (&amp;#039;U&amp;#039;)&lt;br /&gt;
* Faces/uvs selected in UV editor don&amp;#039;t show up by defualt 3D view , sync selection must be turned on:&lt;br /&gt;
https://i.imgur.com/H4M3jci.png&lt;br /&gt;
&lt;br /&gt;
==== Cool Tips ====&lt;br /&gt;
* Spherize: (alt shift s)&lt;br /&gt;
&lt;br /&gt;
=== Rigging/Skinning ===&lt;br /&gt;
&lt;br /&gt;
* To apply armature: select objects, select armature, parent (Ctrl P), should create a modifier&lt;br /&gt;
* To access weight painting, it&amp;#039;s kinda weird. Select bone that you want to move in Pose mode, select mesh in object mode somehow (doesn&amp;#039;t work with shift here), access weight paint mode (Ctrl Tab wheel)&lt;br /&gt;
* To reset bones when moving around when skinning alt+g,r,s&lt;br /&gt;
* Methods for smoothing skinning: https://blender.stackexchange.com/questions/58913/simple-methods-to-smooth-out-weight-paints (editing brush in tool tab works too)&lt;br /&gt;
* Mirror skin weights: https://www.youtube.com/watch?v=Ha_YU5xJsSc in any situation. Sometimes when painting mirrored weights, you&amp;#039;ll need to be on the PosX side to work.&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
==== Playblasts ====&lt;br /&gt;
There&amp;#039;s no way (?) of creating a simple playblast. It seems you have to use the OpenGL render. Which means going into the render options (workbench), you lose your background viewport image etc... Seems like a big oversight.&lt;br /&gt;
&lt;br /&gt;
==== Adding expressions ====&lt;br /&gt;
Using drivers! Try it out, it&amp;#039;s fairly self-explanatory.&lt;br /&gt;
Quicktip for rotating an asset:&lt;br /&gt;
* Write &amp;#039;#frame&amp;#039; into rotate Y, edit and write &amp;#039;radians(frame)&amp;#039; frame number = angle.&lt;br /&gt;
==== How do I animate text ? ====&lt;br /&gt;
Apparently you can&amp;#039;t keyframe text easily. That sucks. However you can open up a python script, it seems you have to relaunch the script at every session.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# assumes you have a text object called &amp;#039;Text&amp;#039;&lt;br /&gt;
import bpy&lt;br /&gt;
&lt;br /&gt;
scene = bpy.context.scene&lt;br /&gt;
obj = scene.objects[&amp;#039;Text&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
def recalculate_text(scene):&lt;br /&gt;
    text = &amp;#039;--&amp;#039;&lt;br /&gt;
    cf = scene.frame_current&lt;br /&gt;
    if cf &amp;lt; 1800:&lt;br /&gt;
        text = &amp;#039; &amp;#039;&lt;br /&gt;
    elif cf &amp;lt; 1920:&lt;br /&gt;
        text = 8&lt;br /&gt;
    elif cf &amp;lt; 2030:&lt;br /&gt;
        text = 5&lt;br /&gt;
    elif cf &amp;lt; 2180:&lt;br /&gt;
        text = 1&lt;br /&gt;
    elif cf &amp;lt; 2300:&lt;br /&gt;
        text = 2&lt;br /&gt;
   &lt;br /&gt;
    obj.data.body = f&amp;#039;{text}&amp;#039;&lt;br /&gt;
&lt;br /&gt;
bpy.app.handlers.frame_change_pre.append(recalculate_text)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Will modify this if I find a more user friendly way of doing this.&lt;br /&gt;
&lt;br /&gt;
==== How do I switch cameras during my animation ====&lt;br /&gt;
&lt;br /&gt;
You can add [https://docs.blender.org/manual/en/latest/animation/markers.html markers] (M in the timeline) and then bind the current camera (Ctrl B in the timeline too)&lt;br /&gt;
&lt;br /&gt;
=== UI ===&lt;br /&gt;
&lt;br /&gt;
==== How do I lock camera to view ====&lt;br /&gt;
&lt;br /&gt;
This is kinda hidden amirite ? Shortcut to twirl/untwirl that little menu is &amp;#039;n&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1PW3NEt.gif&lt;br /&gt;
&lt;br /&gt;
==== Adding a slider in the viewport ====&lt;br /&gt;
&lt;br /&gt;
There are so many contexts that I understand there&amp;#039;s no easy universal way to add a floating UI over everything to drive a rig or anything. For rigging you can use custom drivers on bones and it&amp;#039;s show up while animating.&lt;br /&gt;
&lt;br /&gt;
=== Hair ===&lt;br /&gt;
&lt;br /&gt;
Right-click and object, Curve&amp;gt;Fur. Check modifiers. Missing attributes (like length) can be added from the Asset Browser (Shift-F1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Geometry Nodes ===&lt;br /&gt;
&lt;br /&gt;
ooooh the fun stuff coming from houdini! &lt;br /&gt;
&lt;br /&gt;
* Ctrl-right click to knife connections&lt;br /&gt;
* Alt-move node to disconnect it&lt;br /&gt;
&lt;br /&gt;
https://blender.stackexchange.com/questions/295375/tangent-grooming-with-geometry-nodes/295404#295404&lt;br /&gt;
&lt;br /&gt;
==== From houdini gotcha&amp;#039;s ====&lt;br /&gt;
&lt;br /&gt;
* No Wrangles. Yikes.&lt;br /&gt;
* Something that can be confusing is that you don&amp;#039;t explicitly call an attribute: _screenshot tbd_.&lt;br /&gt;
&lt;br /&gt;
=== Other random notes, frustrations that I need to solve ===&lt;br /&gt;
&lt;br /&gt;
* The outliner forces sorting by alphabetical name ? Not by creation order.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039;Uncheck the &amp;#039;Alphabetical&amp;#039; option in the Filter menu at the top, objects will be sorted by creation order&amp;#039;&amp;#039; Custom ordering [https://projects.blender.org/blender/blender/issues/68502  can&amp;#039;t be done though for now].&lt;br /&gt;
&lt;br /&gt;
* It seems super hard to do per-renderlayer material override, which is something maya is good at. You can create linked asset overrides, but it&amp;#039;s very cumbersome.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; no solution yet &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* How do you add variables to render items, like the scene number. It seems like a lot of work to increment everything sometimes. Just like &amp;lt;Scene&amp;gt;/&amp;lt;Layer&amp;gt; in maya or $HIPNAME in H.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; probably python, need to answer that one &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* It seems like Layers are really neat to pack versions of a single scene (?) but it also seems you can have multiple render layers setup per layer setup. So how do you know which one is the &amp;#039;official&amp;#039; one when you open up a scene ? Seems confusing.&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
Will move this page when it gets big.&lt;br /&gt;
=== Set Crypto Matte Ids using Python===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#here i&amp;#039;m setting the matte_id property of a crypto node, traversing all materials in the scene that have &amp;quot;bordure&amp;quot; in their name&lt;br /&gt;
import bpy&lt;br /&gt;
obj = bpy.context.object&lt;br /&gt;
b = bpy.data.scenes[&amp;quot;Scene&amp;quot;].node_tree.nodes[&amp;quot;bordure&amp;quot;]&lt;br /&gt;
materials = [m.name for m in bpy.data.materials if &amp;quot;bordure&amp;quot; in m.name]&lt;br /&gt;
setattr(b, &amp;quot;matte_id&amp;quot;, &amp;#039;,&amp;#039;.join(materials))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Add-ons ==&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s been 5 seconds and I&amp;#039;m already adding add-ons :0. Will sort as I go along and use them&lt;br /&gt;
&lt;br /&gt;
* Loom, to help with render management: https://github.com/p2or/blender-loom&lt;br /&gt;
* Polyhaven (30$) all of the assets (which are free) integrated into a nice UI https://polyhaven.com/plugins/blender&lt;br /&gt;
* Bsurfaces (tutorial: https://www.youtube.com/watch?v=X2GNyEUvpD4 ), to draw topology (add &amp;#039;F2&amp;#039; and &amp;#039;loop tools&amp;#039; too)&lt;br /&gt;
* Blender to AfterEffects to export cameras and nulls, thanks to https://les-fees-speciales.coop/ : https://extensions.blender.org/add-ons/io-export-after-effects/&lt;br /&gt;
* Geometry Nodes https://blender-addons.org/sverchok-nodes-quick-start/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=903</id>
		<title>Blender 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=903"/>
		<updated>2026-03-18T16:02:09Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Add-ons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I&amp;#039;m slowly catching up and trying to learn blender so I&amp;#039;m just writing down a few things that frustrate me as an old maya/houdini user, and answer it as i go.&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
&lt;br /&gt;
* Cool cards/non-video tutorials. Would be great to have them in a normal scrollable page! https://www.3dbestie.com/&lt;br /&gt;
* In the same spirit, a Github readme page for the standard shortcuts w/ gifs: https://hollisbrown.github.io/blendershortcuts/&lt;br /&gt;
* French Studio Les Fées Speciales&amp;#039; blender tech blog: https://lacuisine.tech/&lt;br /&gt;
* Maya to blender in 30 minutes: https://www.youtube.com/watch?v=0C0njghXPAI&lt;br /&gt;
&lt;br /&gt;
=== Some shortcuts ===&lt;br /&gt;
Will sort! Trying to be a GoodBoy and learning the blender standard. God it&amp;#039;s hard not to hit the Alt key for navigation and the marking menus are so baked in muscle memory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;function&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;key&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;equivalent to&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| Add objects&lt;br /&gt;
| shift-a&lt;br /&gt;
| Tab-drop node (houdini)&lt;br /&gt;
|-&lt;br /&gt;
| Frame/focus object&lt;br /&gt;
| . (numpad)&lt;br /&gt;
| f (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Make active camera&lt;br /&gt;
| ctrl-num0 (?)&lt;br /&gt;
| mmb drag and drop cam from outliner to VP (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Toggle object/faces edges/vertices&lt;br /&gt;
| tab 1 2 3&lt;br /&gt;
| Maya F8 F9 etc...&lt;br /&gt;
|-&lt;br /&gt;
| Search for function&lt;br /&gt;
| F3&lt;br /&gt;
| Maya&amp;#039;s Find Menu but actually looks nice&lt;br /&gt;
|-&lt;br /&gt;
| Change sudbdiv&lt;br /&gt;
| ctrl+0,1,2...(top row keyboard digits)&lt;br /&gt;
| Maya&amp;#039;s numpad 1 2 3&lt;br /&gt;
|-&lt;br /&gt;
| Select Linked&lt;br /&gt;
| ctrl-L&lt;br /&gt;
| double click in maya to select shell&lt;br /&gt;
|-&lt;br /&gt;
| Select Edge Loops/Rings&lt;br /&gt;
| alt-LMB, ctrl-alt-LMB&lt;br /&gt;
| double click in maya to select loops, marking menus&lt;br /&gt;
|-&lt;br /&gt;
| Select More/Less&lt;br /&gt;
| ctrl-Num+&lt;br /&gt;
| Maya&amp;#039;s grow/shrink selection &amp;lt;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Proportional Editing&lt;br /&gt;
| o / scrollwheel (while using it)&lt;br /&gt;
| Soft selection in maya (B/shift MMB)&lt;br /&gt;
|-&lt;br /&gt;
| Isolate Selection&lt;br /&gt;
| Numpad-/ or Alt/Shift-H&lt;br /&gt;
| Shift-i in Maya&lt;br /&gt;
|-&lt;br /&gt;
| Add Edge Loop&lt;br /&gt;
| Ctrl-R + scrollwheel &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Wire/shaded&lt;br /&gt;
| Shift-Z&lt;br /&gt;
| 1 2 3 in Maya&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Some Blender Concepts ===&lt;br /&gt;
&lt;br /&gt;
==== Collections ====&lt;br /&gt;
&lt;br /&gt;
Is &amp;#039;&amp;#039;kind&amp;#039;&amp;#039; of like maya&amp;#039;s set+renderlayer on steroids. You can have an item in several collections (ctrl drag).&lt;br /&gt;
&lt;br /&gt;
==== Groups ====&lt;br /&gt;
&lt;br /&gt;
No such thing, but use a null object&lt;br /&gt;
&lt;br /&gt;
==== Asset/References  ====&lt;br /&gt;
&lt;br /&gt;
So this one is super weird. When you import an asset, or link it (like a reference in maya or .hda in Hou), you&amp;#039;re note linking a .blend file but a subset of the file, could be a mesh/object/material. Kind of tedious TBH, but the blenderheads seem fine with it. It helps if you work with collections, so you just append/link a specific collection.&lt;br /&gt;
&lt;br /&gt;
==== Help &amp;amp; Community ====&lt;br /&gt;
&lt;br /&gt;
Amazing how many people have created tutorials. Make sure to search for your version of Blender as I feel it&amp;#039;s a &amp;#039;move fast, break things&amp;#039; software so shortcuts and things might work differently, unlike maya (you can still safely import files from maya 5.0).&lt;br /&gt;
&lt;br /&gt;
Pretty cool that people can vote for what they wish to see: https://blender.community/c/rightclickselect/?sorting=hot (not in the community enough to know if it has some say into newer versions though).&lt;br /&gt;
&lt;br /&gt;
https://docs.blender.org/manual/en/latest/ kind of terse, but works well. Big up to most software packages that take care of their documentations. (&amp;lt;3 Houdini)&lt;br /&gt;
&lt;br /&gt;
== Specific Tips ==&lt;br /&gt;
&lt;br /&gt;
to be sorted once I get a few&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where can I edit the pivot point, what&amp;#039;s with the weird cursor that looks like a lifebuoy ? ===&lt;br /&gt;
&lt;br /&gt;
The cursor which is a bit jarring is actually quite useful. &amp;#039;&amp;#039;Shift + right click&amp;#039;&amp;#039; to move it around, &amp;#039;&amp;#039;Shift + s&amp;#039;&amp;#039; to send it places (like the origin)&lt;br /&gt;
&lt;br /&gt;
For pivots, top of the viewport, shortcut is &amp;#039;.&amp;#039; dot but apparently not on a french keyboard. Still don&amp;#039;t know what the first box does, I&amp;#039;ll remove this line once I know ;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7dXmNgq.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
* Material Override per render layer https://blender.stackexchange.com/questions/154321/material-overrides-for-view-layers-per-object&lt;br /&gt;
* /!\ &amp;#039;&amp;#039;&amp;#039;Alpha in render&amp;#039;&amp;#039;&amp;#039; needs to be toggled on in &amp;#039;Film&amp;gt;Transparent&amp;#039;. Why isn&amp;#039;t this on by default ???&lt;br /&gt;
* You can&amp;#039;t use maya&amp;#039;s &amp;lt;code&amp;gt;&amp;lt;scene&amp;gt;/%s&amp;lt;/code&amp;gt; or houdini&amp;#039;s &amp;lt;code&amp;gt;$HIPNAME&amp;lt;/code&amp;gt; or write custom python directy in a string field. See the bottom for the Loom extension.&lt;br /&gt;
* To render multilayer EXRs w/o having to connect layer&amp;gt;outputs by hand &amp;#039;&amp;#039;&amp;#039;untoggle&amp;#039;&amp;#039;&amp;#039; use nodes in compositor.&lt;br /&gt;
&lt;br /&gt;
=== Modeling ===&lt;br /&gt;
* Bake transforms: Ctrl-A (also work when over modifiers to bake them&lt;br /&gt;
* Soft selection (&amp;#039;Proportional Editing&amp;#039;) has some nice options, but its UI kinda sucks compared to maya. Most of the time the radius of your brush will be outside the viewport window, and you have to scroll the mousewheel to change radius. In maya, you get the big yellowy-orangy-red-black mesh that shows the falloff. Also I &amp;#039;&amp;#039;think&amp;#039;&amp;#039; blender doesn&amp;#039;t have a &amp;#039;surface&amp;#039; vs volume falloff.&lt;br /&gt;
==== Snapping ====&lt;br /&gt;
* Ctrl-Shift-Tab &amp;gt; Include Active&lt;br /&gt;
&lt;br /&gt;
==== Extract a Curve from an edgeloop ====&lt;br /&gt;
* Select your edge loop, duplicate(Shift-D)&lt;br /&gt;
* Extract using P (separate)&lt;br /&gt;
* Go back to object mode, RMB convert mesh to curve. Done&lt;br /&gt;
&lt;br /&gt;
==== UVs ====&lt;br /&gt;
* You can mark seams in the 3d view for the unwrap (&amp;#039;U&amp;#039;)&lt;br /&gt;
* Faces/uvs selected in UV editor don&amp;#039;t show up by defualt 3D view , sync selection must be turned on:&lt;br /&gt;
https://i.imgur.com/H4M3jci.png&lt;br /&gt;
&lt;br /&gt;
==== Cool Tips ====&lt;br /&gt;
* Spherize: (alt shift s)&lt;br /&gt;
&lt;br /&gt;
=== Rigging/Skinning ===&lt;br /&gt;
&lt;br /&gt;
* To apply armature: select objects, select armature, parent (Ctrl P), should create a modifier&lt;br /&gt;
* To access weight painting, it&amp;#039;s kinda weird. Select bone that you want to move in Pose mode, select mesh in object mode somehow (doesn&amp;#039;t work with shift here), access weight paint mode (Ctrl Tab wheel)&lt;br /&gt;
* To reset bones when moving around when skinning alt+g,r,s&lt;br /&gt;
* Methods for smoothing skinning: https://blender.stackexchange.com/questions/58913/simple-methods-to-smooth-out-weight-paints (editing brush in tool tab works too)&lt;br /&gt;
* Mirror skin weights: https://www.youtube.com/watch?v=Ha_YU5xJsSc in any situation. Sometimes when painting mirrored weights, you&amp;#039;ll need to be on the PosX side to work.&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
==== Playblasts ====&lt;br /&gt;
There&amp;#039;s no way (?) of creating a simple playblast. It seems you have to use the OpenGL render. Which means going into the render options (workbench), you lose your background viewport image etc... Seems like a big oversight.&lt;br /&gt;
&lt;br /&gt;
==== Adding expressions ====&lt;br /&gt;
Using drivers! Try it out, it&amp;#039;s fairly self-explanatory.&lt;br /&gt;
Quicktip for rotating an asset:&lt;br /&gt;
* Write &amp;#039;#frame&amp;#039; into rotate Y, edit and write &amp;#039;radians(frame)&amp;#039; frame number = angle.&lt;br /&gt;
==== How do I animate text ? ====&lt;br /&gt;
Apparently you can&amp;#039;t keyframe text easily. That sucks. However you can open up a python script, it seems you have to relaunch the script at every session.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# assumes you have a text object called &amp;#039;Text&amp;#039;&lt;br /&gt;
import bpy&lt;br /&gt;
&lt;br /&gt;
scene = bpy.context.scene&lt;br /&gt;
obj = scene.objects[&amp;#039;Text&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
def recalculate_text(scene):&lt;br /&gt;
    text = &amp;#039;--&amp;#039;&lt;br /&gt;
    cf = scene.frame_current&lt;br /&gt;
    if cf &amp;lt; 1800:&lt;br /&gt;
        text = &amp;#039; &amp;#039;&lt;br /&gt;
    elif cf &amp;lt; 1920:&lt;br /&gt;
        text = 8&lt;br /&gt;
    elif cf &amp;lt; 2030:&lt;br /&gt;
        text = 5&lt;br /&gt;
    elif cf &amp;lt; 2180:&lt;br /&gt;
        text = 1&lt;br /&gt;
    elif cf &amp;lt; 2300:&lt;br /&gt;
        text = 2&lt;br /&gt;
   &lt;br /&gt;
    obj.data.body = f&amp;#039;{text}&amp;#039;&lt;br /&gt;
&lt;br /&gt;
bpy.app.handlers.frame_change_pre.append(recalculate_text)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Will modify this if I find a more user friendly way of doing this.&lt;br /&gt;
&lt;br /&gt;
==== How do I switch cameras during my animation ====&lt;br /&gt;
&lt;br /&gt;
You can add [https://docs.blender.org/manual/en/latest/animation/markers.html markers] (M in the timeline) and then bind the current camera (Ctrl B in the timeline too)&lt;br /&gt;
&lt;br /&gt;
=== UI ===&lt;br /&gt;
&lt;br /&gt;
==== How do I lock camera to view ====&lt;br /&gt;
&lt;br /&gt;
This is kinda hidden amirite ? Shortcut to twirl/untwirl that little menu is &amp;#039;n&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1PW3NEt.gif&lt;br /&gt;
&lt;br /&gt;
==== Adding a slider in the viewport ====&lt;br /&gt;
&lt;br /&gt;
There are so many contexts that I understand there&amp;#039;s no easy universal way to add a floating UI over everything to drive a rig or anything. For rigging you can use custom drivers on bones and it&amp;#039;s show up while animating.&lt;br /&gt;
&lt;br /&gt;
=== Hair ===&lt;br /&gt;
&lt;br /&gt;
Right-click and object, Curve&amp;gt;Fur. Check modifiers. Missing attributes (like length) can be added from the Asset Browser (Shift-F1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Geometry Nodes ===&lt;br /&gt;
&lt;br /&gt;
ooooh the fun stuff coming from houdini! &lt;br /&gt;
&lt;br /&gt;
* Ctrl-right click to knife connections&lt;br /&gt;
* Alt-move node to disconnect it&lt;br /&gt;
&lt;br /&gt;
https://blender.stackexchange.com/questions/295375/tangent-grooming-with-geometry-nodes/295404#295404&lt;br /&gt;
&lt;br /&gt;
=== Other random notes, frustrations that I need to solve ===&lt;br /&gt;
&lt;br /&gt;
* The outliner forces sorting by alphabetical name ? Not by creation order.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039;Uncheck the &amp;#039;Alphabetical&amp;#039; option in the Filter menu at the top, objects will be sorted by creation order&amp;#039;&amp;#039; Custom ordering [https://projects.blender.org/blender/blender/issues/68502  can&amp;#039;t be done though for now].&lt;br /&gt;
&lt;br /&gt;
* It seems super hard to do per-renderlayer material override, which is something maya is good at. You can create linked asset overrides, but it&amp;#039;s very cumbersome.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; no solution yet &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* How do you add variables to render items, like the scene number. It seems like a lot of work to increment everything sometimes. Just like &amp;lt;Scene&amp;gt;/&amp;lt;Layer&amp;gt; in maya or $HIPNAME in H.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; probably python, need to answer that one &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* It seems like Layers are really neat to pack versions of a single scene (?) but it also seems you can have multiple render layers setup per layer setup. So how do you know which one is the &amp;#039;official&amp;#039; one when you open up a scene ? Seems confusing.&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
Will move this page when it gets big.&lt;br /&gt;
=== Set Crypto Matte Ids using Python===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#here i&amp;#039;m setting the matte_id property of a crypto node, traversing all materials in the scene that have &amp;quot;bordure&amp;quot; in their name&lt;br /&gt;
import bpy&lt;br /&gt;
obj = bpy.context.object&lt;br /&gt;
b = bpy.data.scenes[&amp;quot;Scene&amp;quot;].node_tree.nodes[&amp;quot;bordure&amp;quot;]&lt;br /&gt;
materials = [m.name for m in bpy.data.materials if &amp;quot;bordure&amp;quot; in m.name]&lt;br /&gt;
setattr(b, &amp;quot;matte_id&amp;quot;, &amp;#039;,&amp;#039;.join(materials))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Add-ons ==&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s been 5 seconds and I&amp;#039;m already adding add-ons :0. Will sort as I go along and use them&lt;br /&gt;
&lt;br /&gt;
* Loom, to help with render management: https://github.com/p2or/blender-loom&lt;br /&gt;
* Polyhaven (30$) all of the assets (which are free) integrated into a nice UI https://polyhaven.com/plugins/blender&lt;br /&gt;
* Bsurfaces (tutorial: https://www.youtube.com/watch?v=X2GNyEUvpD4 ), to draw topology (add &amp;#039;F2&amp;#039; and &amp;#039;loop tools&amp;#039; too)&lt;br /&gt;
* Blender to AfterEffects to export cameras and nulls, thanks to https://les-fees-speciales.coop/ : https://extensions.blender.org/add-ons/io-export-after-effects/&lt;br /&gt;
* Geometry Nodes https://blender-addons.org/sverchok-nodes-quick-start/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_Tutorials&amp;diff=902</id>
		<title>Blender Tutorials</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_Tutorials&amp;diff=902"/>
		<updated>2026-03-12T16:21:55Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Delaunay Triangulation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Geometry Nodes ==&lt;br /&gt;
=== Delaunay Triangulation ===&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/HyW54cI8Qdo&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://www.youtube.com/watch?v=HyW54cI8Qdo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Custom curve deformer ===&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/uv0E_FQLQYI&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://www.youtube.com/watch?v=uv0E_FQLQYI&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=901</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=901"/>
		<updated>2026-03-11T17:02:51Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hi i&amp;#039;m Bernie, here lies my personal wiki. It&amp;#039;s unsightly but it works and someone &amp;#039;&amp;#039;has&amp;#039;&amp;#039; to feed (crap) to the AI. &lt;br /&gt;
It&amp;#039;s a collection of thoughts and things I&amp;#039;ve gathered over the years and placed here haphazardly.&lt;br /&gt;
&lt;br /&gt;
If you see something &amp;lt;s&amp;gt;ugly&amp;lt;/s&amp;gt; wrong, lemme know ! bernie a@t berniebernie dot(.) fr&lt;br /&gt;
&lt;br /&gt;
Some pages haven&amp;#039;t been updated in a long, long time, they are here for historical purposes only. &lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
! &lt;br /&gt;
! &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
* [[Special:RecentChanges]] (what was updated recently)&lt;br /&gt;
* 3d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Maya&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Maya Shelf]]&lt;br /&gt;
*** Mel (.mel)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Mel|Mel Scripts]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Mel Temp|Mel Scripts Temp]]&lt;br /&gt;
**** [[Mel Keyboard and Snippets]]&lt;br /&gt;
**** [[Mel Functions]] (+useful mels on pastebin)&lt;br /&gt;
**** [[Advanced Skeleton Specific]] / [[BernieMelLibrary]] / [[Maya Settings]]&lt;br /&gt;
*** Python (.py)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Python]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Maya Python Temp]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Houdini&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini 101]]&lt;br /&gt;
*** [[Houdini VEX]] / [[Houdini VEX Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Python]]&amp;#039;&amp;#039;&amp;#039; / [[Houdini Python Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Stupid Questions]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini UI Customization]]&lt;br /&gt;
*** [[Houdini Webinars and Videos]]&lt;br /&gt;
*** [[Houdini Octane|Houdini Octane/Arnold]]&lt;br /&gt;
** [[Work Specific Scripts]] hodgepodge maya/houdini/whatever&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Blender&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Blender 101]]&lt;br /&gt;
*** [[Blender Tutorials]]&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* 2d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;After Effects&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Afx Javascript]]&amp;#039;&amp;#039;&amp;#039; (.jsx)&lt;br /&gt;
*** [[Afx Javascript Temp]]&lt;br /&gt;
*** [[Expressions and Tips|Afx Expressions and Tips]]&lt;br /&gt;
*** [[Afx Shortcuts FR]] and [[Shelf|Custom Shelf UI]] (wip)&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Nuke / Natron&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Nuke Python]] / [[Nuke Python Temp]]&lt;br /&gt;
*** [[Example Gizmos]]&lt;br /&gt;
** [[Adobe Animate]]&lt;br /&gt;
* Other&lt;br /&gt;
** [[WebGl]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Operating System / General&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Software]] i use&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Windows batch|Windows batch &amp;amp; Powershell]]&amp;#039;&amp;#039;&amp;#039; .bat/.vbs/.ps1 ([[Windows_batch#l.bat|l.bat]])&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Python]]&amp;#039;&amp;#039;&amp;#039; .py scripts for work/ day usage&lt;br /&gt;
***[[Auto Hotkey]]&lt;br /&gt;
*** [[Linux]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Misc&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Gobelins 2d 3d integration]]&lt;br /&gt;
***[[Showreel]]&lt;br /&gt;
***[[Maxscript]] .ms [[Misc Scripting]] [[RandomDump]]&lt;br /&gt;
***[[Software Shortcuts]] [[vvvv]] [[vj]]&lt;br /&gt;
***[[Photoshop Javascript]] .js&lt;br /&gt;
***[[7550A Hp Plotter]]&lt;br /&gt;
***[[Color (Aces, LUTs, Gamma etc)]] wip&lt;br /&gt;
***[[OSL|OSLs]]&lt;br /&gt;
***[[AI]]&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=900</id>
		<title>Blender 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=900"/>
		<updated>2026-03-11T17:01:40Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Geometry Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I&amp;#039;m slowly catching up and trying to learn blender so I&amp;#039;m just writing down a few things that frustrate me as an old maya/houdini user, and answer it as i go.&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
&lt;br /&gt;
* Cool cards/non-video tutorials. Would be great to have them in a normal scrollable page! https://www.3dbestie.com/&lt;br /&gt;
* In the same spirit, a Github readme page for the standard shortcuts w/ gifs: https://hollisbrown.github.io/blendershortcuts/&lt;br /&gt;
* French Studio Les Fées Speciales&amp;#039; blender tech blog: https://lacuisine.tech/&lt;br /&gt;
* Maya to blender in 30 minutes: https://www.youtube.com/watch?v=0C0njghXPAI&lt;br /&gt;
&lt;br /&gt;
=== Some shortcuts ===&lt;br /&gt;
Will sort! Trying to be a GoodBoy and learning the blender standard. God it&amp;#039;s hard not to hit the Alt key for navigation and the marking menus are so baked in muscle memory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;function&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;key&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;equivalent to&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| Add objects&lt;br /&gt;
| shift-a&lt;br /&gt;
| Tab-drop node (houdini)&lt;br /&gt;
|-&lt;br /&gt;
| Frame/focus object&lt;br /&gt;
| . (numpad)&lt;br /&gt;
| f (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Make active camera&lt;br /&gt;
| ctrl-num0 (?)&lt;br /&gt;
| mmb drag and drop cam from outliner to VP (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Toggle object/faces edges/vertices&lt;br /&gt;
| tab 1 2 3&lt;br /&gt;
| Maya F8 F9 etc...&lt;br /&gt;
|-&lt;br /&gt;
| Search for function&lt;br /&gt;
| F3&lt;br /&gt;
| Maya&amp;#039;s Find Menu but actually looks nice&lt;br /&gt;
|-&lt;br /&gt;
| Change sudbdiv&lt;br /&gt;
| ctrl+0,1,2...(top row keyboard digits)&lt;br /&gt;
| Maya&amp;#039;s numpad 1 2 3&lt;br /&gt;
|-&lt;br /&gt;
| Select Linked&lt;br /&gt;
| ctrl-L&lt;br /&gt;
| double click in maya to select shell&lt;br /&gt;
|-&lt;br /&gt;
| Select Edge Loops/Rings&lt;br /&gt;
| alt-LMB, ctrl-alt-LMB&lt;br /&gt;
| double click in maya to select loops, marking menus&lt;br /&gt;
|-&lt;br /&gt;
| Select More/Less&lt;br /&gt;
| ctrl-Num+&lt;br /&gt;
| Maya&amp;#039;s grow/shrink selection &amp;lt;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Proportional Editing&lt;br /&gt;
| o / scrollwheel (while using it)&lt;br /&gt;
| Soft selection in maya (B/shift MMB)&lt;br /&gt;
|-&lt;br /&gt;
| Isolate Selection&lt;br /&gt;
| Numpad-/ or Alt/Shift-H&lt;br /&gt;
| Shift-i in Maya&lt;br /&gt;
|-&lt;br /&gt;
| Add Edge Loop&lt;br /&gt;
| Ctrl-R + scrollwheel &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Wire/shaded&lt;br /&gt;
| Shift-Z&lt;br /&gt;
| 1 2 3 in Maya&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Some Blender Concepts ===&lt;br /&gt;
&lt;br /&gt;
==== Collections ====&lt;br /&gt;
&lt;br /&gt;
Is &amp;#039;&amp;#039;kind&amp;#039;&amp;#039; of like maya&amp;#039;s set+renderlayer on steroids. You can have an item in several collections (ctrl drag).&lt;br /&gt;
&lt;br /&gt;
==== Groups ====&lt;br /&gt;
&lt;br /&gt;
No such thing, but use a null object&lt;br /&gt;
&lt;br /&gt;
==== Asset/References  ====&lt;br /&gt;
&lt;br /&gt;
So this one is super weird. When you import an asset, or link it (like a reference in maya or .hda in Hou), you&amp;#039;re note linking a .blend file but a subset of the file, could be a mesh/object/material. Kind of tedious TBH, but the blenderheads seem fine with it. It helps if you work with collections, so you just append/link a specific collection.&lt;br /&gt;
&lt;br /&gt;
==== Help &amp;amp; Community ====&lt;br /&gt;
&lt;br /&gt;
Amazing how many people have created tutorials. Make sure to search for your version of Blender as I feel it&amp;#039;s a &amp;#039;move fast, break things&amp;#039; software so shortcuts and things might work differently, unlike maya (you can still safely import files from maya 5.0).&lt;br /&gt;
&lt;br /&gt;
Pretty cool that people can vote for what they wish to see: https://blender.community/c/rightclickselect/?sorting=hot (not in the community enough to know if it has some say into newer versions though).&lt;br /&gt;
&lt;br /&gt;
https://docs.blender.org/manual/en/latest/ kind of terse, but works well. Big up to most software packages that take care of their documentations. (&amp;lt;3 Houdini)&lt;br /&gt;
&lt;br /&gt;
== Specific Tips ==&lt;br /&gt;
&lt;br /&gt;
to be sorted once I get a few&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where can I edit the pivot point, what&amp;#039;s with the weird cursor that looks like a lifebuoy ? ===&lt;br /&gt;
&lt;br /&gt;
The cursor which is a bit jarring is actually quite useful. &amp;#039;&amp;#039;Shift + right click&amp;#039;&amp;#039; to move it around, &amp;#039;&amp;#039;Shift + s&amp;#039;&amp;#039; to send it places (like the origin)&lt;br /&gt;
&lt;br /&gt;
For pivots, top of the viewport, shortcut is &amp;#039;.&amp;#039; dot but apparently not on a french keyboard. Still don&amp;#039;t know what the first box does, I&amp;#039;ll remove this line once I know ;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7dXmNgq.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
* Material Override per render layer https://blender.stackexchange.com/questions/154321/material-overrides-for-view-layers-per-object&lt;br /&gt;
* /!\ &amp;#039;&amp;#039;&amp;#039;Alpha in render&amp;#039;&amp;#039;&amp;#039; needs to be toggled on in &amp;#039;Film&amp;gt;Transparent&amp;#039;. Why isn&amp;#039;t this on by default ???&lt;br /&gt;
* You can&amp;#039;t use maya&amp;#039;s &amp;lt;code&amp;gt;&amp;lt;scene&amp;gt;/%s&amp;lt;/code&amp;gt; or houdini&amp;#039;s &amp;lt;code&amp;gt;$HIPNAME&amp;lt;/code&amp;gt; or write custom python directy in a string field. See the bottom for the Loom extension.&lt;br /&gt;
* To render multilayer EXRs w/o having to connect layer&amp;gt;outputs by hand &amp;#039;&amp;#039;&amp;#039;untoggle&amp;#039;&amp;#039;&amp;#039; use nodes in compositor.&lt;br /&gt;
&lt;br /&gt;
=== Modeling ===&lt;br /&gt;
* Bake transforms: Ctrl-A (also work when over modifiers to bake them&lt;br /&gt;
* Soft selection (&amp;#039;Proportional Editing&amp;#039;) has some nice options, but its UI kinda sucks compared to maya. Most of the time the radius of your brush will be outside the viewport window, and you have to scroll the mousewheel to change radius. In maya, you get the big yellowy-orangy-red-black mesh that shows the falloff. Also I &amp;#039;&amp;#039;think&amp;#039;&amp;#039; blender doesn&amp;#039;t have a &amp;#039;surface&amp;#039; vs volume falloff.&lt;br /&gt;
==== Snapping ====&lt;br /&gt;
* Ctrl-Shift-Tab &amp;gt; Include Active&lt;br /&gt;
&lt;br /&gt;
==== Extract a Curve from an edgeloop ====&lt;br /&gt;
* Select your edge loop, duplicate(Shift-D)&lt;br /&gt;
* Extract using P (separate)&lt;br /&gt;
* Go back to object mode, RMB convert mesh to curve. Done&lt;br /&gt;
&lt;br /&gt;
==== UVs ====&lt;br /&gt;
* You can mark seams in the 3d view for the unwrap (&amp;#039;U&amp;#039;)&lt;br /&gt;
* Faces/uvs selected in UV editor don&amp;#039;t show up by defualt 3D view , sync selection must be turned on:&lt;br /&gt;
https://i.imgur.com/H4M3jci.png&lt;br /&gt;
&lt;br /&gt;
==== Cool Tips ====&lt;br /&gt;
* Spherize: (alt shift s)&lt;br /&gt;
&lt;br /&gt;
=== Rigging/Skinning ===&lt;br /&gt;
&lt;br /&gt;
* To apply armature: select objects, select armature, parent (Ctrl P), should create a modifier&lt;br /&gt;
* To access weight painting, it&amp;#039;s kinda weird. Select bone that you want to move in Pose mode, select mesh in object mode somehow (doesn&amp;#039;t work with shift here), access weight paint mode (Ctrl Tab wheel)&lt;br /&gt;
* To reset bones when moving around when skinning alt+g,r,s&lt;br /&gt;
* Methods for smoothing skinning: https://blender.stackexchange.com/questions/58913/simple-methods-to-smooth-out-weight-paints (editing brush in tool tab works too)&lt;br /&gt;
* Mirror skin weights: https://www.youtube.com/watch?v=Ha_YU5xJsSc in any situation. Sometimes when painting mirrored weights, you&amp;#039;ll need to be on the PosX side to work.&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
==== Playblasts ====&lt;br /&gt;
There&amp;#039;s no way (?) of creating a simple playblast. It seems you have to use the OpenGL render. Which means going into the render options (workbench), you lose your background viewport image etc... Seems like a big oversight.&lt;br /&gt;
&lt;br /&gt;
==== Adding expressions ====&lt;br /&gt;
Using drivers! Try it out, it&amp;#039;s fairly self-explanatory.&lt;br /&gt;
Quicktip for rotating an asset:&lt;br /&gt;
* Write &amp;#039;#frame&amp;#039; into rotate Y, edit and write &amp;#039;radians(frame)&amp;#039; frame number = angle.&lt;br /&gt;
==== How do I animate text ? ====&lt;br /&gt;
Apparently you can&amp;#039;t keyframe text easily. That sucks. However you can open up a python script, it seems you have to relaunch the script at every session.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# assumes you have a text object called &amp;#039;Text&amp;#039;&lt;br /&gt;
import bpy&lt;br /&gt;
&lt;br /&gt;
scene = bpy.context.scene&lt;br /&gt;
obj = scene.objects[&amp;#039;Text&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
def recalculate_text(scene):&lt;br /&gt;
    text = &amp;#039;--&amp;#039;&lt;br /&gt;
    cf = scene.frame_current&lt;br /&gt;
    if cf &amp;lt; 1800:&lt;br /&gt;
        text = &amp;#039; &amp;#039;&lt;br /&gt;
    elif cf &amp;lt; 1920:&lt;br /&gt;
        text = 8&lt;br /&gt;
    elif cf &amp;lt; 2030:&lt;br /&gt;
        text = 5&lt;br /&gt;
    elif cf &amp;lt; 2180:&lt;br /&gt;
        text = 1&lt;br /&gt;
    elif cf &amp;lt; 2300:&lt;br /&gt;
        text = 2&lt;br /&gt;
   &lt;br /&gt;
    obj.data.body = f&amp;#039;{text}&amp;#039;&lt;br /&gt;
&lt;br /&gt;
bpy.app.handlers.frame_change_pre.append(recalculate_text)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Will modify this if I find a more user friendly way of doing this.&lt;br /&gt;
&lt;br /&gt;
==== How do I switch cameras during my animation ====&lt;br /&gt;
&lt;br /&gt;
You can add [https://docs.blender.org/manual/en/latest/animation/markers.html markers] (M in the timeline) and then bind the current camera (Ctrl B in the timeline too)&lt;br /&gt;
&lt;br /&gt;
=== UI ===&lt;br /&gt;
&lt;br /&gt;
==== How do I lock camera to view ====&lt;br /&gt;
&lt;br /&gt;
This is kinda hidden amirite ? Shortcut to twirl/untwirl that little menu is &amp;#039;n&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1PW3NEt.gif&lt;br /&gt;
&lt;br /&gt;
==== Adding a slider in the viewport ====&lt;br /&gt;
&lt;br /&gt;
There are so many contexts that I understand there&amp;#039;s no easy universal way to add a floating UI over everything to drive a rig or anything. For rigging you can use custom drivers on bones and it&amp;#039;s show up while animating.&lt;br /&gt;
&lt;br /&gt;
=== Hair ===&lt;br /&gt;
&lt;br /&gt;
Right-click and object, Curve&amp;gt;Fur. Check modifiers. Missing attributes (like length) can be added from the Asset Browser (Shift-F1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Geometry Nodes ===&lt;br /&gt;
&lt;br /&gt;
ooooh the fun stuff coming from houdini! &lt;br /&gt;
&lt;br /&gt;
* Ctrl-right click to knife connections&lt;br /&gt;
* Alt-move node to disconnect it&lt;br /&gt;
&lt;br /&gt;
https://blender.stackexchange.com/questions/295375/tangent-grooming-with-geometry-nodes/295404#295404&lt;br /&gt;
&lt;br /&gt;
=== Other random notes, frustrations that I need to solve ===&lt;br /&gt;
&lt;br /&gt;
* The outliner forces sorting by alphabetical name ? Not by creation order.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039;Uncheck the &amp;#039;Alphabetical&amp;#039; option in the Filter menu at the top, objects will be sorted by creation order&amp;#039;&amp;#039; Custom ordering [https://projects.blender.org/blender/blender/issues/68502  can&amp;#039;t be done though for now].&lt;br /&gt;
&lt;br /&gt;
* It seems super hard to do per-renderlayer material override, which is something maya is good at. You can create linked asset overrides, but it&amp;#039;s very cumbersome.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; no solution yet &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* How do you add variables to render items, like the scene number. It seems like a lot of work to increment everything sometimes. Just like &amp;lt;Scene&amp;gt;/&amp;lt;Layer&amp;gt; in maya or $HIPNAME in H.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; probably python, need to answer that one &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* It seems like Layers are really neat to pack versions of a single scene (?) but it also seems you can have multiple render layers setup per layer setup. So how do you know which one is the &amp;#039;official&amp;#039; one when you open up a scene ? Seems confusing.&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
Will move this page when it gets big.&lt;br /&gt;
=== Set Crypto Matte Ids using Python===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#here i&amp;#039;m setting the matte_id property of a crypto node, traversing all materials in the scene that have &amp;quot;bordure&amp;quot; in their name&lt;br /&gt;
import bpy&lt;br /&gt;
obj = bpy.context.object&lt;br /&gt;
b = bpy.data.scenes[&amp;quot;Scene&amp;quot;].node_tree.nodes[&amp;quot;bordure&amp;quot;]&lt;br /&gt;
materials = [m.name for m in bpy.data.materials if &amp;quot;bordure&amp;quot; in m.name]&lt;br /&gt;
setattr(b, &amp;quot;matte_id&amp;quot;, &amp;#039;,&amp;#039;.join(materials))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Add-ons ==&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s been 5 seconds and I&amp;#039;m already adding add-ons :0. Will sort as I go along and use them&lt;br /&gt;
&lt;br /&gt;
* Loom, to help with render management: https://github.com/p2or/blender-loom&lt;br /&gt;
* Polyhaven (30$) all of the assets (which are free) integrated into a nice UI https://polyhaven.com/plugins/blender&lt;br /&gt;
* Bsurfaces (tutorial: https://www.youtube.com/watch?v=X2GNyEUvpD4 ), to draw topology (add &amp;#039;F2&amp;#039; and &amp;#039;loop tools&amp;#039; too)&lt;br /&gt;
* Blender to AfterEffects to export cameras and nulls, thanks to https://les-fees-speciales.coop/ : https://extensions.blender.org/add-ons/io-export-after-effects/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=899</id>
		<title>Blender 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=899"/>
		<updated>2026-03-09T11:21:03Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Hair */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I&amp;#039;m slowly catching up and trying to learn blender so I&amp;#039;m just writing down a few things that frustrate me as an old maya/houdini user, and answer it as i go.&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
&lt;br /&gt;
* Cool cards/non-video tutorials. Would be great to have them in a normal scrollable page! https://www.3dbestie.com/&lt;br /&gt;
* In the same spirit, a Github readme page for the standard shortcuts w/ gifs: https://hollisbrown.github.io/blendershortcuts/&lt;br /&gt;
* French Studio Les Fées Speciales&amp;#039; blender tech blog: https://lacuisine.tech/&lt;br /&gt;
* Maya to blender in 30 minutes: https://www.youtube.com/watch?v=0C0njghXPAI&lt;br /&gt;
&lt;br /&gt;
=== Some shortcuts ===&lt;br /&gt;
Will sort! Trying to be a GoodBoy and learning the blender standard. God it&amp;#039;s hard not to hit the Alt key for navigation and the marking menus are so baked in muscle memory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;function&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;key&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;equivalent to&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| Add objects&lt;br /&gt;
| shift-a&lt;br /&gt;
| Tab-drop node (houdini)&lt;br /&gt;
|-&lt;br /&gt;
| Frame/focus object&lt;br /&gt;
| . (numpad)&lt;br /&gt;
| f (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Make active camera&lt;br /&gt;
| ctrl-num0 (?)&lt;br /&gt;
| mmb drag and drop cam from outliner to VP (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Toggle object/faces edges/vertices&lt;br /&gt;
| tab 1 2 3&lt;br /&gt;
| Maya F8 F9 etc...&lt;br /&gt;
|-&lt;br /&gt;
| Search for function&lt;br /&gt;
| F3&lt;br /&gt;
| Maya&amp;#039;s Find Menu but actually looks nice&lt;br /&gt;
|-&lt;br /&gt;
| Change sudbdiv&lt;br /&gt;
| ctrl+0,1,2...(top row keyboard digits)&lt;br /&gt;
| Maya&amp;#039;s numpad 1 2 3&lt;br /&gt;
|-&lt;br /&gt;
| Select Linked&lt;br /&gt;
| ctrl-L&lt;br /&gt;
| double click in maya to select shell&lt;br /&gt;
|-&lt;br /&gt;
| Select Edge Loops/Rings&lt;br /&gt;
| alt-LMB, ctrl-alt-LMB&lt;br /&gt;
| double click in maya to select loops, marking menus&lt;br /&gt;
|-&lt;br /&gt;
| Select More/Less&lt;br /&gt;
| ctrl-Num+&lt;br /&gt;
| Maya&amp;#039;s grow/shrink selection &amp;lt;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Proportional Editing&lt;br /&gt;
| o / scrollwheel (while using it)&lt;br /&gt;
| Soft selection in maya (B/shift MMB)&lt;br /&gt;
|-&lt;br /&gt;
| Isolate Selection&lt;br /&gt;
| Numpad-/ or Alt/Shift-H&lt;br /&gt;
| Shift-i in Maya&lt;br /&gt;
|-&lt;br /&gt;
| Add Edge Loop&lt;br /&gt;
| Ctrl-R + scrollwheel &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Wire/shaded&lt;br /&gt;
| Shift-Z&lt;br /&gt;
| 1 2 3 in Maya&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Some Blender Concepts ===&lt;br /&gt;
&lt;br /&gt;
==== Collections ====&lt;br /&gt;
&lt;br /&gt;
Is &amp;#039;&amp;#039;kind&amp;#039;&amp;#039; of like maya&amp;#039;s set+renderlayer on steroids. You can have an item in several collections (ctrl drag).&lt;br /&gt;
&lt;br /&gt;
==== Groups ====&lt;br /&gt;
&lt;br /&gt;
No such thing, but use a null object&lt;br /&gt;
&lt;br /&gt;
==== Asset/References  ====&lt;br /&gt;
&lt;br /&gt;
So this one is super weird. When you import an asset, or link it (like a reference in maya or .hda in Hou), you&amp;#039;re note linking a .blend file but a subset of the file, could be a mesh/object/material. Kind of tedious TBH, but the blenderheads seem fine with it. It helps if you work with collections, so you just append/link a specific collection.&lt;br /&gt;
&lt;br /&gt;
==== Help &amp;amp; Community ====&lt;br /&gt;
&lt;br /&gt;
Amazing how many people have created tutorials. Make sure to search for your version of Blender as I feel it&amp;#039;s a &amp;#039;move fast, break things&amp;#039; software so shortcuts and things might work differently, unlike maya (you can still safely import files from maya 5.0).&lt;br /&gt;
&lt;br /&gt;
Pretty cool that people can vote for what they wish to see: https://blender.community/c/rightclickselect/?sorting=hot (not in the community enough to know if it has some say into newer versions though).&lt;br /&gt;
&lt;br /&gt;
https://docs.blender.org/manual/en/latest/ kind of terse, but works well. Big up to most software packages that take care of their documentations. (&amp;lt;3 Houdini)&lt;br /&gt;
&lt;br /&gt;
== Specific Tips ==&lt;br /&gt;
&lt;br /&gt;
to be sorted once I get a few&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where can I edit the pivot point, what&amp;#039;s with the weird cursor that looks like a lifebuoy ? ===&lt;br /&gt;
&lt;br /&gt;
The cursor which is a bit jarring is actually quite useful. &amp;#039;&amp;#039;Shift + right click&amp;#039;&amp;#039; to move it around, &amp;#039;&amp;#039;Shift + s&amp;#039;&amp;#039; to send it places (like the origin)&lt;br /&gt;
&lt;br /&gt;
For pivots, top of the viewport, shortcut is &amp;#039;.&amp;#039; dot but apparently not on a french keyboard. Still don&amp;#039;t know what the first box does, I&amp;#039;ll remove this line once I know ;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7dXmNgq.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
* Material Override per render layer https://blender.stackexchange.com/questions/154321/material-overrides-for-view-layers-per-object&lt;br /&gt;
* /!\ &amp;#039;&amp;#039;&amp;#039;Alpha in render&amp;#039;&amp;#039;&amp;#039; needs to be toggled on in &amp;#039;Film&amp;gt;Transparent&amp;#039;. Why isn&amp;#039;t this on by default ???&lt;br /&gt;
* You can&amp;#039;t use maya&amp;#039;s &amp;lt;code&amp;gt;&amp;lt;scene&amp;gt;/%s&amp;lt;/code&amp;gt; or houdini&amp;#039;s &amp;lt;code&amp;gt;$HIPNAME&amp;lt;/code&amp;gt; or write custom python directy in a string field. See the bottom for the Loom extension.&lt;br /&gt;
* To render multilayer EXRs w/o having to connect layer&amp;gt;outputs by hand &amp;#039;&amp;#039;&amp;#039;untoggle&amp;#039;&amp;#039;&amp;#039; use nodes in compositor.&lt;br /&gt;
&lt;br /&gt;
=== Modeling ===&lt;br /&gt;
* Bake transforms: Ctrl-A (also work when over modifiers to bake them&lt;br /&gt;
* Soft selection (&amp;#039;Proportional Editing&amp;#039;) has some nice options, but its UI kinda sucks compared to maya. Most of the time the radius of your brush will be outside the viewport window, and you have to scroll the mousewheel to change radius. In maya, you get the big yellowy-orangy-red-black mesh that shows the falloff. Also I &amp;#039;&amp;#039;think&amp;#039;&amp;#039; blender doesn&amp;#039;t have a &amp;#039;surface&amp;#039; vs volume falloff.&lt;br /&gt;
==== Snapping ====&lt;br /&gt;
* Ctrl-Shift-Tab &amp;gt; Include Active&lt;br /&gt;
&lt;br /&gt;
==== Extract a Curve from an edgeloop ====&lt;br /&gt;
* Select your edge loop, duplicate(Shift-D)&lt;br /&gt;
* Extract using P (separate)&lt;br /&gt;
* Go back to object mode, RMB convert mesh to curve. Done&lt;br /&gt;
&lt;br /&gt;
==== UVs ====&lt;br /&gt;
* You can mark seams in the 3d view for the unwrap (&amp;#039;U&amp;#039;)&lt;br /&gt;
* Faces/uvs selected in UV editor don&amp;#039;t show up by defualt 3D view , sync selection must be turned on:&lt;br /&gt;
https://i.imgur.com/H4M3jci.png&lt;br /&gt;
&lt;br /&gt;
==== Cool Tips ====&lt;br /&gt;
* Spherize: (alt shift s)&lt;br /&gt;
&lt;br /&gt;
=== Rigging/Skinning ===&lt;br /&gt;
&lt;br /&gt;
* To apply armature: select objects, select armature, parent (Ctrl P), should create a modifier&lt;br /&gt;
* To access weight painting, it&amp;#039;s kinda weird. Select bone that you want to move in Pose mode, select mesh in object mode somehow (doesn&amp;#039;t work with shift here), access weight paint mode (Ctrl Tab wheel)&lt;br /&gt;
* To reset bones when moving around when skinning alt+g,r,s&lt;br /&gt;
* Methods for smoothing skinning: https://blender.stackexchange.com/questions/58913/simple-methods-to-smooth-out-weight-paints (editing brush in tool tab works too)&lt;br /&gt;
* Mirror skin weights: https://www.youtube.com/watch?v=Ha_YU5xJsSc in any situation. Sometimes when painting mirrored weights, you&amp;#039;ll need to be on the PosX side to work.&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
==== Playblasts ====&lt;br /&gt;
There&amp;#039;s no way (?) of creating a simple playblast. It seems you have to use the OpenGL render. Which means going into the render options (workbench), you lose your background viewport image etc... Seems like a big oversight.&lt;br /&gt;
&lt;br /&gt;
==== Adding expressions ====&lt;br /&gt;
Using drivers! Try it out, it&amp;#039;s fairly self-explanatory.&lt;br /&gt;
Quicktip for rotating an asset:&lt;br /&gt;
* Write &amp;#039;#frame&amp;#039; into rotate Y, edit and write &amp;#039;radians(frame)&amp;#039; frame number = angle.&lt;br /&gt;
==== How do I animate text ? ====&lt;br /&gt;
Apparently you can&amp;#039;t keyframe text easily. That sucks. However you can open up a python script, it seems you have to relaunch the script at every session.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# assumes you have a text object called &amp;#039;Text&amp;#039;&lt;br /&gt;
import bpy&lt;br /&gt;
&lt;br /&gt;
scene = bpy.context.scene&lt;br /&gt;
obj = scene.objects[&amp;#039;Text&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
def recalculate_text(scene):&lt;br /&gt;
    text = &amp;#039;--&amp;#039;&lt;br /&gt;
    cf = scene.frame_current&lt;br /&gt;
    if cf &amp;lt; 1800:&lt;br /&gt;
        text = &amp;#039; &amp;#039;&lt;br /&gt;
    elif cf &amp;lt; 1920:&lt;br /&gt;
        text = 8&lt;br /&gt;
    elif cf &amp;lt; 2030:&lt;br /&gt;
        text = 5&lt;br /&gt;
    elif cf &amp;lt; 2180:&lt;br /&gt;
        text = 1&lt;br /&gt;
    elif cf &amp;lt; 2300:&lt;br /&gt;
        text = 2&lt;br /&gt;
   &lt;br /&gt;
    obj.data.body = f&amp;#039;{text}&amp;#039;&lt;br /&gt;
&lt;br /&gt;
bpy.app.handlers.frame_change_pre.append(recalculate_text)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Will modify this if I find a more user friendly way of doing this.&lt;br /&gt;
&lt;br /&gt;
==== How do I switch cameras during my animation ====&lt;br /&gt;
&lt;br /&gt;
You can add [https://docs.blender.org/manual/en/latest/animation/markers.html markers] (M in the timeline) and then bind the current camera (Ctrl B in the timeline too)&lt;br /&gt;
&lt;br /&gt;
=== UI ===&lt;br /&gt;
&lt;br /&gt;
==== How do I lock camera to view ====&lt;br /&gt;
&lt;br /&gt;
This is kinda hidden amirite ? Shortcut to twirl/untwirl that little menu is &amp;#039;n&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1PW3NEt.gif&lt;br /&gt;
&lt;br /&gt;
==== Adding a slider in the viewport ====&lt;br /&gt;
&lt;br /&gt;
There are so many contexts that I understand there&amp;#039;s no easy universal way to add a floating UI over everything to drive a rig or anything. For rigging you can use custom drivers on bones and it&amp;#039;s show up while animating.&lt;br /&gt;
&lt;br /&gt;
=== Hair ===&lt;br /&gt;
&lt;br /&gt;
Right-click and object, Curve&amp;gt;Fur. Check modifiers. Missing attributes (like length) can be added from the Asset Browser (Shift-F1).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Geometry Nodes ===&lt;br /&gt;
&lt;br /&gt;
ooooh the fun stuff coming from houdini! &lt;br /&gt;
&lt;br /&gt;
* Ctrl-right click to knife connections&lt;br /&gt;
* Alt-move node to disconnect it&lt;br /&gt;
&lt;br /&gt;
=== Other random notes, frustrations that I need to solve ===&lt;br /&gt;
&lt;br /&gt;
* The outliner forces sorting by alphabetical name ? Not by creation order.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039;Uncheck the &amp;#039;Alphabetical&amp;#039; option in the Filter menu at the top, objects will be sorted by creation order&amp;#039;&amp;#039; Custom ordering [https://projects.blender.org/blender/blender/issues/68502  can&amp;#039;t be done though for now].&lt;br /&gt;
&lt;br /&gt;
* It seems super hard to do per-renderlayer material override, which is something maya is good at. You can create linked asset overrides, but it&amp;#039;s very cumbersome.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; no solution yet &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* How do you add variables to render items, like the scene number. It seems like a lot of work to increment everything sometimes. Just like &amp;lt;Scene&amp;gt;/&amp;lt;Layer&amp;gt; in maya or $HIPNAME in H.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; probably python, need to answer that one &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* It seems like Layers are really neat to pack versions of a single scene (?) but it also seems you can have multiple render layers setup per layer setup. So how do you know which one is the &amp;#039;official&amp;#039; one when you open up a scene ? Seems confusing.&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
Will move this page when it gets big.&lt;br /&gt;
=== Set Crypto Matte Ids using Python===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#here i&amp;#039;m setting the matte_id property of a crypto node, traversing all materials in the scene that have &amp;quot;bordure&amp;quot; in their name&lt;br /&gt;
import bpy&lt;br /&gt;
obj = bpy.context.object&lt;br /&gt;
b = bpy.data.scenes[&amp;quot;Scene&amp;quot;].node_tree.nodes[&amp;quot;bordure&amp;quot;]&lt;br /&gt;
materials = [m.name for m in bpy.data.materials if &amp;quot;bordure&amp;quot; in m.name]&lt;br /&gt;
setattr(b, &amp;quot;matte_id&amp;quot;, &amp;#039;,&amp;#039;.join(materials))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Add-ons ==&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s been 5 seconds and I&amp;#039;m already adding add-ons :0. Will sort as I go along and use them&lt;br /&gt;
&lt;br /&gt;
* Loom, to help with render management: https://github.com/p2or/blender-loom&lt;br /&gt;
* Polyhaven (30$) all of the assets (which are free) integrated into a nice UI https://polyhaven.com/plugins/blender&lt;br /&gt;
* Bsurfaces (tutorial: https://www.youtube.com/watch?v=X2GNyEUvpD4 ), to draw topology (add &amp;#039;F2&amp;#039; and &amp;#039;loop tools&amp;#039; too)&lt;br /&gt;
* Blender to AfterEffects to export cameras and nulls, thanks to https://les-fees-speciales.coop/ : https://extensions.blender.org/add-ons/io-export-after-effects/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_Tutorials&amp;diff=898</id>
		<title>Blender Tutorials</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_Tutorials&amp;diff=898"/>
		<updated>2026-03-06T11:37:42Z</updated>

		<summary type="html">&lt;p&gt;Bernie: Created page with &amp;quot;== Geometry Nodes == === Delaunay Triangulation === &amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/HyW54cI8Qdo&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;  https://www.youtube.com/watch?v=HyW54cI8Qdo&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Geometry Nodes ==&lt;br /&gt;
=== Delaunay Triangulation ===&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;iframe width=&amp;quot;640&amp;quot; height=&amp;quot;360&amp;quot; src=&amp;quot;https://www.youtube.com/embed/HyW54cI8Qdo&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://www.youtube.com/watch?v=HyW54cI8Qdo&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Index.php&amp;diff=897</id>
		<title>Index.php</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Index.php&amp;diff=897"/>
		<updated>2026-03-05T18:21:06Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If you&amp;#039;re seeing this page, it&amp;#039;s probably because you were looking at an old link. And because I upgraded my mediawiki and I suck at clean redirects!&lt;br /&gt;
&lt;br /&gt;
Fortunately you should be able to go to the [[Main Page|Main Page]] to find the article in question (or use the search box above). &lt;br /&gt;
&lt;br /&gt;
Sorry about that, I&amp;#039;ll do anything to lose the A.I bots :)&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Software&amp;diff=896</id>
		<title>Software</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Software&amp;diff=896"/>
		<updated>2026-02-23T09:40:38Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Small things */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is the stuff I use to help me in my daily work:&lt;br /&gt;
&lt;br /&gt;
==== Everything ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/XGbrLdZ.png&lt;br /&gt;
&lt;br /&gt;
https://www.voidtools.com/&lt;br /&gt;
&lt;br /&gt;
Replaces Windows&amp;#039; absolutely crappy© search tool which is just fucking annoying. How did they break it so badly since Win7.&lt;br /&gt;
Anyways, it is:&lt;br /&gt;
&lt;br /&gt;
* Blazing fast&lt;br /&gt;
* Works with wildcards like you expect it to&lt;br /&gt;
* Can use remote folder drives (and only index at low frequency as though not to spam your servers), it will need admin rights.&lt;br /&gt;
&lt;br /&gt;
The only gotcha I&amp;#039;ve found so far is that if you give it a _really_ big network drive (like alot of folder and TBytes), it crashes/freezes. Anyways it&amp;#039;s not recommended to do so.&lt;br /&gt;
&lt;br /&gt;
==== Bulk Rename Utility ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZICbZ3D.png&lt;br /&gt;
&lt;br /&gt;
https://www.bulkrenameutility.co.uk/&lt;br /&gt;
&lt;br /&gt;
Aka the Space Shuttle Controls renaming tool. The interface looks like a 90s badly designed UI, I love it. It does everything you would want out of a renaming tool, because we often screw up padding, naming, you name it.&lt;br /&gt;
&lt;br /&gt;
==== ffmpeg ====&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;iframe width=&amp;quot;560&amp;quot; height=&amp;quot;315&amp;quot; src=&amp;quot;https://www.youtube.com/embed/9kaIXkImCAM&amp;quot; title=&amp;quot;YouTube video player&amp;quot; frameborder=&amp;quot;0&amp;quot; allow=&amp;quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&amp;quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Seriously though, it is a real life saver.&lt;br /&gt;
&lt;br /&gt;
I have added right-click contexts in windows: https://berniebernie.fr/wiki/Windows_batch#Parse_folder_with_image_sequence(s)_to_mp4_videos_with_ffmpeg_(tested_on_windows) to automate the creation of MP4s from file sequences or vice-versa&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== screentogif ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/DRwPjp5.png&lt;br /&gt;
&lt;br /&gt;
https://www.screentogif.com/&lt;br /&gt;
&lt;br /&gt;
Fast and simple tool to create animated gifs, nice for videos. I tried Gyazo back in the day but this feels more complete. GIFski can be used for smooth Gifs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Small things ====&lt;br /&gt;
* https://github.com/valinet/ExplorerPatcher: Make Windows 10/11 Great Again&lt;br /&gt;
&lt;br /&gt;
==== Web-based Free Tools ====&lt;br /&gt;
&lt;br /&gt;
* Photoshop Clone: https://www.photopea.com/&lt;br /&gt;
* NLE Edtior/AFX Tool: https://pikimov.com/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=AI&amp;diff=895</id>
		<title>AI</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=AI&amp;diff=895"/>
		<updated>2026-01-13T13:16:42Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Python ===&lt;br /&gt;
3.10&lt;br /&gt;
&lt;br /&gt;
Use conda for virtual environments: https://www.reddit.com/r/StableDiffusion/comments/15z2krk/installing_stable_diffusion_within_anaconda/ for example to use the hdri maker: https://github.com/DiffusionLight/DiffusionLight&lt;br /&gt;
&lt;br /&gt;
=== UIs ===&lt;br /&gt;
* automatic1111&lt;br /&gt;
* chaiNNEr&lt;br /&gt;
** https://www.youtube.com/watch?v=-8N2dXuRnK4 (upscale à la magnific)&lt;br /&gt;
** https://www.youtube.com/watch?v=jwJFNwkccE4 (upscale à la magnific)&lt;br /&gt;
* comfyui&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
* https://openmodeldb.info/&lt;br /&gt;
&lt;br /&gt;
=== Houdini ===&lt;br /&gt;
&lt;br /&gt;
* Using ONNX: https://www.youtube.com/watch?v=aCAatiY53s8&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Hunyuan3d ===&lt;br /&gt;
&lt;br /&gt;
https://github.com/visualbruno/ComfyUI-Hunyuan3d-2-1&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=894</id>
		<title>Blender 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=894"/>
		<updated>2025-12-22T14:58:26Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Resources */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I&amp;#039;m slowly catching up and trying to learn blender so I&amp;#039;m just writing down a few things that frustrate me as an old maya/houdini user, and answer it as i go.&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
&lt;br /&gt;
* Cool cards/non-video tutorials. Would be great to have them in a normal scrollable page! https://www.3dbestie.com/&lt;br /&gt;
* In the same spirit, a Github readme page for the standard shortcuts w/ gifs: https://hollisbrown.github.io/blendershortcuts/&lt;br /&gt;
* French Studio Les Fées Speciales&amp;#039; blender tech blog: https://lacuisine.tech/&lt;br /&gt;
* Maya to blender in 30 minutes: https://www.youtube.com/watch?v=0C0njghXPAI&lt;br /&gt;
&lt;br /&gt;
=== Some shortcuts ===&lt;br /&gt;
Will sort! Trying to be a GoodBoy and learning the blender standard. God it&amp;#039;s hard not to hit the Alt key for navigation and the marking menus are so baked in muscle memory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;function&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;key&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;equivalent to&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| Add objects&lt;br /&gt;
| shift-a&lt;br /&gt;
| Tab-drop node (houdini)&lt;br /&gt;
|-&lt;br /&gt;
| Frame/focus object&lt;br /&gt;
| . (numpad)&lt;br /&gt;
| f (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Make active camera&lt;br /&gt;
| ctrl-num0 (?)&lt;br /&gt;
| mmb drag and drop cam from outliner to VP (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Toggle object/faces edges/vertices&lt;br /&gt;
| tab 1 2 3&lt;br /&gt;
| Maya F8 F9 etc...&lt;br /&gt;
|-&lt;br /&gt;
| Search for function&lt;br /&gt;
| F3&lt;br /&gt;
| Maya&amp;#039;s Find Menu but actually looks nice&lt;br /&gt;
|-&lt;br /&gt;
| Change sudbdiv&lt;br /&gt;
| ctrl+0,1,2...(top row keyboard digits)&lt;br /&gt;
| Maya&amp;#039;s numpad 1 2 3&lt;br /&gt;
|-&lt;br /&gt;
| Select Linked&lt;br /&gt;
| ctrl-L&lt;br /&gt;
| double click in maya to select shell&lt;br /&gt;
|-&lt;br /&gt;
| Select Edge Loops/Rings&lt;br /&gt;
| alt-LMB, ctrl-alt-LMB&lt;br /&gt;
| double click in maya to select loops, marking menus&lt;br /&gt;
|-&lt;br /&gt;
| Select More/Less&lt;br /&gt;
| ctrl-Num+&lt;br /&gt;
| Maya&amp;#039;s grow/shrink selection &amp;lt;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Proportional Editing&lt;br /&gt;
| o / scrollwheel (while using it)&lt;br /&gt;
| Soft selection in maya (B/shift MMB)&lt;br /&gt;
|-&lt;br /&gt;
| Isolate Selection&lt;br /&gt;
| Numpad-/ or Alt/Shift-H&lt;br /&gt;
| Shift-i in Maya&lt;br /&gt;
|-&lt;br /&gt;
| Add Edge Loop&lt;br /&gt;
| Ctrl-R + scrollwheel &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Wire/shaded&lt;br /&gt;
| Shift-Z&lt;br /&gt;
| 1 2 3 in Maya&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Some Blender Concepts ===&lt;br /&gt;
&lt;br /&gt;
==== Collections ====&lt;br /&gt;
&lt;br /&gt;
Is &amp;#039;&amp;#039;kind&amp;#039;&amp;#039; of like maya&amp;#039;s set+renderlayer on steroids. You can have an item in several collections (ctrl drag).&lt;br /&gt;
&lt;br /&gt;
==== Groups ====&lt;br /&gt;
&lt;br /&gt;
No such thing, but use a null object&lt;br /&gt;
&lt;br /&gt;
==== Asset/References  ====&lt;br /&gt;
&lt;br /&gt;
So this one is super weird. When you import an asset, or link it (like a reference in maya or .hda in Hou), you&amp;#039;re note linking a .blend file but a subset of the file, could be a mesh/object/material. Kind of tedious TBH, but the blenderheads seem fine with it. It helps if you work with collections, so you just append/link a specific collection.&lt;br /&gt;
&lt;br /&gt;
==== Help &amp;amp; Community ====&lt;br /&gt;
&lt;br /&gt;
Amazing how many people have created tutorials. Make sure to search for your version of Blender as I feel it&amp;#039;s a &amp;#039;move fast, break things&amp;#039; software so shortcuts and things might work differently, unlike maya (you can still safely import files from maya 5.0).&lt;br /&gt;
&lt;br /&gt;
Pretty cool that people can vote for what they wish to see: https://blender.community/c/rightclickselect/?sorting=hot (not in the community enough to know if it has some say into newer versions though).&lt;br /&gt;
&lt;br /&gt;
https://docs.blender.org/manual/en/latest/ kind of terse, but works well. Big up to most software packages that take care of their documentations. (&amp;lt;3 Houdini)&lt;br /&gt;
&lt;br /&gt;
== Specific Tips ==&lt;br /&gt;
&lt;br /&gt;
to be sorted once I get a few&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where can I edit the pivot point, what&amp;#039;s with the weird cursor that looks like a lifebuoy ? ===&lt;br /&gt;
&lt;br /&gt;
The cursor which is a bit jarring is actually quite useful. &amp;#039;&amp;#039;Shift + right click&amp;#039;&amp;#039; to move it around, &amp;#039;&amp;#039;Shift + s&amp;#039;&amp;#039; to send it places (like the origin)&lt;br /&gt;
&lt;br /&gt;
For pivots, top of the viewport, shortcut is &amp;#039;.&amp;#039; dot but apparently not on a french keyboard. Still don&amp;#039;t know what the first box does, I&amp;#039;ll remove this line once I know ;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7dXmNgq.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
* Material Override per render layer https://blender.stackexchange.com/questions/154321/material-overrides-for-view-layers-per-object&lt;br /&gt;
* /!\ &amp;#039;&amp;#039;&amp;#039;Alpha in render&amp;#039;&amp;#039;&amp;#039; needs to be toggled on in &amp;#039;Film&amp;gt;Transparent&amp;#039;. Why isn&amp;#039;t this on by default ???&lt;br /&gt;
* You can&amp;#039;t use maya&amp;#039;s &amp;lt;code&amp;gt;&amp;lt;scene&amp;gt;/%s&amp;lt;/code&amp;gt; or houdini&amp;#039;s &amp;lt;code&amp;gt;$HIPNAME&amp;lt;/code&amp;gt; or write custom python directy in a string field. See the bottom for the Loom extension.&lt;br /&gt;
* To render multilayer EXRs w/o having to connect layer&amp;gt;outputs by hand &amp;#039;&amp;#039;&amp;#039;untoggle&amp;#039;&amp;#039;&amp;#039; use nodes in compositor.&lt;br /&gt;
&lt;br /&gt;
=== Modeling ===&lt;br /&gt;
* Bake transforms: Ctrl-A (also work when over modifiers to bake them&lt;br /&gt;
* Soft selection (&amp;#039;Proportional Editing&amp;#039;) has some nice options, but its UI kinda sucks compared to maya. Most of the time the radius of your brush will be outside the viewport window, and you have to scroll the mousewheel to change radius. In maya, you get the big yellowy-orangy-red-black mesh that shows the falloff. Also I &amp;#039;&amp;#039;think&amp;#039;&amp;#039; blender doesn&amp;#039;t have a &amp;#039;surface&amp;#039; vs volume falloff.&lt;br /&gt;
==== Snapping ====&lt;br /&gt;
* Ctrl-Shift-Tab &amp;gt; Include Active&lt;br /&gt;
&lt;br /&gt;
==== Extract a Curve from an edgeloop ====&lt;br /&gt;
* Select your edge loop, duplicate(Shift-D)&lt;br /&gt;
* Extract using P (separate)&lt;br /&gt;
* Go back to object mode, RMB convert mesh to curve. Done&lt;br /&gt;
&lt;br /&gt;
==== UVs ====&lt;br /&gt;
* You can mark seams in the 3d view for the unwrap (&amp;#039;U&amp;#039;)&lt;br /&gt;
* Faces/uvs selected in UV editor don&amp;#039;t show up by defualt 3D view , sync selection must be turned on:&lt;br /&gt;
https://i.imgur.com/H4M3jci.png&lt;br /&gt;
&lt;br /&gt;
==== Cool Tips ====&lt;br /&gt;
* Spherize: (alt shift s)&lt;br /&gt;
&lt;br /&gt;
=== Rigging/Skinning ===&lt;br /&gt;
&lt;br /&gt;
* To apply armature: select objects, select armature, parent (Ctrl P), should create a modifier&lt;br /&gt;
* To access weight painting, it&amp;#039;s kinda weird. Select bone that you want to move in Pose mode, select mesh in object mode somehow (doesn&amp;#039;t work with shift here), access weight paint mode (Ctrl Tab wheel)&lt;br /&gt;
* To reset bones when moving around when skinning alt+g,r,s&lt;br /&gt;
* Methods for smoothing skinning: https://blender.stackexchange.com/questions/58913/simple-methods-to-smooth-out-weight-paints (editing brush in tool tab works too)&lt;br /&gt;
* Mirror skin weights: https://www.youtube.com/watch?v=Ha_YU5xJsSc in any situation. Sometimes when painting mirrored weights, you&amp;#039;ll need to be on the PosX side to work.&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
==== Playblasts ====&lt;br /&gt;
There&amp;#039;s no way (?) of creating a simple playblast. It seems you have to use the OpenGL render. Which means going into the render options (workbench), you lose your background viewport image etc... Seems like a big oversight.&lt;br /&gt;
&lt;br /&gt;
==== Adding expressions ====&lt;br /&gt;
Using drivers! Try it out, it&amp;#039;s fairly self-explanatory.&lt;br /&gt;
Quicktip for rotating an asset:&lt;br /&gt;
* Write &amp;#039;#frame&amp;#039; into rotate Y, edit and write &amp;#039;radians(frame)&amp;#039; frame number = angle.&lt;br /&gt;
==== How do I animate text ? ====&lt;br /&gt;
Apparently you can&amp;#039;t keyframe text easily. That sucks. However you can open up a python script, it seems you have to relaunch the script at every session.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# assumes you have a text object called &amp;#039;Text&amp;#039;&lt;br /&gt;
import bpy&lt;br /&gt;
&lt;br /&gt;
scene = bpy.context.scene&lt;br /&gt;
obj = scene.objects[&amp;#039;Text&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
def recalculate_text(scene):&lt;br /&gt;
    text = &amp;#039;--&amp;#039;&lt;br /&gt;
    cf = scene.frame_current&lt;br /&gt;
    if cf &amp;lt; 1800:&lt;br /&gt;
        text = &amp;#039; &amp;#039;&lt;br /&gt;
    elif cf &amp;lt; 1920:&lt;br /&gt;
        text = 8&lt;br /&gt;
    elif cf &amp;lt; 2030:&lt;br /&gt;
        text = 5&lt;br /&gt;
    elif cf &amp;lt; 2180:&lt;br /&gt;
        text = 1&lt;br /&gt;
    elif cf &amp;lt; 2300:&lt;br /&gt;
        text = 2&lt;br /&gt;
   &lt;br /&gt;
    obj.data.body = f&amp;#039;{text}&amp;#039;&lt;br /&gt;
&lt;br /&gt;
bpy.app.handlers.frame_change_pre.append(recalculate_text)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Will modify this if I find a more user friendly way of doing this.&lt;br /&gt;
&lt;br /&gt;
==== How do I switch cameras during my animation ====&lt;br /&gt;
&lt;br /&gt;
You can add [https://docs.blender.org/manual/en/latest/animation/markers.html markers] (M in the timeline) and then bind the current camera (Ctrl B in the timeline too)&lt;br /&gt;
&lt;br /&gt;
=== UI ===&lt;br /&gt;
&lt;br /&gt;
==== How do I lock camera to view ====&lt;br /&gt;
&lt;br /&gt;
This is kinda hidden amirite ? Shortcut to twirl/untwirl that little menu is &amp;#039;n&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1PW3NEt.gif&lt;br /&gt;
&lt;br /&gt;
==== Adding a slider in the viewport ====&lt;br /&gt;
&lt;br /&gt;
There are so many contexts that I understand there&amp;#039;s no easy universal way to add a floating UI over everything to drive a rig or anything. For rigging you can use custom drivers on bones and it&amp;#039;s show up while animating.&lt;br /&gt;
&lt;br /&gt;
=== Hair ===&lt;br /&gt;
&lt;br /&gt;
Right-click and object, Curve&amp;gt;Fur. Check modifiers. Missing attributes (like length) can be added from the Asset Browser (Shift-F1).&lt;br /&gt;
&lt;br /&gt;
=== Other random notes, frustrations that I need to solve ===&lt;br /&gt;
&lt;br /&gt;
* The outliner forces sorting by alphabetical name ? Not by creation order.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039;Uncheck the &amp;#039;Alphabetical&amp;#039; option in the Filter menu at the top, objects will be sorted by creation order&amp;#039;&amp;#039; Custom ordering [https://projects.blender.org/blender/blender/issues/68502  can&amp;#039;t be done though for now].&lt;br /&gt;
&lt;br /&gt;
* It seems super hard to do per-renderlayer material override, which is something maya is good at. You can create linked asset overrides, but it&amp;#039;s very cumbersome.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; no solution yet &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* How do you add variables to render items, like the scene number. It seems like a lot of work to increment everything sometimes. Just like &amp;lt;Scene&amp;gt;/&amp;lt;Layer&amp;gt; in maya or $HIPNAME in H.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; probably python, need to answer that one &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* It seems like Layers are really neat to pack versions of a single scene (?) but it also seems you can have multiple render layers setup per layer setup. So how do you know which one is the &amp;#039;official&amp;#039; one when you open up a scene ? Seems confusing.&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
Will move this page when it gets big.&lt;br /&gt;
=== Set Crypto Matte Ids using Python===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#here i&amp;#039;m setting the matte_id property of a crypto node, traversing all materials in the scene that have &amp;quot;bordure&amp;quot; in their name&lt;br /&gt;
import bpy&lt;br /&gt;
obj = bpy.context.object&lt;br /&gt;
b = bpy.data.scenes[&amp;quot;Scene&amp;quot;].node_tree.nodes[&amp;quot;bordure&amp;quot;]&lt;br /&gt;
materials = [m.name for m in bpy.data.materials if &amp;quot;bordure&amp;quot; in m.name]&lt;br /&gt;
setattr(b, &amp;quot;matte_id&amp;quot;, &amp;#039;,&amp;#039;.join(materials))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Add-ons ==&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s been 5 seconds and I&amp;#039;m already adding add-ons :0. Will sort as I go along and use them&lt;br /&gt;
&lt;br /&gt;
* Loom, to help with render management: https://github.com/p2or/blender-loom&lt;br /&gt;
* Polyhaven (30$) all of the assets (which are free) integrated into a nice UI https://polyhaven.com/plugins/blender&lt;br /&gt;
* Bsurfaces (tutorial: https://www.youtube.com/watch?v=X2GNyEUvpD4 ), to draw topology (add &amp;#039;F2&amp;#039; and &amp;#039;loop tools&amp;#039; too)&lt;br /&gt;
* Blender to AfterEffects to export cameras and nulls, thanks to https://les-fees-speciales.coop/ : https://extensions.blender.org/add-ons/io-export-after-effects/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=893</id>
		<title>Blender 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Blender_101&amp;diff=893"/>
		<updated>2025-12-22T14:57:25Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Rendering */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I&amp;#039;m slowly catching up and trying to learn blender so I&amp;#039;m just writing down a few things that frustrate me as an old maya/houdini user, and answer it as i go.&lt;br /&gt;
&lt;br /&gt;
=== Resources ===&lt;br /&gt;
&lt;br /&gt;
* Cool cards/non-video tutorials. Would be great to have them in a normal scrollable page! https://www.3dbestie.com/&lt;br /&gt;
* French Studio Les Fées Speciales&amp;#039; blender tech blog: https://lacuisine.tech/&lt;br /&gt;
* Maya to blender in 30 minutes: https://www.youtube.com/watch?v=0C0njghXPAI&lt;br /&gt;
&lt;br /&gt;
=== Some shortcuts ===&lt;br /&gt;
Will sort! Trying to be a GoodBoy and learning the blender standard. God it&amp;#039;s hard not to hit the Alt key for navigation and the marking menus are so baked in muscle memory.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| &amp;#039;&amp;#039;function&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;key&amp;#039;&amp;#039;&lt;br /&gt;
| &amp;#039;&amp;#039;equivalent to&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| Add objects&lt;br /&gt;
| shift-a&lt;br /&gt;
| Tab-drop node (houdini)&lt;br /&gt;
|-&lt;br /&gt;
| Frame/focus object&lt;br /&gt;
| . (numpad)&lt;br /&gt;
| f (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Make active camera&lt;br /&gt;
| ctrl-num0 (?)&lt;br /&gt;
| mmb drag and drop cam from outliner to VP (maya)&lt;br /&gt;
|-&lt;br /&gt;
| Toggle object/faces edges/vertices&lt;br /&gt;
| tab 1 2 3&lt;br /&gt;
| Maya F8 F9 etc...&lt;br /&gt;
|-&lt;br /&gt;
| Search for function&lt;br /&gt;
| F3&lt;br /&gt;
| Maya&amp;#039;s Find Menu but actually looks nice&lt;br /&gt;
|-&lt;br /&gt;
| Change sudbdiv&lt;br /&gt;
| ctrl+0,1,2...(top row keyboard digits)&lt;br /&gt;
| Maya&amp;#039;s numpad 1 2 3&lt;br /&gt;
|-&lt;br /&gt;
| Select Linked&lt;br /&gt;
| ctrl-L&lt;br /&gt;
| double click in maya to select shell&lt;br /&gt;
|-&lt;br /&gt;
| Select Edge Loops/Rings&lt;br /&gt;
| alt-LMB, ctrl-alt-LMB&lt;br /&gt;
| double click in maya to select loops, marking menus&lt;br /&gt;
|-&lt;br /&gt;
| Select More/Less&lt;br /&gt;
| ctrl-Num+&lt;br /&gt;
| Maya&amp;#039;s grow/shrink selection &amp;lt;/&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Proportional Editing&lt;br /&gt;
| o / scrollwheel (while using it)&lt;br /&gt;
| Soft selection in maya (B/shift MMB)&lt;br /&gt;
|-&lt;br /&gt;
| Isolate Selection&lt;br /&gt;
| Numpad-/ or Alt/Shift-H&lt;br /&gt;
| Shift-i in Maya&lt;br /&gt;
|-&lt;br /&gt;
| Add Edge Loop&lt;br /&gt;
| Ctrl-R + scrollwheel &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Wire/shaded&lt;br /&gt;
| Shift-Z&lt;br /&gt;
| 1 2 3 in Maya&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Some Blender Concepts ===&lt;br /&gt;
&lt;br /&gt;
==== Collections ====&lt;br /&gt;
&lt;br /&gt;
Is &amp;#039;&amp;#039;kind&amp;#039;&amp;#039; of like maya&amp;#039;s set+renderlayer on steroids. You can have an item in several collections (ctrl drag).&lt;br /&gt;
&lt;br /&gt;
==== Groups ====&lt;br /&gt;
&lt;br /&gt;
No such thing, but use a null object&lt;br /&gt;
&lt;br /&gt;
==== Asset/References  ====&lt;br /&gt;
&lt;br /&gt;
So this one is super weird. When you import an asset, or link it (like a reference in maya or .hda in Hou), you&amp;#039;re note linking a .blend file but a subset of the file, could be a mesh/object/material. Kind of tedious TBH, but the blenderheads seem fine with it. It helps if you work with collections, so you just append/link a specific collection.&lt;br /&gt;
&lt;br /&gt;
==== Help &amp;amp; Community ====&lt;br /&gt;
&lt;br /&gt;
Amazing how many people have created tutorials. Make sure to search for your version of Blender as I feel it&amp;#039;s a &amp;#039;move fast, break things&amp;#039; software so shortcuts and things might work differently, unlike maya (you can still safely import files from maya 5.0).&lt;br /&gt;
&lt;br /&gt;
Pretty cool that people can vote for what they wish to see: https://blender.community/c/rightclickselect/?sorting=hot (not in the community enough to know if it has some say into newer versions though).&lt;br /&gt;
&lt;br /&gt;
https://docs.blender.org/manual/en/latest/ kind of terse, but works well. Big up to most software packages that take care of their documentations. (&amp;lt;3 Houdini)&lt;br /&gt;
&lt;br /&gt;
== Specific Tips ==&lt;br /&gt;
&lt;br /&gt;
to be sorted once I get a few&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Where can I edit the pivot point, what&amp;#039;s with the weird cursor that looks like a lifebuoy ? ===&lt;br /&gt;
&lt;br /&gt;
The cursor which is a bit jarring is actually quite useful. &amp;#039;&amp;#039;Shift + right click&amp;#039;&amp;#039; to move it around, &amp;#039;&amp;#039;Shift + s&amp;#039;&amp;#039; to send it places (like the origin)&lt;br /&gt;
&lt;br /&gt;
For pivots, top of the viewport, shortcut is &amp;#039;.&amp;#039; dot but apparently not on a french keyboard. Still don&amp;#039;t know what the first box does, I&amp;#039;ll remove this line once I know ;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7dXmNgq.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Rendering ===&lt;br /&gt;
* Material Override per render layer https://blender.stackexchange.com/questions/154321/material-overrides-for-view-layers-per-object&lt;br /&gt;
* /!\ &amp;#039;&amp;#039;&amp;#039;Alpha in render&amp;#039;&amp;#039;&amp;#039; needs to be toggled on in &amp;#039;Film&amp;gt;Transparent&amp;#039;. Why isn&amp;#039;t this on by default ???&lt;br /&gt;
* You can&amp;#039;t use maya&amp;#039;s &amp;lt;code&amp;gt;&amp;lt;scene&amp;gt;/%s&amp;lt;/code&amp;gt; or houdini&amp;#039;s &amp;lt;code&amp;gt;$HIPNAME&amp;lt;/code&amp;gt; or write custom python directy in a string field. See the bottom for the Loom extension.&lt;br /&gt;
* To render multilayer EXRs w/o having to connect layer&amp;gt;outputs by hand &amp;#039;&amp;#039;&amp;#039;untoggle&amp;#039;&amp;#039;&amp;#039; use nodes in compositor.&lt;br /&gt;
&lt;br /&gt;
=== Modeling ===&lt;br /&gt;
* Bake transforms: Ctrl-A (also work when over modifiers to bake them&lt;br /&gt;
* Soft selection (&amp;#039;Proportional Editing&amp;#039;) has some nice options, but its UI kinda sucks compared to maya. Most of the time the radius of your brush will be outside the viewport window, and you have to scroll the mousewheel to change radius. In maya, you get the big yellowy-orangy-red-black mesh that shows the falloff. Also I &amp;#039;&amp;#039;think&amp;#039;&amp;#039; blender doesn&amp;#039;t have a &amp;#039;surface&amp;#039; vs volume falloff.&lt;br /&gt;
==== Snapping ====&lt;br /&gt;
* Ctrl-Shift-Tab &amp;gt; Include Active&lt;br /&gt;
&lt;br /&gt;
==== Extract a Curve from an edgeloop ====&lt;br /&gt;
* Select your edge loop, duplicate(Shift-D)&lt;br /&gt;
* Extract using P (separate)&lt;br /&gt;
* Go back to object mode, RMB convert mesh to curve. Done&lt;br /&gt;
&lt;br /&gt;
==== UVs ====&lt;br /&gt;
* You can mark seams in the 3d view for the unwrap (&amp;#039;U&amp;#039;)&lt;br /&gt;
* Faces/uvs selected in UV editor don&amp;#039;t show up by defualt 3D view , sync selection must be turned on:&lt;br /&gt;
https://i.imgur.com/H4M3jci.png&lt;br /&gt;
&lt;br /&gt;
==== Cool Tips ====&lt;br /&gt;
* Spherize: (alt shift s)&lt;br /&gt;
&lt;br /&gt;
=== Rigging/Skinning ===&lt;br /&gt;
&lt;br /&gt;
* To apply armature: select objects, select armature, parent (Ctrl P), should create a modifier&lt;br /&gt;
* To access weight painting, it&amp;#039;s kinda weird. Select bone that you want to move in Pose mode, select mesh in object mode somehow (doesn&amp;#039;t work with shift here), access weight paint mode (Ctrl Tab wheel)&lt;br /&gt;
* To reset bones when moving around when skinning alt+g,r,s&lt;br /&gt;
* Methods for smoothing skinning: https://blender.stackexchange.com/questions/58913/simple-methods-to-smooth-out-weight-paints (editing brush in tool tab works too)&lt;br /&gt;
* Mirror skin weights: https://www.youtube.com/watch?v=Ha_YU5xJsSc in any situation. Sometimes when painting mirrored weights, you&amp;#039;ll need to be on the PosX side to work.&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
==== Playblasts ====&lt;br /&gt;
There&amp;#039;s no way (?) of creating a simple playblast. It seems you have to use the OpenGL render. Which means going into the render options (workbench), you lose your background viewport image etc... Seems like a big oversight.&lt;br /&gt;
&lt;br /&gt;
==== Adding expressions ====&lt;br /&gt;
Using drivers! Try it out, it&amp;#039;s fairly self-explanatory.&lt;br /&gt;
Quicktip for rotating an asset:&lt;br /&gt;
* Write &amp;#039;#frame&amp;#039; into rotate Y, edit and write &amp;#039;radians(frame)&amp;#039; frame number = angle.&lt;br /&gt;
==== How do I animate text ? ====&lt;br /&gt;
Apparently you can&amp;#039;t keyframe text easily. That sucks. However you can open up a python script, it seems you have to relaunch the script at every session.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# assumes you have a text object called &amp;#039;Text&amp;#039;&lt;br /&gt;
import bpy&lt;br /&gt;
&lt;br /&gt;
scene = bpy.context.scene&lt;br /&gt;
obj = scene.objects[&amp;#039;Text&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
def recalculate_text(scene):&lt;br /&gt;
    text = &amp;#039;--&amp;#039;&lt;br /&gt;
    cf = scene.frame_current&lt;br /&gt;
    if cf &amp;lt; 1800:&lt;br /&gt;
        text = &amp;#039; &amp;#039;&lt;br /&gt;
    elif cf &amp;lt; 1920:&lt;br /&gt;
        text = 8&lt;br /&gt;
    elif cf &amp;lt; 2030:&lt;br /&gt;
        text = 5&lt;br /&gt;
    elif cf &amp;lt; 2180:&lt;br /&gt;
        text = 1&lt;br /&gt;
    elif cf &amp;lt; 2300:&lt;br /&gt;
        text = 2&lt;br /&gt;
   &lt;br /&gt;
    obj.data.body = f&amp;#039;{text}&amp;#039;&lt;br /&gt;
&lt;br /&gt;
bpy.app.handlers.frame_change_pre.append(recalculate_text)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Will modify this if I find a more user friendly way of doing this.&lt;br /&gt;
&lt;br /&gt;
==== How do I switch cameras during my animation ====&lt;br /&gt;
&lt;br /&gt;
You can add [https://docs.blender.org/manual/en/latest/animation/markers.html markers] (M in the timeline) and then bind the current camera (Ctrl B in the timeline too)&lt;br /&gt;
&lt;br /&gt;
=== UI ===&lt;br /&gt;
&lt;br /&gt;
==== How do I lock camera to view ====&lt;br /&gt;
&lt;br /&gt;
This is kinda hidden amirite ? Shortcut to twirl/untwirl that little menu is &amp;#039;n&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1PW3NEt.gif&lt;br /&gt;
&lt;br /&gt;
==== Adding a slider in the viewport ====&lt;br /&gt;
&lt;br /&gt;
There are so many contexts that I understand there&amp;#039;s no easy universal way to add a floating UI over everything to drive a rig or anything. For rigging you can use custom drivers on bones and it&amp;#039;s show up while animating.&lt;br /&gt;
&lt;br /&gt;
=== Hair ===&lt;br /&gt;
&lt;br /&gt;
Right-click and object, Curve&amp;gt;Fur. Check modifiers. Missing attributes (like length) can be added from the Asset Browser (Shift-F1).&lt;br /&gt;
&lt;br /&gt;
=== Other random notes, frustrations that I need to solve ===&lt;br /&gt;
&lt;br /&gt;
* The outliner forces sorting by alphabetical name ? Not by creation order.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039;Uncheck the &amp;#039;Alphabetical&amp;#039; option in the Filter menu at the top, objects will be sorted by creation order&amp;#039;&amp;#039; Custom ordering [https://projects.blender.org/blender/blender/issues/68502  can&amp;#039;t be done though for now].&lt;br /&gt;
&lt;br /&gt;
* It seems super hard to do per-renderlayer material override, which is something maya is good at. You can create linked asset overrides, but it&amp;#039;s very cumbersome.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; no solution yet &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* How do you add variables to render items, like the scene number. It seems like a lot of work to increment everything sometimes. Just like &amp;lt;Scene&amp;gt;/&amp;lt;Layer&amp;gt; in maya or $HIPNAME in H.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; &amp;#039;&amp;#039; probably python, need to answer that one &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* It seems like Layers are really neat to pack versions of a single scene (?) but it also seems you can have multiple render layers setup per layer setup. So how do you know which one is the &amp;#039;official&amp;#039; one when you open up a scene ? Seems confusing.&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
Will move this page when it gets big.&lt;br /&gt;
=== Set Crypto Matte Ids using Python===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#here i&amp;#039;m setting the matte_id property of a crypto node, traversing all materials in the scene that have &amp;quot;bordure&amp;quot; in their name&lt;br /&gt;
import bpy&lt;br /&gt;
obj = bpy.context.object&lt;br /&gt;
b = bpy.data.scenes[&amp;quot;Scene&amp;quot;].node_tree.nodes[&amp;quot;bordure&amp;quot;]&lt;br /&gt;
materials = [m.name for m in bpy.data.materials if &amp;quot;bordure&amp;quot; in m.name]&lt;br /&gt;
setattr(b, &amp;quot;matte_id&amp;quot;, &amp;#039;,&amp;#039;.join(materials))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Add-ons ==&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s been 5 seconds and I&amp;#039;m already adding add-ons :0. Will sort as I go along and use them&lt;br /&gt;
&lt;br /&gt;
* Loom, to help with render management: https://github.com/p2or/blender-loom&lt;br /&gt;
* Polyhaven (30$) all of the assets (which are free) integrated into a nice UI https://polyhaven.com/plugins/blender&lt;br /&gt;
* Bsurfaces (tutorial: https://www.youtube.com/watch?v=X2GNyEUvpD4 ), to draw topology (add &amp;#039;F2&amp;#039; and &amp;#039;loop tools&amp;#039; too)&lt;br /&gt;
* Blender to AfterEffects to export cameras and nulls, thanks to https://les-fees-speciales.coop/ : https://extensions.blender.org/add-ons/io-export-after-effects/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=892</id>
		<title>Afx Javascript</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=892"/>
		<updated>2025-12-19T12:19:42Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Import pos from maya */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Scripts==&lt;br /&gt;
=== Copy footage to local folder as proxy===&lt;br /&gt;
https://i.imgur.com/oZ24pac.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
 * simpleLocalProxy.jsx v1.0&lt;br /&gt;
 *&lt;br /&gt;
 * Copies footage from its current location to one chosen by the user (defaults to /tmp/) and allows to&lt;br /&gt;
 * switch original-proxy with a button&lt;br /&gt;
 * 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&amp;#039; file cache on scratch disks (&amp;#039;conformed media&amp;#039;)&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 * https://github.com/berniebernie/after-effects-scripts&lt;br /&gt;
 *    &lt;br /&gt;
 * Copyright 2015, bernie@berniebernie.fr&lt;br /&gt;
 *    &lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT  &lt;br /&gt;
 *&lt;br /&gt;
 * This script embeds js-md5 from github: https://github.com/emn178/js-md5 for practical reasons&lt;br /&gt;
 *&lt;br /&gt;
 * Script that can be launched or put in the scriptui folder of After Effects to be used as a panel&lt;br /&gt;
 *  &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * js-md5 v0.1.2&lt;br /&gt;
 * https://github.com/emn178/js-md5&lt;br /&gt;
 *&lt;br /&gt;
 * Copyright 2014, emn178@gmail.com&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      js-md5.js&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//(function(root, undefined){&lt;br /&gt;
  //&amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  var HEX_CHARS = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
  var HEX_TABLE = {&lt;br /&gt;
    &amp;#039;0&amp;#039;: 0, &amp;#039;1&amp;#039;: 1, &amp;#039;2&amp;#039;: 2, &amp;#039;3&amp;#039;: 3, &amp;#039;4&amp;#039;: 4, &amp;#039;5&amp;#039;: 5, &amp;#039;6&amp;#039;: 6, &amp;#039;7&amp;#039;: 7, &amp;#039;8&amp;#039;: 8, &amp;#039;9&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;a&amp;#039;: 10, &amp;#039;b&amp;#039;: 11, &amp;#039;c&amp;#039;: 12, &amp;#039;d&amp;#039;: 13, &amp;#039;e&amp;#039;: 14, &amp;#039;f&amp;#039;: 15,&lt;br /&gt;
    &amp;#039;A&amp;#039;: 10, &amp;#039;B&amp;#039;: 11, &amp;#039;C&amp;#039;: 12, &amp;#039;D&amp;#039;: 13, &amp;#039;E&amp;#039;: 14, &amp;#039;F&amp;#039;: 15&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var R = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,&lt;br /&gt;
           5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,&lt;br /&gt;
           4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,&lt;br /&gt;
           6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];&lt;br /&gt;
&lt;br /&gt;
  var K = [0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE,&lt;br /&gt;
           0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,&lt;br /&gt;
           0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,&lt;br /&gt;
           0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821,&lt;br /&gt;
           0XF61E2562, 0XC040B340, 0X265E5A51, 0XE9B6C7AA,&lt;br /&gt;
           0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8,&lt;br /&gt;
           0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED,&lt;br /&gt;
           0XA9E3E905, 0XFCEFA3F8, 0X676F02D9, 0X8D2A4C8A,&lt;br /&gt;
           0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C,&lt;br /&gt;
           0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70,&lt;br /&gt;
           0X289B7EC6, 0XEAA127FA, 0XD4EF3085, 0X04881D05,&lt;br /&gt;
           0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665,&lt;br /&gt;
           0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039,&lt;br /&gt;
           0X655B59C3, 0X8F0CCC92, 0XFFEFF47D, 0X85845DD1,&lt;br /&gt;
           0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1,&lt;br /&gt;
           0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391];&lt;br /&gt;
&lt;br /&gt;
  var jsmd5 = function(message) {&lt;br /&gt;
    var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message);&lt;br /&gt;
    var h0 = 0x67452301;&lt;br /&gt;
    var h1 = 0xEFCDAB89;&lt;br /&gt;
    var h2 = 0x98BADCFE;&lt;br /&gt;
    var h3 = 0x10325476;&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0, length = blocks.length;i &amp;lt; length;i += 16)&lt;br /&gt;
    {&lt;br /&gt;
      var a = h0;&lt;br /&gt;
      var b = h1;&lt;br /&gt;
      var c = h2;&lt;br /&gt;
      var d = h3;&lt;br /&gt;
      var f, g, tmp, x, y;&lt;br /&gt;
&lt;br /&gt;
      for(var j = 0;j &amp;lt; 64;++j)&lt;br /&gt;
      {&lt;br /&gt;
        if(j &amp;lt; 16)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (b &amp;amp; c) | ((~b) &amp;amp; d);&lt;br /&gt;
          f = d ^ (b &amp;amp; (c ^ d));&lt;br /&gt;
          g = j;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 32)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (d &amp;amp; b) | ((~d) &amp;amp; c);&lt;br /&gt;
          f = c ^ (d &amp;amp; (b ^ c));&lt;br /&gt;
          g = (5 * j + 1) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 48)&lt;br /&gt;
        {&lt;br /&gt;
          f = b ^ c ^ d;&lt;br /&gt;
          g = (3 * j + 5) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          f = c ^ (b | (~d));&lt;br /&gt;
          g = (7 * j) % 16;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        tmp = d;&lt;br /&gt;
        d = c&lt;br /&gt;
        c = b&lt;br /&gt;
&lt;br /&gt;
        // leftrotate&lt;br /&gt;
        x = (a + f + K[j] + blocks[i + g]);&lt;br /&gt;
        y = R[j];&lt;br /&gt;
        b += (x &amp;lt;&amp;lt; y) | (x &amp;gt;&amp;gt;&amp;gt; (32 - y));&lt;br /&gt;
        a = tmp;&lt;br /&gt;
      }&lt;br /&gt;
      h0 = (h0 + a) | 0;&lt;br /&gt;
      h1 = (h1 + b) | 0;&lt;br /&gt;
      h2 = (h2 + c) | 0;&lt;br /&gt;
      h3 = (h3 + d) | 0;&lt;br /&gt;
    }&lt;br /&gt;
    return toHexString(h0) + toHexString(h1) + toHexString(h2) + toHexString(h3);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var toHexString = function(num) {&lt;br /&gt;
    var hex = &amp;quot;&amp;quot;;&lt;br /&gt;
    for(var i = 0; i &amp;lt; 4; i++)&lt;br /&gt;
    {&lt;br /&gt;
      var offset = i &amp;lt;&amp;lt; 3;&lt;br /&gt;
      hex += HEX_CHARS.charAt((num &amp;gt;&amp;gt; (offset + 4)) &amp;amp; 0x0F) + HEX_CHARS.charAt((num &amp;gt;&amp;gt; offset) &amp;amp; 0x0F);&lt;br /&gt;
    }&lt;br /&gt;
    return hex;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var hasUTF8 = function(message) {&lt;br /&gt;
    var i = message.length;&lt;br /&gt;
    while(i--)&lt;br /&gt;
      if(message.charCodeAt(i) &amp;gt; 127)&lt;br /&gt;
        return true;&lt;br /&gt;
    return false;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var ASCIItoBlocks = function(message) {&lt;br /&gt;
    // a block is 32 bits(4 bytes), a chunk is 512 bits(64 bytes)&lt;br /&gt;
    var length = message.length;&lt;br /&gt;
    var chunkCount = ((length + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    var i;&lt;br /&gt;
    for(i = 0;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    for(i = 0;i &amp;lt; length;++i)&lt;br /&gt;
      blocks[i &amp;gt;&amp;gt; 2] |= message.charCodeAt(i) &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[i &amp;gt;&amp;gt; 2] |= 0x80 &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[blockCount - 2] = length &amp;lt;&amp;lt; 3; // length * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var UTF8toBlocks = function(message) {&lt;br /&gt;
    var uri = encodeURIComponent(message);&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    for(var i = 0, bytes = 0, length = uri.length;i &amp;lt; length;++i)&lt;br /&gt;
    {&lt;br /&gt;
      var c = uri.charCodeAt(i);&lt;br /&gt;
      if(c == 37) // %&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= ((HEX_TABLE[uri.charAt(++i)] &amp;lt;&amp;lt; 4) | HEX_TABLE[uri.charAt(++i)]) &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      else&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= c &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      ++bytes;&lt;br /&gt;
    }&lt;br /&gt;
    var chunkCount = ((bytes + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var index = bytes &amp;gt;&amp;gt; 2;&lt;br /&gt;
    blocks[index] |= 0x80 &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    for(var i = index + 1;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    blocks[blockCount - 2] = bytes &amp;lt;&amp;lt; 3; // bytes * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  /*if(typeof(module) != &amp;#039;undefined&amp;#039;)&lt;br /&gt;
    module.exports = jsmd5;&lt;br /&gt;
  else if(root)&lt;br /&gt;
    root.jsmd5 = jsmd5;*/&lt;br /&gt;
//}(this));&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      simpleLocalProxy.jsx &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(str){&lt;br /&gt;
    //uncomment to allow debugging&lt;br /&gt;
    //$.writeln(str);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pathToLocalizedPath(path){&lt;br /&gt;
        f = new File(path);&lt;br /&gt;
        return f.fsName.toString();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sequenceFilesWildcard(path){ &lt;br /&gt;
    //returns false or an array with everything before a sequence&amp;#039;s image number, and the extension: /c/path/file.0555.exr &amp;gt; { /c/pathfile. ; .exr }&lt;br /&gt;
    var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g; &lt;br /&gt;
    var match = myRegexp.exec(path);&lt;br /&gt;
    if(!match){&lt;br /&gt;
        return false;&lt;br /&gt;
    }else{&lt;br /&gt;
        return match;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabPaths(footage){&lt;br /&gt;
    //returns false or the path of the given footage, if it&amp;#039;s a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr &amp;gt; /c/path/file.*.exr&lt;br /&gt;
    var f = footage;&lt;br /&gt;
    returnpath = false;&lt;br /&gt;
    if(f instanceof FootageItem &amp;amp;&amp;amp; f.file != null){       &lt;br /&gt;
        var source = f.mainSource.file.toString();&lt;br /&gt;
        if(!f.mainSource.isStill){&lt;br /&gt;
            var pathFromRegex = sequenceFilesWildcard(source);&lt;br /&gt;
            if(pathFromRegex){&lt;br /&gt;
                returnpath = pathFromRegex[1]+&amp;quot;*&amp;quot;+pathFromRegex[2];&lt;br /&gt;
            }else{&lt;br /&gt;
                returnpath = source;&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            returnpath = source;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return returnpath;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function grabFootagePathsAndCopyToLocal(){&lt;br /&gt;
    //main worker function&lt;br /&gt;
    &lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1 || !(sel[0] instanceof FootageItem)){&lt;br /&gt;
        alert(&amp;quot;Select footage(s) and try again&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        &lt;br /&gt;
        var debugcheck = (getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var useforcecopy = (getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        &lt;br /&gt;
        var isMacintosh = ($.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;)==-1);&lt;br /&gt;
        var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        &lt;br /&gt;
        var batchFile = (isMacintosh)?&amp;quot;# bash file used to copy After Effects footage to local storage&amp;quot;:&amp;quot;@echo off\nREM batch file to copy After Effects footage to local storage&amp;quot;;&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            if(!sel[i].useProxy){&lt;br /&gt;
                //grab path from current selection item&lt;br /&gt;
                var curPath = grabPaths(sel[i]);&lt;br /&gt;
                var fileName = curPath.split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                &lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
            &lt;br /&gt;
                if(isMacintosh){&lt;br /&gt;
                    //macos uses rsync to copy files&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrsync -v -a &amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot;-I &amp;quot;:&amp;quot;&amp;quot;);&lt;br /&gt;
                    batchFile += dir+fileName;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;+outputDir;&lt;br /&gt;
                    &lt;br /&gt;
                }else{&lt;br /&gt;
                    &lt;br /&gt;
                    //windows uses robocopy&lt;br /&gt;
                    sourcePath = pathToLocalizedPath(dir);&lt;br /&gt;
                    destinationPath = pathToLocalizedPath(outputDir);&lt;br /&gt;
                    filename = fileName.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrobocopy &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += sourcePath;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    //robocopy filename requires a wildcard, even if it&amp;#039;s a single file&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += destinationPath ;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;                    &lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += filename ;&lt;br /&gt;
                    batchFile += &amp;quot;*\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot; /XO&amp;quot;:&amp;quot;&amp;quot;)+&amp;quot; /FFT&amp;quot;+((debugcheck)?&amp;quot;&amp;quot;:&amp;quot; /NJH /NJS&amp;quot;);&lt;br /&gt;
                    e(batchFile);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        batchFile += ((debugcheck)?(isMacintosh?&amp;quot;\nread a&amp;quot;:&amp;quot;\npause&amp;quot;):&amp;quot;&amp;quot;); //nested tertiary operators, sue me&lt;br /&gt;
        &lt;br /&gt;
        var txtFile;&lt;br /&gt;
        if(isMacintosh){&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.command&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.bat&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        if(txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;) == true){&lt;br /&gt;
&lt;br /&gt;
            txtFile.write(batchFile);&lt;br /&gt;
            txtFile.close();&lt;br /&gt;
	    if(isMacintosh){&lt;br /&gt;
&lt;br /&gt;
            		system.callSystem(&amp;quot;chmod +x &amp;quot; + txtFile.toString() +&amp;quot;&amp;quot;);&lt;br /&gt;
system.callSystem(txtFile.toString());&lt;br /&gt;
		}else{&lt;br /&gt;
            txtFile.execute();&lt;br /&gt;
}&lt;br /&gt;
        }else{&lt;br /&gt;
            alert(&amp;quot;Write permission denied on\n&amp;quot;+localSaveDir);&lt;br /&gt;
        }&lt;br /&gt;
        //txtFile.remove();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabFootagePathsAndSwitchProxy(){&lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1){&lt;br /&gt;
        alert(&amp;quot;Select footage first&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            item = sel[i];&lt;br /&gt;
            if(item.useProxy){&lt;br /&gt;
                item.useProxy = false;&lt;br /&gt;
            }else{&lt;br /&gt;
                var curPath = grabPaths(item);&lt;br /&gt;
                var fileName = item.mainSource.file.toString().split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
                var proxyFilePath = outputDir+&amp;quot;/&amp;quot;+fileName;&lt;br /&gt;
                var proxyFile = new File(proxyFilePath);&lt;br /&gt;
                e(proxyFilePath);&lt;br /&gt;
                if(proxyFile.exists){&lt;br /&gt;
                    if(item.mainSource.isStill){&lt;br /&gt;
                        item.setProxy(proxyFile);&lt;br /&gt;
                    }else{&lt;br /&gt;
                        //dirty workaround if the file is a movie file (.mpg, qucktime etc...)&lt;br /&gt;
                        try {&lt;br /&gt;
                            item.setProxyWithSequence(proxyFile,false);&lt;br /&gt;
                        }&lt;br /&gt;
                        catch(err) {&lt;br /&gt;
                            item.setProxy(proxyFile);&lt;br /&gt;
                        }   &lt;br /&gt;
                    }&lt;br /&gt;
                    item.proxySource.alphaMode = item.mainSource.alphaMode;&lt;br /&gt;
                    item.proxySource.premulColor = item.mainSource.premulColor;&lt;br /&gt;
                    item.proxySource.invertAlpha= item.mainSource.invertAlpha;&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(getPref(&amp;quot;debug&amp;quot;,false)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                        alert(&amp;quot;&amp;gt;&amp;gt;&amp;gt; NO PROXY FOUND\n&amp;quot;+proxyFilePath);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPref(pref,value){&lt;br /&gt;
    app.settings.saveSetting(&amp;quot;simpleLocalProxy&amp;quot;, pref, value);&lt;br /&gt;
}&lt;br /&gt;
function getPref(pref,defaultValue){&lt;br /&gt;
    prefsVar = &amp;quot;simpleLocalProxy&amp;quot;;&lt;br /&gt;
    if(app.settings.haveSetting(prefsVar, pref)){&lt;br /&gt;
        return app.settings.getSetting(prefsVar, pref);&lt;br /&gt;
    }else{&lt;br /&gt;
        app.settings.saveSetting(prefsVar, pref, defaultValue);&lt;br /&gt;
        setPref(pref,defaultValue)&lt;br /&gt;
        return defaultValue;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function simpleLocalProxy(thisObj) {&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Local Proxy&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    &lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        var localFolder = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        // UI DESCRIPTION&lt;br /&gt;
        &lt;br /&gt;
        res = &amp;quot;group { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                        cols: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            col1: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                saveDirText: StaticText {text: &amp;#039;Proxy folder setup&amp;#039;},\&lt;br /&gt;
                                saveDirOptions: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    usemd5rbox: RadioButton {text: &amp;#039; Simple&amp;#039;,helpTip:&amp;#039;Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)&amp;#039;,value:true},\&lt;br /&gt;
                                    usepathrbox: RadioButton {text: &amp;#039; Copy Folder Structure&amp;#039;,helpTip:&amp;#039;Copies the target folder structure inside the proxy folder; more folders&amp;#039;}}},\&lt;br /&gt;
                            col2: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                optionsText: StaticText {text: &amp;#039;Options: &amp;#039;},\&lt;br /&gt;
                                optionsGrp: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    forcecopyChkbox: Checkbox {text: &amp;#039; Force copy&amp;#039;,helpTip:&amp;#039;Overwrite files (otherwise skips existing files)&amp;#039;},\&lt;br /&gt;
                                    debugChkbox: Checkbox {text: &amp;#039; Debug&amp;#039;,helpTip:&amp;#039;Show full batch process and pause at end of copies&amp;#039;}}}},\&lt;br /&gt;
                        cols2: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            localSaveDirBut: Button {text: &amp;#039; Choose Proxy Folder &amp;#039;, helpTip:&amp;#039;choose folder to copy files to&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                            browseBut: Button {text: &amp;#039; Browse &amp;#039; , preferredSize:[-1,30]}} , \&lt;br /&gt;
                        curentDirTxt: EditText {text: &amp;#039;&amp;quot; + localFolder +&amp;quot;&amp;#039;,enabled:false},\&lt;br /&gt;
                        copyFootageBut: Button {text: &amp;#039; Copy Footage(s) to Proxy Folder &amp;#039; ,helpTip:&amp;#039;Launches a background batch copy of selected footage&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                        switchproxyBut: Button {text: &amp;#039; Switch Proxy/Original &amp;#039;,helpTip:&amp;#039;Switches from original to proxy path and back,  warning if no local copy has been found&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        //UI DRAW&lt;br /&gt;
        &lt;br /&gt;
        pan.grp = pan.add(res); &lt;br /&gt;
        pan.layout.layout(true);&lt;br /&gt;
        &lt;br /&gt;
        //radio buttons and checkboxes prefs&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
        var usemd5 = getPref(&amp;quot;usemd5&amp;quot;,true);&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5==&amp;quot;true&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
        var useforcecopy = getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var debugcheck = getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
&lt;br /&gt;
        pan.layout.resize();&lt;br /&gt;
        pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
        // UI ACTIONS&lt;br /&gt;
        &lt;br /&gt;
        //browse button&lt;br /&gt;
        pan.grp.cols2.browseBut.onClick = function(){&lt;br /&gt;
                localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
                localSaveDir.execute();&lt;br /&gt;
        }&lt;br /&gt;
        //choose local folder button&lt;br /&gt;
        pan.grp.cols2.localSaveDirBut.onClick = function(){&lt;br /&gt;
            localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
            o = localSaveDir.selectDlg(&amp;quot;Choose folder to copy footage to&amp;quot;);&lt;br /&gt;
            if(o!=null){&lt;br /&gt;
                    setPref(&amp;quot;localSaveDir&amp;quot;,o.toString());&lt;br /&gt;
                    pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
        pan.grp.copyFootageBut.onClick = function(){&lt;br /&gt;
            //copy footages button (launches the &amp;#039;guts&amp;#039; of this script)&lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
            &lt;br /&gt;
            grabFootagePathsAndCopyToLocal();&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        pan.grp.switchproxyBut.onClick = function(){&lt;br /&gt;
            //checks for a local copy of the footage, and switches to a proxy accordingly &lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            &lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
&lt;br /&gt;
            grabFootagePathsAndSwitchProxy();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (pan instanceof Window) pan.show() ;&lt;br /&gt;
}&lt;br /&gt;
simpleLocalProxy(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Expose Animation ===&lt;br /&gt;
Adds &amp;#039;hold&amp;#039; keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD, also 2025 update vibecodin&amp;#039; an update to make it more consistent: https://berniebernie.fr/wiki/Afx_Javascript_Temp#Auto-expose_rewrite/debug .&lt;br /&gt;
&lt;br /&gt;
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;1000&amp;quot; height=&amp;quot;800&amp;quot; &amp;gt;9-3XVlME2tY&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Step 1: setup detector on layer&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;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 &amp;#039;Detector&amp;#039; picks up a change (value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Step 2: bake to keys&amp;#039; ,preferredSize:[-1,30],enabled:true} , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Bakes detected animation as time remapped keys to animation, this can be long, watch your &amp;quot;Info&amp;quot; panel. If some animation is not detected, change resoltion and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    applyKeys: Button {text: &amp;#039;Step 3: apply as time remapping on selected layers&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Select layers on which to apply time remapping (\&amp;quot;exposed\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        //pan.grp.bakeKeys.enabled = true;        &lt;br /&gt;
        setupDetector();&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    curlayer = layers[0];&lt;br /&gt;
&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer);&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index;&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+&amp;quot;_anim_detection&amp;quot;,true);&lt;br /&gt;
    var precomplayer =  app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    var toplayer = precomp.layers[1];&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration;&lt;br /&gt;
    newlayer.timeRemapEnabled = true;&lt;br /&gt;
    newlayer.inPoint -= app.project.activeItem.frameDuration;&lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;Marker&amp;quot;).setValueAtTime(.5, explainer);&lt;br /&gt;
&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var sliderctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;;&lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
   &lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    var detectorctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider;&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to &amp;#039;clean&amp;#039; expression&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // add &amp;#039;0&amp;#039; key on first frame&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.addKey(0);&lt;br /&gt;
    currentSlider.setValueAtKey(1, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for(i = 0;i&amp;lt;layers.length;i++){&lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;Time Remap&amp;quot;);&lt;br /&gt;
        for(j=1;j&amp;lt;=currentSlider.numKeys;j++){&lt;br /&gt;
            &lt;br /&gt;
            v = currentSlider.keyValue(j);&lt;br /&gt;
            t = currentSlider.keyTime(j);&lt;br /&gt;
            remap.addKey(t);&lt;br /&gt;
            remap.setValueAtKey(j, v);&lt;br /&gt;
            remap.setInterpolationTypeAtKey(j,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\&lt;br /&gt;
        a = timeRemap;\&lt;br /&gt;
        nk = a.nearestKey(time);\&lt;br /&gt;
        curframe = 0;\&lt;br /&gt;
        if(nk.time &amp;gt; time){\&lt;br /&gt;
            curframe = nk.index-1;\&lt;br /&gt;
        }else{\&lt;br /&gt;
            curframe = nk.index;\&lt;br /&gt;
        }\&lt;br /&gt;
        curframe*thisComp.frameDuration;&amp;quot;;&lt;br /&gt;
        remap.expressionEnabled = false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create Null controllers on Puppet pins ===&lt;br /&gt;
http://i.imgur.com/HdFjaYZ.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//nulls created from puppet pins can be parented like normal layers&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Create Null Controls on Puppet Pinsv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    function getLayerFromProperty(prop){&lt;br /&gt;
        return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var c = app.project.activeItem;&lt;br /&gt;
    if( c != null &amp;amp;&amp;amp; c.selectedProperties != null){&lt;br /&gt;
        var props = c.selectedProperties;&lt;br /&gt;
        j = 0;&lt;br /&gt;
        for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
            if(props[i].matchName == &amp;quot;ADBE FreePin3 PosPin Atom&amp;quot;){&lt;br /&gt;
                j++;&lt;br /&gt;
                child = props[i].property(&amp;quot;ADBE FreePin3 PosPin Position&amp;quot;);&lt;br /&gt;
                pos = [child.value[0],child.value[1]];&lt;br /&gt;
                nullLayer = app.project.activeItem.layers.addNull();&lt;br /&gt;
                nullLayer.name = &amp;quot;puppetCtrl&amp;quot;+j;&lt;br /&gt;
                nullLayer.position.setValue(pos);&lt;br /&gt;
                var expr = &amp;quot;thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).toWorld(thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).transform.anchorPoint)&amp;quot;;&lt;br /&gt;
                child.expression = expr;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Batch replace file locations with text file===&lt;br /&gt;
http://i.imgur.com/88QHvlQ.jpg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//now works with sequences or still images, windows only&lt;br /&gt;
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path&lt;br /&gt;
function URIToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function WinPathtoURI(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/ /g, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    //str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Change File Locations&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/tempAE.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txt = &amp;quot;&amp;quot;;&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    var isSequence = new Array();&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
       alert(&amp;quot;Select footage items.&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            isSequence[i] = !sel[i].mainSource.isStill;&lt;br /&gt;
            txt += URIToWinPath(sel[i].mainSource.file.toString())+&amp;quot;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        txtFile.write(&amp;quot;*** 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&amp;quot;);&lt;br /&gt;
        txtFile.write(txt);&lt;br /&gt;
        txtFile.close();&lt;br /&gt;
        txtFile.execute();&lt;br /&gt;
        isOk = confirm(&amp;quot;Change file paths ?\n\nReloading might take a while!&amp;quot;);&lt;br /&gt;
        if(isOk){&lt;br /&gt;
           txtFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
           contents = txtFile.read();&lt;br /&gt;
           arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
           for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
                var tmpFile =  new File(WinPathtoURI(arrayContents[i+2]));&lt;br /&gt;
                //alert(tmpFile);&lt;br /&gt;
                if(isSequence[i]){&lt;br /&gt;
                    sel[i].replaceWithSequence(tmpFile,0);&lt;br /&gt;
                }else{&lt;br /&gt;
                    sel[i].replace(tmpFile);&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                writeLn(Math.round((i+1)/app.project.selection.length*100)+&amp;quot;%&amp;quot;);&lt;br /&gt;
           }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Loop Selected Layers===&lt;br /&gt;
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif&lt;br /&gt;
&lt;br /&gt;
No hassle looping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Set loops&amp;quot;);&lt;br /&gt;
var layersList = app.project.activeItem.selectedLayers;&lt;br /&gt;
var frameD = app.project.activeItem.frameDuration;&lt;br /&gt;
for (i=0;i&amp;lt;layersList.length;i++){&lt;br /&gt;
    if(!layersList[i].timeRemapEnabled){&lt;br /&gt;
        var outP = layersList[i].outPoint;&lt;br /&gt;
        layersList[i].timeRemapEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP,0);&lt;br /&gt;
        layersList[i].timeRemap.expressionEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.expression = &amp;quot;loopOut()&amp;quot;;&lt;br /&gt;
        layersList[i].outPoint = app.project.activeItem.workAreaStart+app.project.activeItem.workAreaDuration;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&lt;br /&gt;
===Find Next Text Layer===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
    a = true;&lt;br /&gt;
                if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
                }&lt;br /&gt;
    if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
        var comp = app.project.item(i);&lt;br /&gt;
        for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
            a = true;&lt;br /&gt;
            comp.layer(j).selected = false;&lt;br /&gt;
            if(comp.layer(j) instanceof TextLayer){&lt;br /&gt;
                comp.layer(j).selected = true;&lt;br /&gt;
                $.writeln(comp.name);&lt;br /&gt;
                a = confirm(&amp;quot;Continue selecting text layers&amp;quot;,true);&lt;br /&gt;
            }&lt;br /&gt;
            if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
            }else{&lt;br /&gt;
                comp.layer(j).selected = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(a){&lt;br /&gt;
    alert(&amp;quot;No (more) text layers found&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simple Watchfolder===&lt;br /&gt;
http://i.imgur.com/poTWO.png&lt;br /&gt;
https://i.imgur.com/wvtgDqE.png&lt;br /&gt;
&lt;br /&gt;
I know a lot of people use media encoder but it&amp;#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Show original source location (WIN only)===&lt;br /&gt;
http://i.imgur.com/04O3r.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    function pathToWinPath(path){&lt;br /&gt;
        var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
        str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
        str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
        str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
        return str;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
        alert(&amp;quot;You need to select source(s) in the project panel&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            a = prompt(&amp;quot;&amp;#039;OK&amp;#039; continues, &amp;#039;cancel&amp;#039; stops displaying original sources\n\n[ &amp;quot;+sel[i].name+&amp;quot; ]&amp;quot;,pathToWinPath(sel[i].mainSource.file.path.toString()));    &lt;br /&gt;
            if(!a){break}&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===List selected layers effects and their properties matchNames ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//windows only&lt;br /&gt;
if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/effectList.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    var col = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for (i=0;i&amp;lt;col.length;i++){&lt;br /&gt;
        effs = col[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
            for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
                txtFile.write(&amp;quot;\n( &amp;quot;+effs.property(j).matchName+&amp;quot; ) &amp;quot;+effs.property(j).name+&amp;quot;\n------------------------------------------\n&amp;quot;);&lt;br /&gt;
                for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
                    txtFile.write(effs.property(j).property(k).matchName+&amp;quot; --&amp;gt; &amp;quot;+effs.property(j).property(k).name);&lt;br /&gt;
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE &amp;amp;&amp;amp; effs.property(j).property(k).value != undefined){&lt;br /&gt;
                        txtFile.write(&amp;quot; [ &amp;quot;+effs.property(j).property(k).value.toString()+&amp;quot; ] &amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    txtFile.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    txtFile.write(&amp;quot;\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v = s.Effects.addProperty(\&amp;quot;CC RepeTile\&amp;quot;);\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v.property(\&amp;quot;CC RepeTile-0001\&amp;quot;).setValue(10);&amp;quot;);    &lt;br /&gt;
    txtFile.close();&lt;br /&gt;
    txtFile.execute();&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Set scripting Prefs to enable to write to disk&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/IEUwe.gif&lt;br /&gt;
&lt;br /&gt;
==== Disable effects ====&lt;br /&gt;
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!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function toggleEffectGlobally() {&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Toggle Effect Globally&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    var sel = app.project.activeItem &amp;amp;&amp;amp; app.project.activeItem.selectedProperties;&lt;br /&gt;
    if (!sel || sel.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var selectedEffect = null;&lt;br /&gt;
&lt;br /&gt;
    // Find the selected effect property group&lt;br /&gt;
    for (var i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
        if (sel[i].matchName &amp;amp;&amp;amp; sel[i].parentProperty &amp;amp;&amp;amp; sel[i].parentProperty.matchName === &amp;quot;ADBE Effect Parade&amp;quot;) {&lt;br /&gt;
            selectedEffect = sel[i];&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (!selectedEffect) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect in the timeline.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var effectName = selectedEffect.matchName;&lt;br /&gt;
    var effectEnabled = selectedEffect.enabled;&lt;br /&gt;
&lt;br /&gt;
    // Loop through all comps in the project&lt;br /&gt;
    for (var p = 1; p &amp;lt;= app.project.numItems; p++) {&lt;br /&gt;
        var item = app.project.item(p);&lt;br /&gt;
        if (item instanceof CompItem) {&lt;br /&gt;
            for (var l = 1; l &amp;lt;= item.numLayers; l++) {&lt;br /&gt;
                var layer = item.layer(l);&lt;br /&gt;
                if (layer.property(&amp;quot;ADBE Effect Parade&amp;quot;)) {&lt;br /&gt;
                    var effects = layer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
                    for (var e = 1; e &amp;lt;= effects.numProperties; e++) {&lt;br /&gt;
                        var eff = effects.property(e);&lt;br /&gt;
                        if (eff.matchName === effectName) {&lt;br /&gt;
                            eff.enabled = !effectEnabled;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===List effects===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 function projEffects(){&lt;br /&gt;
 	var effects = new Array();&lt;br /&gt;
 	var effects2 = new Array();&lt;br /&gt;
 	for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
 		if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
 			   var comp = app.project.item(i);&lt;br /&gt;
 				for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
 					effs = comp.layer(j).property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
 					for(k=1;k&amp;lt;=effs.numProperties;k++){&lt;br /&gt;
 						keyName = effs.property(k).matchName;&lt;br /&gt;
 						effects[keyName]  = 1;&lt;br /&gt;
 						}&lt;br /&gt;
 					   &lt;br /&gt;
 					}&lt;br /&gt;
 			}&lt;br /&gt;
 	}&lt;br /&gt;
 	for(a in effects){&lt;br /&gt;
 		effects2[effects2.length] = a;&lt;br /&gt;
 	}&lt;br /&gt;
 	effects2.sort();&lt;br /&gt;
 	return effects2;&lt;br /&gt;
 }&lt;br /&gt;
 alert(projEffects().join(&amp;quot;\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/hFNiQ.jpg&lt;br /&gt;
&lt;br /&gt;
===Multiple sequences import (Windows)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Multiple sequences import (windows)&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//  bernie@berniebernie.fr&lt;br /&gt;
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file&lt;br /&gt;
//  Access to files network required --- no error checking.&lt;br /&gt;
//   &amp;gt; only works on windows so far&lt;br /&gt;
//   &amp;gt; only finds exrs/pngs/jpgs&lt;br /&gt;
//   &amp;gt; probably fails if your sequences dont have the same start frame&lt;br /&gt;
//   &amp;gt; will work with /path/image.####.jpg or similar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function uniq_fast(a) {&lt;br /&gt;
    var seen = {};&lt;br /&gt;
    var out = [];&lt;br /&gt;
    var len = a.length;&lt;br /&gt;
    var j = 0;&lt;br /&gt;
    for(var i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
         var item = a[i];&lt;br /&gt;
         if(seen[item] !== 1) {&lt;br /&gt;
               seen[item] = 1;&lt;br /&gt;
               out[j++] = item;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return out;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
curScript = new File($.fileName);&lt;br /&gt;
&lt;br /&gt;
startT = Date.now();&lt;br /&gt;
&lt;br /&gt;
sourceFolder = app.project.selection[0].mainSource.file.parent;&lt;br /&gt;
tmpFolder = Folder.temp;&lt;br /&gt;
tmpBat = Folder.temp.toString()+&amp;quot;/ae_dirlist.bat&amp;quot;;&lt;br /&gt;
tmpFilesList = Folder.temp.toString()+&amp;quot;/ae_imageslist.txt&amp;quot;;&lt;br /&gt;
var listFile = new File(tmpFilesList);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var batFile = new File(tmpBat);&lt;br /&gt;
batFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;REM Auto generated by this after effects script file: &amp;quot;+curScript.fsName);&lt;br /&gt;
batFile.write(&amp;quot;\npushd \&amp;quot;&amp;quot;+sourceFolder.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;\ndir /b /on *.exr *.png *.jpg *.jpeg &amp;gt; \&amp;quot;&amp;quot;+listFile.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.close();&lt;br /&gt;
system.callSystem(batFile.fsName);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
listFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
contents = listFile.read();&lt;br /&gt;
arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
listFile.close();&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;via batch: &amp;quot;+arrayContents.length+&amp;quot; files found in &amp;quot;+timer+&amp;quot;s &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myRe = new RegExp(&amp;quot;[0-9]{2,}(\)){0,1}\.[a-z]+$&amp;quot;);&lt;br /&gt;
var myArray = myRe.exec(arrayContents[0]);&lt;br /&gt;
endlength = myArray[0].length;&lt;br /&gt;
&lt;br /&gt;
for(i = 0;i&amp;lt;arrayContents.length;i++){&lt;br /&gt;
    arrayContents[i] = arrayContents[i].slice(0, -endlength);&lt;br /&gt;
}&lt;br /&gt;
unique = uniq_fast(arrayContents);&lt;br /&gt;
&lt;br /&gt;
dialog = confirm(unique.length+&amp;quot; sequences found. Load them ? \nIt might take a long time !&amp;quot;,false,&amp;quot;Confirm Loading&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(dialog){      &lt;br /&gt;
    for(i = 0;i&amp;lt;unique.length;i++){&lt;br /&gt;
        if(unique[i].length&amp;gt;0){&lt;br /&gt;
            sequenceStartFile = new File(sourceFolder.toString()+&amp;quot;/&amp;quot;+unique[i]+myArray[0]);&lt;br /&gt;
            &lt;br /&gt;
            if (sequenceStartFile) {&lt;br /&gt;
            writeLn(&amp;quot;Loading &amp;quot;+(i+1)+&amp;quot;/&amp;quot;+unique.length);&lt;br /&gt;
                try {&lt;br /&gt;
                    // Create a variable containing ImportOptions.&lt;br /&gt;
                    var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
                    importOptions.sequence = true;&lt;br /&gt;
                    try { &lt;br /&gt;
&lt;br /&gt;
                        app.project.importFile(importOptions);&lt;br /&gt;
                    } catch (error) {&lt;br /&gt;
                       e(error.toString());&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (error) {&lt;br /&gt;
                    e(error.toString());&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Canceled&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;after regext: &amp;quot;+timer);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Split to Renderqueue ===&lt;br /&gt;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Split Layers to Renderqueue v2.0&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//&lt;br /&gt;
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp&lt;br /&gt;
//  using the in and out points. I use this along with &amp;#039;Magnum&amp;#039; the edit detector to split &amp;amp; render sequences.&lt;br /&gt;
//&lt;br /&gt;
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the &amp;#039;base&amp;#039; renderqueue item that we derive everything from&lt;br /&gt;
&lt;br /&gt;
function pad(n, width, z) {&lt;br /&gt;
  z = z || &amp;#039;0&amp;#039;;&lt;br /&gt;
  n = n + &amp;#039;&amp;#039;;&lt;br /&gt;
  return n.length &amp;gt;= width ? n : new Array(width - n.length + 1).join(z) + n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Split Layers to Renderqueue&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(app.project.renderQueue.items.length == 1){&lt;br /&gt;
    FE = app.project.renderQueue.items[1];&lt;br /&gt;
    ai = FE.comp;&lt;br /&gt;
    path = FE.outputModule(1).file.path;&lt;br /&gt;
    for(i=1;i&amp;lt;=ai.layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
        RI = app.project.renderQueue.items[1].duplicate();&lt;br /&gt;
        RI.timeSpanStart = ai.layers[i].inPoint;&lt;br /&gt;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;&lt;br /&gt;
        $.writeln(ai.workAreaDuration+&amp;quot; &amp;quot;+ai.layers[i].outPoint);&lt;br /&gt;
        RI.outputModule(1).file =  new File(path+&amp;quot;/&amp;quot;+pad(i,2,&amp;quot;0&amp;quot;)+&amp;quot;_&amp;quot;+ai.layers[i].name);&lt;br /&gt;
    }&lt;br /&gt;
    app.project.renderQueue.items[1].render= false;&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Only 1 element should be in renderqueue&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docked Panel SNIP ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//script panel&lt;br /&gt;
{&lt;br /&gt;
	var nested_file = new File(&amp;quot;U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//called file&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
	var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,[100, 100, 300, 300]);&lt;br /&gt;
	impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
	//impButton.onClick = openfile;&lt;br /&gt;
	return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Watchfolder watcher (WIP)===&lt;br /&gt;
http://i.imgur.com/lhO3k.gif&lt;br /&gt;
&lt;br /&gt;
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)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
todo: check if icons are here&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  // $.writeln(&amp;quot;file &amp;gt;&amp;gt;&amp;gt; &amp;quot;+txtfiles[0].toString());&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
                //info = [&amp;quot;debug&amp;quot;,logFolderLocation.toString(),logFolderLocation.name.toString()];&lt;br /&gt;
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];&lt;br /&gt;
               &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
              //  $.writeln(logFolderLocation.toString()+&amp;quot; &amp;lt;&amp;lt;&amp;lt; &amp;quot;)&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    //$.writeln(&amp;quot;Doing shit&amp;quot;);&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                   // info[2] = &amp;quot;n/a+&amp;quot;;&lt;br /&gt;
//                    info[1] = 1;&lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
                       // $.writeln( lines[i].indexOf(phrase));&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
                               //$.writeln(wfold);&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
                                ///info[3] = pathToWinPath(wfold);&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
                               // info[2] = &amp;quot; &amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                   // info[1] = ;&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
              //  $.writeln(info);&lt;br /&gt;
               // $.writeln(contents);&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    //$.writeln(files.length);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            //$.writeln(&amp;quot;&amp;gt;&amp;gt;&amp;gt; &amp;quot;+state+&amp;quot; - &amp;quot;+out+&amp;quot; - &amp;quot;+folders[folder].name);&lt;br /&gt;
            &lt;br /&gt;
           // $.writeln(out);&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
      // $.writeln(vArray[counter2]);&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                                      //  $.writeln(&amp;quot;Debug&amp;quot;);$.writeln(p);&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 600);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
                    // $.writeln(shotinfos.join(&amp;quot;\n&amp;quot;));&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
                    //$.writeln(sL);&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
                      //$.writeln(a);&lt;br /&gt;
                        //$.writeln(shotinfos[i][1]+&amp;quot; &amp;quot;+list.selection.toString());&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                           // $.writeln(&amp;quot;ww&amp;quot;+);&lt;br /&gt;
                       //  $.writeln(a);&lt;br /&gt;
                            //explore(a[3]+((a[1]==2)?&amp;quot;/&amp;quot;+a[0]:&amp;quot;&amp;quot;));&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    //$.writeln(sel[0].subItems[0].text);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
                //refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                //cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                //}else if(reloadEditTxt.text &amp;gt;=10){&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function grow(palette,value){&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
    //    alert(listItem.selection);&lt;br /&gt;
    var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);&lt;br /&gt;
    //var texted = new Array(&amp;quot;...&amp;quot;,&amp;quot;error:&amp;quot;,&amp;quot;rendering&amp;quot;,&amp;quot;...&amp;quot;,&amp;quot;queued&amp;quot;);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    //$.writeln(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+array[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.image = File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    //item.subItems[0].helpTip = texted(state);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
///////////////////////////////////////////&lt;br /&gt;
           /* if((logFolder =  getFilePath(watchfolderLocation+&amp;quot;/&amp;quot;+shot)) != undefined){&lt;br /&gt;
                $.writeln(logInfo(logFolder));&lt;br /&gt;
            }else{&lt;br /&gt;
                $.writeln(&amp;quot;no log folder&amp;quot;);&lt;br /&gt;
                }*/&lt;br /&gt;
           // $.writeln();&lt;br /&gt;
           // addItem(list,&amp;quot;G12_SC213_T1&amp;quot;,2);&lt;br /&gt;
           &lt;br /&gt;
           &lt;br /&gt;
           /*&lt;br /&gt;
var item1 = list.add (&amp;#039;item&amp;#039;, &amp;#039;GB15_SC138_T1&amp;#039;);&lt;br /&gt;
item1.image = File(&amp;quot;~/Desktop/flag_gray.png&amp;quot;);&lt;br /&gt;
item1.subItems[0].text = &amp;#039;Queued...&amp;#039;;&lt;br /&gt;
item1.enabled = false;&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
            //alert(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot));&lt;br /&gt;
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot)*100);&lt;br /&gt;
          &lt;br /&gt;
                       // alert(&amp;quot;test&amp;quot;);&lt;br /&gt;
              //$.writeln(files[file].path+&amp;quot;/&amp;quot;+files[file].name);&lt;br /&gt;
                            //  a+=  getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name).name+&amp;quot;\n&amp;quot;;&lt;br /&gt;
                            &lt;br /&gt;
/*                            &lt;br /&gt;
                            &lt;br /&gt;
                            {&lt;br /&gt;
                                &lt;br /&gt;
        wfLocBtn.onClick = function(){&lt;br /&gt;
            app.scheduleTask(&amp;quot;loop()&amp;quot;,2000,1);&lt;br /&gt;
&lt;br /&gt;
           &lt;br /&gt;
            fold = new Folder( watchfolderLocation.toString());&lt;br /&gt;
            files = fold.getFiles();&lt;br /&gt;
                a= &amp;quot;&amp;quot;;&lt;br /&gt;
            for(file in files){&lt;br /&gt;
&lt;br /&gt;
              $.writeln(getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name));&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
           alert(a);&lt;br /&gt;
&lt;br /&gt;
            writeLn(pBar.value);&lt;br /&gt;
        }&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parent to last selected layer===&lt;br /&gt;
Why isn&amp;#039;t this by default? Bound to alt-P.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function() {&lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    if (comp &amp;amp;&amp;amp; comp instanceof CompItem) {&lt;br /&gt;
        var selectedLayers = comp.selectedLayers;&lt;br /&gt;
        if (selectedLayers.length &amp;gt; 1) {&lt;br /&gt;
            app.beginUndoGroup(&amp;quot;Parent to Last Selected&amp;quot;);&lt;br /&gt;
            var parentLayer = selectedLayers[selectedLayers.length - 1];&lt;br /&gt;
            for (var i = 0; i &amp;lt; selectedLayers.length - 1; i++) {&lt;br /&gt;
                selectedLayers[i].parent = parentLayer;&lt;br /&gt;
            }&lt;br /&gt;
            app.endUndoGroup();&lt;br /&gt;
        }else{&lt;br /&gt;
			alert(&amp;quot;Requires at least a child an parent layer to be selected.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
    }else{&lt;br /&gt;
		alert(&amp;quot;Select a comp&amp;#039;s layers and run the script again&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Import pos from maya===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,&lt;br /&gt;
[100, 100, 300, 300]);&lt;br /&gt;
impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
impButton.onClick = openfile;&lt;br /&gt;
return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
//myToolsPanel.show();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function openfile(){&lt;br /&gt;
	            var myFile = File.openDialog (&amp;quot;Select track file&amp;quot;,&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
            var fileOK = myFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
	//var fileD = OpenDlg (&amp;quot;Tracking Point File&amp;quot;,&amp;quot;*.txt&amp;quot;,true);&lt;br /&gt;
	//txt = fileD.readln ()&lt;br /&gt;
	alert(&amp;quot;txt&amp;quot;+readTxt(myFile));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function readTxt(myFile){&lt;br /&gt;
var myText = myFile.read();	&lt;br /&gt;
return myText;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dandelion/Amazing World Of Gumball Shotbuilder===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://imgur.com/xKJWB.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For education purposes only, the script was run in production to helb build shots, it:&lt;br /&gt;
* created a list of shots &amp;amp; shows possible given a folder structure (&amp;quot;GB##_SHOWNAME_SC##_T##&amp;quot;)&lt;br /&gt;
* opened the last .AEP file found in said shot folder&lt;br /&gt;
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder&lt;br /&gt;
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the &amp;#039;grab sources&amp;#039; button&lt;br /&gt;
* automatically sent files to the watchfolder to render on a small-ish farm. &lt;br /&gt;
Other buttons allowed to check for missing footage, open the comp&amp;#039;s folder, open the current shows&amp;#039; latest animatic, etc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  &lt;br /&gt;
//  Dandelion Shot Builder for &amp;quot;the Amazing World Of Gumball&amp;quot; v0.7 by Bernie&lt;br /&gt;
//   last update 21/11/10&lt;br /&gt;
//&lt;br /&gt;
//  Known Bugs:&lt;br /&gt;
//      &lt;br /&gt;
//  -you can&amp;#039;t have several sequences in a single folder and expect the script to pick up all the sequences&lt;br /&gt;
//  -this will not set color profiles&lt;br /&gt;
// //TODO&lt;br /&gt;
//  &amp;gt;&amp;gt;&amp;gt; CHECK IF WF FOLDER ALREADY EXISTS&lt;br /&gt;
// &amp;gt;&amp;gt;&amp;gt;&amp;gt; GET TAKE FROM N DRIVE, NOT OUT FOLDER&lt;br /&gt;
//  -v0.7 fixes&lt;br /&gt;
//        -new scene after WF works properly&lt;br /&gt;
//        -changed output folder location to N:&lt;br /&gt;
//  -v0.6 fixes&lt;br /&gt;
//       -cancel watchfolder cancels watchfolder&lt;br /&gt;
//        -fixed GB##_SC_###_T1&lt;br /&gt;
//       -added options&lt;br /&gt;
//       -can work on a new location&lt;br /&gt;
//  -v0.5 fixes&lt;br /&gt;
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)&lt;br /&gt;
//      -will warn if there is missing footage before sending to WF&lt;br /&gt;
//      -removed set take, added &amp;#039;missing&amp;#039; dialog.&lt;br /&gt;
//  -v0.4 fixes&lt;br /&gt;
//      -there shouldn&amp;#039;t be a refresh problem on show change anymore&lt;br /&gt;
//  -v0.3 fixes&lt;br /&gt;
//      -changed watchfolder location to anthony&amp;#039;s mac&lt;br /&gt;
//      -changed save as dialog&lt;br /&gt;
//      -removed unused buttons&lt;br /&gt;
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)&lt;br /&gt;
//  -v0.2 fixes&lt;br /&gt;
//      -sequences weren&amp;#039;t getting imported.&lt;br /&gt;
//      -fixed UI/little problems&lt;br /&gt;
//      -added folders, refresh button, save before watchfoldering&lt;br /&gt;
var version = &amp;quot;0.7&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//default locations&lt;br /&gt;
var watchfolderLocation = &amp;quot;/c/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var rootFolder = &amp;quot;/c/&amp;quot;;&lt;br /&gt;
var forEditFolderLoaction = &amp;quot;//MAC0023DFDF5429/for%20edit&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
rootFolder = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)):rootFolder;&lt;br /&gt;
watchfolderLocation = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)):watchfolderLocation;&lt;br /&gt;
forEditFolderLoaction = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)):forEditFolderLoaction;&lt;br /&gt;
&lt;br /&gt;
          //dirty hack&lt;br /&gt;
            a = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true;&lt;br /&gt;
            b = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)):false;&lt;br /&gt;
            c = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)):true;&lt;br /&gt;
            if(a==&amp;quot;false&amp;quot;) a = false;&lt;br /&gt;
            if(a==&amp;quot;true&amp;quot;) a = true;&lt;br /&gt;
            if(b==&amp;quot;false&amp;quot;) b = false;&lt;br /&gt;
            if(b==&amp;quot;true&amp;quot;) b = true;&lt;br /&gt;
            if(c==&amp;quot;false&amp;quot;) c = false;&lt;br /&gt;
            if(c==&amp;quot;true&amp;quot;) c = true;&lt;br /&gt;
            &lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_&amp;quot;;&lt;br /&gt;
var loadFile = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)):&amp;quot;&amp;quot;;&lt;br /&gt;
checkOutputSettings();&lt;br /&gt;
&lt;br /&gt;
Array.prototype.has = function(value) {&lt;br /&gt;
    var i;&lt;br /&gt;
    for (i=0;i&amp;lt;this.length;i++) {&lt;br /&gt;
        if (this[i] == value) {&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addShotDialog(sel){&lt;br /&gt;
    alert(sel);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    path = path.toString();&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sendToWatchFolder(watchfolderlocation,compname){&lt;br /&gt;
    ocn = compname;&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
        c = confirm(&amp;quot;Save &amp;quot;+app.project.file.name+&amp;quot; ?&amp;quot;,false,&amp;quot;Save Project&amp;quot;);&lt;br /&gt;
        if(c){&lt;br /&gt;
            app.project.save();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    curFile = app.project.file;&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
    compname += &amp;quot;_WF&amp;quot;;&lt;br /&gt;
    myFolder.create();&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;.aep&amp;quot;);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
    myTextFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname.substring(0,22)+&amp;quot;_RCF.txt&amp;quot;);    &lt;br /&gt;
    myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    text = &amp;quot;After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot; ;&lt;br /&gt;
    myTextFile.write(text);&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    writeInfo(watchfolderlocation,ocn);&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;) == &amp;quot;false&amp;quot;){&lt;br /&gt;
        opFile = new File(curFile);&lt;br /&gt;
        if(opFile){&lt;br /&gt;
            app.open(opFile);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
        app.project.new();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    //explore(watchfolderlocation);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function writeInfo(watchfolderlocation,compname){&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_DandyWatch.txt&amp;quot;);    &lt;br /&gt;
    mySaveFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    rq = app.project.renderQueue;&lt;br /&gt;
    text =&amp;quot;&amp;quot;;    &lt;br /&gt;
    for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
        if(rq.items[i].render){&lt;br /&gt;
            text += rq.items[i].outputModules[1].file.path+&amp;quot;\n&amp;quot;;&lt;br /&gt;
            text += (Math.round(rq.items[i].timeSpanDuration*25)+&amp;quot;\n&amp;quot;);&lt;br /&gt;
            text += system.callSystem(&amp;quot;hostname&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    mySaveFile.write(text);&lt;br /&gt;
    mySaveFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkForMissingFootage(dialogFlag){&lt;br /&gt;
        var dialog = dialogFlag;&lt;br /&gt;
        var missing = false;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            app.project.items[i].selected = false;&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                app.project.items[i].selected = true;&lt;br /&gt;
                                missing = true;&lt;br /&gt;
                                if(dialog){&lt;br /&gt;
                                    dialog = prompt(&amp;quot;Original folder of \&amp;quot;&amp;quot;+(app.project.items[i].name)+&amp;quot;\&amp;quot; :&amp;quot;,pathToWinPath(app.project.items[i].file),&amp;quot;Missing footage! (hit cancel to suppress further dialogs)&amp;quot;);&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return missing;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutputSettings(){&lt;br /&gt;
    renderSettingsSet = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;)))?true:false;&lt;br /&gt;
   // renderSettingsSet = 1;&lt;br /&gt;
    if(!renderSettingsSet){&lt;br /&gt;
                alert(&amp;quot;Tiff output being setup, this should happen only once. It needs to close the current comp.&amp;quot;);&lt;br /&gt;
                opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
                if(opFile.exists){&lt;br /&gt;
                     app.open(opFile);&lt;br /&gt;
                     app.project.renderQueue.items[1].outputModules[1].saveAsTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                     app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;,&amp;quot;is there&amp;quot;);&lt;br /&gt;
                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
                     app.newProject();&lt;br /&gt;
                 }else{&lt;br /&gt;
                     alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutFolder(tpath){&lt;br /&gt;
    foldFile = new Folder(tpath);&lt;br /&gt;
    //$.writeln(tpath);&lt;br /&gt;
    if(foldFile.exists){&lt;br /&gt;
        files = foldFile.getFiles();  &lt;br /&gt;
        if(files.length &amp;gt; 1){&lt;br /&gt;
            return false;&lt;br /&gt;
        }else{&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
      newF = foldFile.create();&lt;br /&gt;
        return newF;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function getSetTake(force){&lt;br /&gt;
    show = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;);// showDDL.selection.toString();&lt;br /&gt;
    shot = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;); //shotDDL.selection.toString();&lt;br /&gt;
    projectTake = getTake(findMainShot().name);&lt;br /&gt;
//    paths  = rootFolder+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/OUT/&amp;quot;;&lt;br /&gt;
    paths  = &amp;quot;/n/01_OUT/&amp;quot;+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/&amp;quot;;&lt;br /&gt;
    lastFolder = getLastModified(paths);&lt;br /&gt;
    //alert(lastFolder.name+&amp;quot; --&amp;gt;&amp;quot;+paths);&lt;br /&gt;
    if(lastFolder){&lt;br /&gt;
        folderTake = getTake(lastFolder.name);&lt;br /&gt;
    }else{&lt;br /&gt;
        folderTake = false;&lt;br /&gt;
    }&lt;br /&gt;
    msgPt1 = (folderTake)? &amp;quot;The last take in OUT folder is take &amp;quot;+(folderTake)+&amp;quot;.\n&amp;quot;:&amp;quot;No take was found in the OUT folder.\n&amp;quot;;&lt;br /&gt;
    msgPt2 = &amp;quot;The current project take is &amp;quot;+projectTake+&amp;quot;. \n\nChoose the current take:&amp;quot;;&lt;br /&gt;
    if(force || (folderTake != (projectTake-1))){&lt;br /&gt;
        recommended = (folderTake)?((folderTake)+1):projectTake;&lt;br /&gt;
        answer = prompt(msgPt1+msgPt2,recommended);&lt;br /&gt;
        //$.writeln(&amp;quot;blabal &amp;quot;+answer);&lt;br /&gt;
        if(answer != null){&lt;br /&gt;
            return answer;&lt;br /&gt;
        }else{&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return projectTake;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setTake(value){&lt;br /&gt;
    comp = findMainShot();&lt;br /&gt;
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf(&amp;quot;_T&amp;quot;)+2))+value;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTake(normalizedName){&lt;br /&gt;
    tmp = normalizedName.split(&amp;quot;_T&amp;quot;);&lt;br /&gt;
    return  parseFloat(tmp[tmp.length-1]);&lt;br /&gt;
}&lt;br /&gt;
	// Smart Import.jsx&lt;br /&gt;
	//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases&lt;br /&gt;
    //the only problem is that you can still not have 2 sequences in a single folder&lt;br /&gt;
   &lt;br /&gt;
function SmartImport(targetFolder){&lt;br /&gt;
         writeLn(&amp;quot;Importing files...&amp;quot;);&lt;br /&gt;
         var importedFiles = 0;&lt;br /&gt;
		var scriptName = &amp;quot;Smart Import&amp;quot;;&lt;br /&gt;
		var sourcePaths = getSourcePathsArray();&lt;br /&gt;
		// Ask the user for a folder whose contents are to be imported.&lt;br /&gt;
		//var targetFolder = Folder.selectDialog(&amp;quot;Import items from folder...&amp;quot;);&lt;br /&gt;
		if (targetFolder != null) {&lt;br /&gt;
			// If no project open, create a new project to import the files into.&lt;br /&gt;
			function processFile(theFile)&lt;br /&gt;
			{&lt;br /&gt;
				try {&lt;br /&gt;
					// Create a variable containing ImportOptions.&lt;br /&gt;
					var importOptions = new ImportOptions(theFile);&lt;br /&gt;
                    //alert();&lt;br /&gt;
                     if(theFile.name.toString().toLowerCase().lastIndexOf(&amp;quot;.psd&amp;quot;) != -1){&lt;br /&gt;
                        importOptions.importAs = ImportAsType.COMP;&lt;br /&gt;
                     }&lt;br /&gt;
                       importSafeWithError(importOptions);&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					// Ignore errors.&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			function testForSequences(files)&lt;br /&gt;
			{&lt;br /&gt;
				var searcher = new RegExp(&amp;quot;[0-9]{3,}$&amp;quot;);&lt;br /&gt;
				var movieFileSearcher = new RegExp(&amp;quot;(mov|avi|mpg)$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
				var parseResults = new Array;&lt;br /&gt;
                  var isFolder = new Array;&lt;br /&gt;
				&lt;br /&gt;
				// Test that we have a sequence. Stop parsing after 10 files.&lt;br /&gt;
				for (x = 0; (x &amp;lt; files.length) &amp;amp; x &amp;lt; 10; x++) {&lt;br /&gt;
					var movieFileResult = movieFileSearcher.exec(files[x].name);&lt;br /&gt;
					if (!movieFileResult) {&lt;br /&gt;
//******                           splitName = files[x].name.split(&amp;#039;.&amp;#039;)[0];&lt;br /&gt;
                            splitName=files[x].name.substring(0,files[x].name.length-4);&lt;br /&gt;
&lt;br /&gt;
                           //splitName[splitNameA.length] = &lt;br /&gt;
                           //alert(splitName);	&lt;br /&gt;
                         //  alert(files[x].name+&amp;quot; &amp;gt;&amp;gt;&amp;gt; &amp;quot;+files[x].name.split(&amp;#039;.&amp;#039;)[0]);&lt;br /&gt;
						var currentResult = searcher.exec(splitName);&lt;br /&gt;
						// Regular expressions return null if no match was found.&lt;br /&gt;
						// Otherwise, they return an array with the following information:&lt;br /&gt;
						// array[0] = the matched string.&lt;br /&gt;
						// array[1..n] = the matched capturing parentheses.&lt;br /&gt;
                   				&lt;br /&gt;
                    if (currentResult) { // We have a match -- the string contains numbers.&lt;br /&gt;
                           &lt;br /&gt;
                            // The match of those numbers is stored in the array[1]&lt;br /&gt;
							// Take that number and save it into parseResults.&lt;br /&gt;
							parseResults[parseResults.length] = currentResult[0];&lt;br /&gt;
						} else {&lt;br /&gt;
							parseResults[parseResults.length] = null;&lt;br /&gt;
						}&lt;br /&gt;
                           if(files[x].name == splitName){&lt;br /&gt;
                                isFolder[isFolder.length] = true;&lt;br /&gt;
                               // alert(&amp;quot;Folder &amp;quot;+files[x].name);&lt;br /&gt;
                           }else{&lt;br /&gt;
                                isFolder[isFolder.length] = false;&lt;br /&gt;
                           }&lt;br /&gt;
					} else {&lt;br /&gt;
						parseResults[parseResults.length] = null;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// If all the files we just went through have a number in their file names, &lt;br /&gt;
				// assume they are part of a sequence and return the first file.&lt;br /&gt;
				&lt;br /&gt;
				var result = null;&lt;br /&gt;
				for (i = 0; i &amp;lt; parseResults.length; ++i) {&lt;br /&gt;
                   				&lt;br /&gt;
                   if(!isFolder[i]){&lt;br /&gt;
                    if (parseResults[i]) {&lt;br /&gt;
						if (!result) {&lt;br /&gt;
                            &lt;br /&gt;
							result = files[i];		&lt;br /&gt;
						}&lt;br /&gt;
					} else {&lt;br /&gt;
                        &lt;br /&gt;
						// In this case, a file name did not contain a number.&lt;br /&gt;
						result = null;&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
                   }&lt;br /&gt;
                }&lt;br /&gt;
				&lt;br /&gt;
				return result;&lt;br /&gt;
			}&lt;br /&gt;
        			&lt;br /&gt;
			&lt;br /&gt;
			function importSafeWithError(importOptions)&lt;br /&gt;
			{&lt;br /&gt;
              if(!(sourcePaths.has(importOptions.file.toString()))){&lt;br /&gt;
 				try { &lt;br /&gt;
                  b = app.project.importFile(importOptions);&lt;br /&gt;
                    writeLn(importOptions.file.toString());&lt;br /&gt;
                 sfi = sourcesFolderItem();&lt;br /&gt;
                   b.parentFolder = sfi;&lt;br /&gt;
                    if(importOptions.importAs == ImportAsType.COMP){&lt;br /&gt;
                        for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                            if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name.lastIndexOf(&amp;quot; Layers&amp;quot;) != -1){&lt;br /&gt;
                         app.project.items[i].parentFolder = sfi;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                        writeLn(&amp;quot;Importing file (&amp;quot;+importedFiles+&amp;quot;)&amp;quot;);&lt;br /&gt;
                        importedFiles++;&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					alert(error.toString() + importOptions.file.fsName, scriptName);&lt;br /&gt;
				}&lt;br /&gt;
                // }else{&lt;br /&gt;
                //        alert(&amp;quot;file already here&amp;quot;);&lt;br /&gt;
                 }&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			&lt;br /&gt;
			function processFolder(theFolder)&lt;br /&gt;
			{&lt;br /&gt;
				// Get an array of files in the target folder.&lt;br /&gt;
				var files = theFolder.getFiles();&lt;br /&gt;
				//alert(files);&lt;br /&gt;
				// Test whether theFolder contains a sequence.&lt;br /&gt;
				var sequenceStartFile = testForSequences(files);&lt;br /&gt;
				&lt;br /&gt;
				// If it does contain a sequence, import the sequence,&lt;br /&gt;
				if (sequenceStartFile) {&lt;br /&gt;
					try {&lt;br /&gt;
						// Create a variable containing ImportOptions.&lt;br /&gt;
						var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
						importOptions.sequence = true;&lt;br /&gt;
						// importOptions.forceAlphabetical = true;		// Un-comment this if you want to force alpha order by default.&lt;br /&gt;
						importSafeWithError(importOptions);&lt;br /&gt;
					} catch (error) {&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// Otherwise, import the files and recurse.&lt;br /&gt;
				&lt;br /&gt;
				for (index in files) { // Go through the array and set each element to singleFile, then run the following.&lt;br /&gt;
					if (files[index] instanceof File) {&lt;br /&gt;
						if (!sequenceStartFile) { // If file is already part of a sequence, don&amp;#039;t import it individually.&lt;br /&gt;
							processFile(files[index]); // Calls the processFile function above.&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					if (files[index] instanceof Folder) {&lt;br /&gt;
						processFolder(files[index]); // recursion&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// Recursively examine that folder.&lt;br /&gt;
			processFolder(targetFolder);&lt;br /&gt;
		}&lt;br /&gt;
    clearOutput();	&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
   &lt;br /&gt;
function sourcesFolderItem(){&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
       // alert(app.project.items[i].name);&lt;br /&gt;
     if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name == &amp;quot;Sources&amp;quot;){&lt;br /&gt;
           return app.project.items[i];&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
    return app.project.items.addFolder(&amp;quot;Sources&amp;quot;);&lt;br /&gt;
    //else&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getSourcePathsArray(){&lt;br /&gt;
    var footageLocations = new Array();&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
        if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
            footageLocations[footageLocations.length] = app.project.item(i).file;&lt;br /&gt;
        }    &lt;br /&gt;
    }&lt;br /&gt;
    return footageLocations;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getLastModified(location,extension){&lt;br /&gt;
      myFolder = new Folder(location.toString());&lt;br /&gt;
      if (!myFolder instanceof Folder){&lt;br /&gt;
          return false;&lt;br /&gt;
     }&lt;br /&gt;
     listFiles = myFolder.getFiles(extension);&lt;br /&gt;
     listDates = new Array();&lt;br /&gt;
     maxId = 0;&lt;br /&gt;
     maxValue = 0;&lt;br /&gt;
     for(i=0;i&amp;lt;listFiles.length;i++){&lt;br /&gt;
         if (!(extension == &amp;quot;&amp;quot; &amp;amp;&amp;amp; (listFiles[i] instanceof File))){&lt;br /&gt;
             d = listFiles[i].modified;&lt;br /&gt;
             formattedDate = d.getFullYear()+&amp;quot;&amp;quot;+pad(d.getMonth()+1,2,0)+&amp;quot;&amp;quot;+pad(d.getDate(),2,0)+&amp;quot;&amp;quot;+pad(d.getHours(),2,0)+&amp;quot;&amp;quot;+pad(d.getMinutes(),2,0)+&amp;quot;&amp;quot;+pad(d.getSeconds(),2,0);&lt;br /&gt;
             listDates[i] = formattedDate;&lt;br /&gt;
             if(Math.max(maxValue,formattedDate) == formattedDate){&lt;br /&gt;
                 maxValue = formattedDate;&lt;br /&gt;
                 maxId = i;&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
     &lt;br /&gt;
     }&lt;br /&gt;
    if(listFiles[maxId] == &amp;quot;&amp;quot; ||listFiles[maxId] == &amp;quot;undefined&amp;quot;||listFiles[maxId] == null){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return listFiles[maxId];     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(number, length, character) {&lt;br /&gt;
    if (character == null) {&lt;br /&gt;
        character = &amp;quot;0&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    var numberStr = &amp;quot;&amp;quot; + number;&lt;br /&gt;
    while (numberStr.length &amp;lt; length) {&lt;br /&gt;
        numberStr = character + numberStr;&lt;br /&gt;
    }&lt;br /&gt;
    return numberStr;&lt;br /&gt;
}&lt;br /&gt;
function grabAnimatic(folderLocation){&lt;br /&gt;
    newLocation = new Folder(folderLocation);&lt;br /&gt;
    animatic = newLocation.getFiles(&amp;quot;*.mov&amp;quot;);&lt;br /&gt;
    sfi = sourcesFolderItem();&lt;br /&gt;
    if(animatic[0]){&lt;br /&gt;
        try {&lt;br /&gt;
            var importOptions = new ImportOptions(animatic[0]);&lt;br /&gt;
            animatic = app.project.importFile(importOptions);&lt;br /&gt;
            for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                    if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
                   app.project.item(i).parentFolder = sfi;&lt;br /&gt;
                    }    &lt;br /&gt;
            }&lt;br /&gt;
            return animatic;&lt;br /&gt;
        }catch (error){&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function figureOutShotName(filepath){&lt;br /&gt;
    folders = filepath.split(&amp;quot;/&amp;quot;);&lt;br /&gt;
    showFolder = folders[folders.length-2];&lt;br /&gt;
    showFolder = showFolder.split(&amp;quot;_&amp;quot;)[0];&lt;br /&gt;
    shotFolder = folders[folders.length-1];&lt;br /&gt;
    var searcher = new RegExp(&amp;quot;[0-9]{2,}$&amp;quot;);&lt;br /&gt;
    var currentResult = searcher.exec(shotFolder);&lt;br /&gt;
    if (currentResult){&lt;br /&gt;
        shotFolder = currentResult[0];&lt;br /&gt;
    }&lt;br /&gt;
    return showFolder+&amp;quot;_SC&amp;quot;+shotFolder;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function createMainComp(path){&lt;br /&gt;
    grabAnimatic(path);&lt;br /&gt;
    shotName = figureOutShotName(path);&lt;br /&gt;
    workComp = app.project.items.addComp(shotName+&amp;quot;_T1&amp;quot;, 1920, 1080,1.0 ,animatic.duration,25.0);&lt;br /&gt;
    animaticLayer = workComp.layers.add(animatic);&lt;br /&gt;
    animaticLayer.guideLayer = true;&lt;br /&gt;
    return workComp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnList2(type){&lt;br /&gt;
     if(type == &amp;quot;show&amp;quot;){&lt;br /&gt;
        rArray = returnFolderArray(rootFolder);&lt;br /&gt;
        for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
            }&lt;br /&gt;
    }else if(type == &amp;quot;scene&amp;quot;){&lt;br /&gt;
        rArray = [&amp;quot;a&amp;quot;,&amp;quot;b&amp;quot;];&lt;br /&gt;
        alert(dPanel.shotLoad.showDDL.selection.toString());&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function returnList(folder){&lt;br /&gt;
    rArray = returnFolderArray(folder);&lt;br /&gt;
    for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function findMainShot(){&lt;br /&gt;
    //fix GB##_SC_###_T1&lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               nuName = app.project.items[i].name;&lt;br /&gt;
               nuName = nuName.split(&amp;quot;SC_&amp;quot;);&lt;br /&gt;
               app.project.items[i].name = nuName[0]+&amp;quot;SC&amp;quot;+nuName[1];&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    var shot = null;&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               shot = app.project.items[i];&lt;br /&gt;
               break;&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if(shot == null){&lt;br /&gt;
        alert(&amp;quot;Could not find a comp named like \&amp;quot;GB##_SC###(a-z)_T#\&amp;quot;\n\nRename main comp accordingly, render again&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return shot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function dandyShotBuilderUI(thisObj){&lt;br /&gt;
    &lt;br /&gt;
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
    &lt;br /&gt;
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;DandyShotBuilderFix&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
           var msg = &amp;quot;This script requires the scripting security preference to be set. \nGo the \&amp;quot;General\&amp;quot; panel of your application preferences and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.\n\n Restart AFX&amp;quot;;&lt;br /&gt;
           var shotInfo = dPanel.add(&amp;quot;edittext&amp;quot;,[10,5,235,305],msg,{multiline:true}); &lt;br /&gt;
         }else{&lt;br /&gt;
      var shotLoad = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,175],&amp;quot;Load Shot&amp;quot;);&lt;br /&gt;
         var shotLoadTxt1 = shotLoad.add(&amp;quot;statictext&amp;quot;,[15,24,52,44],&amp;quot;Show:&amp;quot;);&lt;br /&gt;
         var showDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,19,210,44], returnList(rootFolder));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)){&lt;br /&gt;
                 showDDL.selection = showDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 showDDL.selection = 0;&lt;br /&gt;
                 app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
                }&lt;br /&gt;
         var shotLoadTxt2 = shotLoad.add(&amp;quot;statictext&amp;quot;,[12,59,52,79],&amp;quot;Scene:&amp;quot;);&lt;br /&gt;
         var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;shot&amp;quot;)){&lt;br /&gt;
                 shotDDL.selection = shotDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 shotDDL.selection = 0;&lt;br /&gt;
                }&lt;br /&gt;
         &lt;br /&gt;
          &lt;br /&gt;
         var shotStatusTxt = shotLoad.add(&amp;quot;statictext&amp;quot;,[25,95,52,115],&amp;quot;File:&amp;quot;);&lt;br /&gt;
         var shotStatusEditTxt = shotLoad.add(&amp;quot;edittext&amp;quot;,[56,92,210,115],&amp;quot;...&amp;quot;);&lt;br /&gt;
            shotStatusEditTxt.active = false;&lt;br /&gt;
            shotStatusEditTxt.enabled = false;&lt;br /&gt;
         var shotLoadBtn = shotLoad.add(&amp;quot;button&amp;quot;,[56,125,96,150],&amp;quot;Load&amp;quot;);&lt;br /&gt;
            shotLoadBtn.enabled = false;&lt;br /&gt;
         var shotBuildBtn = shotLoad.add(&amp;quot;button&amp;quot;,[101,125,141,150],&amp;quot;Build&amp;quot;);&lt;br /&gt;
            shotBuildBtn.enabled = false;&lt;br /&gt;
         var shotRefreshBtn = shotLoad.add(&amp;quot;button&amp;quot;,[146,125,168,150],&amp;quot;r&amp;quot;);&lt;br /&gt;
         //var shotOptns = shotLoad.add(&amp;quot;button&amp;quot;,[174,125,210,150],&amp;quot;optns&amp;quot;);&lt;br /&gt;
            //shotSetBtn.enabled = false;&lt;br /&gt;
        &lt;br /&gt;
       var shotTools = dPanel.add(&amp;quot;panel&amp;quot;,[10,185,235,300],&amp;quot;Misc&amp;quot;);&lt;br /&gt;
            shotToolsBtn1 =  shotTools.add(&amp;quot;button&amp;quot;,[12,12,79,35],&amp;quot;Open Folder&amp;quot;);&lt;br /&gt;
            shotToolsBtn1Xtra =  shotTools.add(&amp;quot;button&amp;quot;,[82,12,102,35],&amp;quot;N:&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2 =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,197,35],&amp;quot;Flick Last Take&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2.enabled = false; &lt;br /&gt;
            shotToolsBtnWF =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,135,35],&amp;quot;WF&amp;quot;);&lt;br /&gt;
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};&lt;br /&gt;
             shotToolsBtnFE =  shotTools.add(&amp;quot;button&amp;quot;,[139,12,167,35],&amp;quot;FE&amp;quot;);&lt;br /&gt;
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};&lt;br /&gt;
            shotToolsBtnXLS =  shotTools.add(&amp;quot;button&amp;quot;,[172,12,197,35],&amp;quot;xls&amp;quot;);&lt;br /&gt;
            //shotToolsBtnXLS.enabled = false;             &lt;br /&gt;
                &lt;br /&gt;
            shotToolsBtn3 =  shotTools.add(&amp;quot;button&amp;quot;,[12,40,102,63],&amp;quot;Grab Sources&amp;quot;);&lt;br /&gt;
            shotToolsBtn4 =  shotTools.add(&amp;quot;button&amp;quot;,[107,40,135,63],&amp;quot;Miss&amp;quot;);&lt;br /&gt;
            shotToolsBtnANI =  shotTools.add(&amp;quot;button&amp;quot;,[139,40,167,63],&amp;quot;ANI&amp;quot;);&lt;br /&gt;
            shotToolsBtnOPT =  shotTools.add(&amp;quot;button&amp;quot;,[172,40,197,63],&amp;quot;Opt&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
              //  shotToolsBtn4.enabled = false;            &lt;br /&gt;
            shotToolsBtn5 =  shotTools.add(&amp;quot;button&amp;quot;,[12,68,102,91],&amp;quot;Renderqueue&amp;quot;);&lt;br /&gt;
            shotToolsBtn6 =  shotTools.add(&amp;quot;button&amp;quot;,[107,68,197,91],&amp;quot;To Watchfolder&amp;quot;);&lt;br /&gt;
                //shotToolsBtn6.enabled = false; &lt;br /&gt;
            }&lt;br /&gt;
       var optnsGrp = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,300],&amp;quot;Options&amp;quot;);&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
            mgL = 20;&lt;br /&gt;
            mgT=15;&lt;br /&gt;
            mgBet=3;&lt;br /&gt;
            i=0;&lt;br /&gt;
            w=210;&lt;br /&gt;
            h=23;&lt;br /&gt;
            version = optnsGrp.add(&amp;quot;statictext&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;version &amp;quot;+version);i++;&lt;br /&gt;
            optMissingFootage =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Check for missing footage\rlonger, but safer&amp;quot;);i++;&lt;br /&gt;
            //$.writeln(&amp;quot;load missing footage pref set to&amp;quot;+(app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true);&lt;br /&gt;
                optMissingFootage.value = a;&lt;br /&gt;
            optNuComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;New comp after sending to WF&amp;quot;);i++;&lt;br /&gt;
                optNuComp.value = b;&lt;br /&gt;
            optSaveComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Ask to save before sending to WF&amp;quot;);i+=2;&lt;br /&gt;
                optSaveComp.value = c;&lt;br /&gt;
            rootFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Root folder location&amp;quot;);i++;&lt;br /&gt;
            watchFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Watchfolder location&amp;quot;);i+=2;&lt;br /&gt;
            okBtn =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;OK&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
       ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////&lt;br /&gt;
       &lt;br /&gt;
        showDDL.onChange = function(){&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
           // shotLoad.remove(shotDDL);&lt;br /&gt;
          shotDDL.removeAll();&lt;br /&gt;
&lt;br /&gt;
          items = returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString());&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               shotDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
        }&lt;br /&gt;
        shotDDL.onChange = function(){&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;,shotDDL.selection.toString());&lt;br /&gt;
            //$.writeln(&amp;quot;Changing&amp;quot;);&lt;br /&gt;
            var lastAEP = getLastModified(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/AEP&amp;quot;,&amp;quot;*.aep&amp;quot;);&lt;br /&gt;
            if(!lastAEP){&lt;br /&gt;
                shotStatusEditTxt.text = &amp;quot;no shot found, build one!&amp;quot;;&lt;br /&gt;
                   shotLoadBtn.enabled = false;&lt;br /&gt;
                   shotBuildBtn.enabled = true;&lt;br /&gt;
            &lt;br /&gt;
            }else{&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;,lastAEP.toString());&lt;br /&gt;
                loadFile = lastAEP.toString();&lt;br /&gt;
                fullSplitPath = lastAEP.toString().split(&amp;quot;/&amp;quot;);&lt;br /&gt;
                fileName = fullSplitPath[fullSplitPath.length-1];&lt;br /&gt;
                if(fileName.length &amp;gt; 20){&lt;br /&gt;
                    shotStatusEditTxt.text = fileName.substr(0,12)+&amp;quot;[...]&amp;quot;+fileName.substr(fileName.length-8);&lt;br /&gt;
                }else{&lt;br /&gt;
                    shotStatusEditTxt.text = fileName;&lt;br /&gt;
                } &lt;br /&gt;
                    shotLoadBtn.enabled = true;&lt;br /&gt;
                    shotBuildBtn.enabled = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotLoadBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(loadFile);&lt;br /&gt;
            if(opFile){&lt;br /&gt;
             app.open(opFile);&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
        shotRefreshBtn.onClick = function(){&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
            writeLn(&amp;quot;If it doesn&amp;#039;t seem to refresh, close and launch again.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        shotBuildBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
            if(opFile.exists){&lt;br /&gt;
                app.open(opFile);&lt;br /&gt;
                for(i=1;i&amp;lt;=app.project.numItems;i++){                     &lt;br /&gt;
                    app.project.items[i].remove();&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            createMainComp(targetFolder);&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            app.project.items.addFolder(&amp;quot;Precomps&amp;quot;);&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
            saveConf = confirm(&amp;quot;Save &amp;quot;+shotDDL.selection.toString()+&amp;quot; in the right folder ?&amp;quot;,false,&amp;quot;Dandelion Shot Builder&amp;quot;);&lt;br /&gt;
            if(saveConf){&lt;br /&gt;
                saveFile = new File(targetFolder+&amp;quot;/AEP/&amp;quot;+showDDL.selection.toString()+&amp;quot;_&amp;quot;+shotDDL.selection.toString()+&amp;quot;_comp01.aep&amp;quot;);&lt;br /&gt;
                app.project.save(saveFile);&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1.onClick = function(){&lt;br /&gt;
            explore(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString());&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1Xtra.onClick = function(){&lt;br /&gt;
            //$.writeln(&amp;quot;button&amp;quot;);&lt;br /&gt;
            p = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            checkOutFolder(p);&lt;br /&gt;
            explore(p);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn3.onClick = function(){&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnXLS.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.xls*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .xls shortcut found in in:&amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
       shotToolsBtnANI.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.mov*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .mov shortcut found in in: &amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnOPT.onClick = function(){&lt;br /&gt;
            shotTools.visible = false;&lt;br /&gt;
            shotLoad.visible = false;&lt;br /&gt;
            optnsGrp.visible = true;&lt;br /&gt;
        }&lt;br /&gt;
    okBtn.onClick = function(){&lt;br /&gt;
            shotTools.visible = true;&lt;br /&gt;
            shotLoad.visible = true;&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;,optMissingFootage.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;,optNuComp.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;,optSaveComp.value);&lt;br /&gt;
           // $.writeln(&amp;quot;new comp checked: &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
        //send to renderqueue&lt;br /&gt;
        //uses the comp name to figure out folder&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
        shotToolsBtn5.onClick = function(){&lt;br /&gt;
        rflag = false;&lt;br /&gt;
        //$.writeln(&amp;quot;missing fottage &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;));&lt;br /&gt;
        if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
            rflag = checkForMissingFootage(false);&lt;br /&gt;
        }&lt;br /&gt;
        if(!rflag){&lt;br /&gt;
            rcomp = findMainShot();&lt;br /&gt;
                if(rcomp){&lt;br /&gt;
                    shotNumber = getSetTake();&lt;br /&gt;
                    if(shotNumber != false){&lt;br /&gt;
                        //$.writeln(shotNumber);&lt;br /&gt;
                        setTake(shotNumber);&lt;br /&gt;
                        rq = app.project.renderQueue;&lt;br /&gt;
                        for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
                                rq.items[i].render = false;&lt;br /&gt;
                         }&lt;br /&gt;
                        rqitem = app.project.renderQueue.items.add(rcomp);&lt;br /&gt;
                        rqitem.outputModules[1].applyTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                        rqitem.applyTemplate(&amp;quot;Multi-Machine Settings&amp;quot;);&lt;br /&gt;
                        //savePath = rootFolder+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/OUT/&amp;quot;+rcomp.name;&lt;br /&gt;
                        savePath = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/&amp;quot;+rcomp.name;&lt;br /&gt;
                        &lt;br /&gt;
                        &lt;br /&gt;
                        if(checkOutFolder(savePath)){&lt;br /&gt;
                           rqitem.outputModules[1].file = new File(savePath+&amp;quot;/&amp;quot;+rcomp.name+&amp;quot;_[#####].tif&amp;quot;);&lt;br /&gt;
                        }else{&lt;br /&gt;
                           alert(&amp;quot;Couldn&amp;#039;t figure out an output folder for \&amp;quot;&amp;quot;+(rcomp.name)+&amp;quot;\&amp;quot;, select a folder manually.&amp;quot;);&lt;br /&gt;
                          rqitem.outputModules[1].file = new File();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;This project is missing source files and will not render. \nUse the &amp;#039;miss&amp;#039; button to find out where the footage should be&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn4.onClick = function(){&lt;br /&gt;
            //shotNumber = getSetTake(true);&lt;br /&gt;
            //setTake(shotNumber);&lt;br /&gt;
            checkForMissingFootage(true);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn6.onClick = function(){&lt;br /&gt;
           wf = new Folder(watchfolderLocation);&lt;br /&gt;
           if(wf.exists){&lt;br /&gt;
               sendToWatchFolder(wf,findMainShot().name);&lt;br /&gt;
               }else{&lt;br /&gt;
                   alert(&amp;quot;Watchfolder error, sorry!&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    rootFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Root folder is currently set to \n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New root folder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;,v);     &lt;br /&gt;
                rootFolder = v;&lt;br /&gt;
                          showDDL.removeAll();&lt;br /&gt;
                          items = returnList(rootFolder);&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               showDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
                showDDL.notify();&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                //$.writeln(&amp;quot;notified&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Watchfolder is currently set to \n\n\&amp;quot;&amp;quot;+watchfolderLocation+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New watchfolder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;,v);     &lt;br /&gt;
                watchfolderLocation = v;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    shotDDL.notify();&lt;br /&gt;
    }&lt;br /&gt;
    dandyShotBuilderUI(this) ;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===layer Position to .txt===&lt;br /&gt;
https://i.imgur.com/G5DX1T0.gif&lt;br /&gt;
Old, might not work! (but should :)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)&lt;br /&gt;
//&lt;br /&gt;
// The script will write to a text file the x &amp;amp; y values of the layer position for every frame comprised in&lt;br /&gt;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
function timeToFrameNum(myTime){&lt;br /&gt;
   return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);&lt;br /&gt;
}&lt;br /&gt;
function framesToTime(myTime){&lt;br /&gt;
   return myTime/app.project.activeItem.frameRate;&lt;br /&gt;
}&lt;br /&gt;
function timeToTimeCode(myTime){&lt;br /&gt;
   var framesN = myTime * app.project.activeItem.frameRate;&lt;br /&gt;
   fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));&lt;br /&gt;
   ho = addZero(Math.floor(myTime/3600));&lt;br /&gt;
   mi = addZero(Math.floor(myTime/60)-ho*60);&lt;br /&gt;
   se = addZero(Math.floor(myTime)-mi*60-ho*3600);&lt;br /&gt;
   return ho+&amp;quot;:&amp;quot;+mi+&amp;quot;:&amp;quot;+se+&amp;quot;:&amp;quot;+fr;&lt;br /&gt;
}&lt;br /&gt;
function addZero(val){&lt;br /&gt;
   if(val&amp;lt;10){&lt;br /&gt;
      val = &amp;quot;0&amp;quot;+val;&lt;br /&gt;
   }&lt;br /&gt;
   return val;&lt;br /&gt;
}&lt;br /&gt;
var myDisp = app.project.timecodeDisplayType;&lt;br /&gt;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;&lt;br /&gt;
var pText = &amp;quot;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 &amp;#039;16: 230;22&amp;#039;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if(app.project.activeItem != &amp;quot;null&amp;quot; &amp;amp;&amp;amp; app.project.activeItem != null &amp;amp;&amp;amp; app.project.activeItem != 0){&lt;br /&gt;
   if(app.project.activeItem.selectedLayers.length != 0){&lt;br /&gt;
      if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
         curLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
         var textName = &amp;quot;AEcoordinates.txt&amp;quot;;&lt;br /&gt;
         var myTextFile = filePutDialog(&amp;quot;Select a location to save your .txt file&amp;quot;, textName, &amp;quot;TEXT txt&amp;quot;);&lt;br /&gt;
         if(myTextFile == null){&lt;br /&gt;
            alert(&amp;quot;You must choose a place to save the file&amp;quot;);&lt;br /&gt;
         }else{&lt;br /&gt;
            var formatString = prompt(pText,&amp;quot;%i: %x;%y%l&amp;quot;);&lt;br /&gt;
            myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
            myKeys = curLayer.property(&amp;quot;position&amp;quot;).selectedKeys;&lt;br /&gt;
            if(myKeys.length != 0){&lt;br /&gt;
               strTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[0]);&lt;br /&gt;
               endTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[myKeys.length-1]);&lt;br /&gt;
            }else{&lt;br /&gt;
               strTime = app.project.activeItem.workAreaStart;&lt;br /&gt;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;&lt;br /&gt;
            }&lt;br /&gt;
            var startLoop = timeToFrameNum(strTime);&lt;br /&gt;
            var endLoop = timeToFrameNum(endTime);&lt;br /&gt;
            for(i=startLoop;i&amp;lt;=endLoop;i++){&lt;br /&gt;
               var curTime = framesToTime(i);&lt;br /&gt;
               out = formatString.replace(&amp;#039;%x&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[0]);&lt;br /&gt;
               out = out.replace(&amp;#039;%y&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[1]);&lt;br /&gt;
               out = out.replace(&amp;#039;%f&amp;#039;,i);timeToTimeCode&lt;br /&gt;
               out = out.replace(&amp;#039;%t&amp;#039;,timeToTimeCode(curTime));&lt;br /&gt;
               out = out.replace(&amp;#039;%i&amp;#039;,i-startLoop);&lt;br /&gt;
               out = out.replace(&amp;#039;%l&amp;#039;,&amp;#039;\n&amp;#039;);&lt;br /&gt;
               myTextFile.write(out);&lt;br /&gt;
               clearOutput();&lt;br /&gt;
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+&amp;quot;% done...&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
         clearOutput();&lt;br /&gt;
         writeLn(endLoop-startLoop+&amp;quot; positions saved to file&amp;quot;);&lt;br /&gt;
         writeLn(&amp;quot;script written by mlk =)&amp;quot;);&lt;br /&gt;
         myTextFile.close();&lt;br /&gt;
         }&lt;br /&gt;
      } else {&lt;br /&gt;
         alert (&amp;quot;This script requires the scripting security preference to be set.\n&amp;quot; +&lt;br /&gt;
         &amp;quot;Go to the \&amp;quot;General\&amp;quot; panel of your application preferences,\n&amp;quot; +&lt;br /&gt;
         &amp;quot;and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   }else{&lt;br /&gt;
      alert(&amp;quot;Select a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
}else{&lt;br /&gt;
   alert(&amp;quot;Select a composition and a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.project.timecodeDisplayType = myDisp;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Other Scripts==&lt;br /&gt;
&lt;br /&gt;
=== Work specific ===&lt;br /&gt;
&lt;br /&gt;
==== Auto scale precomps ====&lt;br /&gt;
Script that precomposes animation layers (specifically that are in .swf):&lt;br /&gt;
* scales up the layer by X amount &lt;br /&gt;
* scales down the precomp by 1/x amount&lt;br /&gt;
* Toggles the continuously rasterize off on the precomp, on inside.&lt;br /&gt;
This specifically solves the problem that lines out of Animate/Flash are &amp;#039;rough&amp;#039; (aliased) when imported at 100%, no matter which flags are toggled.&lt;br /&gt;
Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)&lt;br /&gt;
&lt;br /&gt;
Usage: save as .jsx, run the script via File&amp;gt;Scripts (Ctrl-Alt-Shift-D will redo latest script to repeat the process).&lt;br /&gt;
If no layers are selected, the script will instead prompt which scale factor it should use (4 by default).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how&lt;br /&gt;
(function afterEffectsSmartScale() {&lt;br /&gt;
	&lt;br /&gt;
    var SCRIPT_PREF_KEY = &amp;quot;SmartScaleFactor&amp;quot;;&lt;br /&gt;
	var SCRIPT_NAME = &amp;quot;SmartScaleTool&amp;quot;;&lt;br /&gt;
    var defaultScale = 4; &lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    var selectedLayers = comp.selectedLayers;&lt;br /&gt;
    var scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    function getScaleFactor(set) {&lt;br /&gt;
		var factor = defaultScale;&lt;br /&gt;
		if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){&lt;br /&gt;
			factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);&lt;br /&gt;
		}&lt;br /&gt;
		if(set){&lt;br /&gt;
			var promptResult = prompt(&amp;quot;Enter the scale factor (default is 400% -&amp;gt; 4)\nThen select layer(s) and run again.&amp;quot;, factor);&lt;br /&gt;
			if (promptResult === null) return null; &lt;br /&gt;
			factor = parseFloat(promptResult);&lt;br /&gt;
			app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
        return factor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (selectedLayers.length === 0) {&lt;br /&gt;
        scaleFactorX = getScaleFactor(1);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    scaleFactorX = getScaleFactor();&lt;br /&gt;
    if (scaleFactorX === null) return;&lt;br /&gt;
&lt;br /&gt;
    var scaleMultiplier = scaleFactorX;&lt;br /&gt;
    var inverseMultiplier = 1 / scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Smart Scale Precomposition&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        for (var i = 0; i &amp;lt; selectedLayers.length; i++) {&lt;br /&gt;
            var layer = selectedLayers[i];&lt;br /&gt;
            &lt;br /&gt;
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + &amp;quot;_Scale&amp;quot;, false);&lt;br /&gt;
            var precompLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
			precompLayer.collapseTransformation = false;&lt;br /&gt;
&lt;br /&gt;
            // 2. Inside the precomposition, scale the layer and composition&lt;br /&gt;
            if (precomp.numLayers &amp;gt; 0) {&lt;br /&gt;
                var innerLayer = precomp.layer(1);&lt;br /&gt;
                innerLayer.collapseTransformation = true;&lt;br /&gt;
                // Scale the inner layer&lt;br /&gt;
                var currentScale = innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);&lt;br /&gt;
                &lt;br /&gt;
                // Adjust composition size accordingly&lt;br /&gt;
                precomp.width *= scaleMultiplier;&lt;br /&gt;
                precomp.height *= scaleMultiplier;&lt;br /&gt;
&lt;br /&gt;
                // Adjust the position of the layer within the precomp to keep it centered &lt;br /&gt;
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Position&amp;quot;).setValue(newPos);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // 3. Back in the top composition, scale down the precomposed layer by 1/X&lt;br /&gt;
            var currentOuterScale = precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
            precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);&lt;br /&gt;
        }&lt;br /&gt;
    } catch (e) {&lt;br /&gt;
        alert(&amp;quot;An error occurred: &amp;quot; + e.toString());&lt;br /&gt;
    } finally {&lt;br /&gt;
        app.endUndoGroup();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx&lt;br /&gt;
&lt;br /&gt;
===Shelf===&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//to hijack admin and load shelf from my documents/shelf.jsx, this needs to be in the scriptUI Panel folder&lt;br /&gt;
{&lt;br /&gt;
    var nested_file = new File(&amp;quot;~/Documents/shelfBernie.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	//alert(nested_file.read());&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=891</id>
		<title>Afx Javascript</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=891"/>
		<updated>2025-12-17T00:39:34Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Auto Expose Animation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Scripts==&lt;br /&gt;
=== Copy footage to local folder as proxy===&lt;br /&gt;
https://i.imgur.com/oZ24pac.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
 * simpleLocalProxy.jsx v1.0&lt;br /&gt;
 *&lt;br /&gt;
 * Copies footage from its current location to one chosen by the user (defaults to /tmp/) and allows to&lt;br /&gt;
 * switch original-proxy with a button&lt;br /&gt;
 * 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&amp;#039; file cache on scratch disks (&amp;#039;conformed media&amp;#039;)&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 * https://github.com/berniebernie/after-effects-scripts&lt;br /&gt;
 *    &lt;br /&gt;
 * Copyright 2015, bernie@berniebernie.fr&lt;br /&gt;
 *    &lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT  &lt;br /&gt;
 *&lt;br /&gt;
 * This script embeds js-md5 from github: https://github.com/emn178/js-md5 for practical reasons&lt;br /&gt;
 *&lt;br /&gt;
 * Script that can be launched or put in the scriptui folder of After Effects to be used as a panel&lt;br /&gt;
 *  &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * js-md5 v0.1.2&lt;br /&gt;
 * https://github.com/emn178/js-md5&lt;br /&gt;
 *&lt;br /&gt;
 * Copyright 2014, emn178@gmail.com&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      js-md5.js&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//(function(root, undefined){&lt;br /&gt;
  //&amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  var HEX_CHARS = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
  var HEX_TABLE = {&lt;br /&gt;
    &amp;#039;0&amp;#039;: 0, &amp;#039;1&amp;#039;: 1, &amp;#039;2&amp;#039;: 2, &amp;#039;3&amp;#039;: 3, &amp;#039;4&amp;#039;: 4, &amp;#039;5&amp;#039;: 5, &amp;#039;6&amp;#039;: 6, &amp;#039;7&amp;#039;: 7, &amp;#039;8&amp;#039;: 8, &amp;#039;9&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;a&amp;#039;: 10, &amp;#039;b&amp;#039;: 11, &amp;#039;c&amp;#039;: 12, &amp;#039;d&amp;#039;: 13, &amp;#039;e&amp;#039;: 14, &amp;#039;f&amp;#039;: 15,&lt;br /&gt;
    &amp;#039;A&amp;#039;: 10, &amp;#039;B&amp;#039;: 11, &amp;#039;C&amp;#039;: 12, &amp;#039;D&amp;#039;: 13, &amp;#039;E&amp;#039;: 14, &amp;#039;F&amp;#039;: 15&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var R = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,&lt;br /&gt;
           5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,&lt;br /&gt;
           4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,&lt;br /&gt;
           6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];&lt;br /&gt;
&lt;br /&gt;
  var K = [0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE,&lt;br /&gt;
           0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,&lt;br /&gt;
           0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,&lt;br /&gt;
           0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821,&lt;br /&gt;
           0XF61E2562, 0XC040B340, 0X265E5A51, 0XE9B6C7AA,&lt;br /&gt;
           0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8,&lt;br /&gt;
           0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED,&lt;br /&gt;
           0XA9E3E905, 0XFCEFA3F8, 0X676F02D9, 0X8D2A4C8A,&lt;br /&gt;
           0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C,&lt;br /&gt;
           0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70,&lt;br /&gt;
           0X289B7EC6, 0XEAA127FA, 0XD4EF3085, 0X04881D05,&lt;br /&gt;
           0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665,&lt;br /&gt;
           0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039,&lt;br /&gt;
           0X655B59C3, 0X8F0CCC92, 0XFFEFF47D, 0X85845DD1,&lt;br /&gt;
           0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1,&lt;br /&gt;
           0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391];&lt;br /&gt;
&lt;br /&gt;
  var jsmd5 = function(message) {&lt;br /&gt;
    var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message);&lt;br /&gt;
    var h0 = 0x67452301;&lt;br /&gt;
    var h1 = 0xEFCDAB89;&lt;br /&gt;
    var h2 = 0x98BADCFE;&lt;br /&gt;
    var h3 = 0x10325476;&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0, length = blocks.length;i &amp;lt; length;i += 16)&lt;br /&gt;
    {&lt;br /&gt;
      var a = h0;&lt;br /&gt;
      var b = h1;&lt;br /&gt;
      var c = h2;&lt;br /&gt;
      var d = h3;&lt;br /&gt;
      var f, g, tmp, x, y;&lt;br /&gt;
&lt;br /&gt;
      for(var j = 0;j &amp;lt; 64;++j)&lt;br /&gt;
      {&lt;br /&gt;
        if(j &amp;lt; 16)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (b &amp;amp; c) | ((~b) &amp;amp; d);&lt;br /&gt;
          f = d ^ (b &amp;amp; (c ^ d));&lt;br /&gt;
          g = j;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 32)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (d &amp;amp; b) | ((~d) &amp;amp; c);&lt;br /&gt;
          f = c ^ (d &amp;amp; (b ^ c));&lt;br /&gt;
          g = (5 * j + 1) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 48)&lt;br /&gt;
        {&lt;br /&gt;
          f = b ^ c ^ d;&lt;br /&gt;
          g = (3 * j + 5) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          f = c ^ (b | (~d));&lt;br /&gt;
          g = (7 * j) % 16;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        tmp = d;&lt;br /&gt;
        d = c&lt;br /&gt;
        c = b&lt;br /&gt;
&lt;br /&gt;
        // leftrotate&lt;br /&gt;
        x = (a + f + K[j] + blocks[i + g]);&lt;br /&gt;
        y = R[j];&lt;br /&gt;
        b += (x &amp;lt;&amp;lt; y) | (x &amp;gt;&amp;gt;&amp;gt; (32 - y));&lt;br /&gt;
        a = tmp;&lt;br /&gt;
      }&lt;br /&gt;
      h0 = (h0 + a) | 0;&lt;br /&gt;
      h1 = (h1 + b) | 0;&lt;br /&gt;
      h2 = (h2 + c) | 0;&lt;br /&gt;
      h3 = (h3 + d) | 0;&lt;br /&gt;
    }&lt;br /&gt;
    return toHexString(h0) + toHexString(h1) + toHexString(h2) + toHexString(h3);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var toHexString = function(num) {&lt;br /&gt;
    var hex = &amp;quot;&amp;quot;;&lt;br /&gt;
    for(var i = 0; i &amp;lt; 4; i++)&lt;br /&gt;
    {&lt;br /&gt;
      var offset = i &amp;lt;&amp;lt; 3;&lt;br /&gt;
      hex += HEX_CHARS.charAt((num &amp;gt;&amp;gt; (offset + 4)) &amp;amp; 0x0F) + HEX_CHARS.charAt((num &amp;gt;&amp;gt; offset) &amp;amp; 0x0F);&lt;br /&gt;
    }&lt;br /&gt;
    return hex;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var hasUTF8 = function(message) {&lt;br /&gt;
    var i = message.length;&lt;br /&gt;
    while(i--)&lt;br /&gt;
      if(message.charCodeAt(i) &amp;gt; 127)&lt;br /&gt;
        return true;&lt;br /&gt;
    return false;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var ASCIItoBlocks = function(message) {&lt;br /&gt;
    // a block is 32 bits(4 bytes), a chunk is 512 bits(64 bytes)&lt;br /&gt;
    var length = message.length;&lt;br /&gt;
    var chunkCount = ((length + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    var i;&lt;br /&gt;
    for(i = 0;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    for(i = 0;i &amp;lt; length;++i)&lt;br /&gt;
      blocks[i &amp;gt;&amp;gt; 2] |= message.charCodeAt(i) &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[i &amp;gt;&amp;gt; 2] |= 0x80 &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[blockCount - 2] = length &amp;lt;&amp;lt; 3; // length * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var UTF8toBlocks = function(message) {&lt;br /&gt;
    var uri = encodeURIComponent(message);&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    for(var i = 0, bytes = 0, length = uri.length;i &amp;lt; length;++i)&lt;br /&gt;
    {&lt;br /&gt;
      var c = uri.charCodeAt(i);&lt;br /&gt;
      if(c == 37) // %&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= ((HEX_TABLE[uri.charAt(++i)] &amp;lt;&amp;lt; 4) | HEX_TABLE[uri.charAt(++i)]) &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      else&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= c &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      ++bytes;&lt;br /&gt;
    }&lt;br /&gt;
    var chunkCount = ((bytes + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var index = bytes &amp;gt;&amp;gt; 2;&lt;br /&gt;
    blocks[index] |= 0x80 &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    for(var i = index + 1;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    blocks[blockCount - 2] = bytes &amp;lt;&amp;lt; 3; // bytes * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  /*if(typeof(module) != &amp;#039;undefined&amp;#039;)&lt;br /&gt;
    module.exports = jsmd5;&lt;br /&gt;
  else if(root)&lt;br /&gt;
    root.jsmd5 = jsmd5;*/&lt;br /&gt;
//}(this));&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      simpleLocalProxy.jsx &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(str){&lt;br /&gt;
    //uncomment to allow debugging&lt;br /&gt;
    //$.writeln(str);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pathToLocalizedPath(path){&lt;br /&gt;
        f = new File(path);&lt;br /&gt;
        return f.fsName.toString();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sequenceFilesWildcard(path){ &lt;br /&gt;
    //returns false or an array with everything before a sequence&amp;#039;s image number, and the extension: /c/path/file.0555.exr &amp;gt; { /c/pathfile. ; .exr }&lt;br /&gt;
    var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g; &lt;br /&gt;
    var match = myRegexp.exec(path);&lt;br /&gt;
    if(!match){&lt;br /&gt;
        return false;&lt;br /&gt;
    }else{&lt;br /&gt;
        return match;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabPaths(footage){&lt;br /&gt;
    //returns false or the path of the given footage, if it&amp;#039;s a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr &amp;gt; /c/path/file.*.exr&lt;br /&gt;
    var f = footage;&lt;br /&gt;
    returnpath = false;&lt;br /&gt;
    if(f instanceof FootageItem &amp;amp;&amp;amp; f.file != null){       &lt;br /&gt;
        var source = f.mainSource.file.toString();&lt;br /&gt;
        if(!f.mainSource.isStill){&lt;br /&gt;
            var pathFromRegex = sequenceFilesWildcard(source);&lt;br /&gt;
            if(pathFromRegex){&lt;br /&gt;
                returnpath = pathFromRegex[1]+&amp;quot;*&amp;quot;+pathFromRegex[2];&lt;br /&gt;
            }else{&lt;br /&gt;
                returnpath = source;&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            returnpath = source;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return returnpath;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function grabFootagePathsAndCopyToLocal(){&lt;br /&gt;
    //main worker function&lt;br /&gt;
    &lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1 || !(sel[0] instanceof FootageItem)){&lt;br /&gt;
        alert(&amp;quot;Select footage(s) and try again&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        &lt;br /&gt;
        var debugcheck = (getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var useforcecopy = (getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        &lt;br /&gt;
        var isMacintosh = ($.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;)==-1);&lt;br /&gt;
        var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        &lt;br /&gt;
        var batchFile = (isMacintosh)?&amp;quot;# bash file used to copy After Effects footage to local storage&amp;quot;:&amp;quot;@echo off\nREM batch file to copy After Effects footage to local storage&amp;quot;;&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            if(!sel[i].useProxy){&lt;br /&gt;
                //grab path from current selection item&lt;br /&gt;
                var curPath = grabPaths(sel[i]);&lt;br /&gt;
                var fileName = curPath.split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                &lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
            &lt;br /&gt;
                if(isMacintosh){&lt;br /&gt;
                    //macos uses rsync to copy files&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrsync -v -a &amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot;-I &amp;quot;:&amp;quot;&amp;quot;);&lt;br /&gt;
                    batchFile += dir+fileName;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;+outputDir;&lt;br /&gt;
                    &lt;br /&gt;
                }else{&lt;br /&gt;
                    &lt;br /&gt;
                    //windows uses robocopy&lt;br /&gt;
                    sourcePath = pathToLocalizedPath(dir);&lt;br /&gt;
                    destinationPath = pathToLocalizedPath(outputDir);&lt;br /&gt;
                    filename = fileName.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrobocopy &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += sourcePath;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    //robocopy filename requires a wildcard, even if it&amp;#039;s a single file&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += destinationPath ;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;                    &lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += filename ;&lt;br /&gt;
                    batchFile += &amp;quot;*\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot; /XO&amp;quot;:&amp;quot;&amp;quot;)+&amp;quot; /FFT&amp;quot;+((debugcheck)?&amp;quot;&amp;quot;:&amp;quot; /NJH /NJS&amp;quot;);&lt;br /&gt;
                    e(batchFile);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        batchFile += ((debugcheck)?(isMacintosh?&amp;quot;\nread a&amp;quot;:&amp;quot;\npause&amp;quot;):&amp;quot;&amp;quot;); //nested tertiary operators, sue me&lt;br /&gt;
        &lt;br /&gt;
        var txtFile;&lt;br /&gt;
        if(isMacintosh){&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.command&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.bat&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        if(txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;) == true){&lt;br /&gt;
&lt;br /&gt;
            txtFile.write(batchFile);&lt;br /&gt;
            txtFile.close();&lt;br /&gt;
	    if(isMacintosh){&lt;br /&gt;
&lt;br /&gt;
            		system.callSystem(&amp;quot;chmod +x &amp;quot; + txtFile.toString() +&amp;quot;&amp;quot;);&lt;br /&gt;
system.callSystem(txtFile.toString());&lt;br /&gt;
		}else{&lt;br /&gt;
            txtFile.execute();&lt;br /&gt;
}&lt;br /&gt;
        }else{&lt;br /&gt;
            alert(&amp;quot;Write permission denied on\n&amp;quot;+localSaveDir);&lt;br /&gt;
        }&lt;br /&gt;
        //txtFile.remove();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabFootagePathsAndSwitchProxy(){&lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1){&lt;br /&gt;
        alert(&amp;quot;Select footage first&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            item = sel[i];&lt;br /&gt;
            if(item.useProxy){&lt;br /&gt;
                item.useProxy = false;&lt;br /&gt;
            }else{&lt;br /&gt;
                var curPath = grabPaths(item);&lt;br /&gt;
                var fileName = item.mainSource.file.toString().split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
                var proxyFilePath = outputDir+&amp;quot;/&amp;quot;+fileName;&lt;br /&gt;
                var proxyFile = new File(proxyFilePath);&lt;br /&gt;
                e(proxyFilePath);&lt;br /&gt;
                if(proxyFile.exists){&lt;br /&gt;
                    if(item.mainSource.isStill){&lt;br /&gt;
                        item.setProxy(proxyFile);&lt;br /&gt;
                    }else{&lt;br /&gt;
                        //dirty workaround if the file is a movie file (.mpg, qucktime etc...)&lt;br /&gt;
                        try {&lt;br /&gt;
                            item.setProxyWithSequence(proxyFile,false);&lt;br /&gt;
                        }&lt;br /&gt;
                        catch(err) {&lt;br /&gt;
                            item.setProxy(proxyFile);&lt;br /&gt;
                        }   &lt;br /&gt;
                    }&lt;br /&gt;
                    item.proxySource.alphaMode = item.mainSource.alphaMode;&lt;br /&gt;
                    item.proxySource.premulColor = item.mainSource.premulColor;&lt;br /&gt;
                    item.proxySource.invertAlpha= item.mainSource.invertAlpha;&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(getPref(&amp;quot;debug&amp;quot;,false)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                        alert(&amp;quot;&amp;gt;&amp;gt;&amp;gt; NO PROXY FOUND\n&amp;quot;+proxyFilePath);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPref(pref,value){&lt;br /&gt;
    app.settings.saveSetting(&amp;quot;simpleLocalProxy&amp;quot;, pref, value);&lt;br /&gt;
}&lt;br /&gt;
function getPref(pref,defaultValue){&lt;br /&gt;
    prefsVar = &amp;quot;simpleLocalProxy&amp;quot;;&lt;br /&gt;
    if(app.settings.haveSetting(prefsVar, pref)){&lt;br /&gt;
        return app.settings.getSetting(prefsVar, pref);&lt;br /&gt;
    }else{&lt;br /&gt;
        app.settings.saveSetting(prefsVar, pref, defaultValue);&lt;br /&gt;
        setPref(pref,defaultValue)&lt;br /&gt;
        return defaultValue;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function simpleLocalProxy(thisObj) {&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Local Proxy&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    &lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        var localFolder = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        // UI DESCRIPTION&lt;br /&gt;
        &lt;br /&gt;
        res = &amp;quot;group { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                        cols: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            col1: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                saveDirText: StaticText {text: &amp;#039;Proxy folder setup&amp;#039;},\&lt;br /&gt;
                                saveDirOptions: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    usemd5rbox: RadioButton {text: &amp;#039; Simple&amp;#039;,helpTip:&amp;#039;Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)&amp;#039;,value:true},\&lt;br /&gt;
                                    usepathrbox: RadioButton {text: &amp;#039; Copy Folder Structure&amp;#039;,helpTip:&amp;#039;Copies the target folder structure inside the proxy folder; more folders&amp;#039;}}},\&lt;br /&gt;
                            col2: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                optionsText: StaticText {text: &amp;#039;Options: &amp;#039;},\&lt;br /&gt;
                                optionsGrp: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    forcecopyChkbox: Checkbox {text: &amp;#039; Force copy&amp;#039;,helpTip:&amp;#039;Overwrite files (otherwise skips existing files)&amp;#039;},\&lt;br /&gt;
                                    debugChkbox: Checkbox {text: &amp;#039; Debug&amp;#039;,helpTip:&amp;#039;Show full batch process and pause at end of copies&amp;#039;}}}},\&lt;br /&gt;
                        cols2: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            localSaveDirBut: Button {text: &amp;#039; Choose Proxy Folder &amp;#039;, helpTip:&amp;#039;choose folder to copy files to&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                            browseBut: Button {text: &amp;#039; Browse &amp;#039; , preferredSize:[-1,30]}} , \&lt;br /&gt;
                        curentDirTxt: EditText {text: &amp;#039;&amp;quot; + localFolder +&amp;quot;&amp;#039;,enabled:false},\&lt;br /&gt;
                        copyFootageBut: Button {text: &amp;#039; Copy Footage(s) to Proxy Folder &amp;#039; ,helpTip:&amp;#039;Launches a background batch copy of selected footage&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                        switchproxyBut: Button {text: &amp;#039; Switch Proxy/Original &amp;#039;,helpTip:&amp;#039;Switches from original to proxy path and back,  warning if no local copy has been found&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        //UI DRAW&lt;br /&gt;
        &lt;br /&gt;
        pan.grp = pan.add(res); &lt;br /&gt;
        pan.layout.layout(true);&lt;br /&gt;
        &lt;br /&gt;
        //radio buttons and checkboxes prefs&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
        var usemd5 = getPref(&amp;quot;usemd5&amp;quot;,true);&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5==&amp;quot;true&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
        var useforcecopy = getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var debugcheck = getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
&lt;br /&gt;
        pan.layout.resize();&lt;br /&gt;
        pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
        // UI ACTIONS&lt;br /&gt;
        &lt;br /&gt;
        //browse button&lt;br /&gt;
        pan.grp.cols2.browseBut.onClick = function(){&lt;br /&gt;
                localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
                localSaveDir.execute();&lt;br /&gt;
        }&lt;br /&gt;
        //choose local folder button&lt;br /&gt;
        pan.grp.cols2.localSaveDirBut.onClick = function(){&lt;br /&gt;
            localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
            o = localSaveDir.selectDlg(&amp;quot;Choose folder to copy footage to&amp;quot;);&lt;br /&gt;
            if(o!=null){&lt;br /&gt;
                    setPref(&amp;quot;localSaveDir&amp;quot;,o.toString());&lt;br /&gt;
                    pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
        pan.grp.copyFootageBut.onClick = function(){&lt;br /&gt;
            //copy footages button (launches the &amp;#039;guts&amp;#039; of this script)&lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
            &lt;br /&gt;
            grabFootagePathsAndCopyToLocal();&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        pan.grp.switchproxyBut.onClick = function(){&lt;br /&gt;
            //checks for a local copy of the footage, and switches to a proxy accordingly &lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            &lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
&lt;br /&gt;
            grabFootagePathsAndSwitchProxy();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (pan instanceof Window) pan.show() ;&lt;br /&gt;
}&lt;br /&gt;
simpleLocalProxy(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Expose Animation ===&lt;br /&gt;
Adds &amp;#039;hold&amp;#039; keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD, also 2025 update vibecodin&amp;#039; an update to make it more consistent: https://berniebernie.fr/wiki/Afx_Javascript_Temp#Auto-expose_rewrite/debug .&lt;br /&gt;
&lt;br /&gt;
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;1000&amp;quot; height=&amp;quot;800&amp;quot; &amp;gt;9-3XVlME2tY&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Step 1: setup detector on layer&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;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 &amp;#039;Detector&amp;#039; picks up a change (value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Step 2: bake to keys&amp;#039; ,preferredSize:[-1,30],enabled:true} , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Bakes detected animation as time remapped keys to animation, this can be long, watch your &amp;quot;Info&amp;quot; panel. If some animation is not detected, change resoltion and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    applyKeys: Button {text: &amp;#039;Step 3: apply as time remapping on selected layers&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Select layers on which to apply time remapping (\&amp;quot;exposed\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        //pan.grp.bakeKeys.enabled = true;        &lt;br /&gt;
        setupDetector();&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    curlayer = layers[0];&lt;br /&gt;
&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer);&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index;&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+&amp;quot;_anim_detection&amp;quot;,true);&lt;br /&gt;
    var precomplayer =  app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    var toplayer = precomp.layers[1];&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration;&lt;br /&gt;
    newlayer.timeRemapEnabled = true;&lt;br /&gt;
    newlayer.inPoint -= app.project.activeItem.frameDuration;&lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;Marker&amp;quot;).setValueAtTime(.5, explainer);&lt;br /&gt;
&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var sliderctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;;&lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
   &lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    var detectorctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider;&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to &amp;#039;clean&amp;#039; expression&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // add &amp;#039;0&amp;#039; key on first frame&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.addKey(0);&lt;br /&gt;
    currentSlider.setValueAtKey(1, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for(i = 0;i&amp;lt;layers.length;i++){&lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;Time Remap&amp;quot;);&lt;br /&gt;
        for(j=1;j&amp;lt;=currentSlider.numKeys;j++){&lt;br /&gt;
            &lt;br /&gt;
            v = currentSlider.keyValue(j);&lt;br /&gt;
            t = currentSlider.keyTime(j);&lt;br /&gt;
            remap.addKey(t);&lt;br /&gt;
            remap.setValueAtKey(j, v);&lt;br /&gt;
            remap.setInterpolationTypeAtKey(j,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\&lt;br /&gt;
        a = timeRemap;\&lt;br /&gt;
        nk = a.nearestKey(time);\&lt;br /&gt;
        curframe = 0;\&lt;br /&gt;
        if(nk.time &amp;gt; time){\&lt;br /&gt;
            curframe = nk.index-1;\&lt;br /&gt;
        }else{\&lt;br /&gt;
            curframe = nk.index;\&lt;br /&gt;
        }\&lt;br /&gt;
        curframe*thisComp.frameDuration;&amp;quot;;&lt;br /&gt;
        remap.expressionEnabled = false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create Null controllers on Puppet pins ===&lt;br /&gt;
http://i.imgur.com/HdFjaYZ.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//nulls created from puppet pins can be parented like normal layers&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Create Null Controls on Puppet Pinsv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    function getLayerFromProperty(prop){&lt;br /&gt;
        return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var c = app.project.activeItem;&lt;br /&gt;
    if( c != null &amp;amp;&amp;amp; c.selectedProperties != null){&lt;br /&gt;
        var props = c.selectedProperties;&lt;br /&gt;
        j = 0;&lt;br /&gt;
        for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
            if(props[i].matchName == &amp;quot;ADBE FreePin3 PosPin Atom&amp;quot;){&lt;br /&gt;
                j++;&lt;br /&gt;
                child = props[i].property(&amp;quot;ADBE FreePin3 PosPin Position&amp;quot;);&lt;br /&gt;
                pos = [child.value[0],child.value[1]];&lt;br /&gt;
                nullLayer = app.project.activeItem.layers.addNull();&lt;br /&gt;
                nullLayer.name = &amp;quot;puppetCtrl&amp;quot;+j;&lt;br /&gt;
                nullLayer.position.setValue(pos);&lt;br /&gt;
                var expr = &amp;quot;thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).toWorld(thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).transform.anchorPoint)&amp;quot;;&lt;br /&gt;
                child.expression = expr;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Batch replace file locations with text file===&lt;br /&gt;
http://i.imgur.com/88QHvlQ.jpg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//now works with sequences or still images, windows only&lt;br /&gt;
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path&lt;br /&gt;
function URIToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function WinPathtoURI(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/ /g, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    //str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Change File Locations&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/tempAE.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txt = &amp;quot;&amp;quot;;&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    var isSequence = new Array();&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
       alert(&amp;quot;Select footage items.&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            isSequence[i] = !sel[i].mainSource.isStill;&lt;br /&gt;
            txt += URIToWinPath(sel[i].mainSource.file.toString())+&amp;quot;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        txtFile.write(&amp;quot;*** 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&amp;quot;);&lt;br /&gt;
        txtFile.write(txt);&lt;br /&gt;
        txtFile.close();&lt;br /&gt;
        txtFile.execute();&lt;br /&gt;
        isOk = confirm(&amp;quot;Change file paths ?\n\nReloading might take a while!&amp;quot;);&lt;br /&gt;
        if(isOk){&lt;br /&gt;
           txtFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
           contents = txtFile.read();&lt;br /&gt;
           arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
           for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
                var tmpFile =  new File(WinPathtoURI(arrayContents[i+2]));&lt;br /&gt;
                //alert(tmpFile);&lt;br /&gt;
                if(isSequence[i]){&lt;br /&gt;
                    sel[i].replaceWithSequence(tmpFile,0);&lt;br /&gt;
                }else{&lt;br /&gt;
                    sel[i].replace(tmpFile);&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                writeLn(Math.round((i+1)/app.project.selection.length*100)+&amp;quot;%&amp;quot;);&lt;br /&gt;
           }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Loop Selected Layers===&lt;br /&gt;
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif&lt;br /&gt;
&lt;br /&gt;
No hassle looping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Set loops&amp;quot;);&lt;br /&gt;
var layersList = app.project.activeItem.selectedLayers;&lt;br /&gt;
var frameD = app.project.activeItem.frameDuration;&lt;br /&gt;
for (i=0;i&amp;lt;layersList.length;i++){&lt;br /&gt;
    if(!layersList[i].timeRemapEnabled){&lt;br /&gt;
        var outP = layersList[i].outPoint;&lt;br /&gt;
        layersList[i].timeRemapEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP,0);&lt;br /&gt;
        layersList[i].timeRemap.expressionEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.expression = &amp;quot;loopOut()&amp;quot;;&lt;br /&gt;
        layersList[i].outPoint = app.project.activeItem.workAreaStart+app.project.activeItem.workAreaDuration;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&lt;br /&gt;
===Find Next Text Layer===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
    a = true;&lt;br /&gt;
                if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
                }&lt;br /&gt;
    if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
        var comp = app.project.item(i);&lt;br /&gt;
        for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
            a = true;&lt;br /&gt;
            comp.layer(j).selected = false;&lt;br /&gt;
            if(comp.layer(j) instanceof TextLayer){&lt;br /&gt;
                comp.layer(j).selected = true;&lt;br /&gt;
                $.writeln(comp.name);&lt;br /&gt;
                a = confirm(&amp;quot;Continue selecting text layers&amp;quot;,true);&lt;br /&gt;
            }&lt;br /&gt;
            if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
            }else{&lt;br /&gt;
                comp.layer(j).selected = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(a){&lt;br /&gt;
    alert(&amp;quot;No (more) text layers found&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simple Watchfolder===&lt;br /&gt;
http://i.imgur.com/poTWO.png&lt;br /&gt;
https://i.imgur.com/wvtgDqE.png&lt;br /&gt;
&lt;br /&gt;
I know a lot of people use media encoder but it&amp;#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Show original source location (WIN only)===&lt;br /&gt;
http://i.imgur.com/04O3r.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    function pathToWinPath(path){&lt;br /&gt;
        var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
        str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
        str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
        str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
        return str;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
        alert(&amp;quot;You need to select source(s) in the project panel&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            a = prompt(&amp;quot;&amp;#039;OK&amp;#039; continues, &amp;#039;cancel&amp;#039; stops displaying original sources\n\n[ &amp;quot;+sel[i].name+&amp;quot; ]&amp;quot;,pathToWinPath(sel[i].mainSource.file.path.toString()));    &lt;br /&gt;
            if(!a){break}&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===List selected layers effects and their properties matchNames ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//windows only&lt;br /&gt;
if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/effectList.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    var col = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for (i=0;i&amp;lt;col.length;i++){&lt;br /&gt;
        effs = col[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
            for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
                txtFile.write(&amp;quot;\n( &amp;quot;+effs.property(j).matchName+&amp;quot; ) &amp;quot;+effs.property(j).name+&amp;quot;\n------------------------------------------\n&amp;quot;);&lt;br /&gt;
                for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
                    txtFile.write(effs.property(j).property(k).matchName+&amp;quot; --&amp;gt; &amp;quot;+effs.property(j).property(k).name);&lt;br /&gt;
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE &amp;amp;&amp;amp; effs.property(j).property(k).value != undefined){&lt;br /&gt;
                        txtFile.write(&amp;quot; [ &amp;quot;+effs.property(j).property(k).value.toString()+&amp;quot; ] &amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    txtFile.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    txtFile.write(&amp;quot;\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v = s.Effects.addProperty(\&amp;quot;CC RepeTile\&amp;quot;);\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v.property(\&amp;quot;CC RepeTile-0001\&amp;quot;).setValue(10);&amp;quot;);    &lt;br /&gt;
    txtFile.close();&lt;br /&gt;
    txtFile.execute();&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Set scripting Prefs to enable to write to disk&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/IEUwe.gif&lt;br /&gt;
&lt;br /&gt;
==== Disable effects ====&lt;br /&gt;
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!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function toggleEffectGlobally() {&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Toggle Effect Globally&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    var sel = app.project.activeItem &amp;amp;&amp;amp; app.project.activeItem.selectedProperties;&lt;br /&gt;
    if (!sel || sel.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var selectedEffect = null;&lt;br /&gt;
&lt;br /&gt;
    // Find the selected effect property group&lt;br /&gt;
    for (var i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
        if (sel[i].matchName &amp;amp;&amp;amp; sel[i].parentProperty &amp;amp;&amp;amp; sel[i].parentProperty.matchName === &amp;quot;ADBE Effect Parade&amp;quot;) {&lt;br /&gt;
            selectedEffect = sel[i];&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (!selectedEffect) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect in the timeline.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var effectName = selectedEffect.matchName;&lt;br /&gt;
    var effectEnabled = selectedEffect.enabled;&lt;br /&gt;
&lt;br /&gt;
    // Loop through all comps in the project&lt;br /&gt;
    for (var p = 1; p &amp;lt;= app.project.numItems; p++) {&lt;br /&gt;
        var item = app.project.item(p);&lt;br /&gt;
        if (item instanceof CompItem) {&lt;br /&gt;
            for (var l = 1; l &amp;lt;= item.numLayers; l++) {&lt;br /&gt;
                var layer = item.layer(l);&lt;br /&gt;
                if (layer.property(&amp;quot;ADBE Effect Parade&amp;quot;)) {&lt;br /&gt;
                    var effects = layer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
                    for (var e = 1; e &amp;lt;= effects.numProperties; e++) {&lt;br /&gt;
                        var eff = effects.property(e);&lt;br /&gt;
                        if (eff.matchName === effectName) {&lt;br /&gt;
                            eff.enabled = !effectEnabled;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===List effects===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 function projEffects(){&lt;br /&gt;
 	var effects = new Array();&lt;br /&gt;
 	var effects2 = new Array();&lt;br /&gt;
 	for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
 		if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
 			   var comp = app.project.item(i);&lt;br /&gt;
 				for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
 					effs = comp.layer(j).property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
 					for(k=1;k&amp;lt;=effs.numProperties;k++){&lt;br /&gt;
 						keyName = effs.property(k).matchName;&lt;br /&gt;
 						effects[keyName]  = 1;&lt;br /&gt;
 						}&lt;br /&gt;
 					   &lt;br /&gt;
 					}&lt;br /&gt;
 			}&lt;br /&gt;
 	}&lt;br /&gt;
 	for(a in effects){&lt;br /&gt;
 		effects2[effects2.length] = a;&lt;br /&gt;
 	}&lt;br /&gt;
 	effects2.sort();&lt;br /&gt;
 	return effects2;&lt;br /&gt;
 }&lt;br /&gt;
 alert(projEffects().join(&amp;quot;\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/hFNiQ.jpg&lt;br /&gt;
&lt;br /&gt;
===Multiple sequences import (Windows)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Multiple sequences import (windows)&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//  bernie@berniebernie.fr&lt;br /&gt;
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file&lt;br /&gt;
//  Access to files network required --- no error checking.&lt;br /&gt;
//   &amp;gt; only works on windows so far&lt;br /&gt;
//   &amp;gt; only finds exrs/pngs/jpgs&lt;br /&gt;
//   &amp;gt; probably fails if your sequences dont have the same start frame&lt;br /&gt;
//   &amp;gt; will work with /path/image.####.jpg or similar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function uniq_fast(a) {&lt;br /&gt;
    var seen = {};&lt;br /&gt;
    var out = [];&lt;br /&gt;
    var len = a.length;&lt;br /&gt;
    var j = 0;&lt;br /&gt;
    for(var i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
         var item = a[i];&lt;br /&gt;
         if(seen[item] !== 1) {&lt;br /&gt;
               seen[item] = 1;&lt;br /&gt;
               out[j++] = item;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return out;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
curScript = new File($.fileName);&lt;br /&gt;
&lt;br /&gt;
startT = Date.now();&lt;br /&gt;
&lt;br /&gt;
sourceFolder = app.project.selection[0].mainSource.file.parent;&lt;br /&gt;
tmpFolder = Folder.temp;&lt;br /&gt;
tmpBat = Folder.temp.toString()+&amp;quot;/ae_dirlist.bat&amp;quot;;&lt;br /&gt;
tmpFilesList = Folder.temp.toString()+&amp;quot;/ae_imageslist.txt&amp;quot;;&lt;br /&gt;
var listFile = new File(tmpFilesList);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var batFile = new File(tmpBat);&lt;br /&gt;
batFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;REM Auto generated by this after effects script file: &amp;quot;+curScript.fsName);&lt;br /&gt;
batFile.write(&amp;quot;\npushd \&amp;quot;&amp;quot;+sourceFolder.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;\ndir /b /on *.exr *.png *.jpg *.jpeg &amp;gt; \&amp;quot;&amp;quot;+listFile.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.close();&lt;br /&gt;
system.callSystem(batFile.fsName);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
listFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
contents = listFile.read();&lt;br /&gt;
arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
listFile.close();&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;via batch: &amp;quot;+arrayContents.length+&amp;quot; files found in &amp;quot;+timer+&amp;quot;s &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myRe = new RegExp(&amp;quot;[0-9]{2,}(\)){0,1}\.[a-z]+$&amp;quot;);&lt;br /&gt;
var myArray = myRe.exec(arrayContents[0]);&lt;br /&gt;
endlength = myArray[0].length;&lt;br /&gt;
&lt;br /&gt;
for(i = 0;i&amp;lt;arrayContents.length;i++){&lt;br /&gt;
    arrayContents[i] = arrayContents[i].slice(0, -endlength);&lt;br /&gt;
}&lt;br /&gt;
unique = uniq_fast(arrayContents);&lt;br /&gt;
&lt;br /&gt;
dialog = confirm(unique.length+&amp;quot; sequences found. Load them ? \nIt might take a long time !&amp;quot;,false,&amp;quot;Confirm Loading&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(dialog){      &lt;br /&gt;
    for(i = 0;i&amp;lt;unique.length;i++){&lt;br /&gt;
        if(unique[i].length&amp;gt;0){&lt;br /&gt;
            sequenceStartFile = new File(sourceFolder.toString()+&amp;quot;/&amp;quot;+unique[i]+myArray[0]);&lt;br /&gt;
            &lt;br /&gt;
            if (sequenceStartFile) {&lt;br /&gt;
            writeLn(&amp;quot;Loading &amp;quot;+(i+1)+&amp;quot;/&amp;quot;+unique.length);&lt;br /&gt;
                try {&lt;br /&gt;
                    // Create a variable containing ImportOptions.&lt;br /&gt;
                    var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
                    importOptions.sequence = true;&lt;br /&gt;
                    try { &lt;br /&gt;
&lt;br /&gt;
                        app.project.importFile(importOptions);&lt;br /&gt;
                    } catch (error) {&lt;br /&gt;
                       e(error.toString());&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (error) {&lt;br /&gt;
                    e(error.toString());&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Canceled&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;after regext: &amp;quot;+timer);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Split to Renderqueue ===&lt;br /&gt;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Split Layers to Renderqueue v2.0&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//&lt;br /&gt;
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp&lt;br /&gt;
//  using the in and out points. I use this along with &amp;#039;Magnum&amp;#039; the edit detector to split &amp;amp; render sequences.&lt;br /&gt;
//&lt;br /&gt;
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the &amp;#039;base&amp;#039; renderqueue item that we derive everything from&lt;br /&gt;
&lt;br /&gt;
function pad(n, width, z) {&lt;br /&gt;
  z = z || &amp;#039;0&amp;#039;;&lt;br /&gt;
  n = n + &amp;#039;&amp;#039;;&lt;br /&gt;
  return n.length &amp;gt;= width ? n : new Array(width - n.length + 1).join(z) + n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Split Layers to Renderqueue&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(app.project.renderQueue.items.length == 1){&lt;br /&gt;
    FE = app.project.renderQueue.items[1];&lt;br /&gt;
    ai = FE.comp;&lt;br /&gt;
    path = FE.outputModule(1).file.path;&lt;br /&gt;
    for(i=1;i&amp;lt;=ai.layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
        RI = app.project.renderQueue.items[1].duplicate();&lt;br /&gt;
        RI.timeSpanStart = ai.layers[i].inPoint;&lt;br /&gt;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;&lt;br /&gt;
        $.writeln(ai.workAreaDuration+&amp;quot; &amp;quot;+ai.layers[i].outPoint);&lt;br /&gt;
        RI.outputModule(1).file =  new File(path+&amp;quot;/&amp;quot;+pad(i,2,&amp;quot;0&amp;quot;)+&amp;quot;_&amp;quot;+ai.layers[i].name);&lt;br /&gt;
    }&lt;br /&gt;
    app.project.renderQueue.items[1].render= false;&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Only 1 element should be in renderqueue&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docked Panel SNIP ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//script panel&lt;br /&gt;
{&lt;br /&gt;
	var nested_file = new File(&amp;quot;U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//called file&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
	var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,[100, 100, 300, 300]);&lt;br /&gt;
	impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
	//impButton.onClick = openfile;&lt;br /&gt;
	return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Watchfolder watcher (WIP)===&lt;br /&gt;
http://i.imgur.com/lhO3k.gif&lt;br /&gt;
&lt;br /&gt;
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)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
todo: check if icons are here&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  // $.writeln(&amp;quot;file &amp;gt;&amp;gt;&amp;gt; &amp;quot;+txtfiles[0].toString());&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
                //info = [&amp;quot;debug&amp;quot;,logFolderLocation.toString(),logFolderLocation.name.toString()];&lt;br /&gt;
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];&lt;br /&gt;
               &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
              //  $.writeln(logFolderLocation.toString()+&amp;quot; &amp;lt;&amp;lt;&amp;lt; &amp;quot;)&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    //$.writeln(&amp;quot;Doing shit&amp;quot;);&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                   // info[2] = &amp;quot;n/a+&amp;quot;;&lt;br /&gt;
//                    info[1] = 1;&lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
                       // $.writeln( lines[i].indexOf(phrase));&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
                               //$.writeln(wfold);&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
                                ///info[3] = pathToWinPath(wfold);&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
                               // info[2] = &amp;quot; &amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                   // info[1] = ;&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
              //  $.writeln(info);&lt;br /&gt;
               // $.writeln(contents);&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    //$.writeln(files.length);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            //$.writeln(&amp;quot;&amp;gt;&amp;gt;&amp;gt; &amp;quot;+state+&amp;quot; - &amp;quot;+out+&amp;quot; - &amp;quot;+folders[folder].name);&lt;br /&gt;
            &lt;br /&gt;
           // $.writeln(out);&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
      // $.writeln(vArray[counter2]);&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                                      //  $.writeln(&amp;quot;Debug&amp;quot;);$.writeln(p);&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 600);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
                    // $.writeln(shotinfos.join(&amp;quot;\n&amp;quot;));&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
                    //$.writeln(sL);&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
                      //$.writeln(a);&lt;br /&gt;
                        //$.writeln(shotinfos[i][1]+&amp;quot; &amp;quot;+list.selection.toString());&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                           // $.writeln(&amp;quot;ww&amp;quot;+);&lt;br /&gt;
                       //  $.writeln(a);&lt;br /&gt;
                            //explore(a[3]+((a[1]==2)?&amp;quot;/&amp;quot;+a[0]:&amp;quot;&amp;quot;));&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    //$.writeln(sel[0].subItems[0].text);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
                //refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                //cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                //}else if(reloadEditTxt.text &amp;gt;=10){&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function grow(palette,value){&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
    //    alert(listItem.selection);&lt;br /&gt;
    var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);&lt;br /&gt;
    //var texted = new Array(&amp;quot;...&amp;quot;,&amp;quot;error:&amp;quot;,&amp;quot;rendering&amp;quot;,&amp;quot;...&amp;quot;,&amp;quot;queued&amp;quot;);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    //$.writeln(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+array[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.image = File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    //item.subItems[0].helpTip = texted(state);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
///////////////////////////////////////////&lt;br /&gt;
           /* if((logFolder =  getFilePath(watchfolderLocation+&amp;quot;/&amp;quot;+shot)) != undefined){&lt;br /&gt;
                $.writeln(logInfo(logFolder));&lt;br /&gt;
            }else{&lt;br /&gt;
                $.writeln(&amp;quot;no log folder&amp;quot;);&lt;br /&gt;
                }*/&lt;br /&gt;
           // $.writeln();&lt;br /&gt;
           // addItem(list,&amp;quot;G12_SC213_T1&amp;quot;,2);&lt;br /&gt;
           &lt;br /&gt;
           &lt;br /&gt;
           /*&lt;br /&gt;
var item1 = list.add (&amp;#039;item&amp;#039;, &amp;#039;GB15_SC138_T1&amp;#039;);&lt;br /&gt;
item1.image = File(&amp;quot;~/Desktop/flag_gray.png&amp;quot;);&lt;br /&gt;
item1.subItems[0].text = &amp;#039;Queued...&amp;#039;;&lt;br /&gt;
item1.enabled = false;&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
            //alert(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot));&lt;br /&gt;
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot)*100);&lt;br /&gt;
          &lt;br /&gt;
                       // alert(&amp;quot;test&amp;quot;);&lt;br /&gt;
              //$.writeln(files[file].path+&amp;quot;/&amp;quot;+files[file].name);&lt;br /&gt;
                            //  a+=  getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name).name+&amp;quot;\n&amp;quot;;&lt;br /&gt;
                            &lt;br /&gt;
/*                            &lt;br /&gt;
                            &lt;br /&gt;
                            {&lt;br /&gt;
                                &lt;br /&gt;
        wfLocBtn.onClick = function(){&lt;br /&gt;
            app.scheduleTask(&amp;quot;loop()&amp;quot;,2000,1);&lt;br /&gt;
&lt;br /&gt;
           &lt;br /&gt;
            fold = new Folder( watchfolderLocation.toString());&lt;br /&gt;
            files = fold.getFiles();&lt;br /&gt;
                a= &amp;quot;&amp;quot;;&lt;br /&gt;
            for(file in files){&lt;br /&gt;
&lt;br /&gt;
              $.writeln(getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name));&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
           alert(a);&lt;br /&gt;
&lt;br /&gt;
            writeLn(pBar.value);&lt;br /&gt;
        }&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Import pos from maya===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,&lt;br /&gt;
[100, 100, 300, 300]);&lt;br /&gt;
impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
impButton.onClick = openfile;&lt;br /&gt;
return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
//myToolsPanel.show();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function openfile(){&lt;br /&gt;
	            var myFile = File.openDialog (&amp;quot;Select track file&amp;quot;,&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
            var fileOK = myFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
	//var fileD = OpenDlg (&amp;quot;Tracking Point File&amp;quot;,&amp;quot;*.txt&amp;quot;,true);&lt;br /&gt;
	//txt = fileD.readln ()&lt;br /&gt;
	alert(&amp;quot;txt&amp;quot;+readTxt(myFile));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function readTxt(myFile){&lt;br /&gt;
var myText = myFile.read();	&lt;br /&gt;
return myText;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Dandelion/Amazing World Of Gumball Shotbuilder===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://imgur.com/xKJWB.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For education purposes only, the script was run in production to helb build shots, it:&lt;br /&gt;
* created a list of shots &amp;amp; shows possible given a folder structure (&amp;quot;GB##_SHOWNAME_SC##_T##&amp;quot;)&lt;br /&gt;
* opened the last .AEP file found in said shot folder&lt;br /&gt;
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder&lt;br /&gt;
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the &amp;#039;grab sources&amp;#039; button&lt;br /&gt;
* automatically sent files to the watchfolder to render on a small-ish farm. &lt;br /&gt;
Other buttons allowed to check for missing footage, open the comp&amp;#039;s folder, open the current shows&amp;#039; latest animatic, etc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  &lt;br /&gt;
//  Dandelion Shot Builder for &amp;quot;the Amazing World Of Gumball&amp;quot; v0.7 by Bernie&lt;br /&gt;
//   last update 21/11/10&lt;br /&gt;
//&lt;br /&gt;
//  Known Bugs:&lt;br /&gt;
//      &lt;br /&gt;
//  -you can&amp;#039;t have several sequences in a single folder and expect the script to pick up all the sequences&lt;br /&gt;
//  -this will not set color profiles&lt;br /&gt;
// //TODO&lt;br /&gt;
//  &amp;gt;&amp;gt;&amp;gt; CHECK IF WF FOLDER ALREADY EXISTS&lt;br /&gt;
// &amp;gt;&amp;gt;&amp;gt;&amp;gt; GET TAKE FROM N DRIVE, NOT OUT FOLDER&lt;br /&gt;
//  -v0.7 fixes&lt;br /&gt;
//        -new scene after WF works properly&lt;br /&gt;
//        -changed output folder location to N:&lt;br /&gt;
//  -v0.6 fixes&lt;br /&gt;
//       -cancel watchfolder cancels watchfolder&lt;br /&gt;
//        -fixed GB##_SC_###_T1&lt;br /&gt;
//       -added options&lt;br /&gt;
//       -can work on a new location&lt;br /&gt;
//  -v0.5 fixes&lt;br /&gt;
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)&lt;br /&gt;
//      -will warn if there is missing footage before sending to WF&lt;br /&gt;
//      -removed set take, added &amp;#039;missing&amp;#039; dialog.&lt;br /&gt;
//  -v0.4 fixes&lt;br /&gt;
//      -there shouldn&amp;#039;t be a refresh problem on show change anymore&lt;br /&gt;
//  -v0.3 fixes&lt;br /&gt;
//      -changed watchfolder location to anthony&amp;#039;s mac&lt;br /&gt;
//      -changed save as dialog&lt;br /&gt;
//      -removed unused buttons&lt;br /&gt;
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)&lt;br /&gt;
//  -v0.2 fixes&lt;br /&gt;
//      -sequences weren&amp;#039;t getting imported.&lt;br /&gt;
//      -fixed UI/little problems&lt;br /&gt;
//      -added folders, refresh button, save before watchfoldering&lt;br /&gt;
var version = &amp;quot;0.7&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//default locations&lt;br /&gt;
var watchfolderLocation = &amp;quot;/c/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var rootFolder = &amp;quot;/c/&amp;quot;;&lt;br /&gt;
var forEditFolderLoaction = &amp;quot;//MAC0023DFDF5429/for%20edit&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
rootFolder = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)):rootFolder;&lt;br /&gt;
watchfolderLocation = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)):watchfolderLocation;&lt;br /&gt;
forEditFolderLoaction = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)):forEditFolderLoaction;&lt;br /&gt;
&lt;br /&gt;
          //dirty hack&lt;br /&gt;
            a = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true;&lt;br /&gt;
            b = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)):false;&lt;br /&gt;
            c = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)):true;&lt;br /&gt;
            if(a==&amp;quot;false&amp;quot;) a = false;&lt;br /&gt;
            if(a==&amp;quot;true&amp;quot;) a = true;&lt;br /&gt;
            if(b==&amp;quot;false&amp;quot;) b = false;&lt;br /&gt;
            if(b==&amp;quot;true&amp;quot;) b = true;&lt;br /&gt;
            if(c==&amp;quot;false&amp;quot;) c = false;&lt;br /&gt;
            if(c==&amp;quot;true&amp;quot;) c = true;&lt;br /&gt;
            &lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_&amp;quot;;&lt;br /&gt;
var loadFile = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)):&amp;quot;&amp;quot;;&lt;br /&gt;
checkOutputSettings();&lt;br /&gt;
&lt;br /&gt;
Array.prototype.has = function(value) {&lt;br /&gt;
    var i;&lt;br /&gt;
    for (i=0;i&amp;lt;this.length;i++) {&lt;br /&gt;
        if (this[i] == value) {&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addShotDialog(sel){&lt;br /&gt;
    alert(sel);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    path = path.toString();&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sendToWatchFolder(watchfolderlocation,compname){&lt;br /&gt;
    ocn = compname;&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
        c = confirm(&amp;quot;Save &amp;quot;+app.project.file.name+&amp;quot; ?&amp;quot;,false,&amp;quot;Save Project&amp;quot;);&lt;br /&gt;
        if(c){&lt;br /&gt;
            app.project.save();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    curFile = app.project.file;&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
    compname += &amp;quot;_WF&amp;quot;;&lt;br /&gt;
    myFolder.create();&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;.aep&amp;quot;);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
    myTextFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname.substring(0,22)+&amp;quot;_RCF.txt&amp;quot;);    &lt;br /&gt;
    myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    text = &amp;quot;After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot; ;&lt;br /&gt;
    myTextFile.write(text);&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    writeInfo(watchfolderlocation,ocn);&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;) == &amp;quot;false&amp;quot;){&lt;br /&gt;
        opFile = new File(curFile);&lt;br /&gt;
        if(opFile){&lt;br /&gt;
            app.open(opFile);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
        app.project.new();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    //explore(watchfolderlocation);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function writeInfo(watchfolderlocation,compname){&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_DandyWatch.txt&amp;quot;);    &lt;br /&gt;
    mySaveFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    rq = app.project.renderQueue;&lt;br /&gt;
    text =&amp;quot;&amp;quot;;    &lt;br /&gt;
    for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
        if(rq.items[i].render){&lt;br /&gt;
            text += rq.items[i].outputModules[1].file.path+&amp;quot;\n&amp;quot;;&lt;br /&gt;
            text += (Math.round(rq.items[i].timeSpanDuration*25)+&amp;quot;\n&amp;quot;);&lt;br /&gt;
            text += system.callSystem(&amp;quot;hostname&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    mySaveFile.write(text);&lt;br /&gt;
    mySaveFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkForMissingFootage(dialogFlag){&lt;br /&gt;
        var dialog = dialogFlag;&lt;br /&gt;
        var missing = false;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            app.project.items[i].selected = false;&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                app.project.items[i].selected = true;&lt;br /&gt;
                                missing = true;&lt;br /&gt;
                                if(dialog){&lt;br /&gt;
                                    dialog = prompt(&amp;quot;Original folder of \&amp;quot;&amp;quot;+(app.project.items[i].name)+&amp;quot;\&amp;quot; :&amp;quot;,pathToWinPath(app.project.items[i].file),&amp;quot;Missing footage! (hit cancel to suppress further dialogs)&amp;quot;);&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return missing;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutputSettings(){&lt;br /&gt;
    renderSettingsSet = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;)))?true:false;&lt;br /&gt;
   // renderSettingsSet = 1;&lt;br /&gt;
    if(!renderSettingsSet){&lt;br /&gt;
                alert(&amp;quot;Tiff output being setup, this should happen only once. It needs to close the current comp.&amp;quot;);&lt;br /&gt;
                opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
                if(opFile.exists){&lt;br /&gt;
                     app.open(opFile);&lt;br /&gt;
                     app.project.renderQueue.items[1].outputModules[1].saveAsTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                     app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;,&amp;quot;is there&amp;quot;);&lt;br /&gt;
                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
                     app.newProject();&lt;br /&gt;
                 }else{&lt;br /&gt;
                     alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutFolder(tpath){&lt;br /&gt;
    foldFile = new Folder(tpath);&lt;br /&gt;
    //$.writeln(tpath);&lt;br /&gt;
    if(foldFile.exists){&lt;br /&gt;
        files = foldFile.getFiles();  &lt;br /&gt;
        if(files.length &amp;gt; 1){&lt;br /&gt;
            return false;&lt;br /&gt;
        }else{&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
      newF = foldFile.create();&lt;br /&gt;
        return newF;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function getSetTake(force){&lt;br /&gt;
    show = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;);// showDDL.selection.toString();&lt;br /&gt;
    shot = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;); //shotDDL.selection.toString();&lt;br /&gt;
    projectTake = getTake(findMainShot().name);&lt;br /&gt;
//    paths  = rootFolder+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/OUT/&amp;quot;;&lt;br /&gt;
    paths  = &amp;quot;/n/01_OUT/&amp;quot;+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/&amp;quot;;&lt;br /&gt;
    lastFolder = getLastModified(paths);&lt;br /&gt;
    //alert(lastFolder.name+&amp;quot; --&amp;gt;&amp;quot;+paths);&lt;br /&gt;
    if(lastFolder){&lt;br /&gt;
        folderTake = getTake(lastFolder.name);&lt;br /&gt;
    }else{&lt;br /&gt;
        folderTake = false;&lt;br /&gt;
    }&lt;br /&gt;
    msgPt1 = (folderTake)? &amp;quot;The last take in OUT folder is take &amp;quot;+(folderTake)+&amp;quot;.\n&amp;quot;:&amp;quot;No take was found in the OUT folder.\n&amp;quot;;&lt;br /&gt;
    msgPt2 = &amp;quot;The current project take is &amp;quot;+projectTake+&amp;quot;. \n\nChoose the current take:&amp;quot;;&lt;br /&gt;
    if(force || (folderTake != (projectTake-1))){&lt;br /&gt;
        recommended = (folderTake)?((folderTake)+1):projectTake;&lt;br /&gt;
        answer = prompt(msgPt1+msgPt2,recommended);&lt;br /&gt;
        //$.writeln(&amp;quot;blabal &amp;quot;+answer);&lt;br /&gt;
        if(answer != null){&lt;br /&gt;
            return answer;&lt;br /&gt;
        }else{&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return projectTake;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setTake(value){&lt;br /&gt;
    comp = findMainShot();&lt;br /&gt;
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf(&amp;quot;_T&amp;quot;)+2))+value;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTake(normalizedName){&lt;br /&gt;
    tmp = normalizedName.split(&amp;quot;_T&amp;quot;);&lt;br /&gt;
    return  parseFloat(tmp[tmp.length-1]);&lt;br /&gt;
}&lt;br /&gt;
	// Smart Import.jsx&lt;br /&gt;
	//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases&lt;br /&gt;
    //the only problem is that you can still not have 2 sequences in a single folder&lt;br /&gt;
   &lt;br /&gt;
function SmartImport(targetFolder){&lt;br /&gt;
         writeLn(&amp;quot;Importing files...&amp;quot;);&lt;br /&gt;
         var importedFiles = 0;&lt;br /&gt;
		var scriptName = &amp;quot;Smart Import&amp;quot;;&lt;br /&gt;
		var sourcePaths = getSourcePathsArray();&lt;br /&gt;
		// Ask the user for a folder whose contents are to be imported.&lt;br /&gt;
		//var targetFolder = Folder.selectDialog(&amp;quot;Import items from folder...&amp;quot;);&lt;br /&gt;
		if (targetFolder != null) {&lt;br /&gt;
			// If no project open, create a new project to import the files into.&lt;br /&gt;
			function processFile(theFile)&lt;br /&gt;
			{&lt;br /&gt;
				try {&lt;br /&gt;
					// Create a variable containing ImportOptions.&lt;br /&gt;
					var importOptions = new ImportOptions(theFile);&lt;br /&gt;
                    //alert();&lt;br /&gt;
                     if(theFile.name.toString().toLowerCase().lastIndexOf(&amp;quot;.psd&amp;quot;) != -1){&lt;br /&gt;
                        importOptions.importAs = ImportAsType.COMP;&lt;br /&gt;
                     }&lt;br /&gt;
                       importSafeWithError(importOptions);&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					// Ignore errors.&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			function testForSequences(files)&lt;br /&gt;
			{&lt;br /&gt;
				var searcher = new RegExp(&amp;quot;[0-9]{3,}$&amp;quot;);&lt;br /&gt;
				var movieFileSearcher = new RegExp(&amp;quot;(mov|avi|mpg)$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
				var parseResults = new Array;&lt;br /&gt;
                  var isFolder = new Array;&lt;br /&gt;
				&lt;br /&gt;
				// Test that we have a sequence. Stop parsing after 10 files.&lt;br /&gt;
				for (x = 0; (x &amp;lt; files.length) &amp;amp; x &amp;lt; 10; x++) {&lt;br /&gt;
					var movieFileResult = movieFileSearcher.exec(files[x].name);&lt;br /&gt;
					if (!movieFileResult) {&lt;br /&gt;
//******                           splitName = files[x].name.split(&amp;#039;.&amp;#039;)[0];&lt;br /&gt;
                            splitName=files[x].name.substring(0,files[x].name.length-4);&lt;br /&gt;
&lt;br /&gt;
                           //splitName[splitNameA.length] = &lt;br /&gt;
                           //alert(splitName);	&lt;br /&gt;
                         //  alert(files[x].name+&amp;quot; &amp;gt;&amp;gt;&amp;gt; &amp;quot;+files[x].name.split(&amp;#039;.&amp;#039;)[0]);&lt;br /&gt;
						var currentResult = searcher.exec(splitName);&lt;br /&gt;
						// Regular expressions return null if no match was found.&lt;br /&gt;
						// Otherwise, they return an array with the following information:&lt;br /&gt;
						// array[0] = the matched string.&lt;br /&gt;
						// array[1..n] = the matched capturing parentheses.&lt;br /&gt;
                   				&lt;br /&gt;
                    if (currentResult) { // We have a match -- the string contains numbers.&lt;br /&gt;
                           &lt;br /&gt;
                            // The match of those numbers is stored in the array[1]&lt;br /&gt;
							// Take that number and save it into parseResults.&lt;br /&gt;
							parseResults[parseResults.length] = currentResult[0];&lt;br /&gt;
						} else {&lt;br /&gt;
							parseResults[parseResults.length] = null;&lt;br /&gt;
						}&lt;br /&gt;
                           if(files[x].name == splitName){&lt;br /&gt;
                                isFolder[isFolder.length] = true;&lt;br /&gt;
                               // alert(&amp;quot;Folder &amp;quot;+files[x].name);&lt;br /&gt;
                           }else{&lt;br /&gt;
                                isFolder[isFolder.length] = false;&lt;br /&gt;
                           }&lt;br /&gt;
					} else {&lt;br /&gt;
						parseResults[parseResults.length] = null;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// If all the files we just went through have a number in their file names, &lt;br /&gt;
				// assume they are part of a sequence and return the first file.&lt;br /&gt;
				&lt;br /&gt;
				var result = null;&lt;br /&gt;
				for (i = 0; i &amp;lt; parseResults.length; ++i) {&lt;br /&gt;
                   				&lt;br /&gt;
                   if(!isFolder[i]){&lt;br /&gt;
                    if (parseResults[i]) {&lt;br /&gt;
						if (!result) {&lt;br /&gt;
                            &lt;br /&gt;
							result = files[i];		&lt;br /&gt;
						}&lt;br /&gt;
					} else {&lt;br /&gt;
                        &lt;br /&gt;
						// In this case, a file name did not contain a number.&lt;br /&gt;
						result = null;&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
                   }&lt;br /&gt;
                }&lt;br /&gt;
				&lt;br /&gt;
				return result;&lt;br /&gt;
			}&lt;br /&gt;
        			&lt;br /&gt;
			&lt;br /&gt;
			function importSafeWithError(importOptions)&lt;br /&gt;
			{&lt;br /&gt;
              if(!(sourcePaths.has(importOptions.file.toString()))){&lt;br /&gt;
 				try { &lt;br /&gt;
                  b = app.project.importFile(importOptions);&lt;br /&gt;
                    writeLn(importOptions.file.toString());&lt;br /&gt;
                 sfi = sourcesFolderItem();&lt;br /&gt;
                   b.parentFolder = sfi;&lt;br /&gt;
                    if(importOptions.importAs == ImportAsType.COMP){&lt;br /&gt;
                        for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                            if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name.lastIndexOf(&amp;quot; Layers&amp;quot;) != -1){&lt;br /&gt;
                         app.project.items[i].parentFolder = sfi;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                        writeLn(&amp;quot;Importing file (&amp;quot;+importedFiles+&amp;quot;)&amp;quot;);&lt;br /&gt;
                        importedFiles++;&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					alert(error.toString() + importOptions.file.fsName, scriptName);&lt;br /&gt;
				}&lt;br /&gt;
                // }else{&lt;br /&gt;
                //        alert(&amp;quot;file already here&amp;quot;);&lt;br /&gt;
                 }&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			&lt;br /&gt;
			function processFolder(theFolder)&lt;br /&gt;
			{&lt;br /&gt;
				// Get an array of files in the target folder.&lt;br /&gt;
				var files = theFolder.getFiles();&lt;br /&gt;
				//alert(files);&lt;br /&gt;
				// Test whether theFolder contains a sequence.&lt;br /&gt;
				var sequenceStartFile = testForSequences(files);&lt;br /&gt;
				&lt;br /&gt;
				// If it does contain a sequence, import the sequence,&lt;br /&gt;
				if (sequenceStartFile) {&lt;br /&gt;
					try {&lt;br /&gt;
						// Create a variable containing ImportOptions.&lt;br /&gt;
						var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
						importOptions.sequence = true;&lt;br /&gt;
						// importOptions.forceAlphabetical = true;		// Un-comment this if you want to force alpha order by default.&lt;br /&gt;
						importSafeWithError(importOptions);&lt;br /&gt;
					} catch (error) {&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// Otherwise, import the files and recurse.&lt;br /&gt;
				&lt;br /&gt;
				for (index in files) { // Go through the array and set each element to singleFile, then run the following.&lt;br /&gt;
					if (files[index] instanceof File) {&lt;br /&gt;
						if (!sequenceStartFile) { // If file is already part of a sequence, don&amp;#039;t import it individually.&lt;br /&gt;
							processFile(files[index]); // Calls the processFile function above.&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					if (files[index] instanceof Folder) {&lt;br /&gt;
						processFolder(files[index]); // recursion&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// Recursively examine that folder.&lt;br /&gt;
			processFolder(targetFolder);&lt;br /&gt;
		}&lt;br /&gt;
    clearOutput();	&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
   &lt;br /&gt;
function sourcesFolderItem(){&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
       // alert(app.project.items[i].name);&lt;br /&gt;
     if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name == &amp;quot;Sources&amp;quot;){&lt;br /&gt;
           return app.project.items[i];&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
    return app.project.items.addFolder(&amp;quot;Sources&amp;quot;);&lt;br /&gt;
    //else&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getSourcePathsArray(){&lt;br /&gt;
    var footageLocations = new Array();&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
        if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
            footageLocations[footageLocations.length] = app.project.item(i).file;&lt;br /&gt;
        }    &lt;br /&gt;
    }&lt;br /&gt;
    return footageLocations;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getLastModified(location,extension){&lt;br /&gt;
      myFolder = new Folder(location.toString());&lt;br /&gt;
      if (!myFolder instanceof Folder){&lt;br /&gt;
          return false;&lt;br /&gt;
     }&lt;br /&gt;
     listFiles = myFolder.getFiles(extension);&lt;br /&gt;
     listDates = new Array();&lt;br /&gt;
     maxId = 0;&lt;br /&gt;
     maxValue = 0;&lt;br /&gt;
     for(i=0;i&amp;lt;listFiles.length;i++){&lt;br /&gt;
         if (!(extension == &amp;quot;&amp;quot; &amp;amp;&amp;amp; (listFiles[i] instanceof File))){&lt;br /&gt;
             d = listFiles[i].modified;&lt;br /&gt;
             formattedDate = d.getFullYear()+&amp;quot;&amp;quot;+pad(d.getMonth()+1,2,0)+&amp;quot;&amp;quot;+pad(d.getDate(),2,0)+&amp;quot;&amp;quot;+pad(d.getHours(),2,0)+&amp;quot;&amp;quot;+pad(d.getMinutes(),2,0)+&amp;quot;&amp;quot;+pad(d.getSeconds(),2,0);&lt;br /&gt;
             listDates[i] = formattedDate;&lt;br /&gt;
             if(Math.max(maxValue,formattedDate) == formattedDate){&lt;br /&gt;
                 maxValue = formattedDate;&lt;br /&gt;
                 maxId = i;&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
     &lt;br /&gt;
     }&lt;br /&gt;
    if(listFiles[maxId] == &amp;quot;&amp;quot; ||listFiles[maxId] == &amp;quot;undefined&amp;quot;||listFiles[maxId] == null){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return listFiles[maxId];     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(number, length, character) {&lt;br /&gt;
    if (character == null) {&lt;br /&gt;
        character = &amp;quot;0&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    var numberStr = &amp;quot;&amp;quot; + number;&lt;br /&gt;
    while (numberStr.length &amp;lt; length) {&lt;br /&gt;
        numberStr = character + numberStr;&lt;br /&gt;
    }&lt;br /&gt;
    return numberStr;&lt;br /&gt;
}&lt;br /&gt;
function grabAnimatic(folderLocation){&lt;br /&gt;
    newLocation = new Folder(folderLocation);&lt;br /&gt;
    animatic = newLocation.getFiles(&amp;quot;*.mov&amp;quot;);&lt;br /&gt;
    sfi = sourcesFolderItem();&lt;br /&gt;
    if(animatic[0]){&lt;br /&gt;
        try {&lt;br /&gt;
            var importOptions = new ImportOptions(animatic[0]);&lt;br /&gt;
            animatic = app.project.importFile(importOptions);&lt;br /&gt;
            for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                    if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
                   app.project.item(i).parentFolder = sfi;&lt;br /&gt;
                    }    &lt;br /&gt;
            }&lt;br /&gt;
            return animatic;&lt;br /&gt;
        }catch (error){&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function figureOutShotName(filepath){&lt;br /&gt;
    folders = filepath.split(&amp;quot;/&amp;quot;);&lt;br /&gt;
    showFolder = folders[folders.length-2];&lt;br /&gt;
    showFolder = showFolder.split(&amp;quot;_&amp;quot;)[0];&lt;br /&gt;
    shotFolder = folders[folders.length-1];&lt;br /&gt;
    var searcher = new RegExp(&amp;quot;[0-9]{2,}$&amp;quot;);&lt;br /&gt;
    var currentResult = searcher.exec(shotFolder);&lt;br /&gt;
    if (currentResult){&lt;br /&gt;
        shotFolder = currentResult[0];&lt;br /&gt;
    }&lt;br /&gt;
    return showFolder+&amp;quot;_SC&amp;quot;+shotFolder;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function createMainComp(path){&lt;br /&gt;
    grabAnimatic(path);&lt;br /&gt;
    shotName = figureOutShotName(path);&lt;br /&gt;
    workComp = app.project.items.addComp(shotName+&amp;quot;_T1&amp;quot;, 1920, 1080,1.0 ,animatic.duration,25.0);&lt;br /&gt;
    animaticLayer = workComp.layers.add(animatic);&lt;br /&gt;
    animaticLayer.guideLayer = true;&lt;br /&gt;
    return workComp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnList2(type){&lt;br /&gt;
     if(type == &amp;quot;show&amp;quot;){&lt;br /&gt;
        rArray = returnFolderArray(rootFolder);&lt;br /&gt;
        for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
            }&lt;br /&gt;
    }else if(type == &amp;quot;scene&amp;quot;){&lt;br /&gt;
        rArray = [&amp;quot;a&amp;quot;,&amp;quot;b&amp;quot;];&lt;br /&gt;
        alert(dPanel.shotLoad.showDDL.selection.toString());&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function returnList(folder){&lt;br /&gt;
    rArray = returnFolderArray(folder);&lt;br /&gt;
    for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function findMainShot(){&lt;br /&gt;
    //fix GB##_SC_###_T1&lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               nuName = app.project.items[i].name;&lt;br /&gt;
               nuName = nuName.split(&amp;quot;SC_&amp;quot;);&lt;br /&gt;
               app.project.items[i].name = nuName[0]+&amp;quot;SC&amp;quot;+nuName[1];&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    var shot = null;&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               shot = app.project.items[i];&lt;br /&gt;
               break;&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if(shot == null){&lt;br /&gt;
        alert(&amp;quot;Could not find a comp named like \&amp;quot;GB##_SC###(a-z)_T#\&amp;quot;\n\nRename main comp accordingly, render again&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return shot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function dandyShotBuilderUI(thisObj){&lt;br /&gt;
    &lt;br /&gt;
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
    &lt;br /&gt;
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;DandyShotBuilderFix&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
           var msg = &amp;quot;This script requires the scripting security preference to be set. \nGo the \&amp;quot;General\&amp;quot; panel of your application preferences and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.\n\n Restart AFX&amp;quot;;&lt;br /&gt;
           var shotInfo = dPanel.add(&amp;quot;edittext&amp;quot;,[10,5,235,305],msg,{multiline:true}); &lt;br /&gt;
         }else{&lt;br /&gt;
      var shotLoad = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,175],&amp;quot;Load Shot&amp;quot;);&lt;br /&gt;
         var shotLoadTxt1 = shotLoad.add(&amp;quot;statictext&amp;quot;,[15,24,52,44],&amp;quot;Show:&amp;quot;);&lt;br /&gt;
         var showDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,19,210,44], returnList(rootFolder));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)){&lt;br /&gt;
                 showDDL.selection = showDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 showDDL.selection = 0;&lt;br /&gt;
                 app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
                }&lt;br /&gt;
         var shotLoadTxt2 = shotLoad.add(&amp;quot;statictext&amp;quot;,[12,59,52,79],&amp;quot;Scene:&amp;quot;);&lt;br /&gt;
         var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;shot&amp;quot;)){&lt;br /&gt;
                 shotDDL.selection = shotDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 shotDDL.selection = 0;&lt;br /&gt;
                }&lt;br /&gt;
         &lt;br /&gt;
          &lt;br /&gt;
         var shotStatusTxt = shotLoad.add(&amp;quot;statictext&amp;quot;,[25,95,52,115],&amp;quot;File:&amp;quot;);&lt;br /&gt;
         var shotStatusEditTxt = shotLoad.add(&amp;quot;edittext&amp;quot;,[56,92,210,115],&amp;quot;...&amp;quot;);&lt;br /&gt;
            shotStatusEditTxt.active = false;&lt;br /&gt;
            shotStatusEditTxt.enabled = false;&lt;br /&gt;
         var shotLoadBtn = shotLoad.add(&amp;quot;button&amp;quot;,[56,125,96,150],&amp;quot;Load&amp;quot;);&lt;br /&gt;
            shotLoadBtn.enabled = false;&lt;br /&gt;
         var shotBuildBtn = shotLoad.add(&amp;quot;button&amp;quot;,[101,125,141,150],&amp;quot;Build&amp;quot;);&lt;br /&gt;
            shotBuildBtn.enabled = false;&lt;br /&gt;
         var shotRefreshBtn = shotLoad.add(&amp;quot;button&amp;quot;,[146,125,168,150],&amp;quot;r&amp;quot;);&lt;br /&gt;
         //var shotOptns = shotLoad.add(&amp;quot;button&amp;quot;,[174,125,210,150],&amp;quot;optns&amp;quot;);&lt;br /&gt;
            //shotSetBtn.enabled = false;&lt;br /&gt;
        &lt;br /&gt;
       var shotTools = dPanel.add(&amp;quot;panel&amp;quot;,[10,185,235,300],&amp;quot;Misc&amp;quot;);&lt;br /&gt;
            shotToolsBtn1 =  shotTools.add(&amp;quot;button&amp;quot;,[12,12,79,35],&amp;quot;Open Folder&amp;quot;);&lt;br /&gt;
            shotToolsBtn1Xtra =  shotTools.add(&amp;quot;button&amp;quot;,[82,12,102,35],&amp;quot;N:&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2 =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,197,35],&amp;quot;Flick Last Take&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2.enabled = false; &lt;br /&gt;
            shotToolsBtnWF =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,135,35],&amp;quot;WF&amp;quot;);&lt;br /&gt;
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};&lt;br /&gt;
             shotToolsBtnFE =  shotTools.add(&amp;quot;button&amp;quot;,[139,12,167,35],&amp;quot;FE&amp;quot;);&lt;br /&gt;
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};&lt;br /&gt;
            shotToolsBtnXLS =  shotTools.add(&amp;quot;button&amp;quot;,[172,12,197,35],&amp;quot;xls&amp;quot;);&lt;br /&gt;
            //shotToolsBtnXLS.enabled = false;             &lt;br /&gt;
                &lt;br /&gt;
            shotToolsBtn3 =  shotTools.add(&amp;quot;button&amp;quot;,[12,40,102,63],&amp;quot;Grab Sources&amp;quot;);&lt;br /&gt;
            shotToolsBtn4 =  shotTools.add(&amp;quot;button&amp;quot;,[107,40,135,63],&amp;quot;Miss&amp;quot;);&lt;br /&gt;
            shotToolsBtnANI =  shotTools.add(&amp;quot;button&amp;quot;,[139,40,167,63],&amp;quot;ANI&amp;quot;);&lt;br /&gt;
            shotToolsBtnOPT =  shotTools.add(&amp;quot;button&amp;quot;,[172,40,197,63],&amp;quot;Opt&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
              //  shotToolsBtn4.enabled = false;            &lt;br /&gt;
            shotToolsBtn5 =  shotTools.add(&amp;quot;button&amp;quot;,[12,68,102,91],&amp;quot;Renderqueue&amp;quot;);&lt;br /&gt;
            shotToolsBtn6 =  shotTools.add(&amp;quot;button&amp;quot;,[107,68,197,91],&amp;quot;To Watchfolder&amp;quot;);&lt;br /&gt;
                //shotToolsBtn6.enabled = false; &lt;br /&gt;
            }&lt;br /&gt;
       var optnsGrp = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,300],&amp;quot;Options&amp;quot;);&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
            mgL = 20;&lt;br /&gt;
            mgT=15;&lt;br /&gt;
            mgBet=3;&lt;br /&gt;
            i=0;&lt;br /&gt;
            w=210;&lt;br /&gt;
            h=23;&lt;br /&gt;
            version = optnsGrp.add(&amp;quot;statictext&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;version &amp;quot;+version);i++;&lt;br /&gt;
            optMissingFootage =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Check for missing footage\rlonger, but safer&amp;quot;);i++;&lt;br /&gt;
            //$.writeln(&amp;quot;load missing footage pref set to&amp;quot;+(app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true);&lt;br /&gt;
                optMissingFootage.value = a;&lt;br /&gt;
            optNuComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;New comp after sending to WF&amp;quot;);i++;&lt;br /&gt;
                optNuComp.value = b;&lt;br /&gt;
            optSaveComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Ask to save before sending to WF&amp;quot;);i+=2;&lt;br /&gt;
                optSaveComp.value = c;&lt;br /&gt;
            rootFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Root folder location&amp;quot;);i++;&lt;br /&gt;
            watchFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Watchfolder location&amp;quot;);i+=2;&lt;br /&gt;
            okBtn =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;OK&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
       ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////&lt;br /&gt;
       &lt;br /&gt;
        showDDL.onChange = function(){&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
           // shotLoad.remove(shotDDL);&lt;br /&gt;
          shotDDL.removeAll();&lt;br /&gt;
&lt;br /&gt;
          items = returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString());&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               shotDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
        }&lt;br /&gt;
        shotDDL.onChange = function(){&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;,shotDDL.selection.toString());&lt;br /&gt;
            //$.writeln(&amp;quot;Changing&amp;quot;);&lt;br /&gt;
            var lastAEP = getLastModified(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/AEP&amp;quot;,&amp;quot;*.aep&amp;quot;);&lt;br /&gt;
            if(!lastAEP){&lt;br /&gt;
                shotStatusEditTxt.text = &amp;quot;no shot found, build one!&amp;quot;;&lt;br /&gt;
                   shotLoadBtn.enabled = false;&lt;br /&gt;
                   shotBuildBtn.enabled = true;&lt;br /&gt;
            &lt;br /&gt;
            }else{&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;,lastAEP.toString());&lt;br /&gt;
                loadFile = lastAEP.toString();&lt;br /&gt;
                fullSplitPath = lastAEP.toString().split(&amp;quot;/&amp;quot;);&lt;br /&gt;
                fileName = fullSplitPath[fullSplitPath.length-1];&lt;br /&gt;
                if(fileName.length &amp;gt; 20){&lt;br /&gt;
                    shotStatusEditTxt.text = fileName.substr(0,12)+&amp;quot;[...]&amp;quot;+fileName.substr(fileName.length-8);&lt;br /&gt;
                }else{&lt;br /&gt;
                    shotStatusEditTxt.text = fileName;&lt;br /&gt;
                } &lt;br /&gt;
                    shotLoadBtn.enabled = true;&lt;br /&gt;
                    shotBuildBtn.enabled = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotLoadBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(loadFile);&lt;br /&gt;
            if(opFile){&lt;br /&gt;
             app.open(opFile);&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
        shotRefreshBtn.onClick = function(){&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
            writeLn(&amp;quot;If it doesn&amp;#039;t seem to refresh, close and launch again.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        shotBuildBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
            if(opFile.exists){&lt;br /&gt;
                app.open(opFile);&lt;br /&gt;
                for(i=1;i&amp;lt;=app.project.numItems;i++){                     &lt;br /&gt;
                    app.project.items[i].remove();&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            createMainComp(targetFolder);&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            app.project.items.addFolder(&amp;quot;Precomps&amp;quot;);&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
            saveConf = confirm(&amp;quot;Save &amp;quot;+shotDDL.selection.toString()+&amp;quot; in the right folder ?&amp;quot;,false,&amp;quot;Dandelion Shot Builder&amp;quot;);&lt;br /&gt;
            if(saveConf){&lt;br /&gt;
                saveFile = new File(targetFolder+&amp;quot;/AEP/&amp;quot;+showDDL.selection.toString()+&amp;quot;_&amp;quot;+shotDDL.selection.toString()+&amp;quot;_comp01.aep&amp;quot;);&lt;br /&gt;
                app.project.save(saveFile);&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1.onClick = function(){&lt;br /&gt;
            explore(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString());&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1Xtra.onClick = function(){&lt;br /&gt;
            //$.writeln(&amp;quot;button&amp;quot;);&lt;br /&gt;
            p = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            checkOutFolder(p);&lt;br /&gt;
            explore(p);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn3.onClick = function(){&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnXLS.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.xls*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .xls shortcut found in in:&amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
       shotToolsBtnANI.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.mov*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .mov shortcut found in in: &amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnOPT.onClick = function(){&lt;br /&gt;
            shotTools.visible = false;&lt;br /&gt;
            shotLoad.visible = false;&lt;br /&gt;
            optnsGrp.visible = true;&lt;br /&gt;
        }&lt;br /&gt;
    okBtn.onClick = function(){&lt;br /&gt;
            shotTools.visible = true;&lt;br /&gt;
            shotLoad.visible = true;&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;,optMissingFootage.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;,optNuComp.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;,optSaveComp.value);&lt;br /&gt;
           // $.writeln(&amp;quot;new comp checked: &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
        //send to renderqueue&lt;br /&gt;
        //uses the comp name to figure out folder&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
        shotToolsBtn5.onClick = function(){&lt;br /&gt;
        rflag = false;&lt;br /&gt;
        //$.writeln(&amp;quot;missing fottage &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;));&lt;br /&gt;
        if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
            rflag = checkForMissingFootage(false);&lt;br /&gt;
        }&lt;br /&gt;
        if(!rflag){&lt;br /&gt;
            rcomp = findMainShot();&lt;br /&gt;
                if(rcomp){&lt;br /&gt;
                    shotNumber = getSetTake();&lt;br /&gt;
                    if(shotNumber != false){&lt;br /&gt;
                        //$.writeln(shotNumber);&lt;br /&gt;
                        setTake(shotNumber);&lt;br /&gt;
                        rq = app.project.renderQueue;&lt;br /&gt;
                        for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
                                rq.items[i].render = false;&lt;br /&gt;
                         }&lt;br /&gt;
                        rqitem = app.project.renderQueue.items.add(rcomp);&lt;br /&gt;
                        rqitem.outputModules[1].applyTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                        rqitem.applyTemplate(&amp;quot;Multi-Machine Settings&amp;quot;);&lt;br /&gt;
                        //savePath = rootFolder+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/OUT/&amp;quot;+rcomp.name;&lt;br /&gt;
                        savePath = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/&amp;quot;+rcomp.name;&lt;br /&gt;
                        &lt;br /&gt;
                        &lt;br /&gt;
                        if(checkOutFolder(savePath)){&lt;br /&gt;
                           rqitem.outputModules[1].file = new File(savePath+&amp;quot;/&amp;quot;+rcomp.name+&amp;quot;_[#####].tif&amp;quot;);&lt;br /&gt;
                        }else{&lt;br /&gt;
                           alert(&amp;quot;Couldn&amp;#039;t figure out an output folder for \&amp;quot;&amp;quot;+(rcomp.name)+&amp;quot;\&amp;quot;, select a folder manually.&amp;quot;);&lt;br /&gt;
                          rqitem.outputModules[1].file = new File();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;This project is missing source files and will not render. \nUse the &amp;#039;miss&amp;#039; button to find out where the footage should be&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn4.onClick = function(){&lt;br /&gt;
            //shotNumber = getSetTake(true);&lt;br /&gt;
            //setTake(shotNumber);&lt;br /&gt;
            checkForMissingFootage(true);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn6.onClick = function(){&lt;br /&gt;
           wf = new Folder(watchfolderLocation);&lt;br /&gt;
           if(wf.exists){&lt;br /&gt;
               sendToWatchFolder(wf,findMainShot().name);&lt;br /&gt;
               }else{&lt;br /&gt;
                   alert(&amp;quot;Watchfolder error, sorry!&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    rootFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Root folder is currently set to \n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New root folder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;,v);     &lt;br /&gt;
                rootFolder = v;&lt;br /&gt;
                          showDDL.removeAll();&lt;br /&gt;
                          items = returnList(rootFolder);&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               showDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
                showDDL.notify();&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                //$.writeln(&amp;quot;notified&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Watchfolder is currently set to \n\n\&amp;quot;&amp;quot;+watchfolderLocation+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New watchfolder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;,v);     &lt;br /&gt;
                watchfolderLocation = v;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    shotDDL.notify();&lt;br /&gt;
    }&lt;br /&gt;
    dandyShotBuilderUI(this) ;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===layer Position to .txt===&lt;br /&gt;
https://i.imgur.com/G5DX1T0.gif&lt;br /&gt;
Old, might not work! (but should :)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)&lt;br /&gt;
//&lt;br /&gt;
// The script will write to a text file the x &amp;amp; y values of the layer position for every frame comprised in&lt;br /&gt;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
function timeToFrameNum(myTime){&lt;br /&gt;
   return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);&lt;br /&gt;
}&lt;br /&gt;
function framesToTime(myTime){&lt;br /&gt;
   return myTime/app.project.activeItem.frameRate;&lt;br /&gt;
}&lt;br /&gt;
function timeToTimeCode(myTime){&lt;br /&gt;
   var framesN = myTime * app.project.activeItem.frameRate;&lt;br /&gt;
   fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));&lt;br /&gt;
   ho = addZero(Math.floor(myTime/3600));&lt;br /&gt;
   mi = addZero(Math.floor(myTime/60)-ho*60);&lt;br /&gt;
   se = addZero(Math.floor(myTime)-mi*60-ho*3600);&lt;br /&gt;
   return ho+&amp;quot;:&amp;quot;+mi+&amp;quot;:&amp;quot;+se+&amp;quot;:&amp;quot;+fr;&lt;br /&gt;
}&lt;br /&gt;
function addZero(val){&lt;br /&gt;
   if(val&amp;lt;10){&lt;br /&gt;
      val = &amp;quot;0&amp;quot;+val;&lt;br /&gt;
   }&lt;br /&gt;
   return val;&lt;br /&gt;
}&lt;br /&gt;
var myDisp = app.project.timecodeDisplayType;&lt;br /&gt;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;&lt;br /&gt;
var pText = &amp;quot;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 &amp;#039;16: 230;22&amp;#039;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if(app.project.activeItem != &amp;quot;null&amp;quot; &amp;amp;&amp;amp; app.project.activeItem != null &amp;amp;&amp;amp; app.project.activeItem != 0){&lt;br /&gt;
   if(app.project.activeItem.selectedLayers.length != 0){&lt;br /&gt;
      if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
         curLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
         var textName = &amp;quot;AEcoordinates.txt&amp;quot;;&lt;br /&gt;
         var myTextFile = filePutDialog(&amp;quot;Select a location to save your .txt file&amp;quot;, textName, &amp;quot;TEXT txt&amp;quot;);&lt;br /&gt;
         if(myTextFile == null){&lt;br /&gt;
            alert(&amp;quot;You must choose a place to save the file&amp;quot;);&lt;br /&gt;
         }else{&lt;br /&gt;
            var formatString = prompt(pText,&amp;quot;%i: %x;%y%l&amp;quot;);&lt;br /&gt;
            myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
            myKeys = curLayer.property(&amp;quot;position&amp;quot;).selectedKeys;&lt;br /&gt;
            if(myKeys.length != 0){&lt;br /&gt;
               strTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[0]);&lt;br /&gt;
               endTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[myKeys.length-1]);&lt;br /&gt;
            }else{&lt;br /&gt;
               strTime = app.project.activeItem.workAreaStart;&lt;br /&gt;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;&lt;br /&gt;
            }&lt;br /&gt;
            var startLoop = timeToFrameNum(strTime);&lt;br /&gt;
            var endLoop = timeToFrameNum(endTime);&lt;br /&gt;
            for(i=startLoop;i&amp;lt;=endLoop;i++){&lt;br /&gt;
               var curTime = framesToTime(i);&lt;br /&gt;
               out = formatString.replace(&amp;#039;%x&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[0]);&lt;br /&gt;
               out = out.replace(&amp;#039;%y&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[1]);&lt;br /&gt;
               out = out.replace(&amp;#039;%f&amp;#039;,i);timeToTimeCode&lt;br /&gt;
               out = out.replace(&amp;#039;%t&amp;#039;,timeToTimeCode(curTime));&lt;br /&gt;
               out = out.replace(&amp;#039;%i&amp;#039;,i-startLoop);&lt;br /&gt;
               out = out.replace(&amp;#039;%l&amp;#039;,&amp;#039;\n&amp;#039;);&lt;br /&gt;
               myTextFile.write(out);&lt;br /&gt;
               clearOutput();&lt;br /&gt;
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+&amp;quot;% done...&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
         clearOutput();&lt;br /&gt;
         writeLn(endLoop-startLoop+&amp;quot; positions saved to file&amp;quot;);&lt;br /&gt;
         writeLn(&amp;quot;script written by mlk =)&amp;quot;);&lt;br /&gt;
         myTextFile.close();&lt;br /&gt;
         }&lt;br /&gt;
      } else {&lt;br /&gt;
         alert (&amp;quot;This script requires the scripting security preference to be set.\n&amp;quot; +&lt;br /&gt;
         &amp;quot;Go to the \&amp;quot;General\&amp;quot; panel of your application preferences,\n&amp;quot; +&lt;br /&gt;
         &amp;quot;and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   }else{&lt;br /&gt;
      alert(&amp;quot;Select a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
}else{&lt;br /&gt;
   alert(&amp;quot;Select a composition and a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.project.timecodeDisplayType = myDisp;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Other Scripts==&lt;br /&gt;
&lt;br /&gt;
=== Work specific ===&lt;br /&gt;
&lt;br /&gt;
==== Auto scale precomps ====&lt;br /&gt;
Script that precomposes animation layers (specifically that are in .swf):&lt;br /&gt;
* scales up the layer by X amount &lt;br /&gt;
* scales down the precomp by 1/x amount&lt;br /&gt;
* Toggles the continuously rasterize off on the precomp, on inside.&lt;br /&gt;
This specifically solves the problem that lines out of Animate/Flash are &amp;#039;rough&amp;#039; (aliased) when imported at 100%, no matter which flags are toggled.&lt;br /&gt;
Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)&lt;br /&gt;
&lt;br /&gt;
Usage: save as .jsx, run the script via File&amp;gt;Scripts (Ctrl-Alt-Shift-D will redo latest script to repeat the process).&lt;br /&gt;
If no layers are selected, the script will instead prompt which scale factor it should use (4 by default).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how&lt;br /&gt;
(function afterEffectsSmartScale() {&lt;br /&gt;
	&lt;br /&gt;
    var SCRIPT_PREF_KEY = &amp;quot;SmartScaleFactor&amp;quot;;&lt;br /&gt;
	var SCRIPT_NAME = &amp;quot;SmartScaleTool&amp;quot;;&lt;br /&gt;
    var defaultScale = 4; &lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    var selectedLayers = comp.selectedLayers;&lt;br /&gt;
    var scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    function getScaleFactor(set) {&lt;br /&gt;
		var factor = defaultScale;&lt;br /&gt;
		if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){&lt;br /&gt;
			factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);&lt;br /&gt;
		}&lt;br /&gt;
		if(set){&lt;br /&gt;
			var promptResult = prompt(&amp;quot;Enter the scale factor (default is 400% -&amp;gt; 4)\nThen select layer(s) and run again.&amp;quot;, factor);&lt;br /&gt;
			if (promptResult === null) return null; &lt;br /&gt;
			factor = parseFloat(promptResult);&lt;br /&gt;
			app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
        return factor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (selectedLayers.length === 0) {&lt;br /&gt;
        scaleFactorX = getScaleFactor(1);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    scaleFactorX = getScaleFactor();&lt;br /&gt;
    if (scaleFactorX === null) return;&lt;br /&gt;
&lt;br /&gt;
    var scaleMultiplier = scaleFactorX;&lt;br /&gt;
    var inverseMultiplier = 1 / scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Smart Scale Precomposition&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        for (var i = 0; i &amp;lt; selectedLayers.length; i++) {&lt;br /&gt;
            var layer = selectedLayers[i];&lt;br /&gt;
            &lt;br /&gt;
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + &amp;quot;_Scale&amp;quot;, false);&lt;br /&gt;
            var precompLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
			precompLayer.collapseTransformation = false;&lt;br /&gt;
&lt;br /&gt;
            // 2. Inside the precomposition, scale the layer and composition&lt;br /&gt;
            if (precomp.numLayers &amp;gt; 0) {&lt;br /&gt;
                var innerLayer = precomp.layer(1);&lt;br /&gt;
                innerLayer.collapseTransformation = true;&lt;br /&gt;
                // Scale the inner layer&lt;br /&gt;
                var currentScale = innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);&lt;br /&gt;
                &lt;br /&gt;
                // Adjust composition size accordingly&lt;br /&gt;
                precomp.width *= scaleMultiplier;&lt;br /&gt;
                precomp.height *= scaleMultiplier;&lt;br /&gt;
&lt;br /&gt;
                // Adjust the position of the layer within the precomp to keep it centered &lt;br /&gt;
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Position&amp;quot;).setValue(newPos);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // 3. Back in the top composition, scale down the precomposed layer by 1/X&lt;br /&gt;
            var currentOuterScale = precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
            precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);&lt;br /&gt;
        }&lt;br /&gt;
    } catch (e) {&lt;br /&gt;
        alert(&amp;quot;An error occurred: &amp;quot; + e.toString());&lt;br /&gt;
    } finally {&lt;br /&gt;
        app.endUndoGroup();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx&lt;br /&gt;
&lt;br /&gt;
===Shelf===&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//to hijack admin and load shelf from my documents/shelf.jsx, this needs to be in the scriptUI Panel folder&lt;br /&gt;
{&lt;br /&gt;
    var nested_file = new File(&amp;quot;~/Documents/shelfBernie.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	//alert(nested_file.read());&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript_Temp&amp;diff=890</id>
		<title>Afx Javascript Temp</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript_Temp&amp;diff=890"/>
		<updated>2025-12-08T13:13:11Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* PNG to image source */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
=== Auto-expose rewrite/debug===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
// v1.1 - made the 8bit switch w/o user input, fixed bugs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
// currentSlider holds a reference to the Detector property after setupDetector runs,&lt;br /&gt;
// making it accessible to bakeKeys and applyKeys.&lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
var savedBitDepth = app.project.bitsPerChannel;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    // Determine if the script is running as a dockable panel or a floating window&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 100]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;Step1: Select a footage or layer in a comp and move to a frame where there is a change in animation.  Click setup button and increase resolution slider until the (red) slider value called \\&amp;#039;Detector\\&amp;#039; picks up a change (a value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Setup detector guide layer&amp;#039; } , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Step2: Bake the detected animation as time remapped keys, this can take some time, keep an eye on your \\\&amp;quot;Info\\\&amp;quot; panel. If some animation is not detected, change resolution slider and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Bake to Keys&amp;#039;} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Step3: Select layers on which to apply time remapping (\\\&amp;quot;exposed\\\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
					applyKeys: Button {text: &amp;#039;Apply as time remapping&amp;#039;} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    &lt;br /&gt;
    // Assign click handlers&lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        setupDetector();&lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    // Layout and resizing management for the UI&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
// Execute the UI function&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// Helper function for writing debug messages&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    // Check if a composition is active&lt;br /&gt;
    if (!(app.project.activeItem instanceof CompItem)) {&lt;br /&gt;
        alert(&amp;quot;Please select a Composition and one layer.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Check if one layer is selected&lt;br /&gt;
    var selectedLayers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    if (selectedLayers.length !== 1) {&lt;br /&gt;
        alert(&amp;quot;Please select exactly one layer to set up the detector on.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
	app.project.bitsPerChannel = 8;&lt;br /&gt;
	&lt;br /&gt;
	//move playhead to frame 1 instead of 0 if user hasn&amp;#039;t moved, so that the script shows a change (should be a fully black frame if no animation is detected)&lt;br /&gt;
	if(app.project.activeItem.time == 0){&lt;br /&gt;
		app.project.activeItem.time = app.project.activeItem.frameDuration;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
    var curlayer = selectedLayers[0];&lt;br /&gt;
&lt;br /&gt;
    // 1. Duplicate the layer and precompose the duplicate&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    // Move the original layer before the duplicate for consistent indexing&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer); &lt;br /&gt;
    &lt;br /&gt;
    // Get the index of the duplicate layer before precomposing&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index; &lt;br /&gt;
    &lt;br /&gt;
    // Precompose the duplicate layer&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index], duplicatelayer.name + &amp;quot;_anim_detection&amp;quot;, true);&lt;br /&gt;
    &lt;br /&gt;
    // Get the layer *in the active comp* that references the new precomp&lt;br /&gt;
    var precomplayer = app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    // 2. Solo the detector precomp layer&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled &amp;amp;&amp;amp; allLayers[i].index !== precomplayer.index){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    // 3. Inside the Precomposition (precomp): setup the difference layer&lt;br /&gt;
    var toplayer = precomp.layers[1]; // The duplicated layer that was precomposed&lt;br /&gt;
&lt;br /&gt;
    // Remove existing effects from the top layer inside the precomp&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Duplicate the top layer inside the precomp&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    // 1 frame shift + difference blendmode = highlight pixel changes&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.timeRemapEnabled = true; // Enable Time Remapping&lt;br /&gt;
    // Shift the duplicated layer&amp;#039;s content by one frame&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration; &lt;br /&gt;
    // Shift the layer&amp;#039;s inPoint to compensate&lt;br /&gt;
    newlayer.inPoint = precomp.frameDuration; &lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;ADBE Marker&amp;quot;).setValueAtTime(.5, explainer); // Use match name &amp;quot;ADBE Marker&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    // Add black solid at the bottom to ensure correct difference result&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // 4. Add Slider Controls to the Precomp Layer in the main comp (precomplayer)&lt;br /&gt;
    var effectsGroup = precomplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Resolution Slider&lt;br /&gt;
    var sliderctrl = effectsGroup.addProperty(&amp;quot;ADBE Slider Control&amp;quot;); &lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;; &lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    // Detector Slider&lt;br /&gt;
    var detectorctrl = effectsGroup.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    // Expression to sample the difference precomp. It samples the previous frame&amp;#039;s result &lt;br /&gt;
    // because the precomp content itself is already a difference calculation between the current &lt;br /&gt;
    // and previous frame (due to the -1 frame shift on the layer inside the precomp).&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            // Sample image at time-1 frameDuration because the comp content itself is the difference.\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time-thisComp.frameDuration);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    // Normalize to 0-1, scale up for sensitivity, then subtract to baseline 0 for no change\&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
	detectorexpression = &amp;quot;\&lt;br /&gt;
	var resolution = effect(&amp;#039;Resolution&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\&lt;br /&gt;
resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
var cw = thisComp.width/resolution;\&lt;br /&gt;
var ch = thisComp.height/resolution;\&lt;br /&gt;
var a = [0,0,0,0];\&lt;br /&gt;
for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
	for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
		center = [cw/resolution/2+cw * j ,ch/resolution/2+ch/resolution * i ];\&lt;br /&gt;
		sampledistance = [cw/2,ch/2];\&lt;br /&gt;
		a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
	}\&lt;br /&gt;
}\&lt;br /&gt;
// show accumulated delta\&lt;br /&gt;
(a[0]+a[1]+a[2])*1000;\&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider; // Assign to the global variable&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // Guardrail: check if setupDetector has been run&lt;br /&gt;
    if (currentSlider === &amp;quot;none&amp;quot; || !(currentSlider instanceof Property)) {&lt;br /&gt;
        alert(&amp;quot;Please run &amp;#039;Step 1: setup detector on layer&amp;#039; first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose bake to keys&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    // 1. Bake slider keys (initial movement detection)&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
    // 2. Apply secondary expression to convert movement value (&amp;gt;0) to 1, or 0&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    &lt;br /&gt;
    // 3. Bake the 0 or 1 expression (clean expression)&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
    // 4. Clean up keys and set the correct key values&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        // If the value is 1 (movement detected)&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            // Set the value to the key&amp;#039;s time, this is the desired exposed time&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            // Remove keys where no movement was detected (value == 0)&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // 5. Add &amp;#039;0&amp;#039; key on first frame (Time 0)&lt;br /&gt;
    var compStartTime = app.project.activeItem.time; &lt;br /&gt;
    &lt;br /&gt;
    // Add key at the comp&amp;#039;s start time (usually 0)&lt;br /&gt;
    currentSlider.addKey(compStartTime); &lt;br /&gt;
    &lt;br /&gt;
    var keyIndex = 1; &lt;br /&gt;
    &lt;br /&gt;
    if (currentSlider.numKeys &amp;gt; 0) {&lt;br /&gt;
        // If the first key is not at compStartTime, insert the new key at index 1&lt;br /&gt;
        if (currentSlider.keyTime(1) !== compStartTime) { &lt;br /&gt;
            currentSlider.setValueAtTime(compStartTime, 0); // Adds key, returns index&lt;br /&gt;
            keyIndex = currentSlider.nearestKeyIndex(compStartTime);&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        // No keys left, add the first key&lt;br /&gt;
        currentSlider.setValueAtTime(compStartTime, 0);&lt;br /&gt;
        keyIndex = 1;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.setValueAtKey(keyIndex, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(keyIndex,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
    app.project.bitsPerChannel = savedBitDepth;&lt;br /&gt;
	app.endUndoGroup();&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // Guardrail: check if detection and baking has been run&lt;br /&gt;
    if (currentSlider === &amp;quot;none&amp;quot; || !(currentSlider instanceof Property)) {&lt;br /&gt;
        alert(&amp;quot;Please run &amp;#039;Step 1: setup detector on layer&amp;#039; first, then &amp;#039;Step 2: bake to keys&amp;#039;.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    if (!(comp instanceof CompItem)) {&lt;br /&gt;
        alert(&amp;quot;Please select a Composition and layers to apply keys to.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var layers = comp.selectedLayers;&lt;br /&gt;
    if (layers.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select layers to apply keys to.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose apply keys&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0; i &amp;lt; layers.length; i++){ &lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        // Use the match name for Time Remap property&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;ADBE Time Remapping&amp;quot;); &lt;br /&gt;
        &lt;br /&gt;
        // Remove existing keys first for a clean application&lt;br /&gt;
        while (remap.numKeys &amp;gt; 0) {&lt;br /&gt;
            remap.removeKey(1);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Copy the baked keys from the detector slider to the layer&amp;#039;s Time Remap&lt;br /&gt;
        for(var j=1;j&amp;lt;=currentSlider.numKeys;j++){ &lt;br /&gt;
            &lt;br /&gt;
            var v = currentSlider.keyValue(j); // Value (the frame time)&lt;br /&gt;
            var t = currentSlider.keyTime(j); // Time (the moment of exposure)&lt;br /&gt;
            &lt;br /&gt;
            remap.addKey(t); &lt;br /&gt;
            // Setting value at time is the robust way to set keys&lt;br /&gt;
            remap.setValueAtTime(t, v); &lt;br /&gt;
            &lt;br /&gt;
            // Find the index of the key we just added&lt;br /&gt;
            var keyIndex = remap.nearestKeyIndex(t); &lt;br /&gt;
&lt;br /&gt;
            remap.setInterpolationTypeAtKey(keyIndex,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Expression for sequential exposure (if enabled by user):&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\n\&lt;br /&gt;
        a = timeRemap;\n\&lt;br /&gt;
        nk = a.nearestKey(time);\n\&lt;br /&gt;
        curframe = 0;\n\&lt;br /&gt;
        if(nk.time &amp;gt; time){\n\&lt;br /&gt;
            // nearest key is in the future, use the previous key index\n\&lt;br /&gt;
            curframe = nk.index-1;\n\&lt;br /&gt;
        }else{\n\&lt;br /&gt;
            // nearest key is now or in the past, use its index\n\&lt;br /&gt;
            curframe = nk.index;\n\&lt;br /&gt;
        }\n\&lt;br /&gt;
        // Get the value of the key (which is the actual time) and hold it\n\&lt;br /&gt;
        if (curframe &amp;gt; 0) {\n\&lt;br /&gt;
            a.keyValue(curframe);\n\&lt;br /&gt;
        } else {\n\&lt;br /&gt;
            // Before the first key, hold the time 0.\n\&lt;br /&gt;
            0;\n\&lt;br /&gt;
        }\n\&lt;br /&gt;
        &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        remap.expressionEnabled = false; // Start with keyframes active, expression disabled&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== PNG to image source ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var path = &amp;#039;/c/temp/temp2/&amp;#039;;&lt;br /&gt;
items = [&amp;#039;blue&amp;#039;,&amp;#039;red&amp;#039;,&amp;#039;red_o&amp;#039;,&amp;#039;green&amp;#039;,&amp;#039;green_o&amp;#039;,&amp;#039;gray&amp;#039;,&amp;#039;gray_o&amp;#039;];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myFile = new File(&amp;quot;/c/temp/temp2/pngs.txt&amp;quot;);&lt;br /&gt;
myFile.open(&amp;quot;w&amp;quot;);&lt;br /&gt;
myFile.encoding = &amp;quot;UTF-8&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var text = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
for (var i = 0, len = items.length; i &amp;lt; len; i++) {&lt;br /&gt;
item = items[i];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var f = File(path+item+&amp;#039;.png&amp;#039;);&lt;br /&gt;
    f.encoding = &amp;#039;BINARY&amp;#039;;&lt;br /&gt;
    f.open(&amp;#039;e&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
    var binary;&lt;br /&gt;
    binary = f.read().toSource();&lt;br /&gt;
&lt;br /&gt;
    //var myFile = new File(&amp;quot;/c/temp/temp2/&amp;quot;+item+&amp;quot;.txt&amp;quot;);&lt;br /&gt;
//            myFile.open(&amp;quot;w&amp;quot;);&lt;br /&gt;
//            myFile.encoding = &amp;quot;UTF-8&amp;quot;;&lt;br /&gt;
text += item + &amp;quot; = &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
temp = binary.toString();&lt;br /&gt;
temp = temp.replace(&amp;#039;(new String(&amp;#039;,&amp;#039;&amp;#039;);&lt;br /&gt;
temp = temp.replace(&amp;#039;))&amp;#039;,&amp;#039;;&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
text += temp;&lt;br /&gt;
text += &amp;#039;\n&amp;#039;;&lt;br /&gt;
            //myFile.write(binary.toString());&lt;br /&gt;
            //myFile.write(&amp;#039;\n\n&amp;#039;);&lt;br /&gt;
//            myFile.close();&lt;br /&gt;
&lt;br /&gt;
    //$.writeln(binary);&lt;br /&gt;
&lt;br /&gt;
    f.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myFile.write(text);&lt;br /&gt;
&lt;br /&gt;
myFile.close();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Watchfolder watcher ===&lt;br /&gt;
tb integrated&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
// v1.1 updated this 15 year old script for a job, surprised that it still worked-ish!&lt;br /&gt;
// &lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//states&lt;br /&gt;
// 0 - queued, paused           -&amp;gt; gray_o&lt;br /&gt;
// 1 - queued, ready to start   -&amp;gt; green_o&lt;br /&gt;
// 1 - done, no errors          -&amp;gt; green&lt;br /&gt;
// 2 - canceled, errors         -&amp;gt; red&lt;br /&gt;
// 3 - paused (?)               -&amp;gt; gray&lt;br /&gt;
// 4 - in progress              -&amp;gt; blue&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//ui pngs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
blue = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00DCIDAT8\u008D\u00A5\u0093\u00B1N\x02A\x14E\u00CF{\u00BB\u0089T\x04$F\u00D8\u0086\u00CE\u00BF\u00D0\u00DE\u00D2X\u0091\u00F8!\u00FE\x0B\u008D\u0095%\r\u00FE\u0085\u00DD~\x01\x1D\x18-\u008C\x15[8\u00D7\u008250\u0082\u00D9\u00D9\u00EC\u00ADf\u0092w\u00CF\u00DC\u00FB\u00921It\u0091wr\x03\u00F9\u00E1\u00C5\x16\u00EF7`s\u00CC\u00F3\u00FF\f;iK\u00F0\u0099\u00EE\u0087e&amp;lt;\u0098e\x0F\x04]AS-Ux\u00B8\x06\u00CA\u00B8\u00824M\u00CAmV!{\u0083\u00BF;\u0090M\u0092\x00\u00B2@\u00F6}\x02\u0080.\u0092\x00(#\u009CL\u00C0 \u00CD\u00CF\x19=\u00DFD\x00{Z\u00F50\x1A\u00B6_\u00CBq\u00DD\u009E\x7F\u00C5\t\u00FA\u0083K\u00D4\u00B8\u00FE\u009D\x02\x1F\u00BF\u00C7\u0083\x17\u00C3\x04c\x03&amp;lt;7\u00D8\x05\u00FEr\f0\x15\u00C0\u00AB\u00EEF\u008FI)j\u00ED+\x18\x05\u00B0nc\u008E\x01b\\Wh\u00A5}\x05g\u0089\u00E9\u00B3-\u00C0\u00BA~\u00E7\x1F\u0090\u00E3&amp;lt;q\u00B2\\\x19\u00D4\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
red = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00E5IDAT8\u008D\u00A5\u0093;N\x031\x14E\u00CF\u00F5D\u0090\u0092\x06\u0085\u00A4\u00A1c\x01\x14tD\u00CA&amp;amp;\u0090\&amp;quot;X\b{\u00A1\u00A1\u00A2\u00A0%\u008B\x18\th\u00B2\t\x06)\x05P\u0091(\u00E3KE\x14gD\u00F0h^eK&amp;gt;\u00E7}l\u00CB6]\&amp;quot;t\u00A2\u0081\u00DE\u00F6\u00E6\u00F1\u00E2|l\u00B8\u00B3\u00D4\u00FB\x0B\x00\x10|G1\u009D\u0096\u00AF\u00F3\u00F4`\b7\u00D8g\u00FA&amp;#039;\u00AB\u00F0R\u00E8\x12\u0098&amp;#039;-\u00D8\u009C\u00E6\u0094m\u00C2\x12\u00FC\x0E\u00BB3\u00B0\u00879\x02\u00E1\u0088\u00D5\x14H:\u00CE\x11\u0080\nQ7\x05\u00C6G9\u00B8\u00F1\u00E1Z\u00FD*\x11\u00DCO&amp;amp;}vneO\u0084\u00EB\u00B2\u00FCJ\x04\x07\u00AB\u00CF\x01\u0090\u00FB\u00AA\x16\u00BF\u008BM\u00C6\u00A2\u00D6\x10\u00A8\u0084\x1E\u00F6\u0091V\u00B4\u00EAb\u00D6\x10\x10\x19\x11x\u00BE*_n3\u00AB\x00\u00B6\u0087(F\u008A\u00BC\u00B5\u0081S\x01\u009C\u0080\u00AB\u00B6\u0082M\x0B\x0Ez\n\u00EB\u00F0\u00D1V\u00A0\u00AE\u00DF\u00F9\x07\x1F\u0090C\u00FA\u00D1U\r\x03\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
red_o = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00FEIDAT8\u008D\u00A5\u00931N\x02A\x18\u0085\u00BF7\x18C,L\u00AC\x168\u00C6\u00C6@\u00A5\u00E1\x10\u00C6hg\u00EB\t\u00F0\f^\u00C2\x16\u00B0\u00E4\x02\u0094\u009B(\x16\u009E\u00C1D\u00E9\u00A4\u00D1\u00A8\u00EC\u00FC6\u00EE\u00C2\u00AC\u00BA,\u00D9\u0097L\u00F16\u00EF\u00FFf\u00E7MFfF\x1D\u00B9Z\u00D3\u00C0\u00CE\u00BA\x19w\u00E3c\u0093\u00AE\u008B\u00DF\u00FF\u00D0\u0087\x17\u0097\u00E7\u00C9\u00EC1\b\u009A\u00DC\u0089`*\u00D9m\u00D9\u00B47\x06\u0082# \x04\b\u00DA^~t\u0096&amp;lt;\u00CC\u00CA\x00\u00E3\u00DE\u00A1\f?\u0087B\x07\u0086E\u0098\u00E6\x1B~?\u00C8\x15Kl\u0089t#`=W\x04DK5_*\x00V93\u00C3\u00CC\u00B8\u00E9\u00F7\u009B\u00C3n\u00FC\u0096\u00F9\u00FF\u00D6O\u00EE=\u00F3y\u0089\u00BB\u009F\u008B\b\u00F45\u00EC\u00C5\u0083\u00B2\u00AD\u00F7\u00D0\u00BE\u00C1S\u00E6s@#U\x1BX8\u00DCA\x19\u00C0\u00E4Mi\u00E3\u00E2\x17\x00O\x07\u00C7\u00DDir\x7FU\u00A1\u0083\\\u00AB\x12EG\u009E\u00E7m\u0086C\x00\u00B4\u00C0\u00AA\u00DC@\u00A0\u00FC\b\u00E64qK\u00F7\u00BA-@u\u009F\u00F37\u0085Ir\x0E\u00B9\u008B\u00D4\u0082\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
green = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00EFIDAT8\u008D\u00A5\u00931J\x03Q\x14E\u00CF}3hJ\x1B\u00D1\u00A4\u00B1s\x01\u00F6\n\u00AEA\x10\u00826\u00EE\&amp;quot;{\u00B1\u00B1\u00B2\u0090t\u008Ak\x10\u00D4&amp;amp;\u009Bp\x04\x0BM\u0095\u0080\u00F97\u00C5\u00A0f\u008C&amp;amp;3\u00CC\u00AB\u00FE\u0087\x7F\u00CF\u00BD\u00EF=\u00BEl\u00D3\u00A6\u00A2\u0095\x1A\u00C8\x17/\x077&amp;#039;G\u00D8\u0097\u00B2\u00F2\u00FF\x04\x00\u00C8\x13R\u00F4\u009F\u00FB\u00C3Q\u00E5a\u00A0s\u00C3&amp;gt;Z\u00EDji*\u00A5C`Tm\u00C1\u00EC\u00D5\u0089\x1D\u00F6\u00D4\u00E8\u00B54\u00AD\u00E8S\u00B7\x0E\u00C0R\x12\u00B1\f\u0090\u00B4]\x07 \u00C8f\u009A\u00FD\u0091\u00C0l\u00C1\u00FA\u00B5\u00DAlv&amp;gt;&amp;#039;E\x05p|u\u00D1\x01r\u00D6M\u00B0D\u00C4\u00C3\u00D9\u00FD\u00B8\x02\u00F8\u00D8\x18\u00EFP\u00C7\x1E\x00\u00BD}\u009D\u00BE\u00D7\u00A8\u008C.PH\u00BA^%MN\u00CE\u00A4\u00BB%\x00\u00B8\x07\u00F1\u00F8t:\x1C\u00D4KQ\u00D6\u00C2\x10\u00A3\u0087xi\&amp;quot;\u00FE\x05H\u00BB\u00C6ES\u00C0\u00CF\f\x1C\u00B7\x11\u00F1\u00DE\x14\u00A0\u00B6\u00DFy\x0E\u00F4\x1CG\u00E7\u00C3S\u00DC\u00E7\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
green_o = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00FFIDAT8\u008D\u00A5\u0093\u00B1J\x03a\x10\u0084g\u00F6b\b\x16\u0082\u00D5%y\u008C`\u00AB\u00E0+\x18\x11\u00C5FK\u009F &amp;gt;\u0083Oa\u00A91`!X[\n\x1A\x0B\u009FA\u00D0t&amp;amp;\u0095\u00A2\u00B7c\u00A1\u00B9\u00DC\u00FD\u00E2\x1F\u00C3M\u00B70\u00F3\u00B1\u00CC\u00B2\u0094\u0084*\u00B2Ji\x00\u00B5\u00E2\u00D0\u00E9w7(\u009C\x00\u00AC\u00FD\x15\u00F8\u0096\u00DE\u00E1&amp;lt;\x1A\u00EE\r\x1EKF\x13\u00B6A\u00DE\u0088\u00D9E4\u00EFI\x0F\u00F4u\x00e\x00\u0088\u0096\u00BB\u00CE\x1Fv/\u0087\u00B1\u00FCZ\u00BFK\x17G@\u00D0\u0081\u00A4\u0094\u00B0Q|\u00FD\u00B2/(\u0091\u00CD\u008C\u00D9\\@\u00D1\x17\x00\u00946&amp;gt;\u00DF^\u00E6\x03f\u00BE\x1C\u00B0yz\u00D8\x00\u00B8t\u00BB\x7F=\u0089E\x7F|\u00F5\u00A9//q\\\u009F\u00A4\u0084&amp;gt;:g[\u00BD\u00E8\u00F2\u00CB\\\u0081\u00F04\u009Ds\x00\x13\u00B4\x00\u008E\u00CD\u00B8\x1A\x03\u00B8\\\ty\u00F0\x0B\x00\u00A8\r\u00D8\u00DD\u00FD\u00CE\u00E08\x06\bU(\u00D1\u00DA \u009E\x17\t\x07\x00o\n\u00FA\u00C7\x05\u00CA\u009Au \u00BB2\u00B3\u00D7E\x01\u00AC\u00FA\u00CE_\u00A28Y\x00\x13L\u00B1)\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
gray = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00EEIDAT8\u008D\u00A5\u00931N\u00C3@\x10E\u00DF\x1F[@\x15\u00D1 H\x1A:n\x01\u0097\b\r2\u00CA9\u00B8\x0B\u008Di((\u00D2\u00C0-\u00E8r\t\x1C\u0089\x02\u00D1@\u0080\u00EC\u00A7!\x01;\&amp;quot;\u00B1\u0095\u00A9f\u00A5\u00FDo\u00FF\x1F\u00ED\u00C86\u00DBTl\u00A5\x06\u00F2\u00BF\u0087\u00F1\u00DD\u00CD\x19\u00E8\x1A)\u00FFO\x00 \u00D2{\n]\f\u0087\u00A3I\u00ED\u00A2B\u00976&amp;#039;\u00B0&amp;gt;\u0096a\x16s\u009D\x02\u0093\u00E6K\u00C7ml\u00DB\u009A!O\u00A19\x03\u00BB\u00DF\x06\x10rJ\u008AU\u0080\u00D1A+\x07(\u00CB\u00E6i\n\u008D!\x1A\u00F6\x01\u00B4\x11\u00C0\u00EE\u00DBWV\u00D5\x1C\u0094e\u00B9&amp;#039;\u00C87\u0089\x7FDQ\x14\u00C5k\r\u00D0\u00EB\u00ED\x1C\u009Av\u00BF\u00CA\u00F0\u00BC\u00E8\x7F#|~\u00F4\x15Q!n\u00D7\u0089el\u00FBa\x05\u00A0,\x1B$\u00FBqx&amp;gt;\u00BAj\u00E3bQ\u00CB\b\u00C9\x1E ?u\x11\u00D7\x00\u0081\u008F\x04UW\u00C02B\u008A\u00B8OI/]\x01\u00DAv\u009D\u00BF\x01\u00E7\u008FN\x13\u00B9\u00E9m\u0087\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
gray_o = &amp;quot;\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\t\u00D7\x00\x00\t\u00D7\x01\u00B1n\x17\u00B7\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE&amp;lt;\x1A\x00\x00\x00\u00FFIDAT8\u008D\u00A5\u00931N\u00C3@\x14Dg\u00BEC\u0094\x02Y\u00A2r\u0092\u00BB\u00903 9T\u0096\x02-&amp;#039;\bg\u00E0\x14&amp;amp;\x1D\u0089\x14\n.@\u008Fh8\x03R\u0092\x0Eh\x10$\u00DE\u00A1!N\u00BC\u00A0\u00B5\&amp;quot;O\u00F7wg\u009E\u00FE\u00FF\u00AB\u00A5$4\u00915J\x03h\u00ED\x17\u00F3\u00E9\u00ED\x00\u00E4\u008D\x7F\u00FE\u008F\u00BEd\u00B8J\u00D3\u00D1\u008Bg\u00E4\x10\u00C4\u00A3I\u00B3P\u00DA\u0081c+x\n\u00C0\x03\x10=\bwg\u00E7\x17\u00CF!\u00C0|6\u00A1\u00A8\x15\u00E0\u00ED\u0080P\u00E2h\u00AB\u009A\u00F6+\u00BE\n@`7*\\-`\u00DF\u00D7\u00AA^ \u00F9\u00DCD\u00CBz\u00C0\u00CEWv\u0090\u00E7y\u0087\u00C0Q\u0096e\x1F\u00A1\u00F0\u00AF\u00AF\u00BD\u00F5\u0095\x1D\u00C4q;Q\u00B1Y\u00DFO&amp;#039;\u00E3\x10 &amp;gt;\u00B6\x18\u00C2\u00EB\u00B6\u00DE\u008D\u00B0\u00FE\u00EE\u00D1\u00EC]\u0086\u0093\x10\u0080\u0082$]\u00FE\x010\u008A\u00FANzJ\u0087\u00A3\u00EB\x10\u00C0W\u00B9\x03&amp;#039;\u00F5A-\x0E\tW\x00\x06u\t\u00D4\u00BE\u0080\u00AFr\x04g\u00F6\u00E0\x1C\u00DF\x0E\x05\u00B0\u00E9w\u00FE\x01\u00C6z]\u00D4\u0097\x17j\u00EE\x00\x00\x00\x00IEND\u00AEB`\u0082&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var states = [gray_o,green_o,green, red, gray, blue];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
          &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 20);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
&lt;br /&gt;
    //var arrayV = new Array(green,green_o,gray,gray_o,red,red_o);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    item.image =  File.decode(states[state]);&lt;br /&gt;
    //palette.add(&amp;quot;image&amp;quot;, undefined, File.decode(gray), {name: &amp;quot;image1&amp;quot;}, [10,10]);&lt;br /&gt;
    //item.image =  File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SimpleWatchfolder WIP ===&lt;br /&gt;
some improvements&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
	var errorList=&amp;quot;&amp;quot;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
				errorList += app.project.items[i].name + &amp;quot;\n&amp;quot;; // + &amp;quot; &amp;gt; &amp;quot; + app.project.items[i].mainSource.file.path.toString + &amp;quot;\n&amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footage(s) in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button\n\n&amp;quot;+errorList);&lt;br /&gt;
	}else{&lt;br /&gt;
		// the important part of this script. Creates the RCF (render control file), the undocumented file that will launch the watchfolder process.&lt;br /&gt;
		// the way it is created is kind of black-boxy, but this setup works for me.&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
	    // todo add start &amp;quot;&amp;quot; &amp;quot;C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\AfterFX.exe&amp;quot; -noui -m -re -wf .......&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
		if (ScriptUI.environment.keyboardState.shiftKey === true) {&lt;br /&gt;
		        alert(&amp;quot;lol&amp;quot;);&lt;br /&gt;
    		}&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Keyframes and Layers counter ===&lt;br /&gt;
wip, doesn&amp;#039;t work with animated shapes for now&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
layersCount = 0;&lt;br /&gt;
keyframesCount = 0;&lt;br /&gt;
collection = app.project.items;&lt;br /&gt;
for(i = 1;i&amp;lt;=collection.length;i++){&lt;br /&gt;
    curItem = collection[i];&lt;br /&gt;
    if(curItem instanceof CompItem){&lt;br /&gt;
        len = curItem.layers.length;&lt;br /&gt;
        layersCount += len;&lt;br /&gt;
        for(j = 1;j&amp;lt;=len;j++){&lt;br /&gt;
            curLayer = curItem.layers[j];&lt;br /&gt;
            for(k = 1;k &amp;lt;= curLayer.numProperties;k++){&lt;br /&gt;
                for(l = 1;l &amp;lt;= curLayer.property(k).numProperties; l++){&lt;br /&gt;
                    //$.writeln(keyframesCount);&lt;br /&gt;
                    if(curLayer.property(k).property(l).numProperties&amp;gt;0){&lt;br /&gt;
                         for(m = 1;m &amp;lt;= curLayer.property(k).property(l).numProperties; m++){&lt;br /&gt;
                            nKeys = curLayer.property(k).property(l).property(m).numKeys;&lt;br /&gt;
                            if(nKeys &amp;gt; 0){&lt;br /&gt;
                                 keyframesCount += nKeys;&lt;br /&gt;
                            }&lt;br /&gt;
                            //ugh i wish i was better at recursiveness&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).property(m).matchName);&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).property(m).numKeys);     &lt;br /&gt;
                        }&lt;br /&gt;
                    }else{&lt;br /&gt;
                            nkeys = curLayer.property(k).property(l).numKeys;&lt;br /&gt;
                            if(nKeys &amp;gt; 0){&lt;br /&gt;
                                keyframesCount += nKeys;&lt;br /&gt;
                            }&lt;br /&gt;
                            //keyframesCount += curLayer.property(k).property(l).numKeys;&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).matchName);&lt;br /&gt;
                            //$.writeln(curLayer.property(k).property(l).numKeys);     &lt;br /&gt;
                    }&lt;br /&gt;
                }  &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
alert(keyframesCount+&amp;quot; keyframes found and\n&amp;quot;+layersCount+&amp;quot; layers counted in the project&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
function iterateProperties(prop){&lt;br /&gt;
           // $.writeln(prop.numProperties);    &lt;br /&gt;
    if(prop.numProperties &amp;gt; 0){&lt;br /&gt;
        for(i = 1;i&amp;lt;=prop.numProperties;i++){&lt;br /&gt;
            iterateProperties(prop.property(i));&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
    //    $.writeln(prop.matchName);&lt;br /&gt;
    }&lt;br /&gt;
    //$.writeln(prop.matchName);&lt;br /&gt;
}&lt;br /&gt;
//alert(app.project.items[2])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Batch render project items ===&lt;br /&gt;
Select project footage and it will render them with the chosen rq preset&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
template = &amp;quot;QTJPG2000&amp;quot;; // chosen by hand, for now&lt;br /&gt;
&lt;br /&gt;
selectedItems = app.project.selection;&lt;br /&gt;
rq = app.project.renderQueue;&lt;br /&gt;
&lt;br /&gt;
for(i=0;i&amp;lt;selectedItems.length;i++){&lt;br /&gt;
    &lt;br /&gt;
    item = selectedItems[i];&lt;br /&gt;
    path = item.mainSource.file.path.toString()+&amp;#039;/&amp;#039;+item.name;&lt;br /&gt;
    p = app.project.items.addComp(item.name,item.width,item.height,1.0,item.duration,item.frameRate);&lt;br /&gt;
    p.layers.add(item);&lt;br /&gt;
    rqitem = rq.items.add(p);&lt;br /&gt;
	rqitem.outputModules[1].file = new File(path);&lt;br /&gt;
	rqitem.outputModules[1].applyTemplate(template);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Comp to Renderqueue with Template ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6iJkeH9.png&lt;br /&gt;
&lt;br /&gt;
Very wip. Features and UI don&amp;#039;t fully work.&lt;br /&gt;
&lt;br /&gt;
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&amp;#039;s name and folder, the name of the After Effects project, the current date etc...&lt;br /&gt;
&lt;br /&gt;
 It still needs work, which will happend if someone ever hires me for AE work again :)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//todo: be able to select existing RQ items instead of simply comps, and edit their existing paths&lt;br /&gt;
&lt;br /&gt;
function e(s) {&lt;br /&gt;
	$.writeln(s)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var win;&lt;br /&gt;
&lt;br /&gt;
function renderAll() {&lt;br /&gt;
	//prefTemplate = (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;))?(app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)):false;&lt;br /&gt;
	sel = app.project.selection;&lt;br /&gt;
	if (sel.length &amp;gt; 0) {&lt;br /&gt;
		//needs a comp to create a render item to get templates&lt;br /&gt;
		getTemplateUI(sel[0]);&lt;br /&gt;
		//if ui worked fine, there should be a template setting string, if there&amp;#039;s a bug or user cancelled, fuck it.&lt;br /&gt;
		prefTemplate = (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) ? (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) : false;&lt;br /&gt;
		if (prefTemplate != false &amp;amp;&amp;amp; prefTemplate != &amp;quot;null&amp;quot;) {&lt;br /&gt;
			for (i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
				renderCompWithTemplate(sel[i], prefTemplate)&lt;br /&gt;
			}&lt;br /&gt;
		} else {&lt;br /&gt;
			writeLn(&amp;quot;Send to renderqueue cancelled.&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	} else {&lt;br /&gt;
		alert(&amp;quot;Select at least one comp or render queue element&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTemplateUI(nullComp) {&lt;br /&gt;
	// get previous render settings, so we can use it as default selection later&lt;br /&gt;
	prefTemplate = (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) ? (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;)) : false;&lt;br /&gt;
	// void render settings if user clicks cancel, to be changed&lt;br /&gt;
	app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;, &amp;quot;null&amp;quot;);&lt;br /&gt;
	ai = app.project.activeItem;&lt;br /&gt;
	// must have at least one comp selected, and the project saved to know where to place the renders&lt;br /&gt;
	// eventually the user shouldn&amp;#039;t have to save his AEP, but have it appear as a warning somewhere that he should&lt;br /&gt;
	if (nullComp != null &amp;amp;&amp;amp; nullComp instanceof CompItem &amp;amp;&amp;amp; app.project.file != null) {&lt;br /&gt;
		rq = app.project.renderQueue;&lt;br /&gt;
		//create null render comp to fetch templates, delete it right afterwards&lt;br /&gt;
		rqitem = rq.items.add(nullComp);&lt;br /&gt;
		rqtemplates = rqitem.outputModules[1].templates;&lt;br /&gt;
		rqitem.remove();&lt;br /&gt;
		// dockable window to do&lt;br /&gt;
		//res = &amp;quot;dialog { properties:{ resizeable:true }, preferredSize: [690, 20],  alignChildren: &amp;#039;fill&amp;#039;,orientation:&amp;#039;column&amp;#039;,\&lt;br /&gt;
		res = &amp;quot;dialog {  \&lt;br /&gt;
				properties:{ resizeable:true }, alignChildren: &amp;#039;fill&amp;#039;,orientation:&amp;#039;column&amp;#039;,\&lt;br /&gt;
				templatesPnl: Panel { \&lt;br /&gt;
					orientation:&amp;#039;column&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;top&amp;#039;],text: &amp;#039;Choose a rendering template&amp;#039;,\&lt;br /&gt;
					templates: DropDownList {}, \&lt;br /&gt;
				}, \&lt;br /&gt;
				filePnl: Panel { \&lt;br /&gt;
					orientation:&amp;#039;column&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;top&amp;#039;],text: &amp;#039;Display Paths&amp;#039;,\&lt;br /&gt;
					pathRbs: Group { \&lt;br /&gt;
						orientation:&amp;#039;row&amp;#039;,  alignment: &amp;#039;left&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;bottom&amp;#039;] \&lt;br /&gt;
						txt: StaticText { alignment:&amp;#039;left&amp;#039;, text:&amp;#039;File path style:&amp;#039; }, \&lt;br /&gt;
						unixStylePathRb: RadioButton { text:&amp;#039;Unix (mac)&amp;#039; }, \&lt;br /&gt;
						winStylePathRb: RadioButton { text:&amp;#039;Windows&amp;#039;, value:true } \&lt;br /&gt;
					}, \&lt;br /&gt;
					showPathText: StaticText {alignment: &amp;#039;fill&amp;#039;},  \&lt;br /&gt;
					edithPathGrp: Group { \&lt;br /&gt;
						orientation:&amp;#039;row&amp;#039;, spacing:2, alignment: &amp;#039;left&amp;#039;, \&lt;br /&gt;
						editPathBox: EditText { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],  margins: 8, text:&amp;#039;&amp;#039;, properties:{borderless:true}},  \&lt;br /&gt;
						testBtn: Button { alignement:[center&amp;#039;,&amp;#039;center&amp;#039;], text:&amp;#039;Test&amp;#039;, properties:{name:&amp;#039;test&amp;#039;} } ,\&lt;br /&gt;
					}, \&lt;br /&gt;
					explainPathText: StaticText {alignment: &amp;#039;fill&amp;#039;, properties:{multiline: true } }, \&lt;br /&gt;
					explainPathTextGrp: Group { \&lt;br /&gt;
						orientation:&amp;#039;row&amp;#039;,  alignment: &amp;#039;left&amp;#039;, alignChildren:[&amp;#039;left&amp;#039;, &amp;#039;center&amp;#039;] \&lt;br /&gt;
						c1: StaticText {alignment: &amp;#039;fill&amp;#039;, properties:{multiline: true } },  \&lt;br /&gt;
						c2: StaticText {alignment: &amp;#039;fill&amp;#039;, properties:{multiline: true } } \&lt;br /&gt;
					}  \&lt;br /&gt;
				}, \&lt;br /&gt;
				buttons: Group { orientation: &amp;#039;row&amp;#039;, alignment: &amp;#039;right&amp;#039;, \&lt;br /&gt;
					okBtn: Button { text:&amp;#039;OK&amp;#039;, properties:{name:&amp;#039;ok&amp;#039;} }, \&lt;br /&gt;
					cancelBtn: Button { text:&amp;#039;Cancel&amp;#039;, properties:{name:&amp;#039;cancel&amp;#039;} }, \&lt;br /&gt;
				}, \&lt;br /&gt;
			}&amp;quot;;&lt;br /&gt;
		// get/set the display path preference (it will always be unix style internally) 1=windows 0=mac/unix&lt;br /&gt;
		//input base text &lt;br /&gt;
		explanationText = &amp;quot;Here are variables you can use:\n&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;\t{compname} {compfolder} {projpath} {compid}\n&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;\t{projname} {date} (using date:y/m/d)&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;\t[#], [##], [####] (padding)\n&amp;quot;;&lt;br /&gt;
		explanationText += &amp;quot;You can write &amp;#039;/../&amp;#039; to go &amp;#039;up&amp;#039; one directory &amp;quot;;&lt;br /&gt;
		// create window resource&lt;br /&gt;
		win = new Window(res);&lt;br /&gt;
		//set the preferred template&lt;br /&gt;
		preferredItem = 0;&lt;br /&gt;
		for (i = 0; i &amp;lt; rqtemplates.length; i++) {&lt;br /&gt;
			//skip hidden templates&lt;br /&gt;
			if (rqtemplates[i].indexOf(&amp;#039;_HIDDEN&amp;#039;) != 0) {&lt;br /&gt;
				item = win.templatesPnl.templates.add(&amp;#039;item&amp;#039;, rqtemplates[i]);&lt;br /&gt;
				if (rqtemplates[i] == prefTemplate) {&lt;br /&gt;
					preferredItem = i;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		win.templatesPnl.templates.selection = win.templatesPnl.templates.items[preferredItem];&lt;br /&gt;
		// can&amp;#039;t add newline \n character in res, so fill in text now&lt;br /&gt;
		win.filePnl.explainPathText.text = explanationText;&lt;br /&gt;
		// set os choice radio button, figure out if it&amp;#039;s in prefs &lt;br /&gt;
		// eventually users shouldn&amp;#039;t see this, the script should be tailored for mac or windows&lt;br /&gt;
		os = $.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;) != -1;&lt;br /&gt;
		if (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;)) {&lt;br /&gt;
			os = (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;) == 1) ? 1 : 0;&lt;br /&gt;
		} else {&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;, os);&lt;br /&gt;
		}&lt;br /&gt;
		win.filePnl.pathRbs.unixStylePathRb.value = !os;&lt;br /&gt;
		// set path template box, if it doesn&amp;#039;t have one, use a default;&lt;br /&gt;
		inputBox = win.filePnl.edithPathGrp.editPathBox;&lt;br /&gt;
		outPutBox = win.filePnl.showPathText;&lt;br /&gt;
		var pathTemplate = &amp;quot;&amp;quot;;&lt;br /&gt;
		if (app.settings.haveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;)) {&lt;br /&gt;
			pathTemplate = app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;);&lt;br /&gt;
		} else {&lt;br /&gt;
			pathTemplate = &amp;quot;{projpath}/RENDER/{date:ymd}_{compname}/{compname}.[####]&amp;quot;;&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;, pathTemplate);&lt;br /&gt;
		}&lt;br /&gt;
		inputBox.text = pathTemplate;&lt;br /&gt;
		//add ui callbacks&lt;br /&gt;
		//call once, to fill the result text box&lt;br /&gt;
		updatePath(inputBox, outPutBox);&lt;br /&gt;
		inputBox.onChange = inputBox.onChanging = function() {&lt;br /&gt;
			updatePath(inputBox, outPutBox)&lt;br /&gt;
		};&lt;br /&gt;
		/*-----TO BE CUT---------*/&lt;br /&gt;
		win.filePnl.pathRbs.unixStylePathRb.onClick = win.filePnl.pathRbs.winStylePathRb.onClick = function() {&lt;br /&gt;
			//save preferences&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;, (!win.filePnl.pathRbs.unixStylePathRb.value) ? 1 : 0);&lt;br /&gt;
			updatePath(inputBox, outPutBox);&lt;br /&gt;
		}&lt;br /&gt;
		/*-----END CUT-----------*/&lt;br /&gt;
		win.filePnl.edithPathGrp.testBtn.onClick = function() {&lt;br /&gt;
			fp = descriptionToFilePath(&amp;quot;&amp;quot;, 1, inputBox.text, 0);&lt;br /&gt;
			fpFolder = new Folder(fp);&lt;br /&gt;
			e(fpFolder.absoluteURI);&lt;br /&gt;
		}&lt;br /&gt;
		win.buttons.okBtn.onClick = function() {&lt;br /&gt;
			//save preferences&lt;br /&gt;
			prefTemplate = win.templatesPnl.templates.selection;&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;pathtemplate&amp;quot;, inputBox.text);&lt;br /&gt;
			//app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;, (!win.filePnl.pathRbs.unixStylePathRb.value)?1:0);&lt;br /&gt;
			app.settings.saveSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;template&amp;quot;, prefTemplate);&lt;br /&gt;
			win.close();&lt;br /&gt;
		}&lt;br /&gt;
		win.layout.layout(true);&lt;br /&gt;
		win.onResizing = win.onResize = function() {&lt;br /&gt;
			this.layout.resize()&lt;br /&gt;
		};&lt;br /&gt;
		win.center();&lt;br /&gt;
		win.show();&lt;br /&gt;
	} else {&lt;br /&gt;
		//alert dialogs suck, but they are efficient&lt;br /&gt;
		alert(&amp;quot;Select at least one comp to render, and make sure your After Effects file is saved to disk.&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function resizeUI(windowObj) {&lt;br /&gt;
	//nasty function which creates a new textbox to figure out how big the editbox is, so no text is clipped&lt;br /&gt;
	var textbox = windowObj.filePnl.edithPathGrp.editPathBox;&lt;br /&gt;
	//var testbutton = windowObj.filePnl.edithPathGrp.testBtn;&lt;br /&gt;
	g = windowObj.filePnl.add(&amp;quot;group&amp;quot;, [0, 0, 0, 0]);&lt;br /&gt;
	g.enabled = g.visible = false;&lt;br /&gt;
	tmp = g.add(&amp;quot;edittext&amp;quot;, undefined, textbox.text, {&lt;br /&gt;
		enabled: false,&lt;br /&gt;
		visible: false&lt;br /&gt;
	});&lt;br /&gt;
	var preferredWidth = tmp.preferredSize[0];&lt;br /&gt;
	//e(&amp;quot;newpref: &amp;quot;+r.preferredSize[0]);&lt;br /&gt;
	windowObj.filePnl.remove(g);&lt;br /&gt;
	textbox.size = [preferredWidth, textbox.preferredSize[1]];&lt;br /&gt;
	//testbutton.size = testbutton.preferredSize;&lt;br /&gt;
	windowObj.layout.layout(true);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function updatePath(inputRes, outputRes) {&lt;br /&gt;
	os = (app.settings.getSetting(&amp;quot;renderCompWithTemplate&amp;quot;, &amp;quot;oschoice&amp;quot;) == 1) ? 1 : 0;&lt;br /&gt;
	input = inputRes.text;&lt;br /&gt;
	input = descriptionToFilePath(&amp;quot;&amp;quot;, 1, input, 1);&lt;br /&gt;
	input = setPathStyle(input, !os); //backslashes to slashes&lt;br /&gt;
	outputRes.text = input;&lt;br /&gt;
	resizeUI(inputRes.parent.parent.parent);&lt;br /&gt;
	//windowObj.layout.layout(true);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function descriptionToFilePath(comp, rqid, descriptionString, forDisplay) {&lt;br /&gt;
	// takes a description like {projpath}/{projname}_{compname}/{compname}_[#####] and transforms it into a usable unix path&lt;br /&gt;
	// if there is no comp fed, use the first in the selection.&lt;br /&gt;
	// &amp;#039;forDisplay&amp;#039; int decides if it&amp;#039;s for display (add frame count and turn %20s to spaces) or actual path to be used&lt;br /&gt;
	// &amp;#039;rqid&amp;#039; is the number of the comp in the renderqueue, default is 1&lt;br /&gt;
	var str = descriptionString;&lt;br /&gt;
	if (comp == &amp;quot;&amp;quot; || comp == null) {&lt;br /&gt;
		comp = app.project.selection[0];&lt;br /&gt;
	}&lt;br /&gt;
	var projName = app.project.file.name.toString();&lt;br /&gt;
	//standard stuff, comp path name etc....&lt;br /&gt;
	str = str.replace(/\{projpath\}/gi, app.project.file.path.toString());&lt;br /&gt;
	projName = projName.substr(0, projName.lastIndexOf(&amp;#039;.&amp;#039;)) || projName;&lt;br /&gt;
	str = str.replace(/\{projname\}/gi, projName);&lt;br /&gt;
	str = str.replace(/\{compname\}/gi, comp.name);&lt;br /&gt;
	var parentFolder = (comp.parentFolder.name == &amp;quot;Root&amp;quot;) ? &amp;quot;&amp;quot; : comp.parentFolder.name;&lt;br /&gt;
	str = str.replace(/\{compfolder\}/gi, parentFolder);&lt;br /&gt;
	//if we use {id}, make sure it&amp;#039;s padded &lt;br /&gt;
	var rq = app.project.renderQueue;&lt;br /&gt;
	str = str.replace(/\{id\}/gi, pad(rq.items.length.toString().length, rqid.toString(), &amp;quot;0&amp;quot;));&lt;br /&gt;
	//figure out padding, if it&amp;#039;s forDisplay, show it to the user&lt;br /&gt;
	startPad = str.indexOf(&amp;quot;[#&amp;quot;);&lt;br /&gt;
	endPad = str.lastIndexOf(&amp;quot;#]&amp;quot;);&lt;br /&gt;
	if (startPad &amp;gt; 0 &amp;amp;&amp;amp; endPad &amp;gt; 0 &amp;amp;&amp;amp; endPad &amp;gt; startPad &amp;amp;&amp;amp; forDisplay) {&lt;br /&gt;
		startFrame = comp.workAreaStart / comp.frameDuration;&lt;br /&gt;
		str = str.substring(0, startPad) + pad(endPad - startPad, startFrame.toString(), &amp;quot;0&amp;quot;) + str.substring(endPad + 2, str.length);&lt;br /&gt;
	}&lt;br /&gt;
	//date&lt;br /&gt;
	var datesArray = str.split(&amp;quot;{date:&amp;quot;);&lt;br /&gt;
	var tempstr = &amp;quot;&amp;quot;;&lt;br /&gt;
	if (datesArray.length &amp;gt; 0) {&lt;br /&gt;
		var today = new Date();&lt;br /&gt;
		var dd = today.getDate().toString();&lt;br /&gt;
		var mm = (today.getMonth() + 1).toString();&lt;br /&gt;
		var yyyy = today.getFullYear();&lt;br /&gt;
		tempstr = datesArray[0];&lt;br /&gt;
		for (i = 1; i &amp;lt; datesArray.length; i++) {&lt;br /&gt;
			endBracketPos = datesArray[i].indexOf(&amp;quot;}&amp;quot;);&lt;br /&gt;
			nextBracketPos = datesArray[i].indexOf(&amp;quot;{&amp;quot;);&lt;br /&gt;
			nextBracketPos = (nextBracketPos == -1) ? 9999 : nextBracketPos; //case if date is the last used tag&lt;br /&gt;
			if (endBracketPos &amp;gt; -1 &amp;amp;&amp;amp; endBracketPos &amp;lt; nextBracketPos) {&lt;br /&gt;
				dateString = datesArray[i].substring(0, endBracketPos);&lt;br /&gt;
				dateString = dateString.replace(/y/gi, yyyy);&lt;br /&gt;
				dateString = dateString.replace(/d/gi, pad(2, dd, &amp;quot;0&amp;quot;));&lt;br /&gt;
				dateString = dateString.replace(/m/gi, pad(2, mm, &amp;quot;0&amp;quot;));&lt;br /&gt;
				tempstr += dateString + datesArray[i].substring(endBracketPos + 1, datesArray[i].length);&lt;br /&gt;
			} else {&lt;br /&gt;
				tempstr = str;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		str = tempstr;&lt;br /&gt;
	}&lt;br /&gt;
	if (forDisplay) {&lt;br /&gt;
		str += &amp;quot;.ext&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function renderCompWithTemplate(comp, template) {&lt;br /&gt;
	//sends the comp to the renderqueue with the selected file path, creating folders and subfolders if needed&lt;br /&gt;
	rq = app.project.renderQueue;&lt;br /&gt;
	rqitem = rq.items.add(comp);&lt;br /&gt;
	rqtemplates = rqitem.outputModules[1].templates;&lt;br /&gt;
	filename = app.project.file.name;&lt;br /&gt;
	filepath = app.project.file.toString();&lt;br /&gt;
	projpath = filepath.slice(0, filepath.length - filename.length);&lt;br /&gt;
	savePath = projpath + comp.name + &amp;quot;/&amp;quot; + filename.slice(0, filename.length - 4) + &amp;quot;/&amp;quot;;&lt;br /&gt;
	saveFile = comp.name + &amp;quot;.[####].tif&amp;quot;;&lt;br /&gt;
	saveFolder = new Folder(savePath);&lt;br /&gt;
	saveFolder.create();&lt;br /&gt;
	rqitem.outputModules[1].file = new File(savePath + &amp;quot;/&amp;quot; + saveFile);&lt;br /&gt;
	rqitem.outputModules[1].applyTemplate(template);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPathStyle(path, toUnixPath) {&lt;br /&gt;
	// takes a string and creates either a unix path like /c/my%20folder/ if toUnixPath is set to true&lt;br /&gt;
	// or a windows path like c:\my folder\. Returns false if there&amp;#039;s a problem (tbd)&lt;br /&gt;
	if (!toUnixPath) {&lt;br /&gt;
		//unix -&amp;gt; win&lt;br /&gt;
		path = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
		path = path.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
		path = path.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
		path = path.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
		path = path.charAt(0).toUpperCase() + path.slice(1);&lt;br /&gt;
	} else {&lt;br /&gt;
		path = &amp;quot;/&amp;quot; + path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
		path = path.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
		path = path.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
		//path = path.substring(0,path.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
	}&lt;br /&gt;
	return path;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(width, string, padding) {&lt;br /&gt;
	return (width &amp;lt;= string.length) ? string : pad(width, padding + string, padding);&lt;br /&gt;
}&lt;br /&gt;
renderAll();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== create effect creator ===&lt;br /&gt;
==== ui ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(s){&lt;br /&gt;
	$.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
var windowObj;&lt;br /&gt;
function ui(thisObj) {&lt;br /&gt;
	windowObj = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;ui_test&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
	addButton(&amp;quot;flip&amp;quot;,windowObj);&lt;br /&gt;
	return windowObj;&lt;br /&gt;
}*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addButton(buttonname,ui){&lt;br /&gt;
	//ui.add(&amp;quot;button&amp;quot;, [0, 0, 20, 20], But_01[0]);&lt;br /&gt;
	return ui.add(&amp;quot;button&amp;quot;, undefined, buttonname);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function ui(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
     }else{&lt;br /&gt;
                var res = &lt;br /&gt;
            &amp;quot;group { \&lt;br /&gt;
                        alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                        alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                        orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                            setWF: Button {text: &amp;#039;add btn&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                            sendWF: Button {text: &amp;#039;Send To Watchfolder&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;	&lt;br /&gt;
            pan.grp = pan.add(res);        &lt;br /&gt;
            pan.grp.setWF.onClick = function () {&lt;br /&gt;
            	addButton(&amp;quot;joe&amp;quot;,pan.grp);&lt;br /&gt;
            	resfreshUI(pan);&lt;br /&gt;
                    }&lt;br /&gt;
            pan.grp.sendWF.onClick = function () {&lt;br /&gt;
                }&lt;br /&gt;
            resfreshUI(pan);    &lt;br /&gt;
            /*&lt;br /&gt;
            pan.layout.layout(true);&lt;br /&gt;
            pan.layout.resize();&lt;br /&gt;
            pan.onResizing = pan.onResize = function () {this.layout.resize();}*/&lt;br /&gt;
            return pan;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function resfreshUI(src){&lt;br /&gt;
	src.layout.layout(true);&lt;br /&gt;
	src.layout.resize();&lt;br /&gt;
	src.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ui(this);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== fn ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(s){&lt;br /&gt;
	$.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// to be done:&lt;br /&gt;
// 		save keys&lt;br /&gt;
// 		save masks&lt;br /&gt;
//		save text if text layer, camera zoom if camera etc&lt;br /&gt;
//		use FFX as custom values&lt;br /&gt;
&lt;br /&gt;
function grabEffects(layers){&lt;br /&gt;
	&lt;br /&gt;
	var buffer = &amp;quot;&amp;quot;;&lt;br /&gt;
	var chosenLayerName = &amp;quot;curLayer&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	for (i=0;i&amp;lt;layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
		effs = layers[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
&lt;br /&gt;
			//create the effects, no matter if properties below have values&lt;br /&gt;
&lt;br /&gt;
			buffer += &amp;quot;\n\tprop = &amp;quot;+ chosenLayerName +&amp;quot;.Effects.addProperty(\&amp;quot;&amp;quot;+ effs.property(j).matchName +&amp;quot;\&amp;quot;);\n&amp;quot;;&lt;br /&gt;
			buffer += &amp;quot;\tprop.name = \&amp;quot;&amp;quot;+ effs.property(j).name +&amp;quot;\&amp;quot;;\n&amp;quot;;&lt;br /&gt;
			buffer += &amp;quot;\tprop.enabled = &amp;quot;+ effs.property(j).enabled +&amp;quot;;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
			for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
&lt;br /&gt;
				curProp = effs.property(j).property(k);&lt;br /&gt;
				&lt;br /&gt;
				//only add value if it&amp;#039;s non-default&lt;br /&gt;
&lt;br /&gt;
				if(curProp.isModified){&lt;br /&gt;
&lt;br /&gt;
					curPropValue = propType(curProp);&lt;br /&gt;
&lt;br /&gt;
					// if it returns -1, it&amp;#039;s (probably) a useless &amp;#039;parent&amp;#039; property, skip&lt;br /&gt;
&lt;br /&gt;
					if( curPropValue != -1){&lt;br /&gt;
						&lt;br /&gt;
						// /!\ if it&amp;#039;sa  custom value type, there&amp;#039;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 &amp;#039;-1&amp;#039; as curpropvalue&lt;br /&gt;
&lt;br /&gt;
						buffer += &amp;quot;\t\t// &amp;quot;+curProp.name+((curPropValue===false)?&amp;quot; /!\\ Can&amp;#039;t use this custom data! could be a false negative, but unlikely &amp;quot;:&amp;quot;&amp;quot;)+&amp;quot;\n&amp;quot;;                                        &lt;br /&gt;
						buffer += &amp;quot;\t\t&amp;quot;+((curPropValue===false)?&amp;quot;//&amp;quot;:&amp;quot;&amp;quot;) + &amp;quot;prop.property(\&amp;quot;&amp;quot;+curProp.matchName+&amp;quot;\&amp;quot;).setValue(&amp;quot; + curPropValue + &amp;quot;);\n&amp;quot;;&lt;br /&gt;
						&lt;br /&gt;
						// if there is an expression and it&amp;#039;s enabled, use it&lt;br /&gt;
&lt;br /&gt;
						if(curProp.expression != &amp;quot;&amp;quot; &amp;amp;&amp;amp; curProp.expressionEnabled){&lt;br /&gt;
							buffer += &amp;quot;\t\t&amp;quot;+((curPropValue===false)?&amp;quot;//&amp;quot;:&amp;quot;&amp;quot;) + &amp;quot;prop.property(\&amp;quot;&amp;quot;+curProp.matchName+&amp;quot;\&amp;quot;).expression = \&amp;quot;&amp;quot;+curProp.expression+&amp;quot;\&amp;quot;;\n&amp;quot;;&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return buffer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function outputCode(){&lt;br /&gt;
	&lt;br /&gt;
	effectsLayers = app.project.activeItem.selectedLayers; //use effects from all selected layers&lt;br /&gt;
&lt;br /&gt;
	buffer = &amp;quot;&amp;quot;;&lt;br /&gt;
	if(effectsLayers.length&amp;gt;0){&lt;br /&gt;
		buffer = &amp;quot;var selectedLayers = app.project.activeItem.selectedLayers;\n&amp;quot;;&lt;br /&gt;
		buffer += &amp;quot;for (i=0;i&amp;lt;selectedLayers.length;i++){\n&amp;quot;;&lt;br /&gt;
		buffer += &amp;quot;\n\tcurLayer = selectedLayers[i];\n&amp;quot;; &lt;br /&gt;
		buffer += grabEffects(effectsLayers) + &amp;quot;\n}&amp;quot;;&lt;br /&gt;
		//e(buffer);&lt;br /&gt;
	}&lt;br /&gt;
	return buffer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
e(outputCode());&lt;br /&gt;
e(&amp;quot;\n------------------------------------------------------&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function propType(property){&lt;br /&gt;
&lt;br /&gt;
	returnvalue = false;&lt;br /&gt;
	&lt;br /&gt;
	e(&amp;quot;---&amp;gt;&amp;quot;+property.name+&amp;quot; &amp;quot;+property.propertyValueType+&amp;quot;\n&amp;quot;); //debug&lt;br /&gt;
	//e(&amp;quot;---&amp;gt;&amp;quot;+property.name+&amp;quot; &amp;quot;+property.canVaryOverTime+&amp;quot;\n&amp;quot;); //debug		&lt;br /&gt;
	&lt;br /&gt;
	switch(property.propertyValueType){&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.ThreeD_SPATIAL:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;,&amp;quot;+property.value[2]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
		case PropertyValueType.ThreeD:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;,&amp;quot;+property.value[2]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.TwoD_SPATIAL:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
		case PropertyValueType.TwoD:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;			&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.OneD:&lt;br /&gt;
			returnvalue = property.value;&lt;br /&gt;
			break;		&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.COLOR:&lt;br /&gt;
			returnvalue = &amp;quot;[&amp;quot;+property.value[0]+&amp;quot;,&amp;quot;+property.value[1]+&amp;quot;,&amp;quot;+property.value[2]+&amp;quot;,&amp;quot;+property.value[3]+&amp;quot;]&amp;quot;;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.LAYER_INDEX:&lt;br /&gt;
			returnvalue = property.value;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		case PropertyValueType.MASK_INDEX:&lt;br /&gt;
			returnvalue = property.value;&lt;br /&gt;
			break;			&lt;br /&gt;
&lt;br /&gt;
		//no way to store custom value but FFX, tbd&lt;br /&gt;
		case PropertyValueType.CUSTOM_VALUE:&lt;br /&gt;
			returnvalue = false;&lt;br /&gt;
			break;&lt;br /&gt;
&lt;br /&gt;
		// if we land on default, it is _most likely_ a property &amp;#039;parent&amp;#039; with no real use&lt;br /&gt;
		default:&lt;br /&gt;
			//e(&amp;quot;using default: ---&amp;gt;&amp;quot;+property.name+&amp;quot; &amp;quot;+property.propertyValueType+&amp;quot;\n&amp;quot;)&lt;br /&gt;
			returnvalue = -1;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
	return (returnvalue);&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== left to right ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(s){&lt;br /&gt;
	//debug&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function replaceLR(s){&lt;br /&gt;
    &lt;br /&gt;
    s = s.replace(/left/g, &amp;quot;right&amp;quot;);&lt;br /&gt;
    s = s.replace(/Left/g, &amp;quot;Right&amp;quot;);&lt;br /&gt;
    s = s.replace(/LEFT/, &amp;quot;RIGHT&amp;quot;);&lt;br /&gt;
    return s;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function checkImportLeftRight(layer){&lt;br /&gt;
&lt;br /&gt;
	// is it a footage layer (includes solids etc.. not Comps)&lt;br /&gt;
	if(layer.source instanceof FootageItem){&lt;br /&gt;
		&lt;br /&gt;
		//is it a footage item (.exr)&lt;br /&gt;
		if(layer.source.file != null){&lt;br /&gt;
			path = layer.source.mainSource.file.toString();&lt;br /&gt;
	&lt;br /&gt;
			 //check path, if it has &amp;#039;left&amp;#039; in it, grab &amp;#039;right&amp;#039; and import and replace footage&lt;br /&gt;
			if(path.toLowerCase().indexOf(&amp;quot;left&amp;quot;) &amp;gt; -1){&lt;br /&gt;
&lt;br /&gt;
				rightPath = replaceLR(path);&lt;br /&gt;
				rightFile = new File(replaceLR(path));&lt;br /&gt;
&lt;br /&gt;
				//if file exists, import right file, replace layer with it&lt;br /&gt;
				if(rightFile.exists){&lt;br /&gt;
					&lt;br /&gt;
					&lt;br /&gt;
					var io = new ImportOptions(rightFile);&lt;br /&gt;
					if(io.canImportAs(ImportAsType.FOOTAGE)){&lt;br /&gt;
					&lt;br /&gt;
						io.importAs = ImportAsType.FOOTAGE;&lt;br /&gt;
						io.sequence = true;&lt;br /&gt;
						src = app.project.importFile(io);&lt;br /&gt;
						&lt;br /&gt;
						return(layer.replaceSource(src,1));&lt;br /&gt;
						&lt;br /&gt;
					}else{&lt;br /&gt;
						return false&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
				}else{&lt;br /&gt;
					alert(&amp;quot;file not found! &amp;quot;+rightPath);&lt;br /&gt;
					return false&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
	&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
a = app.project.activeItem;&lt;br /&gt;
&lt;br /&gt;
for(i=1;i&amp;lt;=a.layers.length;i++){&lt;br /&gt;
 //e(a.layers[i].name);&lt;br /&gt;
 e(checkImportLeftRight(a.layers[i]));&lt;br /&gt;
    //if it&amp;#039;s footage&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
for(i = 1;i&amp;lt;app.project.numItems;i++){&lt;br /&gt;
	e(i+&amp;quot; &amp;quot;+app.project.items[i].name); &lt;br /&gt;
}&lt;br /&gt;
// 3 and 4&lt;br /&gt;
*/&lt;br /&gt;
function new3dComp(leftcomp,rightcomp){&lt;br /&gt;
	threedcomp = leftcomp.name.replace(/left/g,&amp;quot;3D&amp;quot;);&lt;br /&gt;
	w = leftcomp.width;&lt;br /&gt;
	h = leftcomp.height;&lt;br /&gt;
	duration = leftcomp.duration;&lt;br /&gt;
	frameRate = leftcomp.frameRate;&lt;br /&gt;
	newComp = app.project.items.addComp(threedcomp, w , h , 1.0, duration, frameRate);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	newComp.layers.add(rightcomp);&lt;br /&gt;
	newComp.layers.add(leftcomp);&lt;br /&gt;
	&lt;br /&gt;
	threed = newComp.layers.addSolid([1,1,1],&amp;quot;3D Glasses&amp;quot;,w,h,1.0);&lt;br /&gt;
	threed.adjustmentLayer = true;&lt;br /&gt;
	&lt;br /&gt;
	threedglasses = threed.Effects.addProperty(&amp;quot;ADBE 3D Glasses2&amp;quot;);&lt;br /&gt;
	threedglasses.property(&amp;quot;Left View&amp;quot;).setValue(2);&lt;br /&gt;
	threedglasses.property(&amp;quot;Right View&amp;quot;).setValue(3);&lt;br /&gt;
	threedglasses.property(&amp;quot;3D View&amp;quot;).setValue(12);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
new3dComp(app.project.items[3],app.project.items[4]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//e(app.project.activeItem.name+&amp;quot; --- &amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Output .SRT from layer markers ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function pad10(n){&lt;br /&gt;
    return (n&amp;lt;10)?&amp;quot;0&amp;quot;+n:n;&lt;br /&gt;
}&lt;br /&gt;
function pad100(n){&lt;br /&gt;
    return (n&amp;lt;100)?&amp;quot;0&amp;quot;+pad10(n):n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function formatTime(time){&lt;br /&gt;
    time =~~ (time*1000)/1000;&lt;br /&gt;
&lt;br /&gt;
    var hrs = ~~(time / 3600);&lt;br /&gt;
    var mins = ~~((time % 3600) / 60);&lt;br /&gt;
    var secs =  ~~(time % 60);    &lt;br /&gt;
    var ms = ~~((time-Math.floor(time))*1000);&lt;br /&gt;
    &lt;br /&gt;
    time = pad10(hrs)+&amp;quot;:&amp;quot;+pad10(mins)+&amp;quot;:&amp;quot;+pad10(secs)+&amp;quot;,&amp;quot;+pad100(ms);&lt;br /&gt;
    return time;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
data = &amp;quot;&amp;quot;;&lt;br /&gt;
var ai = app.project.activeItem;&lt;br /&gt;
if ( ai instanceof CompItem &amp;amp;&amp;amp; ai.selectedLayers.length == 1) {&lt;br /&gt;
    var m = ai.selectedLayers[0].marker;&lt;br /&gt;
    for(i=1;i&amp;lt;=m.numKeys;i++){&lt;br /&gt;
        data += i;&lt;br /&gt;
        data += &amp;quot;\n&amp;quot;+ formatTime(m.keyTime(i))+&amp;quot; --&amp;gt; &amp;quot;+ formatTime(m.keyTime(i)+m.keyValue(i).duration);&lt;br /&gt;
        data += &amp;quot;\n&amp;quot;+m.keyValue(i).comment;&lt;br /&gt;
        data += &amp;quot;\n&amp;quot;;&lt;br /&gt;
       }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
alert(data)&lt;br /&gt;
&lt;br /&gt;
//result:&lt;br /&gt;
//&lt;br /&gt;
//1&lt;br /&gt;
//00:03:16,945 --&amp;gt; 00:03:17,364&lt;br /&gt;
//testing comment 1&lt;br /&gt;
//&lt;br /&gt;
//2&lt;br /&gt;
//01:59:47,228 --&amp;gt; 01:59:49,396&lt;br /&gt;
//second subtitle at 2 hours in&lt;br /&gt;
//&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shift keys ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function e(str){&lt;br /&gt;
      $.writeln(str);&lt;br /&gt;
}&lt;br /&gt;
function getLayerFromProperty(prop){&lt;br /&gt;
    return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
}&lt;br /&gt;
var c = app.project.activeItem;&lt;br /&gt;
if( c != null){&lt;br /&gt;
    var props = c.selectedProperties;&lt;br /&gt;
&lt;br /&gt;
    //e(props[0].matchName);&lt;br /&gt;
    for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
        e(&amp;quot;\n---------------------------\n&amp;quot;+props[i].matchName+&amp;quot; / &amp;quot;+getLayerFromProperty(props[i]).name);&lt;br /&gt;
        k = props[i].selectedKeys;&lt;br /&gt;
        for(keyVar in k){&lt;br /&gt;
            //k[keyVar]&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
           // e( ); &lt;br /&gt;
           e(props[i].keyTime(k[keyVar])); &lt;br /&gt;
           //e(k[keyVar]); &lt;br /&gt;
        }&lt;br /&gt;
    //CANT FUCKING NUDGE KEYFRAMES&lt;br /&gt;
      //  e(props[i].selectedKeys);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Selection tool palette ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
//tmpdir = $.getenv(&amp;quot;tmp&amp;quot;);&lt;br /&gt;
    function toolWindow(thisObj){&lt;br /&gt;
        function drawUI(){&lt;br /&gt;
            var my_palette = new Window(&amp;quot;palette&amp;quot;,&amp;quot;Selection Tool&amp;quot;);&lt;br /&gt;
            my_palette.bounds = [300,200,300,285];&lt;br /&gt;
            /*var button1 = addScriptButton(my_palette,[l_button_left,   5, l_button_right, 25], &lt;br /&gt;
                    &amp;quot;Find and Replace Text&amp;quot;,    demosDirectory, &amp;quot;Find and Replace Text.jsx&amp;quot;);&lt;br /&gt;
            var button3 = addScriptButton(my_palette,[l_button_left,  30, l_button_right, 50], &lt;br /&gt;
                    &amp;quot;Scale Composition&amp;quot;, 		demosDirectory, &amp;quot;Scale Composition.jsx&amp;quot;);&lt;br /&gt;
            var button4 = addScriptButton(my_palette,[l_button_left,  55, l_button_right, 75], &lt;br /&gt;
                    &amp;quot;Scale Selected Layers&amp;quot;, demosDirectory, &amp;quot;Scale Selected Layers.jsx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            var button6 = addScriptButton(my_palette,[r_button_left,   5, r_button_right, 25], &lt;br /&gt;
                    &amp;quot;Sort Layers by In Point&amp;quot;,     demosDirectory, &amp;quot;Sort Layers by In Point.jsx&amp;quot;);&lt;br /&gt;
            var button8 = addScriptButton(my_palette,[r_button_left,  30, r_button_right, 50], &lt;br /&gt;
                    &amp;quot;Render and Email&amp;quot;,    myDirectory,    &amp;quot;Render and Email.jsx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
            var button12 = addHelpButton(my_palette,[r_button_left,  55, r_button_right, 75]);*/&lt;br /&gt;
&lt;br /&gt;
            my_palette.show();&lt;br /&gt;
        }&lt;br /&gt;
    drawUI();&lt;br /&gt;
    }&lt;br /&gt;
 toolWindow(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Make .bat file (WIP)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
    str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
    str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
    str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function createBat(){&lt;br /&gt;
    d = new Date();&lt;br /&gt;
    m = d.getMonth()+1;&lt;br /&gt;
    j = d.getDate();&lt;br /&gt;
    if(m&amp;lt;10){&lt;br /&gt;
        m=&amp;quot;0&amp;quot;+m;&lt;br /&gt;
    }&lt;br /&gt;
    if(j&amp;lt;10){&lt;br /&gt;
        j=&amp;quot;0&amp;quot;+j;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/ae_render&amp;quot;+m+&amp;quot;_&amp;quot;+j+&amp;quot;.bat&amp;quot;);&lt;br /&gt;
    alert(pathToWinPath(app.project.file));&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txtFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
res =&lt;br /&gt;
&amp;quot;dialog { \&lt;br /&gt;
    allGroups: Panel {\&lt;br /&gt;
        orientation:&amp;#039;column&amp;#039;,\&lt;br /&gt;
        alignChildren:&amp;#039;fill&amp;#039;,\&lt;br /&gt;
        text:&amp;#039;Options&amp;#039;, \&lt;br /&gt;
        chckOff: Checkbox {text:&amp;#039;Shutdown PC when finished&amp;#039;}, \&lt;br /&gt;
        chckAppend: Checkbox {text:&amp;#039;Append (instead of overwriting)&amp;#039;},\&lt;br /&gt;
        aePath: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;fill&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
            aeLocBtn: Button {text:&amp;#039; aerender.exe location : &amp;#039;}, \&lt;br /&gt;
            aeLocTst: StaticText {text:&amp;#039;C:\\Program fil...&amp;#039;}} \&lt;br /&gt;
        okCancel: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;fill&amp;#039;, alignChildren:&amp;#039;center&amp;#039;,\&lt;br /&gt;
            okBtn: Button { text:&amp;#039;OK&amp;#039;, properties:{name:&amp;#039;ok&amp;#039;}} , \&lt;br /&gt;
            cancelBtn: Button { text:&amp;#039;Cancel&amp;#039;, properties:{name:&amp;#039;cancel&amp;#039;}},\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
}&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
win = new Window (res);&lt;br /&gt;
win.allGroups.chckAppend.value = true;&lt;br /&gt;
win.center();&lt;br /&gt;
win.show();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Carnet De Voyage rangement===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//rangement pour carnet de voyage&lt;br /&gt;
app.beginUndoGroup(&amp;quot;CDV Rangement&amp;quot;);&lt;br /&gt;
boolVal = false;&lt;br /&gt;
sel = app.project.selection;&lt;br /&gt;
if(sel.length == 1){&lt;br /&gt;
    shot = sel[0];&lt;br /&gt;
    allItems = app.project.items;&lt;br /&gt;
    sortItems = new Array();&lt;br /&gt;
    for(i=1;i&amp;lt;=allItems.length;i++){&lt;br /&gt;
        if(!(allItems[i] instanceof FolderItem)){&lt;br /&gt;
            if(allItems[i].parentFolder.name == &amp;quot;Root&amp;quot;){&lt;br /&gt;
                sortItems[sortItems.length] = allItems[i];&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            if(allItems[i].parentFolder.name == &amp;quot;Root&amp;quot;){&lt;br /&gt;
                    sortItems[sortItems.length] = allItems[i];&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    t = app.project.items.addFolder(&amp;quot;ELMTS&amp;quot;);&lt;br /&gt;
    for(i=0;i&amp;lt;=sortItems.length-1;i++){&lt;br /&gt;
        if(sortItems[i] != shot){&lt;br /&gt;
        sortItems[i].parentFolder = t;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(app.project.renderQueue.item(1).outputModules.length == 2){ //copie d&amp;#039;un output module à la main nécéssaire&lt;br /&gt;
    fileP = app.project.renderQueue.item(1).outputModules[1].file.toString();&lt;br /&gt;
    fileP = fileP.replace(&amp;quot;/m/EPISODE&amp;quot;,&amp;quot;/b/EPISODE&amp;quot;);&lt;br /&gt;
    fileP = new File(fileP);&lt;br /&gt;
    app.project.renderQueue.item(1).outputModules[2].file = fileP;&lt;br /&gt;
    app.project.renderQueue.item(1).outputModules[2].applyTemplate(&amp;quot;PNGSeq&amp;quot;);&lt;br /&gt;
    var curFile = app.project.file.name;&lt;br /&gt;
    curFile = curFile.substring(0,curFile.length-4);&lt;br /&gt;
    var pth = app.project.file.path+&amp;quot;/&amp;quot;+curFile+&amp;quot;_MOV+PNG.aep&amp;quot;;&lt;br /&gt;
    var mySaveFile = new File(pth);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Duplicate output modules first&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Chat==&lt;br /&gt;
&amp;#039;&amp;#039;JSX&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var myPalette = buildUI(this);&lt;br /&gt;
&lt;br /&gt;
    if (myPalette != null &amp;amp;&amp;amp; myPalette instanceof Window) {&lt;br /&gt;
        myPalette.show()&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    function buildUI (thisObject) {&lt;br /&gt;
&lt;br /&gt;
    if (thisObject instanceof Panel) {&lt;br /&gt;
        var myWindow = thisObject;&lt;br /&gt;
        } else { &lt;br /&gt;
        var myWindow = new Window (&amp;quot;palette&amp;quot;, &amp;quot;My Window&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
g = myWindow.add(&amp;quot;group&amp;quot;);&lt;br /&gt;
g.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
g.alignChildren = &amp;quot;left&amp;quot;;&lt;br /&gt;
//g.alignement = &amp;quot;top&amp;quot;;&lt;br /&gt;
bt1 = g.add(&amp;quot;button&amp;quot;,undefined,&amp;quot;test&amp;quot;);&lt;br /&gt;
bt1.onClick = bob;&lt;br /&gt;
//myWindow.myPanel.titleText = myWindow.myPanel.add(&amp;quot;staticText&amp;quot;);&lt;br /&gt;
//myWindow.myPanel.titleText.text =  &amp;quot;Move After Render v1.0&amp;quot;;  &lt;br /&gt;
&lt;br /&gt;
myWindow.layout.layout(true);&lt;br /&gt;
//myWindow.layout = new AutoLayoutManager(myWindow);&lt;br /&gt;
//myWindow.layout.resize();&lt;br /&gt;
return myWindow;&lt;br /&gt;
} &lt;br /&gt;
h = 0;&lt;br /&gt;
function bob(){&lt;br /&gt;
//    alert();&lt;br /&gt;
    w = this.parent.parent;&lt;br /&gt;
    p = this.parent;&lt;br /&gt;
    h++;&lt;br /&gt;
    bt2 = p.add(&amp;quot;button&amp;quot;,undefined,&amp;quot;test&amp;quot;+h);  &lt;br /&gt;
      bt2.alignement = [&amp;quot;left&amp;quot;,&amp;quot;top&amp;quot;];&lt;br /&gt;
    if(h%3==0){&lt;br /&gt;
&lt;br /&gt;
       &lt;br /&gt;
        bt2.helpTip = &amp;quot;TTTTT&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    bt2.onClick = flup;&lt;br /&gt;
   //w.update();&lt;br /&gt;
   w.layout.layout(1); &lt;br /&gt;
  // w.layout.resize();&lt;br /&gt;
&lt;br /&gt;
//    w.show();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function flup(){&lt;br /&gt;
    f = new Folder($.fileName);&lt;br /&gt;
    f = new Folder(f.parent+&amp;quot;/shelves/&amp;quot;);&lt;br /&gt;
   if(!f.exists){f.create()};&lt;br /&gt;
    cfg = new File(f.parent+&amp;quot;/shelves/shelves.cfg&amp;quot;);&lt;br /&gt;
    cfg.open(&amp;#039;e&amp;#039;);&lt;br /&gt;
    cfg.write(this.onClick);&lt;br /&gt;
    cfg.close();&lt;br /&gt;
    cfg.execute();&lt;br /&gt;
}&lt;br /&gt;
function pop(){&lt;br /&gt;
    f = new Folder($.fileName);&lt;br /&gt;
    fi = new File(f.parent+&amp;quot;/shelves/testShelf.jsx&amp;quot;);&lt;br /&gt;
    fi.open(&amp;#039;e&amp;#039;);&lt;br /&gt;
    eval(fi.read());&lt;br /&gt;
    }&lt;br /&gt;
----------------&lt;br /&gt;
function grab(){&lt;br /&gt;
    var reply = &amp;quot;&amp;quot;;&lt;br /&gt;
    c = new Socket;&lt;br /&gt;
    if (c.open (&amp;quot;berniebernie.fr:80&amp;quot;)) {&lt;br /&gt;
        if(c.writeln (&amp;quot;GET /dump/afx/chat.php  HTTP/1.0\nHost: berniebernie.fr\n&amp;quot;)){&lt;br /&gt;
            reply = decodeURIComponent(c.read(1000));&lt;br /&gt;
            reply = reply.split(&amp;quot;--afx chat file log--&amp;quot;);&lt;br /&gt;
            reply = reply[1];&lt;br /&gt;
        }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
        }&lt;br /&gt;
        c.close();&lt;br /&gt;
    }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
    }&lt;br /&gt;
    return reply;&lt;br /&gt;
}&lt;br /&gt;
function talk(str){&lt;br /&gt;
    var reply = &amp;quot;&amp;quot;;&lt;br /&gt;
    c = new Socket;&lt;br /&gt;
    if (c.open (&amp;quot;berniebernie.fr:80&amp;quot;)) {&lt;br /&gt;
        if(c.writeln (&amp;quot;GET /dump/afx/chat.php?msg=&amp;quot;+str+&amp;quot;  HTTP/1.0\nHost: berniebernie.fr\n&amp;quot;)){&lt;br /&gt;
            reply = decodeURIComponent(c.read(1000));&lt;br /&gt;
            reply = reply.split(&amp;quot;--afx chat file log--&amp;quot;);&lt;br /&gt;
            reply = reply[1];&lt;br /&gt;
        }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
        }&lt;br /&gt;
        c.close();&lt;br /&gt;
    }else{&lt;br /&gt;
             return 0;&lt;br /&gt;
    }&lt;br /&gt;
    return reply;&lt;br /&gt;
}&lt;br /&gt;
//alert(grab());&lt;br /&gt;
alert(talk(&amp;quot;flipflap&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
////////////////&lt;br /&gt;
app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;Php&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$myFile = &amp;quot;testFile.txt&amp;quot;;&lt;br /&gt;
if(isset($_GET[&amp;#039;msg&amp;#039;]) &amp;amp;&amp;amp; $_GET[&amp;#039;msg&amp;#039;] != &amp;quot;&amp;quot;){&lt;br /&gt;
    $fh = fopen($myFile, &amp;#039;a&amp;#039;) or die(&amp;quot;can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
    fwrite($fh, ($_GET[&amp;#039;msg&amp;#039;].&amp;quot;\n&amp;quot;));&lt;br /&gt;
    fclose($fh);    &lt;br /&gt;
}else{&lt;br /&gt;
    //$fh = fopen($myFile, &amp;#039;r&amp;#039;) or die(&amp;quot;can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
    //include$str = $str, true);&lt;br /&gt;
    //header(&amp;quot;Content-Type: plain/text&amp;quot;); &lt;br /&gt;
    echo encodeURIComponent(file_get_contents(&amp;quot;testFile.txt&amp;quot;));&lt;br /&gt;
    //echo nl2br(htmlentities(file_get_contents(&amp;quot;testFile.txt&amp;quot;)));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=889</id>
		<title>Afx Javascript</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=889"/>
		<updated>2025-12-02T10:29:50Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Auto scale precomps */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Scripts==&lt;br /&gt;
=== Copy footage to local folder as proxy===&lt;br /&gt;
https://i.imgur.com/oZ24pac.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
 * simpleLocalProxy.jsx v1.0&lt;br /&gt;
 *&lt;br /&gt;
 * Copies footage from its current location to one chosen by the user (defaults to /tmp/) and allows to&lt;br /&gt;
 * switch original-proxy with a button&lt;br /&gt;
 * 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&amp;#039; file cache on scratch disks (&amp;#039;conformed media&amp;#039;)&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 * https://github.com/berniebernie/after-effects-scripts&lt;br /&gt;
 *    &lt;br /&gt;
 * Copyright 2015, bernie@berniebernie.fr&lt;br /&gt;
 *    &lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT  &lt;br /&gt;
 *&lt;br /&gt;
 * This script embeds js-md5 from github: https://github.com/emn178/js-md5 for practical reasons&lt;br /&gt;
 *&lt;br /&gt;
 * Script that can be launched or put in the scriptui folder of After Effects to be used as a panel&lt;br /&gt;
 *  &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * js-md5 v0.1.2&lt;br /&gt;
 * https://github.com/emn178/js-md5&lt;br /&gt;
 *&lt;br /&gt;
 * Copyright 2014, emn178@gmail.com&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      js-md5.js&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//(function(root, undefined){&lt;br /&gt;
  //&amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  var HEX_CHARS = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
  var HEX_TABLE = {&lt;br /&gt;
    &amp;#039;0&amp;#039;: 0, &amp;#039;1&amp;#039;: 1, &amp;#039;2&amp;#039;: 2, &amp;#039;3&amp;#039;: 3, &amp;#039;4&amp;#039;: 4, &amp;#039;5&amp;#039;: 5, &amp;#039;6&amp;#039;: 6, &amp;#039;7&amp;#039;: 7, &amp;#039;8&amp;#039;: 8, &amp;#039;9&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;a&amp;#039;: 10, &amp;#039;b&amp;#039;: 11, &amp;#039;c&amp;#039;: 12, &amp;#039;d&amp;#039;: 13, &amp;#039;e&amp;#039;: 14, &amp;#039;f&amp;#039;: 15,&lt;br /&gt;
    &amp;#039;A&amp;#039;: 10, &amp;#039;B&amp;#039;: 11, &amp;#039;C&amp;#039;: 12, &amp;#039;D&amp;#039;: 13, &amp;#039;E&amp;#039;: 14, &amp;#039;F&amp;#039;: 15&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var R = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,&lt;br /&gt;
           5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,&lt;br /&gt;
           4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,&lt;br /&gt;
           6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];&lt;br /&gt;
&lt;br /&gt;
  var K = [0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE,&lt;br /&gt;
           0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,&lt;br /&gt;
           0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,&lt;br /&gt;
           0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821,&lt;br /&gt;
           0XF61E2562, 0XC040B340, 0X265E5A51, 0XE9B6C7AA,&lt;br /&gt;
           0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8,&lt;br /&gt;
           0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED,&lt;br /&gt;
           0XA9E3E905, 0XFCEFA3F8, 0X676F02D9, 0X8D2A4C8A,&lt;br /&gt;
           0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C,&lt;br /&gt;
           0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70,&lt;br /&gt;
           0X289B7EC6, 0XEAA127FA, 0XD4EF3085, 0X04881D05,&lt;br /&gt;
           0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665,&lt;br /&gt;
           0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039,&lt;br /&gt;
           0X655B59C3, 0X8F0CCC92, 0XFFEFF47D, 0X85845DD1,&lt;br /&gt;
           0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1,&lt;br /&gt;
           0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391];&lt;br /&gt;
&lt;br /&gt;
  var jsmd5 = function(message) {&lt;br /&gt;
    var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message);&lt;br /&gt;
    var h0 = 0x67452301;&lt;br /&gt;
    var h1 = 0xEFCDAB89;&lt;br /&gt;
    var h2 = 0x98BADCFE;&lt;br /&gt;
    var h3 = 0x10325476;&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0, length = blocks.length;i &amp;lt; length;i += 16)&lt;br /&gt;
    {&lt;br /&gt;
      var a = h0;&lt;br /&gt;
      var b = h1;&lt;br /&gt;
      var c = h2;&lt;br /&gt;
      var d = h3;&lt;br /&gt;
      var f, g, tmp, x, y;&lt;br /&gt;
&lt;br /&gt;
      for(var j = 0;j &amp;lt; 64;++j)&lt;br /&gt;
      {&lt;br /&gt;
        if(j &amp;lt; 16)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (b &amp;amp; c) | ((~b) &amp;amp; d);&lt;br /&gt;
          f = d ^ (b &amp;amp; (c ^ d));&lt;br /&gt;
          g = j;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 32)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (d &amp;amp; b) | ((~d) &amp;amp; c);&lt;br /&gt;
          f = c ^ (d &amp;amp; (b ^ c));&lt;br /&gt;
          g = (5 * j + 1) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 48)&lt;br /&gt;
        {&lt;br /&gt;
          f = b ^ c ^ d;&lt;br /&gt;
          g = (3 * j + 5) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          f = c ^ (b | (~d));&lt;br /&gt;
          g = (7 * j) % 16;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        tmp = d;&lt;br /&gt;
        d = c&lt;br /&gt;
        c = b&lt;br /&gt;
&lt;br /&gt;
        // leftrotate&lt;br /&gt;
        x = (a + f + K[j] + blocks[i + g]);&lt;br /&gt;
        y = R[j];&lt;br /&gt;
        b += (x &amp;lt;&amp;lt; y) | (x &amp;gt;&amp;gt;&amp;gt; (32 - y));&lt;br /&gt;
        a = tmp;&lt;br /&gt;
      }&lt;br /&gt;
      h0 = (h0 + a) | 0;&lt;br /&gt;
      h1 = (h1 + b) | 0;&lt;br /&gt;
      h2 = (h2 + c) | 0;&lt;br /&gt;
      h3 = (h3 + d) | 0;&lt;br /&gt;
    }&lt;br /&gt;
    return toHexString(h0) + toHexString(h1) + toHexString(h2) + toHexString(h3);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var toHexString = function(num) {&lt;br /&gt;
    var hex = &amp;quot;&amp;quot;;&lt;br /&gt;
    for(var i = 0; i &amp;lt; 4; i++)&lt;br /&gt;
    {&lt;br /&gt;
      var offset = i &amp;lt;&amp;lt; 3;&lt;br /&gt;
      hex += HEX_CHARS.charAt((num &amp;gt;&amp;gt; (offset + 4)) &amp;amp; 0x0F) + HEX_CHARS.charAt((num &amp;gt;&amp;gt; offset) &amp;amp; 0x0F);&lt;br /&gt;
    }&lt;br /&gt;
    return hex;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var hasUTF8 = function(message) {&lt;br /&gt;
    var i = message.length;&lt;br /&gt;
    while(i--)&lt;br /&gt;
      if(message.charCodeAt(i) &amp;gt; 127)&lt;br /&gt;
        return true;&lt;br /&gt;
    return false;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var ASCIItoBlocks = function(message) {&lt;br /&gt;
    // a block is 32 bits(4 bytes), a chunk is 512 bits(64 bytes)&lt;br /&gt;
    var length = message.length;&lt;br /&gt;
    var chunkCount = ((length + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    var i;&lt;br /&gt;
    for(i = 0;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    for(i = 0;i &amp;lt; length;++i)&lt;br /&gt;
      blocks[i &amp;gt;&amp;gt; 2] |= message.charCodeAt(i) &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[i &amp;gt;&amp;gt; 2] |= 0x80 &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[blockCount - 2] = length &amp;lt;&amp;lt; 3; // length * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var UTF8toBlocks = function(message) {&lt;br /&gt;
    var uri = encodeURIComponent(message);&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    for(var i = 0, bytes = 0, length = uri.length;i &amp;lt; length;++i)&lt;br /&gt;
    {&lt;br /&gt;
      var c = uri.charCodeAt(i);&lt;br /&gt;
      if(c == 37) // %&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= ((HEX_TABLE[uri.charAt(++i)] &amp;lt;&amp;lt; 4) | HEX_TABLE[uri.charAt(++i)]) &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      else&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= c &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      ++bytes;&lt;br /&gt;
    }&lt;br /&gt;
    var chunkCount = ((bytes + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var index = bytes &amp;gt;&amp;gt; 2;&lt;br /&gt;
    blocks[index] |= 0x80 &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    for(var i = index + 1;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    blocks[blockCount - 2] = bytes &amp;lt;&amp;lt; 3; // bytes * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  /*if(typeof(module) != &amp;#039;undefined&amp;#039;)&lt;br /&gt;
    module.exports = jsmd5;&lt;br /&gt;
  else if(root)&lt;br /&gt;
    root.jsmd5 = jsmd5;*/&lt;br /&gt;
//}(this));&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      simpleLocalProxy.jsx &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(str){&lt;br /&gt;
    //uncomment to allow debugging&lt;br /&gt;
    //$.writeln(str);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pathToLocalizedPath(path){&lt;br /&gt;
        f = new File(path);&lt;br /&gt;
        return f.fsName.toString();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sequenceFilesWildcard(path){ &lt;br /&gt;
    //returns false or an array with everything before a sequence&amp;#039;s image number, and the extension: /c/path/file.0555.exr &amp;gt; { /c/pathfile. ; .exr }&lt;br /&gt;
    var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g; &lt;br /&gt;
    var match = myRegexp.exec(path);&lt;br /&gt;
    if(!match){&lt;br /&gt;
        return false;&lt;br /&gt;
    }else{&lt;br /&gt;
        return match;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabPaths(footage){&lt;br /&gt;
    //returns false or the path of the given footage, if it&amp;#039;s a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr &amp;gt; /c/path/file.*.exr&lt;br /&gt;
    var f = footage;&lt;br /&gt;
    returnpath = false;&lt;br /&gt;
    if(f instanceof FootageItem &amp;amp;&amp;amp; f.file != null){       &lt;br /&gt;
        var source = f.mainSource.file.toString();&lt;br /&gt;
        if(!f.mainSource.isStill){&lt;br /&gt;
            var pathFromRegex = sequenceFilesWildcard(source);&lt;br /&gt;
            if(pathFromRegex){&lt;br /&gt;
                returnpath = pathFromRegex[1]+&amp;quot;*&amp;quot;+pathFromRegex[2];&lt;br /&gt;
            }else{&lt;br /&gt;
                returnpath = source;&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            returnpath = source;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return returnpath;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function grabFootagePathsAndCopyToLocal(){&lt;br /&gt;
    //main worker function&lt;br /&gt;
    &lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1 || !(sel[0] instanceof FootageItem)){&lt;br /&gt;
        alert(&amp;quot;Select footage(s) and try again&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        &lt;br /&gt;
        var debugcheck = (getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var useforcecopy = (getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        &lt;br /&gt;
        var isMacintosh = ($.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;)==-1);&lt;br /&gt;
        var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        &lt;br /&gt;
        var batchFile = (isMacintosh)?&amp;quot;# bash file used to copy After Effects footage to local storage&amp;quot;:&amp;quot;@echo off\nREM batch file to copy After Effects footage to local storage&amp;quot;;&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            if(!sel[i].useProxy){&lt;br /&gt;
                //grab path from current selection item&lt;br /&gt;
                var curPath = grabPaths(sel[i]);&lt;br /&gt;
                var fileName = curPath.split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                &lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
            &lt;br /&gt;
                if(isMacintosh){&lt;br /&gt;
                    //macos uses rsync to copy files&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrsync -v -a &amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot;-I &amp;quot;:&amp;quot;&amp;quot;);&lt;br /&gt;
                    batchFile += dir+fileName;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;+outputDir;&lt;br /&gt;
                    &lt;br /&gt;
                }else{&lt;br /&gt;
                    &lt;br /&gt;
                    //windows uses robocopy&lt;br /&gt;
                    sourcePath = pathToLocalizedPath(dir);&lt;br /&gt;
                    destinationPath = pathToLocalizedPath(outputDir);&lt;br /&gt;
                    filename = fileName.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrobocopy &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += sourcePath;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    //robocopy filename requires a wildcard, even if it&amp;#039;s a single file&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += destinationPath ;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;                    &lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += filename ;&lt;br /&gt;
                    batchFile += &amp;quot;*\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot; /XO&amp;quot;:&amp;quot;&amp;quot;)+&amp;quot; /FFT&amp;quot;+((debugcheck)?&amp;quot;&amp;quot;:&amp;quot; /NJH /NJS&amp;quot;);&lt;br /&gt;
                    e(batchFile);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        batchFile += ((debugcheck)?(isMacintosh?&amp;quot;\nread a&amp;quot;:&amp;quot;\npause&amp;quot;):&amp;quot;&amp;quot;); //nested tertiary operators, sue me&lt;br /&gt;
        &lt;br /&gt;
        var txtFile;&lt;br /&gt;
        if(isMacintosh){&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.command&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.bat&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        if(txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;) == true){&lt;br /&gt;
&lt;br /&gt;
            txtFile.write(batchFile);&lt;br /&gt;
            txtFile.close();&lt;br /&gt;
	    if(isMacintosh){&lt;br /&gt;
&lt;br /&gt;
            		system.callSystem(&amp;quot;chmod +x &amp;quot; + txtFile.toString() +&amp;quot;&amp;quot;);&lt;br /&gt;
system.callSystem(txtFile.toString());&lt;br /&gt;
		}else{&lt;br /&gt;
            txtFile.execute();&lt;br /&gt;
}&lt;br /&gt;
        }else{&lt;br /&gt;
            alert(&amp;quot;Write permission denied on\n&amp;quot;+localSaveDir);&lt;br /&gt;
        }&lt;br /&gt;
        //txtFile.remove();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabFootagePathsAndSwitchProxy(){&lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1){&lt;br /&gt;
        alert(&amp;quot;Select footage first&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            item = sel[i];&lt;br /&gt;
            if(item.useProxy){&lt;br /&gt;
                item.useProxy = false;&lt;br /&gt;
            }else{&lt;br /&gt;
                var curPath = grabPaths(item);&lt;br /&gt;
                var fileName = item.mainSource.file.toString().split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
                var proxyFilePath = outputDir+&amp;quot;/&amp;quot;+fileName;&lt;br /&gt;
                var proxyFile = new File(proxyFilePath);&lt;br /&gt;
                e(proxyFilePath);&lt;br /&gt;
                if(proxyFile.exists){&lt;br /&gt;
                    if(item.mainSource.isStill){&lt;br /&gt;
                        item.setProxy(proxyFile);&lt;br /&gt;
                    }else{&lt;br /&gt;
                        //dirty workaround if the file is a movie file (.mpg, qucktime etc...)&lt;br /&gt;
                        try {&lt;br /&gt;
                            item.setProxyWithSequence(proxyFile,false);&lt;br /&gt;
                        }&lt;br /&gt;
                        catch(err) {&lt;br /&gt;
                            item.setProxy(proxyFile);&lt;br /&gt;
                        }   &lt;br /&gt;
                    }&lt;br /&gt;
                    item.proxySource.alphaMode = item.mainSource.alphaMode;&lt;br /&gt;
                    item.proxySource.premulColor = item.mainSource.premulColor;&lt;br /&gt;
                    item.proxySource.invertAlpha= item.mainSource.invertAlpha;&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(getPref(&amp;quot;debug&amp;quot;,false)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                        alert(&amp;quot;&amp;gt;&amp;gt;&amp;gt; NO PROXY FOUND\n&amp;quot;+proxyFilePath);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPref(pref,value){&lt;br /&gt;
    app.settings.saveSetting(&amp;quot;simpleLocalProxy&amp;quot;, pref, value);&lt;br /&gt;
}&lt;br /&gt;
function getPref(pref,defaultValue){&lt;br /&gt;
    prefsVar = &amp;quot;simpleLocalProxy&amp;quot;;&lt;br /&gt;
    if(app.settings.haveSetting(prefsVar, pref)){&lt;br /&gt;
        return app.settings.getSetting(prefsVar, pref);&lt;br /&gt;
    }else{&lt;br /&gt;
        app.settings.saveSetting(prefsVar, pref, defaultValue);&lt;br /&gt;
        setPref(pref,defaultValue)&lt;br /&gt;
        return defaultValue;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function simpleLocalProxy(thisObj) {&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Local Proxy&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    &lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        var localFolder = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        // UI DESCRIPTION&lt;br /&gt;
        &lt;br /&gt;
        res = &amp;quot;group { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                        cols: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            col1: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                saveDirText: StaticText {text: &amp;#039;Proxy folder setup&amp;#039;},\&lt;br /&gt;
                                saveDirOptions: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    usemd5rbox: RadioButton {text: &amp;#039; Simple&amp;#039;,helpTip:&amp;#039;Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)&amp;#039;,value:true},\&lt;br /&gt;
                                    usepathrbox: RadioButton {text: &amp;#039; Copy Folder Structure&amp;#039;,helpTip:&amp;#039;Copies the target folder structure inside the proxy folder; more folders&amp;#039;}}},\&lt;br /&gt;
                            col2: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                optionsText: StaticText {text: &amp;#039;Options: &amp;#039;},\&lt;br /&gt;
                                optionsGrp: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    forcecopyChkbox: Checkbox {text: &amp;#039; Force copy&amp;#039;,helpTip:&amp;#039;Overwrite files (otherwise skips existing files)&amp;#039;},\&lt;br /&gt;
                                    debugChkbox: Checkbox {text: &amp;#039; Debug&amp;#039;,helpTip:&amp;#039;Show full batch process and pause at end of copies&amp;#039;}}}},\&lt;br /&gt;
                        cols2: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            localSaveDirBut: Button {text: &amp;#039; Choose Proxy Folder &amp;#039;, helpTip:&amp;#039;choose folder to copy files to&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                            browseBut: Button {text: &amp;#039; Browse &amp;#039; , preferredSize:[-1,30]}} , \&lt;br /&gt;
                        curentDirTxt: EditText {text: &amp;#039;&amp;quot; + localFolder +&amp;quot;&amp;#039;,enabled:false},\&lt;br /&gt;
                        copyFootageBut: Button {text: &amp;#039; Copy Footage(s) to Proxy Folder &amp;#039; ,helpTip:&amp;#039;Launches a background batch copy of selected footage&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                        switchproxyBut: Button {text: &amp;#039; Switch Proxy/Original &amp;#039;,helpTip:&amp;#039;Switches from original to proxy path and back,  warning if no local copy has been found&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        //UI DRAW&lt;br /&gt;
        &lt;br /&gt;
        pan.grp = pan.add(res); &lt;br /&gt;
        pan.layout.layout(true);&lt;br /&gt;
        &lt;br /&gt;
        //radio buttons and checkboxes prefs&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
        var usemd5 = getPref(&amp;quot;usemd5&amp;quot;,true);&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5==&amp;quot;true&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
        var useforcecopy = getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var debugcheck = getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
&lt;br /&gt;
        pan.layout.resize();&lt;br /&gt;
        pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
        // UI ACTIONS&lt;br /&gt;
        &lt;br /&gt;
        //browse button&lt;br /&gt;
        pan.grp.cols2.browseBut.onClick = function(){&lt;br /&gt;
                localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
                localSaveDir.execute();&lt;br /&gt;
        }&lt;br /&gt;
        //choose local folder button&lt;br /&gt;
        pan.grp.cols2.localSaveDirBut.onClick = function(){&lt;br /&gt;
            localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
            o = localSaveDir.selectDlg(&amp;quot;Choose folder to copy footage to&amp;quot;);&lt;br /&gt;
            if(o!=null){&lt;br /&gt;
                    setPref(&amp;quot;localSaveDir&amp;quot;,o.toString());&lt;br /&gt;
                    pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
        pan.grp.copyFootageBut.onClick = function(){&lt;br /&gt;
            //copy footages button (launches the &amp;#039;guts&amp;#039; of this script)&lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
            &lt;br /&gt;
            grabFootagePathsAndCopyToLocal();&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        pan.grp.switchproxyBut.onClick = function(){&lt;br /&gt;
            //checks for a local copy of the footage, and switches to a proxy accordingly &lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            &lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
&lt;br /&gt;
            grabFootagePathsAndSwitchProxy();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (pan instanceof Window) pan.show() ;&lt;br /&gt;
}&lt;br /&gt;
simpleLocalProxy(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Expose Animation ===&lt;br /&gt;
Adds &amp;#039;hold&amp;#039; keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD.&lt;br /&gt;
&lt;br /&gt;
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;1000&amp;quot; height=&amp;quot;800&amp;quot; &amp;gt;9-3XVlME2tY&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Step 1: setup detector on layer&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;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 &amp;#039;Detector&amp;#039; picks up a change (value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Step 2: bake to keys&amp;#039; ,preferredSize:[-1,30],enabled:true} , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Bakes detected animation as time remapped keys to animation, this can be long, watch your &amp;quot;Info&amp;quot; panel. If some animation is not detected, change resoltion and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    applyKeys: Button {text: &amp;#039;Step 3: apply as time remapping on selected layers&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Select layers on which to apply time remapping (\&amp;quot;exposed\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        //pan.grp.bakeKeys.enabled = true;        &lt;br /&gt;
        setupDetector();&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    curlayer = layers[0];&lt;br /&gt;
&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer);&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index;&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+&amp;quot;_anim_detection&amp;quot;,true);&lt;br /&gt;
    var precomplayer =  app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    var toplayer = precomp.layers[1];&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration;&lt;br /&gt;
    newlayer.timeRemapEnabled = true;&lt;br /&gt;
    newlayer.inPoint -= app.project.activeItem.frameDuration;&lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;Marker&amp;quot;).setValueAtTime(.5, explainer);&lt;br /&gt;
&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var sliderctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;;&lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
   &lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    var detectorctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider;&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to &amp;#039;clean&amp;#039; expression&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // add &amp;#039;0&amp;#039; key on first frame&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.addKey(0);&lt;br /&gt;
    currentSlider.setValueAtKey(1, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for(i = 0;i&amp;lt;layers.length;i++){&lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;Time Remap&amp;quot;);&lt;br /&gt;
        for(j=1;j&amp;lt;=currentSlider.numKeys;j++){&lt;br /&gt;
            &lt;br /&gt;
            v = currentSlider.keyValue(j);&lt;br /&gt;
            t = currentSlider.keyTime(j);&lt;br /&gt;
            remap.addKey(t);&lt;br /&gt;
            remap.setValueAtKey(j, v);&lt;br /&gt;
            remap.setInterpolationTypeAtKey(j,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\&lt;br /&gt;
        a = timeRemap;\&lt;br /&gt;
        nk = a.nearestKey(time);\&lt;br /&gt;
        curframe = 0;\&lt;br /&gt;
        if(nk.time &amp;gt; time){\&lt;br /&gt;
            curframe = nk.index-1;\&lt;br /&gt;
        }else{\&lt;br /&gt;
            curframe = nk.index;\&lt;br /&gt;
        }\&lt;br /&gt;
        curframe*thisComp.frameDuration;&amp;quot;;&lt;br /&gt;
        remap.expressionEnabled = false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create Null controllers on Puppet pins ===&lt;br /&gt;
http://i.imgur.com/HdFjaYZ.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//nulls created from puppet pins can be parented like normal layers&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Create Null Controls on Puppet Pinsv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    function getLayerFromProperty(prop){&lt;br /&gt;
        return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var c = app.project.activeItem;&lt;br /&gt;
    if( c != null &amp;amp;&amp;amp; c.selectedProperties != null){&lt;br /&gt;
        var props = c.selectedProperties;&lt;br /&gt;
        j = 0;&lt;br /&gt;
        for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
            if(props[i].matchName == &amp;quot;ADBE FreePin3 PosPin Atom&amp;quot;){&lt;br /&gt;
                j++;&lt;br /&gt;
                child = props[i].property(&amp;quot;ADBE FreePin3 PosPin Position&amp;quot;);&lt;br /&gt;
                pos = [child.value[0],child.value[1]];&lt;br /&gt;
                nullLayer = app.project.activeItem.layers.addNull();&lt;br /&gt;
                nullLayer.name = &amp;quot;puppetCtrl&amp;quot;+j;&lt;br /&gt;
                nullLayer.position.setValue(pos);&lt;br /&gt;
                var expr = &amp;quot;thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).toWorld(thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).transform.anchorPoint)&amp;quot;;&lt;br /&gt;
                child.expression = expr;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Batch replace file locations with text file===&lt;br /&gt;
http://i.imgur.com/88QHvlQ.jpg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//now works with sequences or still images, windows only&lt;br /&gt;
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path&lt;br /&gt;
function URIToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function WinPathtoURI(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/ /g, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    //str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Change File Locations&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/tempAE.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txt = &amp;quot;&amp;quot;;&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    var isSequence = new Array();&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
       alert(&amp;quot;Select footage items.&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            isSequence[i] = !sel[i].mainSource.isStill;&lt;br /&gt;
            txt += URIToWinPath(sel[i].mainSource.file.toString())+&amp;quot;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        txtFile.write(&amp;quot;*** 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&amp;quot;);&lt;br /&gt;
        txtFile.write(txt);&lt;br /&gt;
        txtFile.close();&lt;br /&gt;
        txtFile.execute();&lt;br /&gt;
        isOk = confirm(&amp;quot;Change file paths ?\n\nReloading might take a while!&amp;quot;);&lt;br /&gt;
        if(isOk){&lt;br /&gt;
           txtFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
           contents = txtFile.read();&lt;br /&gt;
           arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
           for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
                var tmpFile =  new File(WinPathtoURI(arrayContents[i+2]));&lt;br /&gt;
                //alert(tmpFile);&lt;br /&gt;
                if(isSequence[i]){&lt;br /&gt;
                    sel[i].replaceWithSequence(tmpFile,0);&lt;br /&gt;
                }else{&lt;br /&gt;
                    sel[i].replace(tmpFile);&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                writeLn(Math.round((i+1)/app.project.selection.length*100)+&amp;quot;%&amp;quot;);&lt;br /&gt;
           }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Loop Selected Layers===&lt;br /&gt;
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif&lt;br /&gt;
&lt;br /&gt;
No hassle looping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Set loops&amp;quot;);&lt;br /&gt;
var layersList = app.project.activeItem.selectedLayers;&lt;br /&gt;
var frameD = app.project.activeItem.frameDuration;&lt;br /&gt;
for (i=0;i&amp;lt;layersList.length;i++){&lt;br /&gt;
    if(!layersList[i].timeRemapEnabled){&lt;br /&gt;
        var outP = layersList[i].outPoint;&lt;br /&gt;
        layersList[i].timeRemapEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP,0);&lt;br /&gt;
        layersList[i].timeRemap.expressionEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.expression = &amp;quot;loopOut()&amp;quot;;&lt;br /&gt;
        layersList[i].outPoint = app.project.activeItem.workAreaStart+app.project.activeItem.workAreaDuration;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&lt;br /&gt;
===Find Next Text Layer===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
    a = true;&lt;br /&gt;
                if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
                }&lt;br /&gt;
    if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
        var comp = app.project.item(i);&lt;br /&gt;
        for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
            a = true;&lt;br /&gt;
            comp.layer(j).selected = false;&lt;br /&gt;
            if(comp.layer(j) instanceof TextLayer){&lt;br /&gt;
                comp.layer(j).selected = true;&lt;br /&gt;
                $.writeln(comp.name);&lt;br /&gt;
                a = confirm(&amp;quot;Continue selecting text layers&amp;quot;,true);&lt;br /&gt;
            }&lt;br /&gt;
            if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
            }else{&lt;br /&gt;
                comp.layer(j).selected = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(a){&lt;br /&gt;
    alert(&amp;quot;No (more) text layers found&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simple Watchfolder===&lt;br /&gt;
http://i.imgur.com/poTWO.png&lt;br /&gt;
https://i.imgur.com/wvtgDqE.png&lt;br /&gt;
&lt;br /&gt;
I know a lot of people use media encoder but it&amp;#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Show original source location (WIN only)===&lt;br /&gt;
http://i.imgur.com/04O3r.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    function pathToWinPath(path){&lt;br /&gt;
        var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
        str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
        str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
        str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
        return str;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
        alert(&amp;quot;You need to select source(s) in the project panel&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            a = prompt(&amp;quot;&amp;#039;OK&amp;#039; continues, &amp;#039;cancel&amp;#039; stops displaying original sources\n\n[ &amp;quot;+sel[i].name+&amp;quot; ]&amp;quot;,pathToWinPath(sel[i].mainSource.file.path.toString()));    &lt;br /&gt;
            if(!a){break}&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===List selected layers effects and their properties matchNames ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//windows only&lt;br /&gt;
if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/effectList.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    var col = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for (i=0;i&amp;lt;col.length;i++){&lt;br /&gt;
        effs = col[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
            for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
                txtFile.write(&amp;quot;\n( &amp;quot;+effs.property(j).matchName+&amp;quot; ) &amp;quot;+effs.property(j).name+&amp;quot;\n------------------------------------------\n&amp;quot;);&lt;br /&gt;
                for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
                    txtFile.write(effs.property(j).property(k).matchName+&amp;quot; --&amp;gt; &amp;quot;+effs.property(j).property(k).name);&lt;br /&gt;
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE &amp;amp;&amp;amp; effs.property(j).property(k).value != undefined){&lt;br /&gt;
                        txtFile.write(&amp;quot; [ &amp;quot;+effs.property(j).property(k).value.toString()+&amp;quot; ] &amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    txtFile.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    txtFile.write(&amp;quot;\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v = s.Effects.addProperty(\&amp;quot;CC RepeTile\&amp;quot;);\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v.property(\&amp;quot;CC RepeTile-0001\&amp;quot;).setValue(10);&amp;quot;);    &lt;br /&gt;
    txtFile.close();&lt;br /&gt;
    txtFile.execute();&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Set scripting Prefs to enable to write to disk&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/IEUwe.gif&lt;br /&gt;
&lt;br /&gt;
==== Disable effects ====&lt;br /&gt;
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!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function toggleEffectGlobally() {&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Toggle Effect Globally&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    var sel = app.project.activeItem &amp;amp;&amp;amp; app.project.activeItem.selectedProperties;&lt;br /&gt;
    if (!sel || sel.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var selectedEffect = null;&lt;br /&gt;
&lt;br /&gt;
    // Find the selected effect property group&lt;br /&gt;
    for (var i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
        if (sel[i].matchName &amp;amp;&amp;amp; sel[i].parentProperty &amp;amp;&amp;amp; sel[i].parentProperty.matchName === &amp;quot;ADBE Effect Parade&amp;quot;) {&lt;br /&gt;
            selectedEffect = sel[i];&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (!selectedEffect) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect in the timeline.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var effectName = selectedEffect.matchName;&lt;br /&gt;
    var effectEnabled = selectedEffect.enabled;&lt;br /&gt;
&lt;br /&gt;
    // Loop through all comps in the project&lt;br /&gt;
    for (var p = 1; p &amp;lt;= app.project.numItems; p++) {&lt;br /&gt;
        var item = app.project.item(p);&lt;br /&gt;
        if (item instanceof CompItem) {&lt;br /&gt;
            for (var l = 1; l &amp;lt;= item.numLayers; l++) {&lt;br /&gt;
                var layer = item.layer(l);&lt;br /&gt;
                if (layer.property(&amp;quot;ADBE Effect Parade&amp;quot;)) {&lt;br /&gt;
                    var effects = layer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
                    for (var e = 1; e &amp;lt;= effects.numProperties; e++) {&lt;br /&gt;
                        var eff = effects.property(e);&lt;br /&gt;
                        if (eff.matchName === effectName) {&lt;br /&gt;
                            eff.enabled = !effectEnabled;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===List effects===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 function projEffects(){&lt;br /&gt;
 	var effects = new Array();&lt;br /&gt;
 	var effects2 = new Array();&lt;br /&gt;
 	for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
 		if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
 			   var comp = app.project.item(i);&lt;br /&gt;
 				for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
 					effs = comp.layer(j).property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
 					for(k=1;k&amp;lt;=effs.numProperties;k++){&lt;br /&gt;
 						keyName = effs.property(k).matchName;&lt;br /&gt;
 						effects[keyName]  = 1;&lt;br /&gt;
 						}&lt;br /&gt;
 					   &lt;br /&gt;
 					}&lt;br /&gt;
 			}&lt;br /&gt;
 	}&lt;br /&gt;
 	for(a in effects){&lt;br /&gt;
 		effects2[effects2.length] = a;&lt;br /&gt;
 	}&lt;br /&gt;
 	effects2.sort();&lt;br /&gt;
 	return effects2;&lt;br /&gt;
 }&lt;br /&gt;
 alert(projEffects().join(&amp;quot;\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/hFNiQ.jpg&lt;br /&gt;
&lt;br /&gt;
===Multiple sequences import (Windows)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Multiple sequences import (windows)&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//  bernie@berniebernie.fr&lt;br /&gt;
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file&lt;br /&gt;
//  Access to files network required --- no error checking.&lt;br /&gt;
//   &amp;gt; only works on windows so far&lt;br /&gt;
//   &amp;gt; only finds exrs/pngs/jpgs&lt;br /&gt;
//   &amp;gt; probably fails if your sequences dont have the same start frame&lt;br /&gt;
//   &amp;gt; will work with /path/image.####.jpg or similar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function uniq_fast(a) {&lt;br /&gt;
    var seen = {};&lt;br /&gt;
    var out = [];&lt;br /&gt;
    var len = a.length;&lt;br /&gt;
    var j = 0;&lt;br /&gt;
    for(var i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
         var item = a[i];&lt;br /&gt;
         if(seen[item] !== 1) {&lt;br /&gt;
               seen[item] = 1;&lt;br /&gt;
               out[j++] = item;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return out;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
curScript = new File($.fileName);&lt;br /&gt;
&lt;br /&gt;
startT = Date.now();&lt;br /&gt;
&lt;br /&gt;
sourceFolder = app.project.selection[0].mainSource.file.parent;&lt;br /&gt;
tmpFolder = Folder.temp;&lt;br /&gt;
tmpBat = Folder.temp.toString()+&amp;quot;/ae_dirlist.bat&amp;quot;;&lt;br /&gt;
tmpFilesList = Folder.temp.toString()+&amp;quot;/ae_imageslist.txt&amp;quot;;&lt;br /&gt;
var listFile = new File(tmpFilesList);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var batFile = new File(tmpBat);&lt;br /&gt;
batFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;REM Auto generated by this after effects script file: &amp;quot;+curScript.fsName);&lt;br /&gt;
batFile.write(&amp;quot;\npushd \&amp;quot;&amp;quot;+sourceFolder.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;\ndir /b /on *.exr *.png *.jpg *.jpeg &amp;gt; \&amp;quot;&amp;quot;+listFile.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.close();&lt;br /&gt;
system.callSystem(batFile.fsName);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
listFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
contents = listFile.read();&lt;br /&gt;
arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
listFile.close();&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;via batch: &amp;quot;+arrayContents.length+&amp;quot; files found in &amp;quot;+timer+&amp;quot;s &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myRe = new RegExp(&amp;quot;[0-9]{2,}(\)){0,1}\.[a-z]+$&amp;quot;);&lt;br /&gt;
var myArray = myRe.exec(arrayContents[0]);&lt;br /&gt;
endlength = myArray[0].length;&lt;br /&gt;
&lt;br /&gt;
for(i = 0;i&amp;lt;arrayContents.length;i++){&lt;br /&gt;
    arrayContents[i] = arrayContents[i].slice(0, -endlength);&lt;br /&gt;
}&lt;br /&gt;
unique = uniq_fast(arrayContents);&lt;br /&gt;
&lt;br /&gt;
dialog = confirm(unique.length+&amp;quot; sequences found. Load them ? \nIt might take a long time !&amp;quot;,false,&amp;quot;Confirm Loading&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(dialog){      &lt;br /&gt;
    for(i = 0;i&amp;lt;unique.length;i++){&lt;br /&gt;
        if(unique[i].length&amp;gt;0){&lt;br /&gt;
            sequenceStartFile = new File(sourceFolder.toString()+&amp;quot;/&amp;quot;+unique[i]+myArray[0]);&lt;br /&gt;
            &lt;br /&gt;
            if (sequenceStartFile) {&lt;br /&gt;
            writeLn(&amp;quot;Loading &amp;quot;+(i+1)+&amp;quot;/&amp;quot;+unique.length);&lt;br /&gt;
                try {&lt;br /&gt;
                    // Create a variable containing ImportOptions.&lt;br /&gt;
                    var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
                    importOptions.sequence = true;&lt;br /&gt;
                    try { &lt;br /&gt;
&lt;br /&gt;
                        app.project.importFile(importOptions);&lt;br /&gt;
                    } catch (error) {&lt;br /&gt;
                       e(error.toString());&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (error) {&lt;br /&gt;
                    e(error.toString());&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Canceled&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;after regext: &amp;quot;+timer);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Split to Renderqueue ===&lt;br /&gt;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Split Layers to Renderqueue v2.0&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//&lt;br /&gt;
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp&lt;br /&gt;
//  using the in and out points. I use this along with &amp;#039;Magnum&amp;#039; the edit detector to split &amp;amp; render sequences.&lt;br /&gt;
//&lt;br /&gt;
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the &amp;#039;base&amp;#039; renderqueue item that we derive everything from&lt;br /&gt;
&lt;br /&gt;
function pad(n, width, z) {&lt;br /&gt;
  z = z || &amp;#039;0&amp;#039;;&lt;br /&gt;
  n = n + &amp;#039;&amp;#039;;&lt;br /&gt;
  return n.length &amp;gt;= width ? n : new Array(width - n.length + 1).join(z) + n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Split Layers to Renderqueue&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(app.project.renderQueue.items.length == 1){&lt;br /&gt;
    FE = app.project.renderQueue.items[1];&lt;br /&gt;
    ai = FE.comp;&lt;br /&gt;
    path = FE.outputModule(1).file.path;&lt;br /&gt;
    for(i=1;i&amp;lt;=ai.layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
        RI = app.project.renderQueue.items[1].duplicate();&lt;br /&gt;
        RI.timeSpanStart = ai.layers[i].inPoint;&lt;br /&gt;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;&lt;br /&gt;
        $.writeln(ai.workAreaDuration+&amp;quot; &amp;quot;+ai.layers[i].outPoint);&lt;br /&gt;
        RI.outputModule(1).file =  new File(path+&amp;quot;/&amp;quot;+pad(i,2,&amp;quot;0&amp;quot;)+&amp;quot;_&amp;quot;+ai.layers[i].name);&lt;br /&gt;
    }&lt;br /&gt;
    app.project.renderQueue.items[1].render= false;&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Only 1 element should be in renderqueue&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docked Panel SNIP ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//script panel&lt;br /&gt;
{&lt;br /&gt;
	var nested_file = new File(&amp;quot;U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//called file&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
	var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,[100, 100, 300, 300]);&lt;br /&gt;
	impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
	//impButton.onClick = openfile;&lt;br /&gt;
	return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Watchfolder watcher (WIP)===&lt;br /&gt;
http://i.imgur.com/lhO3k.gif&lt;br /&gt;
&lt;br /&gt;
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)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
todo: check if icons are here&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  // $.writeln(&amp;quot;file &amp;gt;&amp;gt;&amp;gt; &amp;quot;+txtfiles[0].toString());&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
                //info = [&amp;quot;debug&amp;quot;,logFolderLocation.toString(),logFolderLocation.name.toString()];&lt;br /&gt;
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];&lt;br /&gt;
               &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
              //  $.writeln(logFolderLocation.toString()+&amp;quot; &amp;lt;&amp;lt;&amp;lt; &amp;quot;)&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    //$.writeln(&amp;quot;Doing shit&amp;quot;);&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                   // info[2] = &amp;quot;n/a+&amp;quot;;&lt;br /&gt;
//                    info[1] = 1;&lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
                       // $.writeln( lines[i].indexOf(phrase));&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
                               //$.writeln(wfold);&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
                                ///info[3] = pathToWinPath(wfold);&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
                               // info[2] = &amp;quot; &amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                   // info[1] = ;&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
              //  $.writeln(info);&lt;br /&gt;
               // $.writeln(contents);&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    //$.writeln(files.length);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            //$.writeln(&amp;quot;&amp;gt;&amp;gt;&amp;gt; &amp;quot;+state+&amp;quot; - &amp;quot;+out+&amp;quot; - &amp;quot;+folders[folder].name);&lt;br /&gt;
            &lt;br /&gt;
           // $.writeln(out);&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
      // $.writeln(vArray[counter2]);&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                                      //  $.writeln(&amp;quot;Debug&amp;quot;);$.writeln(p);&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 600);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
                    // $.writeln(shotinfos.join(&amp;quot;\n&amp;quot;));&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
                    //$.writeln(sL);&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
                      //$.writeln(a);&lt;br /&gt;
                        //$.writeln(shotinfos[i][1]+&amp;quot; &amp;quot;+list.selection.toString());&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                           // $.writeln(&amp;quot;ww&amp;quot;+);&lt;br /&gt;
                       //  $.writeln(a);&lt;br /&gt;
                            //explore(a[3]+((a[1]==2)?&amp;quot;/&amp;quot;+a[0]:&amp;quot;&amp;quot;));&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    //$.writeln(sel[0].subItems[0].text);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
                //refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                //cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                //}else if(reloadEditTxt.text &amp;gt;=10){&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function grow(palette,value){&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
    //    alert(listItem.selection);&lt;br /&gt;
    var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);&lt;br /&gt;
    //var texted = new Array(&amp;quot;...&amp;quot;,&amp;quot;error:&amp;quot;,&amp;quot;rendering&amp;quot;,&amp;quot;...&amp;quot;,&amp;quot;queued&amp;quot;);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    //$.writeln(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+array[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.image = File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    //item.subItems[0].helpTip = texted(state);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
///////////////////////////////////////////&lt;br /&gt;
           /* if((logFolder =  getFilePath(watchfolderLocation+&amp;quot;/&amp;quot;+shot)) != undefined){&lt;br /&gt;
                $.writeln(logInfo(logFolder));&lt;br /&gt;
            }else{&lt;br /&gt;
                $.writeln(&amp;quot;no log folder&amp;quot;);&lt;br /&gt;
                }*/&lt;br /&gt;
           // $.writeln();&lt;br /&gt;
           // addItem(list,&amp;quot;G12_SC213_T1&amp;quot;,2);&lt;br /&gt;
           &lt;br /&gt;
           &lt;br /&gt;
           /*&lt;br /&gt;
var item1 = list.add (&amp;#039;item&amp;#039;, &amp;#039;GB15_SC138_T1&amp;#039;);&lt;br /&gt;
item1.image = File(&amp;quot;~/Desktop/flag_gray.png&amp;quot;);&lt;br /&gt;
item1.subItems[0].text = &amp;#039;Queued...&amp;#039;;&lt;br /&gt;
item1.enabled = false;&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
            //alert(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot));&lt;br /&gt;
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot)*100);&lt;br /&gt;
          &lt;br /&gt;
                       // alert(&amp;quot;test&amp;quot;);&lt;br /&gt;
              //$.writeln(files[file].path+&amp;quot;/&amp;quot;+files[file].name);&lt;br /&gt;
                            //  a+=  getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name).name+&amp;quot;\n&amp;quot;;&lt;br /&gt;
                            &lt;br /&gt;
/*                            &lt;br /&gt;
                            &lt;br /&gt;
                            {&lt;br /&gt;
                                &lt;br /&gt;
        wfLocBtn.onClick = function(){&lt;br /&gt;
            app.scheduleTask(&amp;quot;loop()&amp;quot;,2000,1);&lt;br /&gt;
&lt;br /&gt;
           &lt;br /&gt;
            fold = new Folder( watchfolderLocation.toString());&lt;br /&gt;
            files = fold.getFiles();&lt;br /&gt;
                a= &amp;quot;&amp;quot;;&lt;br /&gt;
            for(file in files){&lt;br /&gt;
&lt;br /&gt;
              $.writeln(getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name));&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
           alert(a);&lt;br /&gt;
&lt;br /&gt;
            writeLn(pBar.value);&lt;br /&gt;
        }&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Import pos from maya===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,&lt;br /&gt;
[100, 100, 300, 300]);&lt;br /&gt;
impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
impButton.onClick = openfile;&lt;br /&gt;
return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
//myToolsPanel.show();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function openfile(){&lt;br /&gt;
	            var myFile = File.openDialog (&amp;quot;Select track file&amp;quot;,&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
            var fileOK = myFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
	//var fileD = OpenDlg (&amp;quot;Tracking Point File&amp;quot;,&amp;quot;*.txt&amp;quot;,true);&lt;br /&gt;
	//txt = fileD.readln ()&lt;br /&gt;
	alert(&amp;quot;txt&amp;quot;+readTxt(myFile));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function readTxt(myFile){&lt;br /&gt;
var myText = myFile.read();	&lt;br /&gt;
return myText;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Dandelion/Amazing World Of Gumball Shotbuilder===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://imgur.com/xKJWB.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For education purposes only, the script was run in production to helb build shots, it:&lt;br /&gt;
* created a list of shots &amp;amp; shows possible given a folder structure (&amp;quot;GB##_SHOWNAME_SC##_T##&amp;quot;)&lt;br /&gt;
* opened the last .AEP file found in said shot folder&lt;br /&gt;
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder&lt;br /&gt;
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the &amp;#039;grab sources&amp;#039; button&lt;br /&gt;
* automatically sent files to the watchfolder to render on a small-ish farm. &lt;br /&gt;
Other buttons allowed to check for missing footage, open the comp&amp;#039;s folder, open the current shows&amp;#039; latest animatic, etc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  &lt;br /&gt;
//  Dandelion Shot Builder for &amp;quot;the Amazing World Of Gumball&amp;quot; v0.7 by Bernie&lt;br /&gt;
//   last update 21/11/10&lt;br /&gt;
//&lt;br /&gt;
//  Known Bugs:&lt;br /&gt;
//      &lt;br /&gt;
//  -you can&amp;#039;t have several sequences in a single folder and expect the script to pick up all the sequences&lt;br /&gt;
//  -this will not set color profiles&lt;br /&gt;
// //TODO&lt;br /&gt;
//  &amp;gt;&amp;gt;&amp;gt; CHECK IF WF FOLDER ALREADY EXISTS&lt;br /&gt;
// &amp;gt;&amp;gt;&amp;gt;&amp;gt; GET TAKE FROM N DRIVE, NOT OUT FOLDER&lt;br /&gt;
//  -v0.7 fixes&lt;br /&gt;
//        -new scene after WF works properly&lt;br /&gt;
//        -changed output folder location to N:&lt;br /&gt;
//  -v0.6 fixes&lt;br /&gt;
//       -cancel watchfolder cancels watchfolder&lt;br /&gt;
//        -fixed GB##_SC_###_T1&lt;br /&gt;
//       -added options&lt;br /&gt;
//       -can work on a new location&lt;br /&gt;
//  -v0.5 fixes&lt;br /&gt;
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)&lt;br /&gt;
//      -will warn if there is missing footage before sending to WF&lt;br /&gt;
//      -removed set take, added &amp;#039;missing&amp;#039; dialog.&lt;br /&gt;
//  -v0.4 fixes&lt;br /&gt;
//      -there shouldn&amp;#039;t be a refresh problem on show change anymore&lt;br /&gt;
//  -v0.3 fixes&lt;br /&gt;
//      -changed watchfolder location to anthony&amp;#039;s mac&lt;br /&gt;
//      -changed save as dialog&lt;br /&gt;
//      -removed unused buttons&lt;br /&gt;
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)&lt;br /&gt;
//  -v0.2 fixes&lt;br /&gt;
//      -sequences weren&amp;#039;t getting imported.&lt;br /&gt;
//      -fixed UI/little problems&lt;br /&gt;
//      -added folders, refresh button, save before watchfoldering&lt;br /&gt;
var version = &amp;quot;0.7&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//default locations&lt;br /&gt;
var watchfolderLocation = &amp;quot;/c/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var rootFolder = &amp;quot;/c/&amp;quot;;&lt;br /&gt;
var forEditFolderLoaction = &amp;quot;//MAC0023DFDF5429/for%20edit&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
rootFolder = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)):rootFolder;&lt;br /&gt;
watchfolderLocation = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)):watchfolderLocation;&lt;br /&gt;
forEditFolderLoaction = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)):forEditFolderLoaction;&lt;br /&gt;
&lt;br /&gt;
          //dirty hack&lt;br /&gt;
            a = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true;&lt;br /&gt;
            b = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)):false;&lt;br /&gt;
            c = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)):true;&lt;br /&gt;
            if(a==&amp;quot;false&amp;quot;) a = false;&lt;br /&gt;
            if(a==&amp;quot;true&amp;quot;) a = true;&lt;br /&gt;
            if(b==&amp;quot;false&amp;quot;) b = false;&lt;br /&gt;
            if(b==&amp;quot;true&amp;quot;) b = true;&lt;br /&gt;
            if(c==&amp;quot;false&amp;quot;) c = false;&lt;br /&gt;
            if(c==&amp;quot;true&amp;quot;) c = true;&lt;br /&gt;
            &lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_&amp;quot;;&lt;br /&gt;
var loadFile = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)):&amp;quot;&amp;quot;;&lt;br /&gt;
checkOutputSettings();&lt;br /&gt;
&lt;br /&gt;
Array.prototype.has = function(value) {&lt;br /&gt;
    var i;&lt;br /&gt;
    for (i=0;i&amp;lt;this.length;i++) {&lt;br /&gt;
        if (this[i] == value) {&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addShotDialog(sel){&lt;br /&gt;
    alert(sel);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    path = path.toString();&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sendToWatchFolder(watchfolderlocation,compname){&lt;br /&gt;
    ocn = compname;&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
        c = confirm(&amp;quot;Save &amp;quot;+app.project.file.name+&amp;quot; ?&amp;quot;,false,&amp;quot;Save Project&amp;quot;);&lt;br /&gt;
        if(c){&lt;br /&gt;
            app.project.save();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    curFile = app.project.file;&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
    compname += &amp;quot;_WF&amp;quot;;&lt;br /&gt;
    myFolder.create();&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;.aep&amp;quot;);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
    myTextFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname.substring(0,22)+&amp;quot;_RCF.txt&amp;quot;);    &lt;br /&gt;
    myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    text = &amp;quot;After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot; ;&lt;br /&gt;
    myTextFile.write(text);&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    writeInfo(watchfolderlocation,ocn);&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;) == &amp;quot;false&amp;quot;){&lt;br /&gt;
        opFile = new File(curFile);&lt;br /&gt;
        if(opFile){&lt;br /&gt;
            app.open(opFile);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
        app.project.new();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    //explore(watchfolderlocation);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function writeInfo(watchfolderlocation,compname){&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_DandyWatch.txt&amp;quot;);    &lt;br /&gt;
    mySaveFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    rq = app.project.renderQueue;&lt;br /&gt;
    text =&amp;quot;&amp;quot;;    &lt;br /&gt;
    for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
        if(rq.items[i].render){&lt;br /&gt;
            text += rq.items[i].outputModules[1].file.path+&amp;quot;\n&amp;quot;;&lt;br /&gt;
            text += (Math.round(rq.items[i].timeSpanDuration*25)+&amp;quot;\n&amp;quot;);&lt;br /&gt;
            text += system.callSystem(&amp;quot;hostname&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    mySaveFile.write(text);&lt;br /&gt;
    mySaveFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkForMissingFootage(dialogFlag){&lt;br /&gt;
        var dialog = dialogFlag;&lt;br /&gt;
        var missing = false;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            app.project.items[i].selected = false;&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                app.project.items[i].selected = true;&lt;br /&gt;
                                missing = true;&lt;br /&gt;
                                if(dialog){&lt;br /&gt;
                                    dialog = prompt(&amp;quot;Original folder of \&amp;quot;&amp;quot;+(app.project.items[i].name)+&amp;quot;\&amp;quot; :&amp;quot;,pathToWinPath(app.project.items[i].file),&amp;quot;Missing footage! (hit cancel to suppress further dialogs)&amp;quot;);&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return missing;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutputSettings(){&lt;br /&gt;
    renderSettingsSet = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;)))?true:false;&lt;br /&gt;
   // renderSettingsSet = 1;&lt;br /&gt;
    if(!renderSettingsSet){&lt;br /&gt;
                alert(&amp;quot;Tiff output being setup, this should happen only once. It needs to close the current comp.&amp;quot;);&lt;br /&gt;
                opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
                if(opFile.exists){&lt;br /&gt;
                     app.open(opFile);&lt;br /&gt;
                     app.project.renderQueue.items[1].outputModules[1].saveAsTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                     app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;,&amp;quot;is there&amp;quot;);&lt;br /&gt;
                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
                     app.newProject();&lt;br /&gt;
                 }else{&lt;br /&gt;
                     alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutFolder(tpath){&lt;br /&gt;
    foldFile = new Folder(tpath);&lt;br /&gt;
    //$.writeln(tpath);&lt;br /&gt;
    if(foldFile.exists){&lt;br /&gt;
        files = foldFile.getFiles();  &lt;br /&gt;
        if(files.length &amp;gt; 1){&lt;br /&gt;
            return false;&lt;br /&gt;
        }else{&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
      newF = foldFile.create();&lt;br /&gt;
        return newF;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function getSetTake(force){&lt;br /&gt;
    show = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;);// showDDL.selection.toString();&lt;br /&gt;
    shot = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;); //shotDDL.selection.toString();&lt;br /&gt;
    projectTake = getTake(findMainShot().name);&lt;br /&gt;
//    paths  = rootFolder+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/OUT/&amp;quot;;&lt;br /&gt;
    paths  = &amp;quot;/n/01_OUT/&amp;quot;+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/&amp;quot;;&lt;br /&gt;
    lastFolder = getLastModified(paths);&lt;br /&gt;
    //alert(lastFolder.name+&amp;quot; --&amp;gt;&amp;quot;+paths);&lt;br /&gt;
    if(lastFolder){&lt;br /&gt;
        folderTake = getTake(lastFolder.name);&lt;br /&gt;
    }else{&lt;br /&gt;
        folderTake = false;&lt;br /&gt;
    }&lt;br /&gt;
    msgPt1 = (folderTake)? &amp;quot;The last take in OUT folder is take &amp;quot;+(folderTake)+&amp;quot;.\n&amp;quot;:&amp;quot;No take was found in the OUT folder.\n&amp;quot;;&lt;br /&gt;
    msgPt2 = &amp;quot;The current project take is &amp;quot;+projectTake+&amp;quot;. \n\nChoose the current take:&amp;quot;;&lt;br /&gt;
    if(force || (folderTake != (projectTake-1))){&lt;br /&gt;
        recommended = (folderTake)?((folderTake)+1):projectTake;&lt;br /&gt;
        answer = prompt(msgPt1+msgPt2,recommended);&lt;br /&gt;
        //$.writeln(&amp;quot;blabal &amp;quot;+answer);&lt;br /&gt;
        if(answer != null){&lt;br /&gt;
            return answer;&lt;br /&gt;
        }else{&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return projectTake;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setTake(value){&lt;br /&gt;
    comp = findMainShot();&lt;br /&gt;
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf(&amp;quot;_T&amp;quot;)+2))+value;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTake(normalizedName){&lt;br /&gt;
    tmp = normalizedName.split(&amp;quot;_T&amp;quot;);&lt;br /&gt;
    return  parseFloat(tmp[tmp.length-1]);&lt;br /&gt;
}&lt;br /&gt;
	// Smart Import.jsx&lt;br /&gt;
	//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases&lt;br /&gt;
    //the only problem is that you can still not have 2 sequences in a single folder&lt;br /&gt;
   &lt;br /&gt;
function SmartImport(targetFolder){&lt;br /&gt;
         writeLn(&amp;quot;Importing files...&amp;quot;);&lt;br /&gt;
         var importedFiles = 0;&lt;br /&gt;
		var scriptName = &amp;quot;Smart Import&amp;quot;;&lt;br /&gt;
		var sourcePaths = getSourcePathsArray();&lt;br /&gt;
		// Ask the user for a folder whose contents are to be imported.&lt;br /&gt;
		//var targetFolder = Folder.selectDialog(&amp;quot;Import items from folder...&amp;quot;);&lt;br /&gt;
		if (targetFolder != null) {&lt;br /&gt;
			// If no project open, create a new project to import the files into.&lt;br /&gt;
			function processFile(theFile)&lt;br /&gt;
			{&lt;br /&gt;
				try {&lt;br /&gt;
					// Create a variable containing ImportOptions.&lt;br /&gt;
					var importOptions = new ImportOptions(theFile);&lt;br /&gt;
                    //alert();&lt;br /&gt;
                     if(theFile.name.toString().toLowerCase().lastIndexOf(&amp;quot;.psd&amp;quot;) != -1){&lt;br /&gt;
                        importOptions.importAs = ImportAsType.COMP;&lt;br /&gt;
                     }&lt;br /&gt;
                       importSafeWithError(importOptions);&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					// Ignore errors.&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			function testForSequences(files)&lt;br /&gt;
			{&lt;br /&gt;
				var searcher = new RegExp(&amp;quot;[0-9]{3,}$&amp;quot;);&lt;br /&gt;
				var movieFileSearcher = new RegExp(&amp;quot;(mov|avi|mpg)$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
				var parseResults = new Array;&lt;br /&gt;
                  var isFolder = new Array;&lt;br /&gt;
				&lt;br /&gt;
				// Test that we have a sequence. Stop parsing after 10 files.&lt;br /&gt;
				for (x = 0; (x &amp;lt; files.length) &amp;amp; x &amp;lt; 10; x++) {&lt;br /&gt;
					var movieFileResult = movieFileSearcher.exec(files[x].name);&lt;br /&gt;
					if (!movieFileResult) {&lt;br /&gt;
//******                           splitName = files[x].name.split(&amp;#039;.&amp;#039;)[0];&lt;br /&gt;
                            splitName=files[x].name.substring(0,files[x].name.length-4);&lt;br /&gt;
&lt;br /&gt;
                           //splitName[splitNameA.length] = &lt;br /&gt;
                           //alert(splitName);	&lt;br /&gt;
                         //  alert(files[x].name+&amp;quot; &amp;gt;&amp;gt;&amp;gt; &amp;quot;+files[x].name.split(&amp;#039;.&amp;#039;)[0]);&lt;br /&gt;
						var currentResult = searcher.exec(splitName);&lt;br /&gt;
						// Regular expressions return null if no match was found.&lt;br /&gt;
						// Otherwise, they return an array with the following information:&lt;br /&gt;
						// array[0] = the matched string.&lt;br /&gt;
						// array[1..n] = the matched capturing parentheses.&lt;br /&gt;
                   				&lt;br /&gt;
                    if (currentResult) { // We have a match -- the string contains numbers.&lt;br /&gt;
                           &lt;br /&gt;
                            // The match of those numbers is stored in the array[1]&lt;br /&gt;
							// Take that number and save it into parseResults.&lt;br /&gt;
							parseResults[parseResults.length] = currentResult[0];&lt;br /&gt;
						} else {&lt;br /&gt;
							parseResults[parseResults.length] = null;&lt;br /&gt;
						}&lt;br /&gt;
                           if(files[x].name == splitName){&lt;br /&gt;
                                isFolder[isFolder.length] = true;&lt;br /&gt;
                               // alert(&amp;quot;Folder &amp;quot;+files[x].name);&lt;br /&gt;
                           }else{&lt;br /&gt;
                                isFolder[isFolder.length] = false;&lt;br /&gt;
                           }&lt;br /&gt;
					} else {&lt;br /&gt;
						parseResults[parseResults.length] = null;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// If all the files we just went through have a number in their file names, &lt;br /&gt;
				// assume they are part of a sequence and return the first file.&lt;br /&gt;
				&lt;br /&gt;
				var result = null;&lt;br /&gt;
				for (i = 0; i &amp;lt; parseResults.length; ++i) {&lt;br /&gt;
                   				&lt;br /&gt;
                   if(!isFolder[i]){&lt;br /&gt;
                    if (parseResults[i]) {&lt;br /&gt;
						if (!result) {&lt;br /&gt;
                            &lt;br /&gt;
							result = files[i];		&lt;br /&gt;
						}&lt;br /&gt;
					} else {&lt;br /&gt;
                        &lt;br /&gt;
						// In this case, a file name did not contain a number.&lt;br /&gt;
						result = null;&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
                   }&lt;br /&gt;
                }&lt;br /&gt;
				&lt;br /&gt;
				return result;&lt;br /&gt;
			}&lt;br /&gt;
        			&lt;br /&gt;
			&lt;br /&gt;
			function importSafeWithError(importOptions)&lt;br /&gt;
			{&lt;br /&gt;
              if(!(sourcePaths.has(importOptions.file.toString()))){&lt;br /&gt;
 				try { &lt;br /&gt;
                  b = app.project.importFile(importOptions);&lt;br /&gt;
                    writeLn(importOptions.file.toString());&lt;br /&gt;
                 sfi = sourcesFolderItem();&lt;br /&gt;
                   b.parentFolder = sfi;&lt;br /&gt;
                    if(importOptions.importAs == ImportAsType.COMP){&lt;br /&gt;
                        for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                            if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name.lastIndexOf(&amp;quot; Layers&amp;quot;) != -1){&lt;br /&gt;
                         app.project.items[i].parentFolder = sfi;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                        writeLn(&amp;quot;Importing file (&amp;quot;+importedFiles+&amp;quot;)&amp;quot;);&lt;br /&gt;
                        importedFiles++;&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					alert(error.toString() + importOptions.file.fsName, scriptName);&lt;br /&gt;
				}&lt;br /&gt;
                // }else{&lt;br /&gt;
                //        alert(&amp;quot;file already here&amp;quot;);&lt;br /&gt;
                 }&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			&lt;br /&gt;
			function processFolder(theFolder)&lt;br /&gt;
			{&lt;br /&gt;
				// Get an array of files in the target folder.&lt;br /&gt;
				var files = theFolder.getFiles();&lt;br /&gt;
				//alert(files);&lt;br /&gt;
				// Test whether theFolder contains a sequence.&lt;br /&gt;
				var sequenceStartFile = testForSequences(files);&lt;br /&gt;
				&lt;br /&gt;
				// If it does contain a sequence, import the sequence,&lt;br /&gt;
				if (sequenceStartFile) {&lt;br /&gt;
					try {&lt;br /&gt;
						// Create a variable containing ImportOptions.&lt;br /&gt;
						var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
						importOptions.sequence = true;&lt;br /&gt;
						// importOptions.forceAlphabetical = true;		// Un-comment this if you want to force alpha order by default.&lt;br /&gt;
						importSafeWithError(importOptions);&lt;br /&gt;
					} catch (error) {&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// Otherwise, import the files and recurse.&lt;br /&gt;
				&lt;br /&gt;
				for (index in files) { // Go through the array and set each element to singleFile, then run the following.&lt;br /&gt;
					if (files[index] instanceof File) {&lt;br /&gt;
						if (!sequenceStartFile) { // If file is already part of a sequence, don&amp;#039;t import it individually.&lt;br /&gt;
							processFile(files[index]); // Calls the processFile function above.&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					if (files[index] instanceof Folder) {&lt;br /&gt;
						processFolder(files[index]); // recursion&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// Recursively examine that folder.&lt;br /&gt;
			processFolder(targetFolder);&lt;br /&gt;
		}&lt;br /&gt;
    clearOutput();	&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
   &lt;br /&gt;
function sourcesFolderItem(){&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
       // alert(app.project.items[i].name);&lt;br /&gt;
     if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name == &amp;quot;Sources&amp;quot;){&lt;br /&gt;
           return app.project.items[i];&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
    return app.project.items.addFolder(&amp;quot;Sources&amp;quot;);&lt;br /&gt;
    //else&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getSourcePathsArray(){&lt;br /&gt;
    var footageLocations = new Array();&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
        if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
            footageLocations[footageLocations.length] = app.project.item(i).file;&lt;br /&gt;
        }    &lt;br /&gt;
    }&lt;br /&gt;
    return footageLocations;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getLastModified(location,extension){&lt;br /&gt;
      myFolder = new Folder(location.toString());&lt;br /&gt;
      if (!myFolder instanceof Folder){&lt;br /&gt;
          return false;&lt;br /&gt;
     }&lt;br /&gt;
     listFiles = myFolder.getFiles(extension);&lt;br /&gt;
     listDates = new Array();&lt;br /&gt;
     maxId = 0;&lt;br /&gt;
     maxValue = 0;&lt;br /&gt;
     for(i=0;i&amp;lt;listFiles.length;i++){&lt;br /&gt;
         if (!(extension == &amp;quot;&amp;quot; &amp;amp;&amp;amp; (listFiles[i] instanceof File))){&lt;br /&gt;
             d = listFiles[i].modified;&lt;br /&gt;
             formattedDate = d.getFullYear()+&amp;quot;&amp;quot;+pad(d.getMonth()+1,2,0)+&amp;quot;&amp;quot;+pad(d.getDate(),2,0)+&amp;quot;&amp;quot;+pad(d.getHours(),2,0)+&amp;quot;&amp;quot;+pad(d.getMinutes(),2,0)+&amp;quot;&amp;quot;+pad(d.getSeconds(),2,0);&lt;br /&gt;
             listDates[i] = formattedDate;&lt;br /&gt;
             if(Math.max(maxValue,formattedDate) == formattedDate){&lt;br /&gt;
                 maxValue = formattedDate;&lt;br /&gt;
                 maxId = i;&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
     &lt;br /&gt;
     }&lt;br /&gt;
    if(listFiles[maxId] == &amp;quot;&amp;quot; ||listFiles[maxId] == &amp;quot;undefined&amp;quot;||listFiles[maxId] == null){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return listFiles[maxId];     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(number, length, character) {&lt;br /&gt;
    if (character == null) {&lt;br /&gt;
        character = &amp;quot;0&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    var numberStr = &amp;quot;&amp;quot; + number;&lt;br /&gt;
    while (numberStr.length &amp;lt; length) {&lt;br /&gt;
        numberStr = character + numberStr;&lt;br /&gt;
    }&lt;br /&gt;
    return numberStr;&lt;br /&gt;
}&lt;br /&gt;
function grabAnimatic(folderLocation){&lt;br /&gt;
    newLocation = new Folder(folderLocation);&lt;br /&gt;
    animatic = newLocation.getFiles(&amp;quot;*.mov&amp;quot;);&lt;br /&gt;
    sfi = sourcesFolderItem();&lt;br /&gt;
    if(animatic[0]){&lt;br /&gt;
        try {&lt;br /&gt;
            var importOptions = new ImportOptions(animatic[0]);&lt;br /&gt;
            animatic = app.project.importFile(importOptions);&lt;br /&gt;
            for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                    if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
                   app.project.item(i).parentFolder = sfi;&lt;br /&gt;
                    }    &lt;br /&gt;
            }&lt;br /&gt;
            return animatic;&lt;br /&gt;
        }catch (error){&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function figureOutShotName(filepath){&lt;br /&gt;
    folders = filepath.split(&amp;quot;/&amp;quot;);&lt;br /&gt;
    showFolder = folders[folders.length-2];&lt;br /&gt;
    showFolder = showFolder.split(&amp;quot;_&amp;quot;)[0];&lt;br /&gt;
    shotFolder = folders[folders.length-1];&lt;br /&gt;
    var searcher = new RegExp(&amp;quot;[0-9]{2,}$&amp;quot;);&lt;br /&gt;
    var currentResult = searcher.exec(shotFolder);&lt;br /&gt;
    if (currentResult){&lt;br /&gt;
        shotFolder = currentResult[0];&lt;br /&gt;
    }&lt;br /&gt;
    return showFolder+&amp;quot;_SC&amp;quot;+shotFolder;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function createMainComp(path){&lt;br /&gt;
    grabAnimatic(path);&lt;br /&gt;
    shotName = figureOutShotName(path);&lt;br /&gt;
    workComp = app.project.items.addComp(shotName+&amp;quot;_T1&amp;quot;, 1920, 1080,1.0 ,animatic.duration,25.0);&lt;br /&gt;
    animaticLayer = workComp.layers.add(animatic);&lt;br /&gt;
    animaticLayer.guideLayer = true;&lt;br /&gt;
    return workComp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnList2(type){&lt;br /&gt;
     if(type == &amp;quot;show&amp;quot;){&lt;br /&gt;
        rArray = returnFolderArray(rootFolder);&lt;br /&gt;
        for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
            }&lt;br /&gt;
    }else if(type == &amp;quot;scene&amp;quot;){&lt;br /&gt;
        rArray = [&amp;quot;a&amp;quot;,&amp;quot;b&amp;quot;];&lt;br /&gt;
        alert(dPanel.shotLoad.showDDL.selection.toString());&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function returnList(folder){&lt;br /&gt;
    rArray = returnFolderArray(folder);&lt;br /&gt;
    for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function findMainShot(){&lt;br /&gt;
    //fix GB##_SC_###_T1&lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               nuName = app.project.items[i].name;&lt;br /&gt;
               nuName = nuName.split(&amp;quot;SC_&amp;quot;);&lt;br /&gt;
               app.project.items[i].name = nuName[0]+&amp;quot;SC&amp;quot;+nuName[1];&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    var shot = null;&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               shot = app.project.items[i];&lt;br /&gt;
               break;&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if(shot == null){&lt;br /&gt;
        alert(&amp;quot;Could not find a comp named like \&amp;quot;GB##_SC###(a-z)_T#\&amp;quot;\n\nRename main comp accordingly, render again&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return shot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function dandyShotBuilderUI(thisObj){&lt;br /&gt;
    &lt;br /&gt;
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
    &lt;br /&gt;
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;DandyShotBuilderFix&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
           var msg = &amp;quot;This script requires the scripting security preference to be set. \nGo the \&amp;quot;General\&amp;quot; panel of your application preferences and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.\n\n Restart AFX&amp;quot;;&lt;br /&gt;
           var shotInfo = dPanel.add(&amp;quot;edittext&amp;quot;,[10,5,235,305],msg,{multiline:true}); &lt;br /&gt;
         }else{&lt;br /&gt;
      var shotLoad = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,175],&amp;quot;Load Shot&amp;quot;);&lt;br /&gt;
         var shotLoadTxt1 = shotLoad.add(&amp;quot;statictext&amp;quot;,[15,24,52,44],&amp;quot;Show:&amp;quot;);&lt;br /&gt;
         var showDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,19,210,44], returnList(rootFolder));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)){&lt;br /&gt;
                 showDDL.selection = showDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 showDDL.selection = 0;&lt;br /&gt;
                 app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
                }&lt;br /&gt;
         var shotLoadTxt2 = shotLoad.add(&amp;quot;statictext&amp;quot;,[12,59,52,79],&amp;quot;Scene:&amp;quot;);&lt;br /&gt;
         var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;shot&amp;quot;)){&lt;br /&gt;
                 shotDDL.selection = shotDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 shotDDL.selection = 0;&lt;br /&gt;
                }&lt;br /&gt;
         &lt;br /&gt;
          &lt;br /&gt;
         var shotStatusTxt = shotLoad.add(&amp;quot;statictext&amp;quot;,[25,95,52,115],&amp;quot;File:&amp;quot;);&lt;br /&gt;
         var shotStatusEditTxt = shotLoad.add(&amp;quot;edittext&amp;quot;,[56,92,210,115],&amp;quot;...&amp;quot;);&lt;br /&gt;
            shotStatusEditTxt.active = false;&lt;br /&gt;
            shotStatusEditTxt.enabled = false;&lt;br /&gt;
         var shotLoadBtn = shotLoad.add(&amp;quot;button&amp;quot;,[56,125,96,150],&amp;quot;Load&amp;quot;);&lt;br /&gt;
            shotLoadBtn.enabled = false;&lt;br /&gt;
         var shotBuildBtn = shotLoad.add(&amp;quot;button&amp;quot;,[101,125,141,150],&amp;quot;Build&amp;quot;);&lt;br /&gt;
            shotBuildBtn.enabled = false;&lt;br /&gt;
         var shotRefreshBtn = shotLoad.add(&amp;quot;button&amp;quot;,[146,125,168,150],&amp;quot;r&amp;quot;);&lt;br /&gt;
         //var shotOptns = shotLoad.add(&amp;quot;button&amp;quot;,[174,125,210,150],&amp;quot;optns&amp;quot;);&lt;br /&gt;
            //shotSetBtn.enabled = false;&lt;br /&gt;
        &lt;br /&gt;
       var shotTools = dPanel.add(&amp;quot;panel&amp;quot;,[10,185,235,300],&amp;quot;Misc&amp;quot;);&lt;br /&gt;
            shotToolsBtn1 =  shotTools.add(&amp;quot;button&amp;quot;,[12,12,79,35],&amp;quot;Open Folder&amp;quot;);&lt;br /&gt;
            shotToolsBtn1Xtra =  shotTools.add(&amp;quot;button&amp;quot;,[82,12,102,35],&amp;quot;N:&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2 =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,197,35],&amp;quot;Flick Last Take&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2.enabled = false; &lt;br /&gt;
            shotToolsBtnWF =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,135,35],&amp;quot;WF&amp;quot;);&lt;br /&gt;
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};&lt;br /&gt;
             shotToolsBtnFE =  shotTools.add(&amp;quot;button&amp;quot;,[139,12,167,35],&amp;quot;FE&amp;quot;);&lt;br /&gt;
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};&lt;br /&gt;
            shotToolsBtnXLS =  shotTools.add(&amp;quot;button&amp;quot;,[172,12,197,35],&amp;quot;xls&amp;quot;);&lt;br /&gt;
            //shotToolsBtnXLS.enabled = false;             &lt;br /&gt;
                &lt;br /&gt;
            shotToolsBtn3 =  shotTools.add(&amp;quot;button&amp;quot;,[12,40,102,63],&amp;quot;Grab Sources&amp;quot;);&lt;br /&gt;
            shotToolsBtn4 =  shotTools.add(&amp;quot;button&amp;quot;,[107,40,135,63],&amp;quot;Miss&amp;quot;);&lt;br /&gt;
            shotToolsBtnANI =  shotTools.add(&amp;quot;button&amp;quot;,[139,40,167,63],&amp;quot;ANI&amp;quot;);&lt;br /&gt;
            shotToolsBtnOPT =  shotTools.add(&amp;quot;button&amp;quot;,[172,40,197,63],&amp;quot;Opt&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
              //  shotToolsBtn4.enabled = false;            &lt;br /&gt;
            shotToolsBtn5 =  shotTools.add(&amp;quot;button&amp;quot;,[12,68,102,91],&amp;quot;Renderqueue&amp;quot;);&lt;br /&gt;
            shotToolsBtn6 =  shotTools.add(&amp;quot;button&amp;quot;,[107,68,197,91],&amp;quot;To Watchfolder&amp;quot;);&lt;br /&gt;
                //shotToolsBtn6.enabled = false; &lt;br /&gt;
            }&lt;br /&gt;
       var optnsGrp = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,300],&amp;quot;Options&amp;quot;);&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
            mgL = 20;&lt;br /&gt;
            mgT=15;&lt;br /&gt;
            mgBet=3;&lt;br /&gt;
            i=0;&lt;br /&gt;
            w=210;&lt;br /&gt;
            h=23;&lt;br /&gt;
            version = optnsGrp.add(&amp;quot;statictext&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;version &amp;quot;+version);i++;&lt;br /&gt;
            optMissingFootage =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Check for missing footage\rlonger, but safer&amp;quot;);i++;&lt;br /&gt;
            //$.writeln(&amp;quot;load missing footage pref set to&amp;quot;+(app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true);&lt;br /&gt;
                optMissingFootage.value = a;&lt;br /&gt;
            optNuComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;New comp after sending to WF&amp;quot;);i++;&lt;br /&gt;
                optNuComp.value = b;&lt;br /&gt;
            optSaveComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Ask to save before sending to WF&amp;quot;);i+=2;&lt;br /&gt;
                optSaveComp.value = c;&lt;br /&gt;
            rootFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Root folder location&amp;quot;);i++;&lt;br /&gt;
            watchFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Watchfolder location&amp;quot;);i+=2;&lt;br /&gt;
            okBtn =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;OK&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
       ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////&lt;br /&gt;
       &lt;br /&gt;
        showDDL.onChange = function(){&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
           // shotLoad.remove(shotDDL);&lt;br /&gt;
          shotDDL.removeAll();&lt;br /&gt;
&lt;br /&gt;
          items = returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString());&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               shotDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
        }&lt;br /&gt;
        shotDDL.onChange = function(){&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;,shotDDL.selection.toString());&lt;br /&gt;
            //$.writeln(&amp;quot;Changing&amp;quot;);&lt;br /&gt;
            var lastAEP = getLastModified(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/AEP&amp;quot;,&amp;quot;*.aep&amp;quot;);&lt;br /&gt;
            if(!lastAEP){&lt;br /&gt;
                shotStatusEditTxt.text = &amp;quot;no shot found, build one!&amp;quot;;&lt;br /&gt;
                   shotLoadBtn.enabled = false;&lt;br /&gt;
                   shotBuildBtn.enabled = true;&lt;br /&gt;
            &lt;br /&gt;
            }else{&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;,lastAEP.toString());&lt;br /&gt;
                loadFile = lastAEP.toString();&lt;br /&gt;
                fullSplitPath = lastAEP.toString().split(&amp;quot;/&amp;quot;);&lt;br /&gt;
                fileName = fullSplitPath[fullSplitPath.length-1];&lt;br /&gt;
                if(fileName.length &amp;gt; 20){&lt;br /&gt;
                    shotStatusEditTxt.text = fileName.substr(0,12)+&amp;quot;[...]&amp;quot;+fileName.substr(fileName.length-8);&lt;br /&gt;
                }else{&lt;br /&gt;
                    shotStatusEditTxt.text = fileName;&lt;br /&gt;
                } &lt;br /&gt;
                    shotLoadBtn.enabled = true;&lt;br /&gt;
                    shotBuildBtn.enabled = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotLoadBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(loadFile);&lt;br /&gt;
            if(opFile){&lt;br /&gt;
             app.open(opFile);&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
        shotRefreshBtn.onClick = function(){&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
            writeLn(&amp;quot;If it doesn&amp;#039;t seem to refresh, close and launch again.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        shotBuildBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
            if(opFile.exists){&lt;br /&gt;
                app.open(opFile);&lt;br /&gt;
                for(i=1;i&amp;lt;=app.project.numItems;i++){                     &lt;br /&gt;
                    app.project.items[i].remove();&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            createMainComp(targetFolder);&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            app.project.items.addFolder(&amp;quot;Precomps&amp;quot;);&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
            saveConf = confirm(&amp;quot;Save &amp;quot;+shotDDL.selection.toString()+&amp;quot; in the right folder ?&amp;quot;,false,&amp;quot;Dandelion Shot Builder&amp;quot;);&lt;br /&gt;
            if(saveConf){&lt;br /&gt;
                saveFile = new File(targetFolder+&amp;quot;/AEP/&amp;quot;+showDDL.selection.toString()+&amp;quot;_&amp;quot;+shotDDL.selection.toString()+&amp;quot;_comp01.aep&amp;quot;);&lt;br /&gt;
                app.project.save(saveFile);&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1.onClick = function(){&lt;br /&gt;
            explore(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString());&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1Xtra.onClick = function(){&lt;br /&gt;
            //$.writeln(&amp;quot;button&amp;quot;);&lt;br /&gt;
            p = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            checkOutFolder(p);&lt;br /&gt;
            explore(p);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn3.onClick = function(){&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnXLS.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.xls*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .xls shortcut found in in:&amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
       shotToolsBtnANI.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.mov*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .mov shortcut found in in: &amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnOPT.onClick = function(){&lt;br /&gt;
            shotTools.visible = false;&lt;br /&gt;
            shotLoad.visible = false;&lt;br /&gt;
            optnsGrp.visible = true;&lt;br /&gt;
        }&lt;br /&gt;
    okBtn.onClick = function(){&lt;br /&gt;
            shotTools.visible = true;&lt;br /&gt;
            shotLoad.visible = true;&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;,optMissingFootage.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;,optNuComp.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;,optSaveComp.value);&lt;br /&gt;
           // $.writeln(&amp;quot;new comp checked: &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
        //send to renderqueue&lt;br /&gt;
        //uses the comp name to figure out folder&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
        shotToolsBtn5.onClick = function(){&lt;br /&gt;
        rflag = false;&lt;br /&gt;
        //$.writeln(&amp;quot;missing fottage &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;));&lt;br /&gt;
        if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
            rflag = checkForMissingFootage(false);&lt;br /&gt;
        }&lt;br /&gt;
        if(!rflag){&lt;br /&gt;
            rcomp = findMainShot();&lt;br /&gt;
                if(rcomp){&lt;br /&gt;
                    shotNumber = getSetTake();&lt;br /&gt;
                    if(shotNumber != false){&lt;br /&gt;
                        //$.writeln(shotNumber);&lt;br /&gt;
                        setTake(shotNumber);&lt;br /&gt;
                        rq = app.project.renderQueue;&lt;br /&gt;
                        for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
                                rq.items[i].render = false;&lt;br /&gt;
                         }&lt;br /&gt;
                        rqitem = app.project.renderQueue.items.add(rcomp);&lt;br /&gt;
                        rqitem.outputModules[1].applyTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                        rqitem.applyTemplate(&amp;quot;Multi-Machine Settings&amp;quot;);&lt;br /&gt;
                        //savePath = rootFolder+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/OUT/&amp;quot;+rcomp.name;&lt;br /&gt;
                        savePath = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/&amp;quot;+rcomp.name;&lt;br /&gt;
                        &lt;br /&gt;
                        &lt;br /&gt;
                        if(checkOutFolder(savePath)){&lt;br /&gt;
                           rqitem.outputModules[1].file = new File(savePath+&amp;quot;/&amp;quot;+rcomp.name+&amp;quot;_[#####].tif&amp;quot;);&lt;br /&gt;
                        }else{&lt;br /&gt;
                           alert(&amp;quot;Couldn&amp;#039;t figure out an output folder for \&amp;quot;&amp;quot;+(rcomp.name)+&amp;quot;\&amp;quot;, select a folder manually.&amp;quot;);&lt;br /&gt;
                          rqitem.outputModules[1].file = new File();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;This project is missing source files and will not render. \nUse the &amp;#039;miss&amp;#039; button to find out where the footage should be&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn4.onClick = function(){&lt;br /&gt;
            //shotNumber = getSetTake(true);&lt;br /&gt;
            //setTake(shotNumber);&lt;br /&gt;
            checkForMissingFootage(true);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn6.onClick = function(){&lt;br /&gt;
           wf = new Folder(watchfolderLocation);&lt;br /&gt;
           if(wf.exists){&lt;br /&gt;
               sendToWatchFolder(wf,findMainShot().name);&lt;br /&gt;
               }else{&lt;br /&gt;
                   alert(&amp;quot;Watchfolder error, sorry!&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    rootFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Root folder is currently set to \n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New root folder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;,v);     &lt;br /&gt;
                rootFolder = v;&lt;br /&gt;
                          showDDL.removeAll();&lt;br /&gt;
                          items = returnList(rootFolder);&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               showDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
                showDDL.notify();&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                //$.writeln(&amp;quot;notified&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Watchfolder is currently set to \n\n\&amp;quot;&amp;quot;+watchfolderLocation+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New watchfolder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;,v);     &lt;br /&gt;
                watchfolderLocation = v;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    shotDDL.notify();&lt;br /&gt;
    }&lt;br /&gt;
    dandyShotBuilderUI(this) ;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===layer Position to .txt===&lt;br /&gt;
https://i.imgur.com/G5DX1T0.gif&lt;br /&gt;
Old, might not work! (but should :)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)&lt;br /&gt;
//&lt;br /&gt;
// The script will write to a text file the x &amp;amp; y values of the layer position for every frame comprised in&lt;br /&gt;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
function timeToFrameNum(myTime){&lt;br /&gt;
   return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);&lt;br /&gt;
}&lt;br /&gt;
function framesToTime(myTime){&lt;br /&gt;
   return myTime/app.project.activeItem.frameRate;&lt;br /&gt;
}&lt;br /&gt;
function timeToTimeCode(myTime){&lt;br /&gt;
   var framesN = myTime * app.project.activeItem.frameRate;&lt;br /&gt;
   fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));&lt;br /&gt;
   ho = addZero(Math.floor(myTime/3600));&lt;br /&gt;
   mi = addZero(Math.floor(myTime/60)-ho*60);&lt;br /&gt;
   se = addZero(Math.floor(myTime)-mi*60-ho*3600);&lt;br /&gt;
   return ho+&amp;quot;:&amp;quot;+mi+&amp;quot;:&amp;quot;+se+&amp;quot;:&amp;quot;+fr;&lt;br /&gt;
}&lt;br /&gt;
function addZero(val){&lt;br /&gt;
   if(val&amp;lt;10){&lt;br /&gt;
      val = &amp;quot;0&amp;quot;+val;&lt;br /&gt;
   }&lt;br /&gt;
   return val;&lt;br /&gt;
}&lt;br /&gt;
var myDisp = app.project.timecodeDisplayType;&lt;br /&gt;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;&lt;br /&gt;
var pText = &amp;quot;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 &amp;#039;16: 230;22&amp;#039;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if(app.project.activeItem != &amp;quot;null&amp;quot; &amp;amp;&amp;amp; app.project.activeItem != null &amp;amp;&amp;amp; app.project.activeItem != 0){&lt;br /&gt;
   if(app.project.activeItem.selectedLayers.length != 0){&lt;br /&gt;
      if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
         curLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
         var textName = &amp;quot;AEcoordinates.txt&amp;quot;;&lt;br /&gt;
         var myTextFile = filePutDialog(&amp;quot;Select a location to save your .txt file&amp;quot;, textName, &amp;quot;TEXT txt&amp;quot;);&lt;br /&gt;
         if(myTextFile == null){&lt;br /&gt;
            alert(&amp;quot;You must choose a place to save the file&amp;quot;);&lt;br /&gt;
         }else{&lt;br /&gt;
            var formatString = prompt(pText,&amp;quot;%i: %x;%y%l&amp;quot;);&lt;br /&gt;
            myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
            myKeys = curLayer.property(&amp;quot;position&amp;quot;).selectedKeys;&lt;br /&gt;
            if(myKeys.length != 0){&lt;br /&gt;
               strTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[0]);&lt;br /&gt;
               endTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[myKeys.length-1]);&lt;br /&gt;
            }else{&lt;br /&gt;
               strTime = app.project.activeItem.workAreaStart;&lt;br /&gt;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;&lt;br /&gt;
            }&lt;br /&gt;
            var startLoop = timeToFrameNum(strTime);&lt;br /&gt;
            var endLoop = timeToFrameNum(endTime);&lt;br /&gt;
            for(i=startLoop;i&amp;lt;=endLoop;i++){&lt;br /&gt;
               var curTime = framesToTime(i);&lt;br /&gt;
               out = formatString.replace(&amp;#039;%x&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[0]);&lt;br /&gt;
               out = out.replace(&amp;#039;%y&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[1]);&lt;br /&gt;
               out = out.replace(&amp;#039;%f&amp;#039;,i);timeToTimeCode&lt;br /&gt;
               out = out.replace(&amp;#039;%t&amp;#039;,timeToTimeCode(curTime));&lt;br /&gt;
               out = out.replace(&amp;#039;%i&amp;#039;,i-startLoop);&lt;br /&gt;
               out = out.replace(&amp;#039;%l&amp;#039;,&amp;#039;\n&amp;#039;);&lt;br /&gt;
               myTextFile.write(out);&lt;br /&gt;
               clearOutput();&lt;br /&gt;
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+&amp;quot;% done...&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
         clearOutput();&lt;br /&gt;
         writeLn(endLoop-startLoop+&amp;quot; positions saved to file&amp;quot;);&lt;br /&gt;
         writeLn(&amp;quot;script written by mlk =)&amp;quot;);&lt;br /&gt;
         myTextFile.close();&lt;br /&gt;
         }&lt;br /&gt;
      } else {&lt;br /&gt;
         alert (&amp;quot;This script requires the scripting security preference to be set.\n&amp;quot; +&lt;br /&gt;
         &amp;quot;Go to the \&amp;quot;General\&amp;quot; panel of your application preferences,\n&amp;quot; +&lt;br /&gt;
         &amp;quot;and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   }else{&lt;br /&gt;
      alert(&amp;quot;Select a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
}else{&lt;br /&gt;
   alert(&amp;quot;Select a composition and a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.project.timecodeDisplayType = myDisp;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Other Scripts==&lt;br /&gt;
&lt;br /&gt;
=== Work specific ===&lt;br /&gt;
&lt;br /&gt;
==== Auto scale precomps ====&lt;br /&gt;
Script that precomposes animation layers (specifically that are in .swf):&lt;br /&gt;
* scales up the layer by X amount &lt;br /&gt;
* scales down the precomp by 1/x amount&lt;br /&gt;
* Toggles the continuously rasterize off on the precomp, on inside.&lt;br /&gt;
This specifically solves the problem that lines out of Animate/Flash are &amp;#039;rough&amp;#039; (aliased) when imported at 100%, no matter which flags are toggled.&lt;br /&gt;
Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)&lt;br /&gt;
&lt;br /&gt;
Usage: save as .jsx, run the script via File&amp;gt;Scripts (Ctrl-Alt-Shift-D will redo latest script to repeat the process).&lt;br /&gt;
If no layers are selected, the script will instead prompt which scale factor it should use (4 by default).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// see https://berniebernie.fr/wiki/Afx_Javascript#Auto_scale_precomps for the why/how&lt;br /&gt;
(function afterEffectsSmartScale() {&lt;br /&gt;
	&lt;br /&gt;
    var SCRIPT_PREF_KEY = &amp;quot;SmartScaleFactor&amp;quot;;&lt;br /&gt;
	var SCRIPT_NAME = &amp;quot;SmartScaleTool&amp;quot;;&lt;br /&gt;
    var defaultScale = 4; &lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    var selectedLayers = comp.selectedLayers;&lt;br /&gt;
    var scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    function getScaleFactor(set) {&lt;br /&gt;
		var factor = defaultScale;&lt;br /&gt;
		if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){&lt;br /&gt;
			factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);&lt;br /&gt;
		}&lt;br /&gt;
		if(set){&lt;br /&gt;
			var promptResult = prompt(&amp;quot;Enter the scale factor (default is 400% -&amp;gt; 4)\nThen select layer(s) and run again.&amp;quot;, factor);&lt;br /&gt;
			if (promptResult === null) return null; &lt;br /&gt;
			factor = parseFloat(promptResult);&lt;br /&gt;
			app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
        return factor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (selectedLayers.length === 0) {&lt;br /&gt;
        scaleFactorX = getScaleFactor(1);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    scaleFactorX = getScaleFactor();&lt;br /&gt;
    if (scaleFactorX === null) return;&lt;br /&gt;
&lt;br /&gt;
    var scaleMultiplier = scaleFactorX;&lt;br /&gt;
    var inverseMultiplier = 1 / scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Smart Scale Precomposition&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        for (var i = 0; i &amp;lt; selectedLayers.length; i++) {&lt;br /&gt;
            var layer = selectedLayers[i];&lt;br /&gt;
            &lt;br /&gt;
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + &amp;quot;_Scale&amp;quot;, false);&lt;br /&gt;
            var precompLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
			precompLayer.collapseTransformation = false;&lt;br /&gt;
&lt;br /&gt;
            // 2. Inside the precomposition, scale the layer and composition&lt;br /&gt;
            if (precomp.numLayers &amp;gt; 0) {&lt;br /&gt;
                var innerLayer = precomp.layer(1);&lt;br /&gt;
                innerLayer.collapseTransformation = true;&lt;br /&gt;
                // Scale the inner layer&lt;br /&gt;
                var currentScale = innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);&lt;br /&gt;
                &lt;br /&gt;
                // Adjust composition size accordingly&lt;br /&gt;
                precomp.width *= scaleMultiplier;&lt;br /&gt;
                precomp.height *= scaleMultiplier;&lt;br /&gt;
&lt;br /&gt;
                // Adjust the position of the layer within the precomp to keep it centered &lt;br /&gt;
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Position&amp;quot;).setValue(newPos);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // 3. Back in the top composition, scale down the precomposed layer by 1/X&lt;br /&gt;
            var currentOuterScale = precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
            precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);&lt;br /&gt;
        }&lt;br /&gt;
    } catch (e) {&lt;br /&gt;
        alert(&amp;quot;An error occurred: &amp;quot; + e.toString());&lt;br /&gt;
    } finally {&lt;br /&gt;
        app.endUndoGroup();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx&lt;br /&gt;
&lt;br /&gt;
===Shelf===&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//to hijack admin and load shelf from my documents/shelf.jsx, this needs to be in the scriptUI Panel folder&lt;br /&gt;
{&lt;br /&gt;
    var nested_file = new File(&amp;quot;~/Documents/shelfBernie.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	//alert(nested_file.read());&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=888</id>
		<title>Afx Javascript</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=888"/>
		<updated>2025-12-02T10:26:58Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Other Scripts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Scripts==&lt;br /&gt;
=== Copy footage to local folder as proxy===&lt;br /&gt;
https://i.imgur.com/oZ24pac.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
 * simpleLocalProxy.jsx v1.0&lt;br /&gt;
 *&lt;br /&gt;
 * Copies footage from its current location to one chosen by the user (defaults to /tmp/) and allows to&lt;br /&gt;
 * switch original-proxy with a button&lt;br /&gt;
 * 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&amp;#039; file cache on scratch disks (&amp;#039;conformed media&amp;#039;)&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 * https://github.com/berniebernie/after-effects-scripts&lt;br /&gt;
 *    &lt;br /&gt;
 * Copyright 2015, bernie@berniebernie.fr&lt;br /&gt;
 *    &lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT  &lt;br /&gt;
 *&lt;br /&gt;
 * This script embeds js-md5 from github: https://github.com/emn178/js-md5 for practical reasons&lt;br /&gt;
 *&lt;br /&gt;
 * Script that can be launched or put in the scriptui folder of After Effects to be used as a panel&lt;br /&gt;
 *  &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * js-md5 v0.1.2&lt;br /&gt;
 * https://github.com/emn178/js-md5&lt;br /&gt;
 *&lt;br /&gt;
 * Copyright 2014, emn178@gmail.com&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      js-md5.js&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//(function(root, undefined){&lt;br /&gt;
  //&amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  var HEX_CHARS = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
  var HEX_TABLE = {&lt;br /&gt;
    &amp;#039;0&amp;#039;: 0, &amp;#039;1&amp;#039;: 1, &amp;#039;2&amp;#039;: 2, &amp;#039;3&amp;#039;: 3, &amp;#039;4&amp;#039;: 4, &amp;#039;5&amp;#039;: 5, &amp;#039;6&amp;#039;: 6, &amp;#039;7&amp;#039;: 7, &amp;#039;8&amp;#039;: 8, &amp;#039;9&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;a&amp;#039;: 10, &amp;#039;b&amp;#039;: 11, &amp;#039;c&amp;#039;: 12, &amp;#039;d&amp;#039;: 13, &amp;#039;e&amp;#039;: 14, &amp;#039;f&amp;#039;: 15,&lt;br /&gt;
    &amp;#039;A&amp;#039;: 10, &amp;#039;B&amp;#039;: 11, &amp;#039;C&amp;#039;: 12, &amp;#039;D&amp;#039;: 13, &amp;#039;E&amp;#039;: 14, &amp;#039;F&amp;#039;: 15&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var R = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,&lt;br /&gt;
           5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,&lt;br /&gt;
           4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,&lt;br /&gt;
           6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];&lt;br /&gt;
&lt;br /&gt;
  var K = [0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE,&lt;br /&gt;
           0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,&lt;br /&gt;
           0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,&lt;br /&gt;
           0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821,&lt;br /&gt;
           0XF61E2562, 0XC040B340, 0X265E5A51, 0XE9B6C7AA,&lt;br /&gt;
           0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8,&lt;br /&gt;
           0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED,&lt;br /&gt;
           0XA9E3E905, 0XFCEFA3F8, 0X676F02D9, 0X8D2A4C8A,&lt;br /&gt;
           0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C,&lt;br /&gt;
           0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70,&lt;br /&gt;
           0X289B7EC6, 0XEAA127FA, 0XD4EF3085, 0X04881D05,&lt;br /&gt;
           0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665,&lt;br /&gt;
           0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039,&lt;br /&gt;
           0X655B59C3, 0X8F0CCC92, 0XFFEFF47D, 0X85845DD1,&lt;br /&gt;
           0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1,&lt;br /&gt;
           0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391];&lt;br /&gt;
&lt;br /&gt;
  var jsmd5 = function(message) {&lt;br /&gt;
    var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message);&lt;br /&gt;
    var h0 = 0x67452301;&lt;br /&gt;
    var h1 = 0xEFCDAB89;&lt;br /&gt;
    var h2 = 0x98BADCFE;&lt;br /&gt;
    var h3 = 0x10325476;&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0, length = blocks.length;i &amp;lt; length;i += 16)&lt;br /&gt;
    {&lt;br /&gt;
      var a = h0;&lt;br /&gt;
      var b = h1;&lt;br /&gt;
      var c = h2;&lt;br /&gt;
      var d = h3;&lt;br /&gt;
      var f, g, tmp, x, y;&lt;br /&gt;
&lt;br /&gt;
      for(var j = 0;j &amp;lt; 64;++j)&lt;br /&gt;
      {&lt;br /&gt;
        if(j &amp;lt; 16)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (b &amp;amp; c) | ((~b) &amp;amp; d);&lt;br /&gt;
          f = d ^ (b &amp;amp; (c ^ d));&lt;br /&gt;
          g = j;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 32)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (d &amp;amp; b) | ((~d) &amp;amp; c);&lt;br /&gt;
          f = c ^ (d &amp;amp; (b ^ c));&lt;br /&gt;
          g = (5 * j + 1) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 48)&lt;br /&gt;
        {&lt;br /&gt;
          f = b ^ c ^ d;&lt;br /&gt;
          g = (3 * j + 5) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          f = c ^ (b | (~d));&lt;br /&gt;
          g = (7 * j) % 16;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        tmp = d;&lt;br /&gt;
        d = c&lt;br /&gt;
        c = b&lt;br /&gt;
&lt;br /&gt;
        // leftrotate&lt;br /&gt;
        x = (a + f + K[j] + blocks[i + g]);&lt;br /&gt;
        y = R[j];&lt;br /&gt;
        b += (x &amp;lt;&amp;lt; y) | (x &amp;gt;&amp;gt;&amp;gt; (32 - y));&lt;br /&gt;
        a = tmp;&lt;br /&gt;
      }&lt;br /&gt;
      h0 = (h0 + a) | 0;&lt;br /&gt;
      h1 = (h1 + b) | 0;&lt;br /&gt;
      h2 = (h2 + c) | 0;&lt;br /&gt;
      h3 = (h3 + d) | 0;&lt;br /&gt;
    }&lt;br /&gt;
    return toHexString(h0) + toHexString(h1) + toHexString(h2) + toHexString(h3);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var toHexString = function(num) {&lt;br /&gt;
    var hex = &amp;quot;&amp;quot;;&lt;br /&gt;
    for(var i = 0; i &amp;lt; 4; i++)&lt;br /&gt;
    {&lt;br /&gt;
      var offset = i &amp;lt;&amp;lt; 3;&lt;br /&gt;
      hex += HEX_CHARS.charAt((num &amp;gt;&amp;gt; (offset + 4)) &amp;amp; 0x0F) + HEX_CHARS.charAt((num &amp;gt;&amp;gt; offset) &amp;amp; 0x0F);&lt;br /&gt;
    }&lt;br /&gt;
    return hex;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var hasUTF8 = function(message) {&lt;br /&gt;
    var i = message.length;&lt;br /&gt;
    while(i--)&lt;br /&gt;
      if(message.charCodeAt(i) &amp;gt; 127)&lt;br /&gt;
        return true;&lt;br /&gt;
    return false;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var ASCIItoBlocks = function(message) {&lt;br /&gt;
    // a block is 32 bits(4 bytes), a chunk is 512 bits(64 bytes)&lt;br /&gt;
    var length = message.length;&lt;br /&gt;
    var chunkCount = ((length + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    var i;&lt;br /&gt;
    for(i = 0;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    for(i = 0;i &amp;lt; length;++i)&lt;br /&gt;
      blocks[i &amp;gt;&amp;gt; 2] |= message.charCodeAt(i) &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[i &amp;gt;&amp;gt; 2] |= 0x80 &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[blockCount - 2] = length &amp;lt;&amp;lt; 3; // length * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var UTF8toBlocks = function(message) {&lt;br /&gt;
    var uri = encodeURIComponent(message);&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    for(var i = 0, bytes = 0, length = uri.length;i &amp;lt; length;++i)&lt;br /&gt;
    {&lt;br /&gt;
      var c = uri.charCodeAt(i);&lt;br /&gt;
      if(c == 37) // %&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= ((HEX_TABLE[uri.charAt(++i)] &amp;lt;&amp;lt; 4) | HEX_TABLE[uri.charAt(++i)]) &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      else&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= c &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      ++bytes;&lt;br /&gt;
    }&lt;br /&gt;
    var chunkCount = ((bytes + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var index = bytes &amp;gt;&amp;gt; 2;&lt;br /&gt;
    blocks[index] |= 0x80 &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    for(var i = index + 1;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    blocks[blockCount - 2] = bytes &amp;lt;&amp;lt; 3; // bytes * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  /*if(typeof(module) != &amp;#039;undefined&amp;#039;)&lt;br /&gt;
    module.exports = jsmd5;&lt;br /&gt;
  else if(root)&lt;br /&gt;
    root.jsmd5 = jsmd5;*/&lt;br /&gt;
//}(this));&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      simpleLocalProxy.jsx &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(str){&lt;br /&gt;
    //uncomment to allow debugging&lt;br /&gt;
    //$.writeln(str);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pathToLocalizedPath(path){&lt;br /&gt;
        f = new File(path);&lt;br /&gt;
        return f.fsName.toString();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sequenceFilesWildcard(path){ &lt;br /&gt;
    //returns false or an array with everything before a sequence&amp;#039;s image number, and the extension: /c/path/file.0555.exr &amp;gt; { /c/pathfile. ; .exr }&lt;br /&gt;
    var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g; &lt;br /&gt;
    var match = myRegexp.exec(path);&lt;br /&gt;
    if(!match){&lt;br /&gt;
        return false;&lt;br /&gt;
    }else{&lt;br /&gt;
        return match;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabPaths(footage){&lt;br /&gt;
    //returns false or the path of the given footage, if it&amp;#039;s a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr &amp;gt; /c/path/file.*.exr&lt;br /&gt;
    var f = footage;&lt;br /&gt;
    returnpath = false;&lt;br /&gt;
    if(f instanceof FootageItem &amp;amp;&amp;amp; f.file != null){       &lt;br /&gt;
        var source = f.mainSource.file.toString();&lt;br /&gt;
        if(!f.mainSource.isStill){&lt;br /&gt;
            var pathFromRegex = sequenceFilesWildcard(source);&lt;br /&gt;
            if(pathFromRegex){&lt;br /&gt;
                returnpath = pathFromRegex[1]+&amp;quot;*&amp;quot;+pathFromRegex[2];&lt;br /&gt;
            }else{&lt;br /&gt;
                returnpath = source;&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            returnpath = source;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return returnpath;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function grabFootagePathsAndCopyToLocal(){&lt;br /&gt;
    //main worker function&lt;br /&gt;
    &lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1 || !(sel[0] instanceof FootageItem)){&lt;br /&gt;
        alert(&amp;quot;Select footage(s) and try again&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        &lt;br /&gt;
        var debugcheck = (getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var useforcecopy = (getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        &lt;br /&gt;
        var isMacintosh = ($.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;)==-1);&lt;br /&gt;
        var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        &lt;br /&gt;
        var batchFile = (isMacintosh)?&amp;quot;# bash file used to copy After Effects footage to local storage&amp;quot;:&amp;quot;@echo off\nREM batch file to copy After Effects footage to local storage&amp;quot;;&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            if(!sel[i].useProxy){&lt;br /&gt;
                //grab path from current selection item&lt;br /&gt;
                var curPath = grabPaths(sel[i]);&lt;br /&gt;
                var fileName = curPath.split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                &lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
            &lt;br /&gt;
                if(isMacintosh){&lt;br /&gt;
                    //macos uses rsync to copy files&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrsync -v -a &amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot;-I &amp;quot;:&amp;quot;&amp;quot;);&lt;br /&gt;
                    batchFile += dir+fileName;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;+outputDir;&lt;br /&gt;
                    &lt;br /&gt;
                }else{&lt;br /&gt;
                    &lt;br /&gt;
                    //windows uses robocopy&lt;br /&gt;
                    sourcePath = pathToLocalizedPath(dir);&lt;br /&gt;
                    destinationPath = pathToLocalizedPath(outputDir);&lt;br /&gt;
                    filename = fileName.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrobocopy &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += sourcePath;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    //robocopy filename requires a wildcard, even if it&amp;#039;s a single file&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += destinationPath ;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;                    &lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += filename ;&lt;br /&gt;
                    batchFile += &amp;quot;*\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot; /XO&amp;quot;:&amp;quot;&amp;quot;)+&amp;quot; /FFT&amp;quot;+((debugcheck)?&amp;quot;&amp;quot;:&amp;quot; /NJH /NJS&amp;quot;);&lt;br /&gt;
                    e(batchFile);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        batchFile += ((debugcheck)?(isMacintosh?&amp;quot;\nread a&amp;quot;:&amp;quot;\npause&amp;quot;):&amp;quot;&amp;quot;); //nested tertiary operators, sue me&lt;br /&gt;
        &lt;br /&gt;
        var txtFile;&lt;br /&gt;
        if(isMacintosh){&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.command&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.bat&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        if(txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;) == true){&lt;br /&gt;
&lt;br /&gt;
            txtFile.write(batchFile);&lt;br /&gt;
            txtFile.close();&lt;br /&gt;
	    if(isMacintosh){&lt;br /&gt;
&lt;br /&gt;
            		system.callSystem(&amp;quot;chmod +x &amp;quot; + txtFile.toString() +&amp;quot;&amp;quot;);&lt;br /&gt;
system.callSystem(txtFile.toString());&lt;br /&gt;
		}else{&lt;br /&gt;
            txtFile.execute();&lt;br /&gt;
}&lt;br /&gt;
        }else{&lt;br /&gt;
            alert(&amp;quot;Write permission denied on\n&amp;quot;+localSaveDir);&lt;br /&gt;
        }&lt;br /&gt;
        //txtFile.remove();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabFootagePathsAndSwitchProxy(){&lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1){&lt;br /&gt;
        alert(&amp;quot;Select footage first&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            item = sel[i];&lt;br /&gt;
            if(item.useProxy){&lt;br /&gt;
                item.useProxy = false;&lt;br /&gt;
            }else{&lt;br /&gt;
                var curPath = grabPaths(item);&lt;br /&gt;
                var fileName = item.mainSource.file.toString().split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
                var proxyFilePath = outputDir+&amp;quot;/&amp;quot;+fileName;&lt;br /&gt;
                var proxyFile = new File(proxyFilePath);&lt;br /&gt;
                e(proxyFilePath);&lt;br /&gt;
                if(proxyFile.exists){&lt;br /&gt;
                    if(item.mainSource.isStill){&lt;br /&gt;
                        item.setProxy(proxyFile);&lt;br /&gt;
                    }else{&lt;br /&gt;
                        //dirty workaround if the file is a movie file (.mpg, qucktime etc...)&lt;br /&gt;
                        try {&lt;br /&gt;
                            item.setProxyWithSequence(proxyFile,false);&lt;br /&gt;
                        }&lt;br /&gt;
                        catch(err) {&lt;br /&gt;
                            item.setProxy(proxyFile);&lt;br /&gt;
                        }   &lt;br /&gt;
                    }&lt;br /&gt;
                    item.proxySource.alphaMode = item.mainSource.alphaMode;&lt;br /&gt;
                    item.proxySource.premulColor = item.mainSource.premulColor;&lt;br /&gt;
                    item.proxySource.invertAlpha= item.mainSource.invertAlpha;&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(getPref(&amp;quot;debug&amp;quot;,false)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                        alert(&amp;quot;&amp;gt;&amp;gt;&amp;gt; NO PROXY FOUND\n&amp;quot;+proxyFilePath);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPref(pref,value){&lt;br /&gt;
    app.settings.saveSetting(&amp;quot;simpleLocalProxy&amp;quot;, pref, value);&lt;br /&gt;
}&lt;br /&gt;
function getPref(pref,defaultValue){&lt;br /&gt;
    prefsVar = &amp;quot;simpleLocalProxy&amp;quot;;&lt;br /&gt;
    if(app.settings.haveSetting(prefsVar, pref)){&lt;br /&gt;
        return app.settings.getSetting(prefsVar, pref);&lt;br /&gt;
    }else{&lt;br /&gt;
        app.settings.saveSetting(prefsVar, pref, defaultValue);&lt;br /&gt;
        setPref(pref,defaultValue)&lt;br /&gt;
        return defaultValue;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function simpleLocalProxy(thisObj) {&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Local Proxy&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    &lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        var localFolder = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        // UI DESCRIPTION&lt;br /&gt;
        &lt;br /&gt;
        res = &amp;quot;group { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                        cols: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            col1: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                saveDirText: StaticText {text: &amp;#039;Proxy folder setup&amp;#039;},\&lt;br /&gt;
                                saveDirOptions: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    usemd5rbox: RadioButton {text: &amp;#039; Simple&amp;#039;,helpTip:&amp;#039;Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)&amp;#039;,value:true},\&lt;br /&gt;
                                    usepathrbox: RadioButton {text: &amp;#039; Copy Folder Structure&amp;#039;,helpTip:&amp;#039;Copies the target folder structure inside the proxy folder; more folders&amp;#039;}}},\&lt;br /&gt;
                            col2: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                optionsText: StaticText {text: &amp;#039;Options: &amp;#039;},\&lt;br /&gt;
                                optionsGrp: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    forcecopyChkbox: Checkbox {text: &amp;#039; Force copy&amp;#039;,helpTip:&amp;#039;Overwrite files (otherwise skips existing files)&amp;#039;},\&lt;br /&gt;
                                    debugChkbox: Checkbox {text: &amp;#039; Debug&amp;#039;,helpTip:&amp;#039;Show full batch process and pause at end of copies&amp;#039;}}}},\&lt;br /&gt;
                        cols2: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            localSaveDirBut: Button {text: &amp;#039; Choose Proxy Folder &amp;#039;, helpTip:&amp;#039;choose folder to copy files to&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                            browseBut: Button {text: &amp;#039; Browse &amp;#039; , preferredSize:[-1,30]}} , \&lt;br /&gt;
                        curentDirTxt: EditText {text: &amp;#039;&amp;quot; + localFolder +&amp;quot;&amp;#039;,enabled:false},\&lt;br /&gt;
                        copyFootageBut: Button {text: &amp;#039; Copy Footage(s) to Proxy Folder &amp;#039; ,helpTip:&amp;#039;Launches a background batch copy of selected footage&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                        switchproxyBut: Button {text: &amp;#039; Switch Proxy/Original &amp;#039;,helpTip:&amp;#039;Switches from original to proxy path and back,  warning if no local copy has been found&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        //UI DRAW&lt;br /&gt;
        &lt;br /&gt;
        pan.grp = pan.add(res); &lt;br /&gt;
        pan.layout.layout(true);&lt;br /&gt;
        &lt;br /&gt;
        //radio buttons and checkboxes prefs&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
        var usemd5 = getPref(&amp;quot;usemd5&amp;quot;,true);&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5==&amp;quot;true&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
        var useforcecopy = getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var debugcheck = getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
&lt;br /&gt;
        pan.layout.resize();&lt;br /&gt;
        pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
        // UI ACTIONS&lt;br /&gt;
        &lt;br /&gt;
        //browse button&lt;br /&gt;
        pan.grp.cols2.browseBut.onClick = function(){&lt;br /&gt;
                localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
                localSaveDir.execute();&lt;br /&gt;
        }&lt;br /&gt;
        //choose local folder button&lt;br /&gt;
        pan.grp.cols2.localSaveDirBut.onClick = function(){&lt;br /&gt;
            localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
            o = localSaveDir.selectDlg(&amp;quot;Choose folder to copy footage to&amp;quot;);&lt;br /&gt;
            if(o!=null){&lt;br /&gt;
                    setPref(&amp;quot;localSaveDir&amp;quot;,o.toString());&lt;br /&gt;
                    pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
        pan.grp.copyFootageBut.onClick = function(){&lt;br /&gt;
            //copy footages button (launches the &amp;#039;guts&amp;#039; of this script)&lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
            &lt;br /&gt;
            grabFootagePathsAndCopyToLocal();&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        pan.grp.switchproxyBut.onClick = function(){&lt;br /&gt;
            //checks for a local copy of the footage, and switches to a proxy accordingly &lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            &lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
&lt;br /&gt;
            grabFootagePathsAndSwitchProxy();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (pan instanceof Window) pan.show() ;&lt;br /&gt;
}&lt;br /&gt;
simpleLocalProxy(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Expose Animation ===&lt;br /&gt;
Adds &amp;#039;hold&amp;#039; keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD.&lt;br /&gt;
&lt;br /&gt;
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;1000&amp;quot; height=&amp;quot;800&amp;quot; &amp;gt;9-3XVlME2tY&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Step 1: setup detector on layer&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;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 &amp;#039;Detector&amp;#039; picks up a change (value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Step 2: bake to keys&amp;#039; ,preferredSize:[-1,30],enabled:true} , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Bakes detected animation as time remapped keys to animation, this can be long, watch your &amp;quot;Info&amp;quot; panel. If some animation is not detected, change resoltion and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    applyKeys: Button {text: &amp;#039;Step 3: apply as time remapping on selected layers&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Select layers on which to apply time remapping (\&amp;quot;exposed\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        //pan.grp.bakeKeys.enabled = true;        &lt;br /&gt;
        setupDetector();&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    curlayer = layers[0];&lt;br /&gt;
&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer);&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index;&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+&amp;quot;_anim_detection&amp;quot;,true);&lt;br /&gt;
    var precomplayer =  app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    var toplayer = precomp.layers[1];&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration;&lt;br /&gt;
    newlayer.timeRemapEnabled = true;&lt;br /&gt;
    newlayer.inPoint -= app.project.activeItem.frameDuration;&lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;Marker&amp;quot;).setValueAtTime(.5, explainer);&lt;br /&gt;
&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var sliderctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;;&lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
   &lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    var detectorctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider;&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to &amp;#039;clean&amp;#039; expression&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // add &amp;#039;0&amp;#039; key on first frame&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.addKey(0);&lt;br /&gt;
    currentSlider.setValueAtKey(1, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for(i = 0;i&amp;lt;layers.length;i++){&lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;Time Remap&amp;quot;);&lt;br /&gt;
        for(j=1;j&amp;lt;=currentSlider.numKeys;j++){&lt;br /&gt;
            &lt;br /&gt;
            v = currentSlider.keyValue(j);&lt;br /&gt;
            t = currentSlider.keyTime(j);&lt;br /&gt;
            remap.addKey(t);&lt;br /&gt;
            remap.setValueAtKey(j, v);&lt;br /&gt;
            remap.setInterpolationTypeAtKey(j,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\&lt;br /&gt;
        a = timeRemap;\&lt;br /&gt;
        nk = a.nearestKey(time);\&lt;br /&gt;
        curframe = 0;\&lt;br /&gt;
        if(nk.time &amp;gt; time){\&lt;br /&gt;
            curframe = nk.index-1;\&lt;br /&gt;
        }else{\&lt;br /&gt;
            curframe = nk.index;\&lt;br /&gt;
        }\&lt;br /&gt;
        curframe*thisComp.frameDuration;&amp;quot;;&lt;br /&gt;
        remap.expressionEnabled = false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create Null controllers on Puppet pins ===&lt;br /&gt;
http://i.imgur.com/HdFjaYZ.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//nulls created from puppet pins can be parented like normal layers&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Create Null Controls on Puppet Pinsv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    function getLayerFromProperty(prop){&lt;br /&gt;
        return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var c = app.project.activeItem;&lt;br /&gt;
    if( c != null &amp;amp;&amp;amp; c.selectedProperties != null){&lt;br /&gt;
        var props = c.selectedProperties;&lt;br /&gt;
        j = 0;&lt;br /&gt;
        for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
            if(props[i].matchName == &amp;quot;ADBE FreePin3 PosPin Atom&amp;quot;){&lt;br /&gt;
                j++;&lt;br /&gt;
                child = props[i].property(&amp;quot;ADBE FreePin3 PosPin Position&amp;quot;);&lt;br /&gt;
                pos = [child.value[0],child.value[1]];&lt;br /&gt;
                nullLayer = app.project.activeItem.layers.addNull();&lt;br /&gt;
                nullLayer.name = &amp;quot;puppetCtrl&amp;quot;+j;&lt;br /&gt;
                nullLayer.position.setValue(pos);&lt;br /&gt;
                var expr = &amp;quot;thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).toWorld(thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).transform.anchorPoint)&amp;quot;;&lt;br /&gt;
                child.expression = expr;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Batch replace file locations with text file===&lt;br /&gt;
http://i.imgur.com/88QHvlQ.jpg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//now works with sequences or still images, windows only&lt;br /&gt;
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path&lt;br /&gt;
function URIToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function WinPathtoURI(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/ /g, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    //str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Change File Locations&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/tempAE.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txt = &amp;quot;&amp;quot;;&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    var isSequence = new Array();&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
       alert(&amp;quot;Select footage items.&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            isSequence[i] = !sel[i].mainSource.isStill;&lt;br /&gt;
            txt += URIToWinPath(sel[i].mainSource.file.toString())+&amp;quot;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        txtFile.write(&amp;quot;*** 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&amp;quot;);&lt;br /&gt;
        txtFile.write(txt);&lt;br /&gt;
        txtFile.close();&lt;br /&gt;
        txtFile.execute();&lt;br /&gt;
        isOk = confirm(&amp;quot;Change file paths ?\n\nReloading might take a while!&amp;quot;);&lt;br /&gt;
        if(isOk){&lt;br /&gt;
           txtFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
           contents = txtFile.read();&lt;br /&gt;
           arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
           for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
                var tmpFile =  new File(WinPathtoURI(arrayContents[i+2]));&lt;br /&gt;
                //alert(tmpFile);&lt;br /&gt;
                if(isSequence[i]){&lt;br /&gt;
                    sel[i].replaceWithSequence(tmpFile,0);&lt;br /&gt;
                }else{&lt;br /&gt;
                    sel[i].replace(tmpFile);&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                writeLn(Math.round((i+1)/app.project.selection.length*100)+&amp;quot;%&amp;quot;);&lt;br /&gt;
           }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Loop Selected Layers===&lt;br /&gt;
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif&lt;br /&gt;
&lt;br /&gt;
No hassle looping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Set loops&amp;quot;);&lt;br /&gt;
var layersList = app.project.activeItem.selectedLayers;&lt;br /&gt;
var frameD = app.project.activeItem.frameDuration;&lt;br /&gt;
for (i=0;i&amp;lt;layersList.length;i++){&lt;br /&gt;
    if(!layersList[i].timeRemapEnabled){&lt;br /&gt;
        var outP = layersList[i].outPoint;&lt;br /&gt;
        layersList[i].timeRemapEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP,0);&lt;br /&gt;
        layersList[i].timeRemap.expressionEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.expression = &amp;quot;loopOut()&amp;quot;;&lt;br /&gt;
        layersList[i].outPoint = app.project.activeItem.workAreaStart+app.project.activeItem.workAreaDuration;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&lt;br /&gt;
===Find Next Text Layer===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
    a = true;&lt;br /&gt;
                if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
                }&lt;br /&gt;
    if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
        var comp = app.project.item(i);&lt;br /&gt;
        for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
            a = true;&lt;br /&gt;
            comp.layer(j).selected = false;&lt;br /&gt;
            if(comp.layer(j) instanceof TextLayer){&lt;br /&gt;
                comp.layer(j).selected = true;&lt;br /&gt;
                $.writeln(comp.name);&lt;br /&gt;
                a = confirm(&amp;quot;Continue selecting text layers&amp;quot;,true);&lt;br /&gt;
            }&lt;br /&gt;
            if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
            }else{&lt;br /&gt;
                comp.layer(j).selected = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(a){&lt;br /&gt;
    alert(&amp;quot;No (more) text layers found&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simple Watchfolder===&lt;br /&gt;
http://i.imgur.com/poTWO.png&lt;br /&gt;
https://i.imgur.com/wvtgDqE.png&lt;br /&gt;
&lt;br /&gt;
I know a lot of people use media encoder but it&amp;#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Show original source location (WIN only)===&lt;br /&gt;
http://i.imgur.com/04O3r.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    function pathToWinPath(path){&lt;br /&gt;
        var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
        str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
        str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
        str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
        return str;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
        alert(&amp;quot;You need to select source(s) in the project panel&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            a = prompt(&amp;quot;&amp;#039;OK&amp;#039; continues, &amp;#039;cancel&amp;#039; stops displaying original sources\n\n[ &amp;quot;+sel[i].name+&amp;quot; ]&amp;quot;,pathToWinPath(sel[i].mainSource.file.path.toString()));    &lt;br /&gt;
            if(!a){break}&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===List selected layers effects and their properties matchNames ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//windows only&lt;br /&gt;
if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/effectList.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    var col = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for (i=0;i&amp;lt;col.length;i++){&lt;br /&gt;
        effs = col[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
            for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
                txtFile.write(&amp;quot;\n( &amp;quot;+effs.property(j).matchName+&amp;quot; ) &amp;quot;+effs.property(j).name+&amp;quot;\n------------------------------------------\n&amp;quot;);&lt;br /&gt;
                for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
                    txtFile.write(effs.property(j).property(k).matchName+&amp;quot; --&amp;gt; &amp;quot;+effs.property(j).property(k).name);&lt;br /&gt;
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE &amp;amp;&amp;amp; effs.property(j).property(k).value != undefined){&lt;br /&gt;
                        txtFile.write(&amp;quot; [ &amp;quot;+effs.property(j).property(k).value.toString()+&amp;quot; ] &amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    txtFile.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    txtFile.write(&amp;quot;\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v = s.Effects.addProperty(\&amp;quot;CC RepeTile\&amp;quot;);\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v.property(\&amp;quot;CC RepeTile-0001\&amp;quot;).setValue(10);&amp;quot;);    &lt;br /&gt;
    txtFile.close();&lt;br /&gt;
    txtFile.execute();&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Set scripting Prefs to enable to write to disk&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/IEUwe.gif&lt;br /&gt;
&lt;br /&gt;
==== Disable effects ====&lt;br /&gt;
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!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function toggleEffectGlobally() {&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Toggle Effect Globally&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    var sel = app.project.activeItem &amp;amp;&amp;amp; app.project.activeItem.selectedProperties;&lt;br /&gt;
    if (!sel || sel.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var selectedEffect = null;&lt;br /&gt;
&lt;br /&gt;
    // Find the selected effect property group&lt;br /&gt;
    for (var i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
        if (sel[i].matchName &amp;amp;&amp;amp; sel[i].parentProperty &amp;amp;&amp;amp; sel[i].parentProperty.matchName === &amp;quot;ADBE Effect Parade&amp;quot;) {&lt;br /&gt;
            selectedEffect = sel[i];&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (!selectedEffect) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect in the timeline.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var effectName = selectedEffect.matchName;&lt;br /&gt;
    var effectEnabled = selectedEffect.enabled;&lt;br /&gt;
&lt;br /&gt;
    // Loop through all comps in the project&lt;br /&gt;
    for (var p = 1; p &amp;lt;= app.project.numItems; p++) {&lt;br /&gt;
        var item = app.project.item(p);&lt;br /&gt;
        if (item instanceof CompItem) {&lt;br /&gt;
            for (var l = 1; l &amp;lt;= item.numLayers; l++) {&lt;br /&gt;
                var layer = item.layer(l);&lt;br /&gt;
                if (layer.property(&amp;quot;ADBE Effect Parade&amp;quot;)) {&lt;br /&gt;
                    var effects = layer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
                    for (var e = 1; e &amp;lt;= effects.numProperties; e++) {&lt;br /&gt;
                        var eff = effects.property(e);&lt;br /&gt;
                        if (eff.matchName === effectName) {&lt;br /&gt;
                            eff.enabled = !effectEnabled;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===List effects===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 function projEffects(){&lt;br /&gt;
 	var effects = new Array();&lt;br /&gt;
 	var effects2 = new Array();&lt;br /&gt;
 	for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
 		if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
 			   var comp = app.project.item(i);&lt;br /&gt;
 				for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
 					effs = comp.layer(j).property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
 					for(k=1;k&amp;lt;=effs.numProperties;k++){&lt;br /&gt;
 						keyName = effs.property(k).matchName;&lt;br /&gt;
 						effects[keyName]  = 1;&lt;br /&gt;
 						}&lt;br /&gt;
 					   &lt;br /&gt;
 					}&lt;br /&gt;
 			}&lt;br /&gt;
 	}&lt;br /&gt;
 	for(a in effects){&lt;br /&gt;
 		effects2[effects2.length] = a;&lt;br /&gt;
 	}&lt;br /&gt;
 	effects2.sort();&lt;br /&gt;
 	return effects2;&lt;br /&gt;
 }&lt;br /&gt;
 alert(projEffects().join(&amp;quot;\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/hFNiQ.jpg&lt;br /&gt;
&lt;br /&gt;
===Multiple sequences import (Windows)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Multiple sequences import (windows)&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//  bernie@berniebernie.fr&lt;br /&gt;
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file&lt;br /&gt;
//  Access to files network required --- no error checking.&lt;br /&gt;
//   &amp;gt; only works on windows so far&lt;br /&gt;
//   &amp;gt; only finds exrs/pngs/jpgs&lt;br /&gt;
//   &amp;gt; probably fails if your sequences dont have the same start frame&lt;br /&gt;
//   &amp;gt; will work with /path/image.####.jpg or similar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function uniq_fast(a) {&lt;br /&gt;
    var seen = {};&lt;br /&gt;
    var out = [];&lt;br /&gt;
    var len = a.length;&lt;br /&gt;
    var j = 0;&lt;br /&gt;
    for(var i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
         var item = a[i];&lt;br /&gt;
         if(seen[item] !== 1) {&lt;br /&gt;
               seen[item] = 1;&lt;br /&gt;
               out[j++] = item;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return out;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
curScript = new File($.fileName);&lt;br /&gt;
&lt;br /&gt;
startT = Date.now();&lt;br /&gt;
&lt;br /&gt;
sourceFolder = app.project.selection[0].mainSource.file.parent;&lt;br /&gt;
tmpFolder = Folder.temp;&lt;br /&gt;
tmpBat = Folder.temp.toString()+&amp;quot;/ae_dirlist.bat&amp;quot;;&lt;br /&gt;
tmpFilesList = Folder.temp.toString()+&amp;quot;/ae_imageslist.txt&amp;quot;;&lt;br /&gt;
var listFile = new File(tmpFilesList);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var batFile = new File(tmpBat);&lt;br /&gt;
batFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;REM Auto generated by this after effects script file: &amp;quot;+curScript.fsName);&lt;br /&gt;
batFile.write(&amp;quot;\npushd \&amp;quot;&amp;quot;+sourceFolder.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;\ndir /b /on *.exr *.png *.jpg *.jpeg &amp;gt; \&amp;quot;&amp;quot;+listFile.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.close();&lt;br /&gt;
system.callSystem(batFile.fsName);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
listFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
contents = listFile.read();&lt;br /&gt;
arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
listFile.close();&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;via batch: &amp;quot;+arrayContents.length+&amp;quot; files found in &amp;quot;+timer+&amp;quot;s &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myRe = new RegExp(&amp;quot;[0-9]{2,}(\)){0,1}\.[a-z]+$&amp;quot;);&lt;br /&gt;
var myArray = myRe.exec(arrayContents[0]);&lt;br /&gt;
endlength = myArray[0].length;&lt;br /&gt;
&lt;br /&gt;
for(i = 0;i&amp;lt;arrayContents.length;i++){&lt;br /&gt;
    arrayContents[i] = arrayContents[i].slice(0, -endlength);&lt;br /&gt;
}&lt;br /&gt;
unique = uniq_fast(arrayContents);&lt;br /&gt;
&lt;br /&gt;
dialog = confirm(unique.length+&amp;quot; sequences found. Load them ? \nIt might take a long time !&amp;quot;,false,&amp;quot;Confirm Loading&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(dialog){      &lt;br /&gt;
    for(i = 0;i&amp;lt;unique.length;i++){&lt;br /&gt;
        if(unique[i].length&amp;gt;0){&lt;br /&gt;
            sequenceStartFile = new File(sourceFolder.toString()+&amp;quot;/&amp;quot;+unique[i]+myArray[0]);&lt;br /&gt;
            &lt;br /&gt;
            if (sequenceStartFile) {&lt;br /&gt;
            writeLn(&amp;quot;Loading &amp;quot;+(i+1)+&amp;quot;/&amp;quot;+unique.length);&lt;br /&gt;
                try {&lt;br /&gt;
                    // Create a variable containing ImportOptions.&lt;br /&gt;
                    var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
                    importOptions.sequence = true;&lt;br /&gt;
                    try { &lt;br /&gt;
&lt;br /&gt;
                        app.project.importFile(importOptions);&lt;br /&gt;
                    } catch (error) {&lt;br /&gt;
                       e(error.toString());&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (error) {&lt;br /&gt;
                    e(error.toString());&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Canceled&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;after regext: &amp;quot;+timer);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Split to Renderqueue ===&lt;br /&gt;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Split Layers to Renderqueue v2.0&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//&lt;br /&gt;
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp&lt;br /&gt;
//  using the in and out points. I use this along with &amp;#039;Magnum&amp;#039; the edit detector to split &amp;amp; render sequences.&lt;br /&gt;
//&lt;br /&gt;
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the &amp;#039;base&amp;#039; renderqueue item that we derive everything from&lt;br /&gt;
&lt;br /&gt;
function pad(n, width, z) {&lt;br /&gt;
  z = z || &amp;#039;0&amp;#039;;&lt;br /&gt;
  n = n + &amp;#039;&amp;#039;;&lt;br /&gt;
  return n.length &amp;gt;= width ? n : new Array(width - n.length + 1).join(z) + n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Split Layers to Renderqueue&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(app.project.renderQueue.items.length == 1){&lt;br /&gt;
    FE = app.project.renderQueue.items[1];&lt;br /&gt;
    ai = FE.comp;&lt;br /&gt;
    path = FE.outputModule(1).file.path;&lt;br /&gt;
    for(i=1;i&amp;lt;=ai.layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
        RI = app.project.renderQueue.items[1].duplicate();&lt;br /&gt;
        RI.timeSpanStart = ai.layers[i].inPoint;&lt;br /&gt;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;&lt;br /&gt;
        $.writeln(ai.workAreaDuration+&amp;quot; &amp;quot;+ai.layers[i].outPoint);&lt;br /&gt;
        RI.outputModule(1).file =  new File(path+&amp;quot;/&amp;quot;+pad(i,2,&amp;quot;0&amp;quot;)+&amp;quot;_&amp;quot;+ai.layers[i].name);&lt;br /&gt;
    }&lt;br /&gt;
    app.project.renderQueue.items[1].render= false;&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Only 1 element should be in renderqueue&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docked Panel SNIP ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//script panel&lt;br /&gt;
{&lt;br /&gt;
	var nested_file = new File(&amp;quot;U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//called file&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
	var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,[100, 100, 300, 300]);&lt;br /&gt;
	impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
	//impButton.onClick = openfile;&lt;br /&gt;
	return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Watchfolder watcher (WIP)===&lt;br /&gt;
http://i.imgur.com/lhO3k.gif&lt;br /&gt;
&lt;br /&gt;
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)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
todo: check if icons are here&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  // $.writeln(&amp;quot;file &amp;gt;&amp;gt;&amp;gt; &amp;quot;+txtfiles[0].toString());&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
                //info = [&amp;quot;debug&amp;quot;,logFolderLocation.toString(),logFolderLocation.name.toString()];&lt;br /&gt;
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];&lt;br /&gt;
               &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
              //  $.writeln(logFolderLocation.toString()+&amp;quot; &amp;lt;&amp;lt;&amp;lt; &amp;quot;)&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    //$.writeln(&amp;quot;Doing shit&amp;quot;);&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                   // info[2] = &amp;quot;n/a+&amp;quot;;&lt;br /&gt;
//                    info[1] = 1;&lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
                       // $.writeln( lines[i].indexOf(phrase));&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
                               //$.writeln(wfold);&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
                                ///info[3] = pathToWinPath(wfold);&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
                               // info[2] = &amp;quot; &amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                   // info[1] = ;&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
              //  $.writeln(info);&lt;br /&gt;
               // $.writeln(contents);&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    //$.writeln(files.length);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            //$.writeln(&amp;quot;&amp;gt;&amp;gt;&amp;gt; &amp;quot;+state+&amp;quot; - &amp;quot;+out+&amp;quot; - &amp;quot;+folders[folder].name);&lt;br /&gt;
            &lt;br /&gt;
           // $.writeln(out);&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
      // $.writeln(vArray[counter2]);&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                                      //  $.writeln(&amp;quot;Debug&amp;quot;);$.writeln(p);&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 600);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
                    // $.writeln(shotinfos.join(&amp;quot;\n&amp;quot;));&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
                    //$.writeln(sL);&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
                      //$.writeln(a);&lt;br /&gt;
                        //$.writeln(shotinfos[i][1]+&amp;quot; &amp;quot;+list.selection.toString());&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                           // $.writeln(&amp;quot;ww&amp;quot;+);&lt;br /&gt;
                       //  $.writeln(a);&lt;br /&gt;
                            //explore(a[3]+((a[1]==2)?&amp;quot;/&amp;quot;+a[0]:&amp;quot;&amp;quot;));&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    //$.writeln(sel[0].subItems[0].text);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
                //refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                //cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                //}else if(reloadEditTxt.text &amp;gt;=10){&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function grow(palette,value){&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
    //    alert(listItem.selection);&lt;br /&gt;
    var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);&lt;br /&gt;
    //var texted = new Array(&amp;quot;...&amp;quot;,&amp;quot;error:&amp;quot;,&amp;quot;rendering&amp;quot;,&amp;quot;...&amp;quot;,&amp;quot;queued&amp;quot;);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    //$.writeln(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+array[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.image = File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    //item.subItems[0].helpTip = texted(state);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
///////////////////////////////////////////&lt;br /&gt;
           /* if((logFolder =  getFilePath(watchfolderLocation+&amp;quot;/&amp;quot;+shot)) != undefined){&lt;br /&gt;
                $.writeln(logInfo(logFolder));&lt;br /&gt;
            }else{&lt;br /&gt;
                $.writeln(&amp;quot;no log folder&amp;quot;);&lt;br /&gt;
                }*/&lt;br /&gt;
           // $.writeln();&lt;br /&gt;
           // addItem(list,&amp;quot;G12_SC213_T1&amp;quot;,2);&lt;br /&gt;
           &lt;br /&gt;
           &lt;br /&gt;
           /*&lt;br /&gt;
var item1 = list.add (&amp;#039;item&amp;#039;, &amp;#039;GB15_SC138_T1&amp;#039;);&lt;br /&gt;
item1.image = File(&amp;quot;~/Desktop/flag_gray.png&amp;quot;);&lt;br /&gt;
item1.subItems[0].text = &amp;#039;Queued...&amp;#039;;&lt;br /&gt;
item1.enabled = false;&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
            //alert(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot));&lt;br /&gt;
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot)*100);&lt;br /&gt;
          &lt;br /&gt;
                       // alert(&amp;quot;test&amp;quot;);&lt;br /&gt;
              //$.writeln(files[file].path+&amp;quot;/&amp;quot;+files[file].name);&lt;br /&gt;
                            //  a+=  getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name).name+&amp;quot;\n&amp;quot;;&lt;br /&gt;
                            &lt;br /&gt;
/*                            &lt;br /&gt;
                            &lt;br /&gt;
                            {&lt;br /&gt;
                                &lt;br /&gt;
        wfLocBtn.onClick = function(){&lt;br /&gt;
            app.scheduleTask(&amp;quot;loop()&amp;quot;,2000,1);&lt;br /&gt;
&lt;br /&gt;
           &lt;br /&gt;
            fold = new Folder( watchfolderLocation.toString());&lt;br /&gt;
            files = fold.getFiles();&lt;br /&gt;
                a= &amp;quot;&amp;quot;;&lt;br /&gt;
            for(file in files){&lt;br /&gt;
&lt;br /&gt;
              $.writeln(getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name));&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
           alert(a);&lt;br /&gt;
&lt;br /&gt;
            writeLn(pBar.value);&lt;br /&gt;
        }&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Import pos from maya===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,&lt;br /&gt;
[100, 100, 300, 300]);&lt;br /&gt;
impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
impButton.onClick = openfile;&lt;br /&gt;
return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
//myToolsPanel.show();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function openfile(){&lt;br /&gt;
	            var myFile = File.openDialog (&amp;quot;Select track file&amp;quot;,&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
            var fileOK = myFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
	//var fileD = OpenDlg (&amp;quot;Tracking Point File&amp;quot;,&amp;quot;*.txt&amp;quot;,true);&lt;br /&gt;
	//txt = fileD.readln ()&lt;br /&gt;
	alert(&amp;quot;txt&amp;quot;+readTxt(myFile));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function readTxt(myFile){&lt;br /&gt;
var myText = myFile.read();	&lt;br /&gt;
return myText;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Dandelion/Amazing World Of Gumball Shotbuilder===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://imgur.com/xKJWB.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For education purposes only, the script was run in production to helb build shots, it:&lt;br /&gt;
* created a list of shots &amp;amp; shows possible given a folder structure (&amp;quot;GB##_SHOWNAME_SC##_T##&amp;quot;)&lt;br /&gt;
* opened the last .AEP file found in said shot folder&lt;br /&gt;
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder&lt;br /&gt;
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the &amp;#039;grab sources&amp;#039; button&lt;br /&gt;
* automatically sent files to the watchfolder to render on a small-ish farm. &lt;br /&gt;
Other buttons allowed to check for missing footage, open the comp&amp;#039;s folder, open the current shows&amp;#039; latest animatic, etc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  &lt;br /&gt;
//  Dandelion Shot Builder for &amp;quot;the Amazing World Of Gumball&amp;quot; v0.7 by Bernie&lt;br /&gt;
//   last update 21/11/10&lt;br /&gt;
//&lt;br /&gt;
//  Known Bugs:&lt;br /&gt;
//      &lt;br /&gt;
//  -you can&amp;#039;t have several sequences in a single folder and expect the script to pick up all the sequences&lt;br /&gt;
//  -this will not set color profiles&lt;br /&gt;
// //TODO&lt;br /&gt;
//  &amp;gt;&amp;gt;&amp;gt; CHECK IF WF FOLDER ALREADY EXISTS&lt;br /&gt;
// &amp;gt;&amp;gt;&amp;gt;&amp;gt; GET TAKE FROM N DRIVE, NOT OUT FOLDER&lt;br /&gt;
//  -v0.7 fixes&lt;br /&gt;
//        -new scene after WF works properly&lt;br /&gt;
//        -changed output folder location to N:&lt;br /&gt;
//  -v0.6 fixes&lt;br /&gt;
//       -cancel watchfolder cancels watchfolder&lt;br /&gt;
//        -fixed GB##_SC_###_T1&lt;br /&gt;
//       -added options&lt;br /&gt;
//       -can work on a new location&lt;br /&gt;
//  -v0.5 fixes&lt;br /&gt;
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)&lt;br /&gt;
//      -will warn if there is missing footage before sending to WF&lt;br /&gt;
//      -removed set take, added &amp;#039;missing&amp;#039; dialog.&lt;br /&gt;
//  -v0.4 fixes&lt;br /&gt;
//      -there shouldn&amp;#039;t be a refresh problem on show change anymore&lt;br /&gt;
//  -v0.3 fixes&lt;br /&gt;
//      -changed watchfolder location to anthony&amp;#039;s mac&lt;br /&gt;
//      -changed save as dialog&lt;br /&gt;
//      -removed unused buttons&lt;br /&gt;
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)&lt;br /&gt;
//  -v0.2 fixes&lt;br /&gt;
//      -sequences weren&amp;#039;t getting imported.&lt;br /&gt;
//      -fixed UI/little problems&lt;br /&gt;
//      -added folders, refresh button, save before watchfoldering&lt;br /&gt;
var version = &amp;quot;0.7&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//default locations&lt;br /&gt;
var watchfolderLocation = &amp;quot;/c/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var rootFolder = &amp;quot;/c/&amp;quot;;&lt;br /&gt;
var forEditFolderLoaction = &amp;quot;//MAC0023DFDF5429/for%20edit&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
rootFolder = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)):rootFolder;&lt;br /&gt;
watchfolderLocation = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)):watchfolderLocation;&lt;br /&gt;
forEditFolderLoaction = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)):forEditFolderLoaction;&lt;br /&gt;
&lt;br /&gt;
          //dirty hack&lt;br /&gt;
            a = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true;&lt;br /&gt;
            b = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)):false;&lt;br /&gt;
            c = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)):true;&lt;br /&gt;
            if(a==&amp;quot;false&amp;quot;) a = false;&lt;br /&gt;
            if(a==&amp;quot;true&amp;quot;) a = true;&lt;br /&gt;
            if(b==&amp;quot;false&amp;quot;) b = false;&lt;br /&gt;
            if(b==&amp;quot;true&amp;quot;) b = true;&lt;br /&gt;
            if(c==&amp;quot;false&amp;quot;) c = false;&lt;br /&gt;
            if(c==&amp;quot;true&amp;quot;) c = true;&lt;br /&gt;
            &lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_&amp;quot;;&lt;br /&gt;
var loadFile = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)):&amp;quot;&amp;quot;;&lt;br /&gt;
checkOutputSettings();&lt;br /&gt;
&lt;br /&gt;
Array.prototype.has = function(value) {&lt;br /&gt;
    var i;&lt;br /&gt;
    for (i=0;i&amp;lt;this.length;i++) {&lt;br /&gt;
        if (this[i] == value) {&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addShotDialog(sel){&lt;br /&gt;
    alert(sel);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    path = path.toString();&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sendToWatchFolder(watchfolderlocation,compname){&lt;br /&gt;
    ocn = compname;&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
        c = confirm(&amp;quot;Save &amp;quot;+app.project.file.name+&amp;quot; ?&amp;quot;,false,&amp;quot;Save Project&amp;quot;);&lt;br /&gt;
        if(c){&lt;br /&gt;
            app.project.save();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    curFile = app.project.file;&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
    compname += &amp;quot;_WF&amp;quot;;&lt;br /&gt;
    myFolder.create();&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;.aep&amp;quot;);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
    myTextFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname.substring(0,22)+&amp;quot;_RCF.txt&amp;quot;);    &lt;br /&gt;
    myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    text = &amp;quot;After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot; ;&lt;br /&gt;
    myTextFile.write(text);&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    writeInfo(watchfolderlocation,ocn);&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;) == &amp;quot;false&amp;quot;){&lt;br /&gt;
        opFile = new File(curFile);&lt;br /&gt;
        if(opFile){&lt;br /&gt;
            app.open(opFile);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
        app.project.new();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    //explore(watchfolderlocation);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function writeInfo(watchfolderlocation,compname){&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_DandyWatch.txt&amp;quot;);    &lt;br /&gt;
    mySaveFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    rq = app.project.renderQueue;&lt;br /&gt;
    text =&amp;quot;&amp;quot;;    &lt;br /&gt;
    for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
        if(rq.items[i].render){&lt;br /&gt;
            text += rq.items[i].outputModules[1].file.path+&amp;quot;\n&amp;quot;;&lt;br /&gt;
            text += (Math.round(rq.items[i].timeSpanDuration*25)+&amp;quot;\n&amp;quot;);&lt;br /&gt;
            text += system.callSystem(&amp;quot;hostname&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    mySaveFile.write(text);&lt;br /&gt;
    mySaveFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkForMissingFootage(dialogFlag){&lt;br /&gt;
        var dialog = dialogFlag;&lt;br /&gt;
        var missing = false;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            app.project.items[i].selected = false;&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                app.project.items[i].selected = true;&lt;br /&gt;
                                missing = true;&lt;br /&gt;
                                if(dialog){&lt;br /&gt;
                                    dialog = prompt(&amp;quot;Original folder of \&amp;quot;&amp;quot;+(app.project.items[i].name)+&amp;quot;\&amp;quot; :&amp;quot;,pathToWinPath(app.project.items[i].file),&amp;quot;Missing footage! (hit cancel to suppress further dialogs)&amp;quot;);&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return missing;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutputSettings(){&lt;br /&gt;
    renderSettingsSet = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;)))?true:false;&lt;br /&gt;
   // renderSettingsSet = 1;&lt;br /&gt;
    if(!renderSettingsSet){&lt;br /&gt;
                alert(&amp;quot;Tiff output being setup, this should happen only once. It needs to close the current comp.&amp;quot;);&lt;br /&gt;
                opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
                if(opFile.exists){&lt;br /&gt;
                     app.open(opFile);&lt;br /&gt;
                     app.project.renderQueue.items[1].outputModules[1].saveAsTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                     app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;,&amp;quot;is there&amp;quot;);&lt;br /&gt;
                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
                     app.newProject();&lt;br /&gt;
                 }else{&lt;br /&gt;
                     alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutFolder(tpath){&lt;br /&gt;
    foldFile = new Folder(tpath);&lt;br /&gt;
    //$.writeln(tpath);&lt;br /&gt;
    if(foldFile.exists){&lt;br /&gt;
        files = foldFile.getFiles();  &lt;br /&gt;
        if(files.length &amp;gt; 1){&lt;br /&gt;
            return false;&lt;br /&gt;
        }else{&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
      newF = foldFile.create();&lt;br /&gt;
        return newF;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function getSetTake(force){&lt;br /&gt;
    show = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;);// showDDL.selection.toString();&lt;br /&gt;
    shot = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;); //shotDDL.selection.toString();&lt;br /&gt;
    projectTake = getTake(findMainShot().name);&lt;br /&gt;
//    paths  = rootFolder+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/OUT/&amp;quot;;&lt;br /&gt;
    paths  = &amp;quot;/n/01_OUT/&amp;quot;+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/&amp;quot;;&lt;br /&gt;
    lastFolder = getLastModified(paths);&lt;br /&gt;
    //alert(lastFolder.name+&amp;quot; --&amp;gt;&amp;quot;+paths);&lt;br /&gt;
    if(lastFolder){&lt;br /&gt;
        folderTake = getTake(lastFolder.name);&lt;br /&gt;
    }else{&lt;br /&gt;
        folderTake = false;&lt;br /&gt;
    }&lt;br /&gt;
    msgPt1 = (folderTake)? &amp;quot;The last take in OUT folder is take &amp;quot;+(folderTake)+&amp;quot;.\n&amp;quot;:&amp;quot;No take was found in the OUT folder.\n&amp;quot;;&lt;br /&gt;
    msgPt2 = &amp;quot;The current project take is &amp;quot;+projectTake+&amp;quot;. \n\nChoose the current take:&amp;quot;;&lt;br /&gt;
    if(force || (folderTake != (projectTake-1))){&lt;br /&gt;
        recommended = (folderTake)?((folderTake)+1):projectTake;&lt;br /&gt;
        answer = prompt(msgPt1+msgPt2,recommended);&lt;br /&gt;
        //$.writeln(&amp;quot;blabal &amp;quot;+answer);&lt;br /&gt;
        if(answer != null){&lt;br /&gt;
            return answer;&lt;br /&gt;
        }else{&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return projectTake;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setTake(value){&lt;br /&gt;
    comp = findMainShot();&lt;br /&gt;
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf(&amp;quot;_T&amp;quot;)+2))+value;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTake(normalizedName){&lt;br /&gt;
    tmp = normalizedName.split(&amp;quot;_T&amp;quot;);&lt;br /&gt;
    return  parseFloat(tmp[tmp.length-1]);&lt;br /&gt;
}&lt;br /&gt;
	// Smart Import.jsx&lt;br /&gt;
	//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases&lt;br /&gt;
    //the only problem is that you can still not have 2 sequences in a single folder&lt;br /&gt;
   &lt;br /&gt;
function SmartImport(targetFolder){&lt;br /&gt;
         writeLn(&amp;quot;Importing files...&amp;quot;);&lt;br /&gt;
         var importedFiles = 0;&lt;br /&gt;
		var scriptName = &amp;quot;Smart Import&amp;quot;;&lt;br /&gt;
		var sourcePaths = getSourcePathsArray();&lt;br /&gt;
		// Ask the user for a folder whose contents are to be imported.&lt;br /&gt;
		//var targetFolder = Folder.selectDialog(&amp;quot;Import items from folder...&amp;quot;);&lt;br /&gt;
		if (targetFolder != null) {&lt;br /&gt;
			// If no project open, create a new project to import the files into.&lt;br /&gt;
			function processFile(theFile)&lt;br /&gt;
			{&lt;br /&gt;
				try {&lt;br /&gt;
					// Create a variable containing ImportOptions.&lt;br /&gt;
					var importOptions = new ImportOptions(theFile);&lt;br /&gt;
                    //alert();&lt;br /&gt;
                     if(theFile.name.toString().toLowerCase().lastIndexOf(&amp;quot;.psd&amp;quot;) != -1){&lt;br /&gt;
                        importOptions.importAs = ImportAsType.COMP;&lt;br /&gt;
                     }&lt;br /&gt;
                       importSafeWithError(importOptions);&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					// Ignore errors.&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			function testForSequences(files)&lt;br /&gt;
			{&lt;br /&gt;
				var searcher = new RegExp(&amp;quot;[0-9]{3,}$&amp;quot;);&lt;br /&gt;
				var movieFileSearcher = new RegExp(&amp;quot;(mov|avi|mpg)$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
				var parseResults = new Array;&lt;br /&gt;
                  var isFolder = new Array;&lt;br /&gt;
				&lt;br /&gt;
				// Test that we have a sequence. Stop parsing after 10 files.&lt;br /&gt;
				for (x = 0; (x &amp;lt; files.length) &amp;amp; x &amp;lt; 10; x++) {&lt;br /&gt;
					var movieFileResult = movieFileSearcher.exec(files[x].name);&lt;br /&gt;
					if (!movieFileResult) {&lt;br /&gt;
//******                           splitName = files[x].name.split(&amp;#039;.&amp;#039;)[0];&lt;br /&gt;
                            splitName=files[x].name.substring(0,files[x].name.length-4);&lt;br /&gt;
&lt;br /&gt;
                           //splitName[splitNameA.length] = &lt;br /&gt;
                           //alert(splitName);	&lt;br /&gt;
                         //  alert(files[x].name+&amp;quot; &amp;gt;&amp;gt;&amp;gt; &amp;quot;+files[x].name.split(&amp;#039;.&amp;#039;)[0]);&lt;br /&gt;
						var currentResult = searcher.exec(splitName);&lt;br /&gt;
						// Regular expressions return null if no match was found.&lt;br /&gt;
						// Otherwise, they return an array with the following information:&lt;br /&gt;
						// array[0] = the matched string.&lt;br /&gt;
						// array[1..n] = the matched capturing parentheses.&lt;br /&gt;
                   				&lt;br /&gt;
                    if (currentResult) { // We have a match -- the string contains numbers.&lt;br /&gt;
                           &lt;br /&gt;
                            // The match of those numbers is stored in the array[1]&lt;br /&gt;
							// Take that number and save it into parseResults.&lt;br /&gt;
							parseResults[parseResults.length] = currentResult[0];&lt;br /&gt;
						} else {&lt;br /&gt;
							parseResults[parseResults.length] = null;&lt;br /&gt;
						}&lt;br /&gt;
                           if(files[x].name == splitName){&lt;br /&gt;
                                isFolder[isFolder.length] = true;&lt;br /&gt;
                               // alert(&amp;quot;Folder &amp;quot;+files[x].name);&lt;br /&gt;
                           }else{&lt;br /&gt;
                                isFolder[isFolder.length] = false;&lt;br /&gt;
                           }&lt;br /&gt;
					} else {&lt;br /&gt;
						parseResults[parseResults.length] = null;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// If all the files we just went through have a number in their file names, &lt;br /&gt;
				// assume they are part of a sequence and return the first file.&lt;br /&gt;
				&lt;br /&gt;
				var result = null;&lt;br /&gt;
				for (i = 0; i &amp;lt; parseResults.length; ++i) {&lt;br /&gt;
                   				&lt;br /&gt;
                   if(!isFolder[i]){&lt;br /&gt;
                    if (parseResults[i]) {&lt;br /&gt;
						if (!result) {&lt;br /&gt;
                            &lt;br /&gt;
							result = files[i];		&lt;br /&gt;
						}&lt;br /&gt;
					} else {&lt;br /&gt;
                        &lt;br /&gt;
						// In this case, a file name did not contain a number.&lt;br /&gt;
						result = null;&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
                   }&lt;br /&gt;
                }&lt;br /&gt;
				&lt;br /&gt;
				return result;&lt;br /&gt;
			}&lt;br /&gt;
        			&lt;br /&gt;
			&lt;br /&gt;
			function importSafeWithError(importOptions)&lt;br /&gt;
			{&lt;br /&gt;
              if(!(sourcePaths.has(importOptions.file.toString()))){&lt;br /&gt;
 				try { &lt;br /&gt;
                  b = app.project.importFile(importOptions);&lt;br /&gt;
                    writeLn(importOptions.file.toString());&lt;br /&gt;
                 sfi = sourcesFolderItem();&lt;br /&gt;
                   b.parentFolder = sfi;&lt;br /&gt;
                    if(importOptions.importAs == ImportAsType.COMP){&lt;br /&gt;
                        for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                            if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name.lastIndexOf(&amp;quot; Layers&amp;quot;) != -1){&lt;br /&gt;
                         app.project.items[i].parentFolder = sfi;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                        writeLn(&amp;quot;Importing file (&amp;quot;+importedFiles+&amp;quot;)&amp;quot;);&lt;br /&gt;
                        importedFiles++;&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					alert(error.toString() + importOptions.file.fsName, scriptName);&lt;br /&gt;
				}&lt;br /&gt;
                // }else{&lt;br /&gt;
                //        alert(&amp;quot;file already here&amp;quot;);&lt;br /&gt;
                 }&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			&lt;br /&gt;
			function processFolder(theFolder)&lt;br /&gt;
			{&lt;br /&gt;
				// Get an array of files in the target folder.&lt;br /&gt;
				var files = theFolder.getFiles();&lt;br /&gt;
				//alert(files);&lt;br /&gt;
				// Test whether theFolder contains a sequence.&lt;br /&gt;
				var sequenceStartFile = testForSequences(files);&lt;br /&gt;
				&lt;br /&gt;
				// If it does contain a sequence, import the sequence,&lt;br /&gt;
				if (sequenceStartFile) {&lt;br /&gt;
					try {&lt;br /&gt;
						// Create a variable containing ImportOptions.&lt;br /&gt;
						var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
						importOptions.sequence = true;&lt;br /&gt;
						// importOptions.forceAlphabetical = true;		// Un-comment this if you want to force alpha order by default.&lt;br /&gt;
						importSafeWithError(importOptions);&lt;br /&gt;
					} catch (error) {&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// Otherwise, import the files and recurse.&lt;br /&gt;
				&lt;br /&gt;
				for (index in files) { // Go through the array and set each element to singleFile, then run the following.&lt;br /&gt;
					if (files[index] instanceof File) {&lt;br /&gt;
						if (!sequenceStartFile) { // If file is already part of a sequence, don&amp;#039;t import it individually.&lt;br /&gt;
							processFile(files[index]); // Calls the processFile function above.&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					if (files[index] instanceof Folder) {&lt;br /&gt;
						processFolder(files[index]); // recursion&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// Recursively examine that folder.&lt;br /&gt;
			processFolder(targetFolder);&lt;br /&gt;
		}&lt;br /&gt;
    clearOutput();	&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
   &lt;br /&gt;
function sourcesFolderItem(){&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
       // alert(app.project.items[i].name);&lt;br /&gt;
     if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name == &amp;quot;Sources&amp;quot;){&lt;br /&gt;
           return app.project.items[i];&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
    return app.project.items.addFolder(&amp;quot;Sources&amp;quot;);&lt;br /&gt;
    //else&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getSourcePathsArray(){&lt;br /&gt;
    var footageLocations = new Array();&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
        if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
            footageLocations[footageLocations.length] = app.project.item(i).file;&lt;br /&gt;
        }    &lt;br /&gt;
    }&lt;br /&gt;
    return footageLocations;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getLastModified(location,extension){&lt;br /&gt;
      myFolder = new Folder(location.toString());&lt;br /&gt;
      if (!myFolder instanceof Folder){&lt;br /&gt;
          return false;&lt;br /&gt;
     }&lt;br /&gt;
     listFiles = myFolder.getFiles(extension);&lt;br /&gt;
     listDates = new Array();&lt;br /&gt;
     maxId = 0;&lt;br /&gt;
     maxValue = 0;&lt;br /&gt;
     for(i=0;i&amp;lt;listFiles.length;i++){&lt;br /&gt;
         if (!(extension == &amp;quot;&amp;quot; &amp;amp;&amp;amp; (listFiles[i] instanceof File))){&lt;br /&gt;
             d = listFiles[i].modified;&lt;br /&gt;
             formattedDate = d.getFullYear()+&amp;quot;&amp;quot;+pad(d.getMonth()+1,2,0)+&amp;quot;&amp;quot;+pad(d.getDate(),2,0)+&amp;quot;&amp;quot;+pad(d.getHours(),2,0)+&amp;quot;&amp;quot;+pad(d.getMinutes(),2,0)+&amp;quot;&amp;quot;+pad(d.getSeconds(),2,0);&lt;br /&gt;
             listDates[i] = formattedDate;&lt;br /&gt;
             if(Math.max(maxValue,formattedDate) == formattedDate){&lt;br /&gt;
                 maxValue = formattedDate;&lt;br /&gt;
                 maxId = i;&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
     &lt;br /&gt;
     }&lt;br /&gt;
    if(listFiles[maxId] == &amp;quot;&amp;quot; ||listFiles[maxId] == &amp;quot;undefined&amp;quot;||listFiles[maxId] == null){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return listFiles[maxId];     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(number, length, character) {&lt;br /&gt;
    if (character == null) {&lt;br /&gt;
        character = &amp;quot;0&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    var numberStr = &amp;quot;&amp;quot; + number;&lt;br /&gt;
    while (numberStr.length &amp;lt; length) {&lt;br /&gt;
        numberStr = character + numberStr;&lt;br /&gt;
    }&lt;br /&gt;
    return numberStr;&lt;br /&gt;
}&lt;br /&gt;
function grabAnimatic(folderLocation){&lt;br /&gt;
    newLocation = new Folder(folderLocation);&lt;br /&gt;
    animatic = newLocation.getFiles(&amp;quot;*.mov&amp;quot;);&lt;br /&gt;
    sfi = sourcesFolderItem();&lt;br /&gt;
    if(animatic[0]){&lt;br /&gt;
        try {&lt;br /&gt;
            var importOptions = new ImportOptions(animatic[0]);&lt;br /&gt;
            animatic = app.project.importFile(importOptions);&lt;br /&gt;
            for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                    if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
                   app.project.item(i).parentFolder = sfi;&lt;br /&gt;
                    }    &lt;br /&gt;
            }&lt;br /&gt;
            return animatic;&lt;br /&gt;
        }catch (error){&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function figureOutShotName(filepath){&lt;br /&gt;
    folders = filepath.split(&amp;quot;/&amp;quot;);&lt;br /&gt;
    showFolder = folders[folders.length-2];&lt;br /&gt;
    showFolder = showFolder.split(&amp;quot;_&amp;quot;)[0];&lt;br /&gt;
    shotFolder = folders[folders.length-1];&lt;br /&gt;
    var searcher = new RegExp(&amp;quot;[0-9]{2,}$&amp;quot;);&lt;br /&gt;
    var currentResult = searcher.exec(shotFolder);&lt;br /&gt;
    if (currentResult){&lt;br /&gt;
        shotFolder = currentResult[0];&lt;br /&gt;
    }&lt;br /&gt;
    return showFolder+&amp;quot;_SC&amp;quot;+shotFolder;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function createMainComp(path){&lt;br /&gt;
    grabAnimatic(path);&lt;br /&gt;
    shotName = figureOutShotName(path);&lt;br /&gt;
    workComp = app.project.items.addComp(shotName+&amp;quot;_T1&amp;quot;, 1920, 1080,1.0 ,animatic.duration,25.0);&lt;br /&gt;
    animaticLayer = workComp.layers.add(animatic);&lt;br /&gt;
    animaticLayer.guideLayer = true;&lt;br /&gt;
    return workComp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnList2(type){&lt;br /&gt;
     if(type == &amp;quot;show&amp;quot;){&lt;br /&gt;
        rArray = returnFolderArray(rootFolder);&lt;br /&gt;
        for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
            }&lt;br /&gt;
    }else if(type == &amp;quot;scene&amp;quot;){&lt;br /&gt;
        rArray = [&amp;quot;a&amp;quot;,&amp;quot;b&amp;quot;];&lt;br /&gt;
        alert(dPanel.shotLoad.showDDL.selection.toString());&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function returnList(folder){&lt;br /&gt;
    rArray = returnFolderArray(folder);&lt;br /&gt;
    for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function findMainShot(){&lt;br /&gt;
    //fix GB##_SC_###_T1&lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               nuName = app.project.items[i].name;&lt;br /&gt;
               nuName = nuName.split(&amp;quot;SC_&amp;quot;);&lt;br /&gt;
               app.project.items[i].name = nuName[0]+&amp;quot;SC&amp;quot;+nuName[1];&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    var shot = null;&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               shot = app.project.items[i];&lt;br /&gt;
               break;&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if(shot == null){&lt;br /&gt;
        alert(&amp;quot;Could not find a comp named like \&amp;quot;GB##_SC###(a-z)_T#\&amp;quot;\n\nRename main comp accordingly, render again&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return shot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function dandyShotBuilderUI(thisObj){&lt;br /&gt;
    &lt;br /&gt;
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
    &lt;br /&gt;
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;DandyShotBuilderFix&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
           var msg = &amp;quot;This script requires the scripting security preference to be set. \nGo the \&amp;quot;General\&amp;quot; panel of your application preferences and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.\n\n Restart AFX&amp;quot;;&lt;br /&gt;
           var shotInfo = dPanel.add(&amp;quot;edittext&amp;quot;,[10,5,235,305],msg,{multiline:true}); &lt;br /&gt;
         }else{&lt;br /&gt;
      var shotLoad = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,175],&amp;quot;Load Shot&amp;quot;);&lt;br /&gt;
         var shotLoadTxt1 = shotLoad.add(&amp;quot;statictext&amp;quot;,[15,24,52,44],&amp;quot;Show:&amp;quot;);&lt;br /&gt;
         var showDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,19,210,44], returnList(rootFolder));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)){&lt;br /&gt;
                 showDDL.selection = showDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 showDDL.selection = 0;&lt;br /&gt;
                 app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
                }&lt;br /&gt;
         var shotLoadTxt2 = shotLoad.add(&amp;quot;statictext&amp;quot;,[12,59,52,79],&amp;quot;Scene:&amp;quot;);&lt;br /&gt;
         var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;shot&amp;quot;)){&lt;br /&gt;
                 shotDDL.selection = shotDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 shotDDL.selection = 0;&lt;br /&gt;
                }&lt;br /&gt;
         &lt;br /&gt;
          &lt;br /&gt;
         var shotStatusTxt = shotLoad.add(&amp;quot;statictext&amp;quot;,[25,95,52,115],&amp;quot;File:&amp;quot;);&lt;br /&gt;
         var shotStatusEditTxt = shotLoad.add(&amp;quot;edittext&amp;quot;,[56,92,210,115],&amp;quot;...&amp;quot;);&lt;br /&gt;
            shotStatusEditTxt.active = false;&lt;br /&gt;
            shotStatusEditTxt.enabled = false;&lt;br /&gt;
         var shotLoadBtn = shotLoad.add(&amp;quot;button&amp;quot;,[56,125,96,150],&amp;quot;Load&amp;quot;);&lt;br /&gt;
            shotLoadBtn.enabled = false;&lt;br /&gt;
         var shotBuildBtn = shotLoad.add(&amp;quot;button&amp;quot;,[101,125,141,150],&amp;quot;Build&amp;quot;);&lt;br /&gt;
            shotBuildBtn.enabled = false;&lt;br /&gt;
         var shotRefreshBtn = shotLoad.add(&amp;quot;button&amp;quot;,[146,125,168,150],&amp;quot;r&amp;quot;);&lt;br /&gt;
         //var shotOptns = shotLoad.add(&amp;quot;button&amp;quot;,[174,125,210,150],&amp;quot;optns&amp;quot;);&lt;br /&gt;
            //shotSetBtn.enabled = false;&lt;br /&gt;
        &lt;br /&gt;
       var shotTools = dPanel.add(&amp;quot;panel&amp;quot;,[10,185,235,300],&amp;quot;Misc&amp;quot;);&lt;br /&gt;
            shotToolsBtn1 =  shotTools.add(&amp;quot;button&amp;quot;,[12,12,79,35],&amp;quot;Open Folder&amp;quot;);&lt;br /&gt;
            shotToolsBtn1Xtra =  shotTools.add(&amp;quot;button&amp;quot;,[82,12,102,35],&amp;quot;N:&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2 =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,197,35],&amp;quot;Flick Last Take&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2.enabled = false; &lt;br /&gt;
            shotToolsBtnWF =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,135,35],&amp;quot;WF&amp;quot;);&lt;br /&gt;
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};&lt;br /&gt;
             shotToolsBtnFE =  shotTools.add(&amp;quot;button&amp;quot;,[139,12,167,35],&amp;quot;FE&amp;quot;);&lt;br /&gt;
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};&lt;br /&gt;
            shotToolsBtnXLS =  shotTools.add(&amp;quot;button&amp;quot;,[172,12,197,35],&amp;quot;xls&amp;quot;);&lt;br /&gt;
            //shotToolsBtnXLS.enabled = false;             &lt;br /&gt;
                &lt;br /&gt;
            shotToolsBtn3 =  shotTools.add(&amp;quot;button&amp;quot;,[12,40,102,63],&amp;quot;Grab Sources&amp;quot;);&lt;br /&gt;
            shotToolsBtn4 =  shotTools.add(&amp;quot;button&amp;quot;,[107,40,135,63],&amp;quot;Miss&amp;quot;);&lt;br /&gt;
            shotToolsBtnANI =  shotTools.add(&amp;quot;button&amp;quot;,[139,40,167,63],&amp;quot;ANI&amp;quot;);&lt;br /&gt;
            shotToolsBtnOPT =  shotTools.add(&amp;quot;button&amp;quot;,[172,40,197,63],&amp;quot;Opt&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
              //  shotToolsBtn4.enabled = false;            &lt;br /&gt;
            shotToolsBtn5 =  shotTools.add(&amp;quot;button&amp;quot;,[12,68,102,91],&amp;quot;Renderqueue&amp;quot;);&lt;br /&gt;
            shotToolsBtn6 =  shotTools.add(&amp;quot;button&amp;quot;,[107,68,197,91],&amp;quot;To Watchfolder&amp;quot;);&lt;br /&gt;
                //shotToolsBtn6.enabled = false; &lt;br /&gt;
            }&lt;br /&gt;
       var optnsGrp = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,300],&amp;quot;Options&amp;quot;);&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
            mgL = 20;&lt;br /&gt;
            mgT=15;&lt;br /&gt;
            mgBet=3;&lt;br /&gt;
            i=0;&lt;br /&gt;
            w=210;&lt;br /&gt;
            h=23;&lt;br /&gt;
            version = optnsGrp.add(&amp;quot;statictext&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;version &amp;quot;+version);i++;&lt;br /&gt;
            optMissingFootage =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Check for missing footage\rlonger, but safer&amp;quot;);i++;&lt;br /&gt;
            //$.writeln(&amp;quot;load missing footage pref set to&amp;quot;+(app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true);&lt;br /&gt;
                optMissingFootage.value = a;&lt;br /&gt;
            optNuComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;New comp after sending to WF&amp;quot;);i++;&lt;br /&gt;
                optNuComp.value = b;&lt;br /&gt;
            optSaveComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Ask to save before sending to WF&amp;quot;);i+=2;&lt;br /&gt;
                optSaveComp.value = c;&lt;br /&gt;
            rootFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Root folder location&amp;quot;);i++;&lt;br /&gt;
            watchFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Watchfolder location&amp;quot;);i+=2;&lt;br /&gt;
            okBtn =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;OK&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
       ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////&lt;br /&gt;
       &lt;br /&gt;
        showDDL.onChange = function(){&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
           // shotLoad.remove(shotDDL);&lt;br /&gt;
          shotDDL.removeAll();&lt;br /&gt;
&lt;br /&gt;
          items = returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString());&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               shotDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
        }&lt;br /&gt;
        shotDDL.onChange = function(){&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;,shotDDL.selection.toString());&lt;br /&gt;
            //$.writeln(&amp;quot;Changing&amp;quot;);&lt;br /&gt;
            var lastAEP = getLastModified(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/AEP&amp;quot;,&amp;quot;*.aep&amp;quot;);&lt;br /&gt;
            if(!lastAEP){&lt;br /&gt;
                shotStatusEditTxt.text = &amp;quot;no shot found, build one!&amp;quot;;&lt;br /&gt;
                   shotLoadBtn.enabled = false;&lt;br /&gt;
                   shotBuildBtn.enabled = true;&lt;br /&gt;
            &lt;br /&gt;
            }else{&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;,lastAEP.toString());&lt;br /&gt;
                loadFile = lastAEP.toString();&lt;br /&gt;
                fullSplitPath = lastAEP.toString().split(&amp;quot;/&amp;quot;);&lt;br /&gt;
                fileName = fullSplitPath[fullSplitPath.length-1];&lt;br /&gt;
                if(fileName.length &amp;gt; 20){&lt;br /&gt;
                    shotStatusEditTxt.text = fileName.substr(0,12)+&amp;quot;[...]&amp;quot;+fileName.substr(fileName.length-8);&lt;br /&gt;
                }else{&lt;br /&gt;
                    shotStatusEditTxt.text = fileName;&lt;br /&gt;
                } &lt;br /&gt;
                    shotLoadBtn.enabled = true;&lt;br /&gt;
                    shotBuildBtn.enabled = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotLoadBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(loadFile);&lt;br /&gt;
            if(opFile){&lt;br /&gt;
             app.open(opFile);&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
        shotRefreshBtn.onClick = function(){&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
            writeLn(&amp;quot;If it doesn&amp;#039;t seem to refresh, close and launch again.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        shotBuildBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
            if(opFile.exists){&lt;br /&gt;
                app.open(opFile);&lt;br /&gt;
                for(i=1;i&amp;lt;=app.project.numItems;i++){                     &lt;br /&gt;
                    app.project.items[i].remove();&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            createMainComp(targetFolder);&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            app.project.items.addFolder(&amp;quot;Precomps&amp;quot;);&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
            saveConf = confirm(&amp;quot;Save &amp;quot;+shotDDL.selection.toString()+&amp;quot; in the right folder ?&amp;quot;,false,&amp;quot;Dandelion Shot Builder&amp;quot;);&lt;br /&gt;
            if(saveConf){&lt;br /&gt;
                saveFile = new File(targetFolder+&amp;quot;/AEP/&amp;quot;+showDDL.selection.toString()+&amp;quot;_&amp;quot;+shotDDL.selection.toString()+&amp;quot;_comp01.aep&amp;quot;);&lt;br /&gt;
                app.project.save(saveFile);&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1.onClick = function(){&lt;br /&gt;
            explore(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString());&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1Xtra.onClick = function(){&lt;br /&gt;
            //$.writeln(&amp;quot;button&amp;quot;);&lt;br /&gt;
            p = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            checkOutFolder(p);&lt;br /&gt;
            explore(p);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn3.onClick = function(){&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnXLS.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.xls*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .xls shortcut found in in:&amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
       shotToolsBtnANI.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.mov*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .mov shortcut found in in: &amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnOPT.onClick = function(){&lt;br /&gt;
            shotTools.visible = false;&lt;br /&gt;
            shotLoad.visible = false;&lt;br /&gt;
            optnsGrp.visible = true;&lt;br /&gt;
        }&lt;br /&gt;
    okBtn.onClick = function(){&lt;br /&gt;
            shotTools.visible = true;&lt;br /&gt;
            shotLoad.visible = true;&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;,optMissingFootage.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;,optNuComp.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;,optSaveComp.value);&lt;br /&gt;
           // $.writeln(&amp;quot;new comp checked: &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
        //send to renderqueue&lt;br /&gt;
        //uses the comp name to figure out folder&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
        shotToolsBtn5.onClick = function(){&lt;br /&gt;
        rflag = false;&lt;br /&gt;
        //$.writeln(&amp;quot;missing fottage &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;));&lt;br /&gt;
        if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
            rflag = checkForMissingFootage(false);&lt;br /&gt;
        }&lt;br /&gt;
        if(!rflag){&lt;br /&gt;
            rcomp = findMainShot();&lt;br /&gt;
                if(rcomp){&lt;br /&gt;
                    shotNumber = getSetTake();&lt;br /&gt;
                    if(shotNumber != false){&lt;br /&gt;
                        //$.writeln(shotNumber);&lt;br /&gt;
                        setTake(shotNumber);&lt;br /&gt;
                        rq = app.project.renderQueue;&lt;br /&gt;
                        for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
                                rq.items[i].render = false;&lt;br /&gt;
                         }&lt;br /&gt;
                        rqitem = app.project.renderQueue.items.add(rcomp);&lt;br /&gt;
                        rqitem.outputModules[1].applyTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                        rqitem.applyTemplate(&amp;quot;Multi-Machine Settings&amp;quot;);&lt;br /&gt;
                        //savePath = rootFolder+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/OUT/&amp;quot;+rcomp.name;&lt;br /&gt;
                        savePath = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/&amp;quot;+rcomp.name;&lt;br /&gt;
                        &lt;br /&gt;
                        &lt;br /&gt;
                        if(checkOutFolder(savePath)){&lt;br /&gt;
                           rqitem.outputModules[1].file = new File(savePath+&amp;quot;/&amp;quot;+rcomp.name+&amp;quot;_[#####].tif&amp;quot;);&lt;br /&gt;
                        }else{&lt;br /&gt;
                           alert(&amp;quot;Couldn&amp;#039;t figure out an output folder for \&amp;quot;&amp;quot;+(rcomp.name)+&amp;quot;\&amp;quot;, select a folder manually.&amp;quot;);&lt;br /&gt;
                          rqitem.outputModules[1].file = new File();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;This project is missing source files and will not render. \nUse the &amp;#039;miss&amp;#039; button to find out where the footage should be&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn4.onClick = function(){&lt;br /&gt;
            //shotNumber = getSetTake(true);&lt;br /&gt;
            //setTake(shotNumber);&lt;br /&gt;
            checkForMissingFootage(true);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn6.onClick = function(){&lt;br /&gt;
           wf = new Folder(watchfolderLocation);&lt;br /&gt;
           if(wf.exists){&lt;br /&gt;
               sendToWatchFolder(wf,findMainShot().name);&lt;br /&gt;
               }else{&lt;br /&gt;
                   alert(&amp;quot;Watchfolder error, sorry!&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    rootFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Root folder is currently set to \n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New root folder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;,v);     &lt;br /&gt;
                rootFolder = v;&lt;br /&gt;
                          showDDL.removeAll();&lt;br /&gt;
                          items = returnList(rootFolder);&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               showDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
                showDDL.notify();&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                //$.writeln(&amp;quot;notified&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Watchfolder is currently set to \n\n\&amp;quot;&amp;quot;+watchfolderLocation+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New watchfolder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;,v);     &lt;br /&gt;
                watchfolderLocation = v;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    shotDDL.notify();&lt;br /&gt;
    }&lt;br /&gt;
    dandyShotBuilderUI(this) ;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===layer Position to .txt===&lt;br /&gt;
https://i.imgur.com/G5DX1T0.gif&lt;br /&gt;
Old, might not work! (but should :)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)&lt;br /&gt;
//&lt;br /&gt;
// The script will write to a text file the x &amp;amp; y values of the layer position for every frame comprised in&lt;br /&gt;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
function timeToFrameNum(myTime){&lt;br /&gt;
   return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);&lt;br /&gt;
}&lt;br /&gt;
function framesToTime(myTime){&lt;br /&gt;
   return myTime/app.project.activeItem.frameRate;&lt;br /&gt;
}&lt;br /&gt;
function timeToTimeCode(myTime){&lt;br /&gt;
   var framesN = myTime * app.project.activeItem.frameRate;&lt;br /&gt;
   fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));&lt;br /&gt;
   ho = addZero(Math.floor(myTime/3600));&lt;br /&gt;
   mi = addZero(Math.floor(myTime/60)-ho*60);&lt;br /&gt;
   se = addZero(Math.floor(myTime)-mi*60-ho*3600);&lt;br /&gt;
   return ho+&amp;quot;:&amp;quot;+mi+&amp;quot;:&amp;quot;+se+&amp;quot;:&amp;quot;+fr;&lt;br /&gt;
}&lt;br /&gt;
function addZero(val){&lt;br /&gt;
   if(val&amp;lt;10){&lt;br /&gt;
      val = &amp;quot;0&amp;quot;+val;&lt;br /&gt;
   }&lt;br /&gt;
   return val;&lt;br /&gt;
}&lt;br /&gt;
var myDisp = app.project.timecodeDisplayType;&lt;br /&gt;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;&lt;br /&gt;
var pText = &amp;quot;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 &amp;#039;16: 230;22&amp;#039;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if(app.project.activeItem != &amp;quot;null&amp;quot; &amp;amp;&amp;amp; app.project.activeItem != null &amp;amp;&amp;amp; app.project.activeItem != 0){&lt;br /&gt;
   if(app.project.activeItem.selectedLayers.length != 0){&lt;br /&gt;
      if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
         curLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
         var textName = &amp;quot;AEcoordinates.txt&amp;quot;;&lt;br /&gt;
         var myTextFile = filePutDialog(&amp;quot;Select a location to save your .txt file&amp;quot;, textName, &amp;quot;TEXT txt&amp;quot;);&lt;br /&gt;
         if(myTextFile == null){&lt;br /&gt;
            alert(&amp;quot;You must choose a place to save the file&amp;quot;);&lt;br /&gt;
         }else{&lt;br /&gt;
            var formatString = prompt(pText,&amp;quot;%i: %x;%y%l&amp;quot;);&lt;br /&gt;
            myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
            myKeys = curLayer.property(&amp;quot;position&amp;quot;).selectedKeys;&lt;br /&gt;
            if(myKeys.length != 0){&lt;br /&gt;
               strTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[0]);&lt;br /&gt;
               endTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[myKeys.length-1]);&lt;br /&gt;
            }else{&lt;br /&gt;
               strTime = app.project.activeItem.workAreaStart;&lt;br /&gt;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;&lt;br /&gt;
            }&lt;br /&gt;
            var startLoop = timeToFrameNum(strTime);&lt;br /&gt;
            var endLoop = timeToFrameNum(endTime);&lt;br /&gt;
            for(i=startLoop;i&amp;lt;=endLoop;i++){&lt;br /&gt;
               var curTime = framesToTime(i);&lt;br /&gt;
               out = formatString.replace(&amp;#039;%x&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[0]);&lt;br /&gt;
               out = out.replace(&amp;#039;%y&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[1]);&lt;br /&gt;
               out = out.replace(&amp;#039;%f&amp;#039;,i);timeToTimeCode&lt;br /&gt;
               out = out.replace(&amp;#039;%t&amp;#039;,timeToTimeCode(curTime));&lt;br /&gt;
               out = out.replace(&amp;#039;%i&amp;#039;,i-startLoop);&lt;br /&gt;
               out = out.replace(&amp;#039;%l&amp;#039;,&amp;#039;\n&amp;#039;);&lt;br /&gt;
               myTextFile.write(out);&lt;br /&gt;
               clearOutput();&lt;br /&gt;
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+&amp;quot;% done...&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
         clearOutput();&lt;br /&gt;
         writeLn(endLoop-startLoop+&amp;quot; positions saved to file&amp;quot;);&lt;br /&gt;
         writeLn(&amp;quot;script written by mlk =)&amp;quot;);&lt;br /&gt;
         myTextFile.close();&lt;br /&gt;
         }&lt;br /&gt;
      } else {&lt;br /&gt;
         alert (&amp;quot;This script requires the scripting security preference to be set.\n&amp;quot; +&lt;br /&gt;
         &amp;quot;Go to the \&amp;quot;General\&amp;quot; panel of your application preferences,\n&amp;quot; +&lt;br /&gt;
         &amp;quot;and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   }else{&lt;br /&gt;
      alert(&amp;quot;Select a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
}else{&lt;br /&gt;
   alert(&amp;quot;Select a composition and a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.project.timecodeDisplayType = myDisp;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Other Scripts==&lt;br /&gt;
&lt;br /&gt;
=== Work specific ===&lt;br /&gt;
&lt;br /&gt;
==== Auto scale precomps ====&lt;br /&gt;
Script that precomposes animation layers (specifically that are in .swf):&lt;br /&gt;
* scales up the layer by X amount &lt;br /&gt;
* scales down the precomp by 1/x amount&lt;br /&gt;
* Toggles the continuously rasterize off on the precomp, on inside.&lt;br /&gt;
This specifically solves the problem that lines out of Animate/Flash are &amp;#039;rough&amp;#039; (aliased) when imported at 100%, no matter which flags are toggled.&lt;br /&gt;
Had this problem on the Amazing World of Gumball 15 years ago, but AI let me vibecode this in 30 minutes :)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// see &lt;br /&gt;
(function afterEffectsSmartScale() {&lt;br /&gt;
	&lt;br /&gt;
    var SCRIPT_PREF_KEY = &amp;quot;SmartScaleFactor&amp;quot;;&lt;br /&gt;
	var SCRIPT_NAME = &amp;quot;SmartScaleTool&amp;quot;;&lt;br /&gt;
    var defaultScale = 4; &lt;br /&gt;
    var comp = app.project.activeItem;&lt;br /&gt;
    var selectedLayers = comp.selectedLayers;&lt;br /&gt;
    var scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    function getScaleFactor(set) {&lt;br /&gt;
		var factor = defaultScale;&lt;br /&gt;
		if(app.preferences.havePref(SCRIPT_NAME, SCRIPT_PREF_KEY)){&lt;br /&gt;
			factor = app.preferences.getPrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY);&lt;br /&gt;
		}&lt;br /&gt;
		if(set){&lt;br /&gt;
			var promptResult = prompt(&amp;quot;Enter the scale factor (default is 400% -&amp;gt; 4)\nThen select layer(s) and run again.&amp;quot;, factor);&lt;br /&gt;
			if (promptResult === null) return null; &lt;br /&gt;
			factor = parseFloat(promptResult);&lt;br /&gt;
			app.preferences.savePrefAsFloat(SCRIPT_NAME, SCRIPT_PREF_KEY, factor);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
        return factor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (selectedLayers.length === 0) {&lt;br /&gt;
        scaleFactorX = getScaleFactor(1);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    scaleFactorX = getScaleFactor();&lt;br /&gt;
    if (scaleFactorX === null) return;&lt;br /&gt;
&lt;br /&gt;
    var scaleMultiplier = scaleFactorX;&lt;br /&gt;
    var inverseMultiplier = 1 / scaleFactorX;&lt;br /&gt;
&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Smart Scale Precomposition&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        for (var i = 0; i &amp;lt; selectedLayers.length; i++) {&lt;br /&gt;
            var layer = selectedLayers[i];&lt;br /&gt;
            &lt;br /&gt;
            var precomp = app.project.activeItem.layers.precompose([layer.index], layer.name + &amp;quot;_Scale&amp;quot;, false);&lt;br /&gt;
            var precompLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
			precompLayer.collapseTransformation = false;&lt;br /&gt;
&lt;br /&gt;
            // 2. Inside the precomposition, scale the layer and composition&lt;br /&gt;
            if (precomp.numLayers &amp;gt; 0) {&lt;br /&gt;
                var innerLayer = precomp.layer(1);&lt;br /&gt;
                innerLayer.collapseTransformation = true;&lt;br /&gt;
                // Scale the inner layer&lt;br /&gt;
                var currentScale = innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentScale[0] * scaleMultiplier, currentScale[1] * scaleMultiplier]);&lt;br /&gt;
                &lt;br /&gt;
                // Adjust composition size accordingly&lt;br /&gt;
                precomp.width *= scaleMultiplier;&lt;br /&gt;
                precomp.height *= scaleMultiplier;&lt;br /&gt;
&lt;br /&gt;
                // Adjust the position of the layer within the precomp to keep it centered &lt;br /&gt;
                var newPos = [innerLayer.position.value[0] * scaleMultiplier, innerLayer.position.value[1] * scaleMultiplier];&lt;br /&gt;
                innerLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Position&amp;quot;).setValue(newPos);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // 3. Back in the top composition, scale down the precomposed layer by 1/X&lt;br /&gt;
            var currentOuterScale = precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).value;&lt;br /&gt;
            precompLayer.property(&amp;quot;ADBE Transform Group&amp;quot;).property(&amp;quot;ADBE Scale&amp;quot;).setValue([currentOuterScale[0] * inverseMultiplier, currentOuterScale[1] * inverseMultiplier]);&lt;br /&gt;
        }&lt;br /&gt;
    } catch (e) {&lt;br /&gt;
        alert(&amp;quot;An error occurred: &amp;quot; + e.toString());&lt;br /&gt;
    } finally {&lt;br /&gt;
        app.endUndoGroup();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx&lt;br /&gt;
&lt;br /&gt;
===Shelf===&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//to hijack admin and load shelf from my documents/shelf.jsx, this needs to be in the scriptUI Panel folder&lt;br /&gt;
{&lt;br /&gt;
    var nested_file = new File(&amp;quot;~/Documents/shelfBernie.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	//alert(nested_file.read());&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Adobe_Animate&amp;diff=887</id>
		<title>Adobe Animate</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Adobe_Animate&amp;diff=887"/>
		<updated>2025-12-01T09:23:24Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Export layers to single swf file for AE */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Export layers to single swf file for AE ===&lt;br /&gt;
/!\ Make sure in the publish settings, swf &amp;gt; advanced settings &amp;gt; &amp;quot;Include hiden layers&amp;quot; is turned OFF before launching the jsfl file&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * JSFL Script to export each layer of an Adobe Animate FLA file main timeline (no sub-symbols). &lt;br /&gt;
 * as a separate SWF file, using padded layer number + layer name as the output filename.&lt;br /&gt;
 * Some error checking.&lt;br /&gt;
 * Hidden and guide layers will be skipped. Masks should be broken to filled shapes.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
// Check if a document is open&lt;br /&gt;
if (fl.getDocumentDOM() == null) {&lt;br /&gt;
    fl.trace(&amp;#039;Error: Please open an Adobe Animate (FLA) file.&amp;#039;);&lt;br /&gt;
} else {&lt;br /&gt;
    var dom = fl.getDocumentDOM();&lt;br /&gt;
    var timeline = dom.getTimeline();&lt;br /&gt;
    &lt;br /&gt;
	//&lt;br /&gt;
	var dom = fl.getDocumentDOM();&lt;br /&gt;
&lt;br /&gt;
	//patch to toogle off invisible layers in publish settings stolen from https://community.adobe.com/t5/animate-discussions/toggle-options-in-publish-settings-with-javascript-code/td-p/9673165&lt;br /&gt;
	var profileXML = dom.exportPublishProfileString(dom.currentPublishProfile);&lt;br /&gt;
	// WARNING: This is a hack that does direct string manipulation instead of properly parsing the XML&lt;br /&gt;
	// exclude hidden layers (Flash)&lt;br /&gt;
	profileXML = profileXML.replace(/&amp;lt;InvisibleLayer&amp;gt;.&amp;lt;\/InvisibleLayer&amp;gt;/g, &amp;#039;&amp;lt;InvisibleLayer&amp;gt;0&amp;lt;/InvisibleLayer&amp;gt;&amp;#039;);&lt;br /&gt;
	// exclude hidden layers (HTML/SVG)&lt;br /&gt;
	profileXML = profileXML.replace(/&amp;lt;Property name=&amp;quot;includeHiddenLayers&amp;quot;&amp;gt;.+?&amp;lt;\/Property&amp;gt;/g, &amp;#039;&amp;lt;Property name=&amp;quot;includeHiddenLayers&amp;quot;&amp;gt;false&amp;lt;/Property&amp;gt;&amp;#039;);&lt;br /&gt;
	// JPEG quality&lt;br /&gt;
	profileXML = profileXML.replace(/&amp;lt;Quality&amp;gt;.+?&amp;lt;\/Quality&amp;gt;/g, &amp;#039;&amp;lt;Quality&amp;gt;100&amp;lt;/Quality&amp;gt;&amp;#039;);&lt;br /&gt;
	// apply updated profile&lt;br /&gt;
	dom.importPublishProfileString(profileXML);&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
    // Get document path and file name (without extension)&lt;br /&gt;
    var flaPath = dom.path;&lt;br /&gt;
    var flaName = dom.name.substring(0, dom.name.lastIndexOf(&amp;#039;.&amp;#039;));&lt;br /&gt;
	var flaFolder = flaPath.match(/(.*)[\/\\]/)[1]||&amp;#039;&amp;#039;;&lt;br /&gt;
	&lt;br /&gt;
	//horrible flash oddity. Barf. tested on windows, not macos&lt;br /&gt;
	var flaFolderURI = &amp;#039;file:///&amp;#039; + flaFolder.replace(/\\/g, &amp;#039;/&amp;#039;).split(&amp;#039;:&amp;#039;).join(&amp;#039;|&amp;#039;).split(&amp;#039; &amp;#039;).join(&amp;#039;%20&amp;#039;);&lt;br /&gt;
    &lt;br /&gt;
    // Construct the path for the &amp;#039;export&amp;#039; folder&lt;br /&gt;
    // The folder will be next to the FLA file&lt;br /&gt;
    var exportFolderPath = flaFolderURI + &amp;#039;/EXPORT&amp;#039;;&lt;br /&gt;
	&lt;br /&gt;
	// Create folder if it doesn&amp;#039;t exists. Error out if it doesn&amp;#039;t work.&lt;br /&gt;
	if(!FLfile.exists(exportFolderPath) &amp;amp;&amp;amp; !FLfile.createFolder(exportFolderPath)){&lt;br /&gt;
		throw new Error(&amp;#039;Cannot write folder: &amp;#039;+exportFolderPath);&lt;br /&gt;
	}else{&lt;br /&gt;
		fl.trace(&amp;#039; --- Exporting to &amp;#039; + flaFolder + &amp;#039;/EXPORT&amp;#039;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
    // Array of all layers in the current timeline&lt;br /&gt;
    var layers = timeline.layers;&lt;br /&gt;
    var totalLayers = layers.length;&lt;br /&gt;
    &lt;br /&gt;
    // Determine padding length (e.g., 2 for up to 99 layers, 3 for up to 999, 2 minimum)&lt;br /&gt;
    var paddingLength = Math.max(2,totalLayers.toString().length);&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	// Let&amp;#039;s store the visibility of each layer, then hide all of them. Skip visibility of mask layers.&lt;br /&gt;
	var layersVisibility = {};&lt;br /&gt;
	var layersMask = {};&lt;br /&gt;
    for (var i = 0; i &amp;lt; totalLayers; i++) {&lt;br /&gt;
		var currentLayer = layers[i];  &lt;br /&gt;
		layersVisibility[i] = currentLayer.visible;&lt;br /&gt;
		if(currentLayer.layerType == &amp;quot;mask&amp;quot;){&lt;br /&gt;
			layersMask[i] = true;&lt;br /&gt;
		}else{&lt;br /&gt;
			currentLayer.visible = false;&lt;br /&gt;
			layersMask[i] = false;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
    // Loop through all layers&lt;br /&gt;
    for (var i = 0; i &amp;lt; totalLayers; i++) {&lt;br /&gt;
        var currentLayer = layers[i];  &lt;br /&gt;
        &lt;br /&gt;
		// Only proceed if: &lt;br /&gt;
		//	- layer is initially visible&lt;br /&gt;
		//	- layer is not a guide layer&lt;br /&gt;
		//  - layer is not a locked masked layer&lt;br /&gt;
		&lt;br /&gt;
		if(layersVisibility[i] &amp;amp;&amp;amp; currentLayer.layerType != &amp;#039;guide&amp;#039;){&lt;br /&gt;
			//fl.trace(currentLayer.parentLayer)&lt;br /&gt;
			// Make only the current layer visible&lt;br /&gt;
			currentLayer.visible = true;&lt;br /&gt;
			&lt;br /&gt;
			// --- 2. Filename Construction ---&lt;br /&gt;
			&lt;br /&gt;
			// Calculate layer index (1-based). padStart doesn&amp;#039;t seem to work, here&amp;#039;s a workaround for padding, hopefully your file should have less than 10000 layers....&lt;br /&gt;
			var layerIndex = i + 1;&lt;br /&gt;
			layerIndex =  &amp;quot;00000&amp;quot;+layerIndex.toString();&lt;br /&gt;
			layerIndex = layerIndex.slice(-paddingLength);&lt;br /&gt;
&lt;br /&gt;
			// Clean up the layer name to be file-system friendly (remove spaces and special chars)&lt;br /&gt;
			// Replacing non-alphanumeric/hyphen/underscore with an underscore&lt;br /&gt;
			var cleanLayerName = currentLayer.name.replace(/[^a-zA-Z0-9_-]/g, &amp;#039;_&amp;#039;);&lt;br /&gt;
			&lt;br /&gt;
			// Construct the full output filename&lt;br /&gt;
			var outputFileName = layerIndex + &amp;#039;_&amp;#039; + cleanLayerName + &amp;#039;.swf&amp;#039;;&lt;br /&gt;
			var outputFilePath = exportFolderPath + &amp;#039;/&amp;#039; +outputFileName;&lt;br /&gt;
			&lt;br /&gt;
			// --- 3. Export ---&lt;br /&gt;
			&lt;br /&gt;
			// Export the current frame/selection as SWF&lt;br /&gt;
			// The exportSWF method exports based on the current visible state of the layers&lt;br /&gt;
			var success = dom.exportSWF(outputFilePath, false); // &amp;#039;false&amp;#039; means export all frames, which is usually correct for layer-based export&lt;br /&gt;
			&lt;br /&gt;
			if (success) {&lt;br /&gt;
				fl.trace(&amp;quot;✅ Exported: &amp;quot; + outputFileName);&lt;br /&gt;
			} else {&lt;br /&gt;
				fl.trace(&amp;quot;❌ Failed to export: &amp;quot; + outputFileName);&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			//re-hide layer&lt;br /&gt;
			currentLayer.visible = false;&lt;br /&gt;
		}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // --- 4. Cleanup ---&lt;br /&gt;
    &lt;br /&gt;
    // Restore all layers to be visible at the end&lt;br /&gt;
    for (var i = 0; i &amp;lt; totalLayers; i++) {&lt;br /&gt;
        timeline.layers[i].visible = layersVisibility[i];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Select the first frame and first layer for a clean exit state&lt;br /&gt;
    timeline.currentFrame = 0;&lt;br /&gt;
    timeline.currentLayer = 0;&lt;br /&gt;
    &lt;br /&gt;
    fl.trace(&amp;quot;--- Layer Export Complete! ---&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Adobe_Animate&amp;diff=886</id>
		<title>Adobe Animate</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Adobe_Animate&amp;diff=886"/>
		<updated>2025-11-28T17:31:03Z</updated>

		<summary type="html">&lt;p&gt;Bernie: Created page with &amp;quot;=== Export layers to single swf file for AE === /!\ Make sure in the publish settings, swf &amp;gt; advanced settings &amp;gt; &amp;quot;Include hiden layers&amp;quot; is turned OFF before launching the jsfl file &amp;lt;pre&amp;gt; /*  * FLA_LayersTo_SWF_Files.jsfl  * JSFL Script to export each layer of an Adobe Animate FLA file main timeline (no sub-symbols).   * as a separate SWF file, using padded layer number + layer name as the output filename.  * Some error checking.  * Hidden and guide layers will be skipped...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Export layers to single swf file for AE ===&lt;br /&gt;
/!\ Make sure in the publish settings, swf &amp;gt; advanced settings &amp;gt; &amp;quot;Include hiden layers&amp;quot; is turned OFF before launching the jsfl file&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * FLA_LayersTo_SWF_Files.jsfl&lt;br /&gt;
 * JSFL Script to export each layer of an Adobe Animate FLA file main timeline (no sub-symbols). &lt;br /&gt;
 * as a separate SWF file, using padded layer number + layer name as the output filename.&lt;br /&gt;
 * Some error checking.&lt;br /&gt;
 * Hidden and guide layers will be skipped. Masks should be broken to filled shapes.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
// Check if a document is open&lt;br /&gt;
if (fl.getDocumentDOM() == null) {&lt;br /&gt;
    fl.trace(&amp;#039;Error: Please open an Adobe Animate (FLA) file.&amp;#039;);&lt;br /&gt;
} else {&lt;br /&gt;
    var dom = fl.getDocumentDOM();&lt;br /&gt;
    var timeline = dom.getTimeline();&lt;br /&gt;
    &lt;br /&gt;
    // Get document path and file name (without extension)&lt;br /&gt;
    var flaPath = dom.path;&lt;br /&gt;
    var flaName = dom.name.substring(0, dom.name.lastIndexOf(&amp;#039;.&amp;#039;));&lt;br /&gt;
	var flaFolder = flaPath.match(/(.*)[\/\\]/)[1]||&amp;#039;&amp;#039;;&lt;br /&gt;
	&lt;br /&gt;
	//horrible flash oddity. Barf. tested on windows, not macos&lt;br /&gt;
	var flaFolderURI = &amp;#039;file:///&amp;#039; + flaFolder.replace(/\\/g, &amp;#039;/&amp;#039;).split(&amp;#039;:&amp;#039;).join(&amp;#039;|&amp;#039;).split(&amp;#039; &amp;#039;).join(&amp;#039;%20&amp;#039;);&lt;br /&gt;
    &lt;br /&gt;
    // Construct the path for the &amp;#039;export&amp;#039; folder&lt;br /&gt;
    // The folder will be next to the FLA file&lt;br /&gt;
    var exportFolderPath = flaFolderURI + &amp;#039;/EXPORT&amp;#039;;&lt;br /&gt;
	&lt;br /&gt;
	// Create folder if it doesn&amp;#039;t exists. Error out if it doesn&amp;#039;t work.&lt;br /&gt;
	if(!FLfile.exists(exportFolderPath) &amp;amp;&amp;amp; !FLfile.createFolder(exportFolderPath)){&lt;br /&gt;
		throw new Error(&amp;#039;Cannot write folder: &amp;#039;+exportFolderPath);&lt;br /&gt;
	}else{&lt;br /&gt;
		fl.trace(&amp;#039; --- Exporting to &amp;#039; + flaFolder + &amp;#039;/EXPORT&amp;#039;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
    // Array of all layers in the current timeline&lt;br /&gt;
    var layers = timeline.layers;&lt;br /&gt;
    var totalLayers = layers.length;&lt;br /&gt;
    &lt;br /&gt;
    // Determine padding length (e.g., 2 for up to 99 layers, 3 for up to 999, 2 minimum)&lt;br /&gt;
    var paddingLength = Math.max(2,totalLayers.toString().length);&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	// Let&amp;#039;s store the visibility of each layer, then hide all of them. Skip visibility of mask layers.&lt;br /&gt;
	var layersVisibility = {};&lt;br /&gt;
	var layersMask = {};&lt;br /&gt;
    for (var i = 0; i &amp;lt; totalLayers; i++) {&lt;br /&gt;
		var currentLayer = layers[i];  &lt;br /&gt;
		layersVisibility[i] = currentLayer.visible;&lt;br /&gt;
		if(currentLayer.layerType == &amp;quot;mask&amp;quot;){&lt;br /&gt;
			layersMask[i] = true;&lt;br /&gt;
		}else{&lt;br /&gt;
			currentLayer.visible = false;&lt;br /&gt;
			layersMask[i] = false;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
    // Loop through all layers&lt;br /&gt;
    for (var i = 0; i &amp;lt; totalLayers; i++) {&lt;br /&gt;
        var currentLayer = layers[i];  &lt;br /&gt;
        &lt;br /&gt;
		// Only proceed if: &lt;br /&gt;
		//	- layer is initially visible&lt;br /&gt;
		//	- layer is not a guide layer&lt;br /&gt;
		//  - layer is not a locked masked layer&lt;br /&gt;
		&lt;br /&gt;
		if(layersVisibility[i] &amp;amp;&amp;amp; currentLayer.layerType != &amp;#039;guide&amp;#039;){&lt;br /&gt;
			fl.trace(currentLayer.parentLayer)&lt;br /&gt;
			// Make only the current layer visible&lt;br /&gt;
			currentLayer.visible = true;&lt;br /&gt;
			&lt;br /&gt;
			// --- 2. Filename Construction ---&lt;br /&gt;
			&lt;br /&gt;
			// Calculate layer index (1-based). padStart doesn&amp;#039;t seem to work, here&amp;#039;s a workaround for padding, hopefully your file should have less than 10000 layers....&lt;br /&gt;
			var layerIndex = i + 1;&lt;br /&gt;
			layerIndex =  &amp;quot;00000&amp;quot;+layerIndex.toString();&lt;br /&gt;
			layerIndex = layerIndex.slice(-paddingLength);&lt;br /&gt;
&lt;br /&gt;
			// Clean up the layer name to be file-system friendly (remove spaces and special chars)&lt;br /&gt;
			// Replacing non-alphanumeric/hyphen/underscore with an underscore&lt;br /&gt;
			var cleanLayerName = currentLayer.name.replace(/[^a-zA-Z0-9_-]/g, &amp;#039;_&amp;#039;);&lt;br /&gt;
			&lt;br /&gt;
			// Construct the full output filename&lt;br /&gt;
			var outputFileName = layerIndex + &amp;#039;_&amp;#039; + cleanLayerName + &amp;#039;.swf&amp;#039;;&lt;br /&gt;
			var outputFilePath = exportFolderPath + &amp;#039;/&amp;#039; +outputFileName;&lt;br /&gt;
			&lt;br /&gt;
			// --- 3. Export ---&lt;br /&gt;
			&lt;br /&gt;
			// Export the current frame/selection as SWF&lt;br /&gt;
			// The exportSWF method exports based on the current visible state of the layers&lt;br /&gt;
			var success = dom.exportSWF(outputFilePath, false); // &amp;#039;false&amp;#039; means export all frames, which is usually correct for layer-based export&lt;br /&gt;
			&lt;br /&gt;
			if (success) {&lt;br /&gt;
				fl.trace(&amp;quot;✅ Exported: &amp;quot; + outputFileName);&lt;br /&gt;
			} else {&lt;br /&gt;
				fl.trace(&amp;quot;❌ Failed to export: &amp;quot; + outputFileName);&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			//re-hide layer&lt;br /&gt;
			currentLayer.visible = false;&lt;br /&gt;
		}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // --- 4. Cleanup ---&lt;br /&gt;
    &lt;br /&gt;
    // Restore all layers to be visible at the end&lt;br /&gt;
    for (var i = 0; i &amp;lt; totalLayers; i++) {&lt;br /&gt;
        timeline.layers[i].visible = layersVisibility[i];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Select the first frame and first layer for a clean exit state&lt;br /&gt;
    timeline.currentFrame = 0;&lt;br /&gt;
    timeline.currentLayer = 0;&lt;br /&gt;
    &lt;br /&gt;
    fl.trace(&amp;quot;--- Layer Export Complete! ---&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=885</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=885"/>
		<updated>2025-11-28T17:28:38Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hi i&amp;#039;m Bernie, here lies my personal wiki. It&amp;#039;s unsightly but it works and someone &amp;#039;&amp;#039;has&amp;#039;&amp;#039; to feed (crap) to the AI. &lt;br /&gt;
It&amp;#039;s a collection of thoughts and things I&amp;#039;ve gathered over the years and placed here haphazardly.&lt;br /&gt;
&lt;br /&gt;
If you see something &amp;lt;s&amp;gt;ugly&amp;lt;/s&amp;gt; wrong, lemme know ! bernie a@t berniebernie dot(.) fr&lt;br /&gt;
&lt;br /&gt;
Some pages haven&amp;#039;t been updated in a long, long time, they are here for historical purposes only. &lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
! &lt;br /&gt;
! &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
* [[Special:RecentChanges]] (what was updated recently)&lt;br /&gt;
* 3d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Maya&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Maya Shelf]]&lt;br /&gt;
*** Mel (.mel)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Mel|Mel Scripts]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Mel Temp|Mel Scripts Temp]]&lt;br /&gt;
**** [[Mel Keyboard and Snippets]]&lt;br /&gt;
**** [[Mel Functions]] (+useful mels on pastebin)&lt;br /&gt;
**** [[Advanced Skeleton Specific]] / [[BernieMelLibrary]] / [[Maya Settings]]&lt;br /&gt;
*** Python (.py)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Python]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Maya Python Temp]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Houdini&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini 101]]&lt;br /&gt;
*** [[Houdini VEX]] / [[Houdini VEX Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Python]]&amp;#039;&amp;#039;&amp;#039; / [[Houdini Python Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Stupid Questions]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini UI Customization]]&lt;br /&gt;
*** [[Houdini Webinars and Videos]]&lt;br /&gt;
*** [[Houdini Octane|Houdini Octane/Arnold]]&lt;br /&gt;
** [[Work Specific Scripts]] hodgepodge maya/houdini/whatever&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Blender&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Blender 101]]&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* 2d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;After Effects&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Afx Javascript]]&amp;#039;&amp;#039;&amp;#039; (.jsx)&lt;br /&gt;
*** [[Afx Javascript Temp]]&lt;br /&gt;
*** [[Expressions and Tips|Afx Expressions and Tips]]&lt;br /&gt;
*** [[Afx Shortcuts FR]] and [[Shelf|Custom Shelf UI]] (wip)&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Nuke / Natron&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Nuke Python]] / [[Nuke Python Temp]]&lt;br /&gt;
*** [[Example Gizmos]]&lt;br /&gt;
** [[Adobe Animate]]&lt;br /&gt;
* Other&lt;br /&gt;
** [[WebGl]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Operating System / General&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Software]] i use&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Windows batch|Windows batch &amp;amp; Powershell]]&amp;#039;&amp;#039;&amp;#039; .bat/.vbs/.ps1 ([[Windows_batch#l.bat|l.bat]])&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Python]]&amp;#039;&amp;#039;&amp;#039; .py scripts for work/ day usage&lt;br /&gt;
***[[Auto Hotkey]]&lt;br /&gt;
*** [[Linux]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Misc&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Gobelins 2d 3d integration]]&lt;br /&gt;
***[[Showreel]]&lt;br /&gt;
***[[Maxscript]] .ms [[Misc Scripting]] [[RandomDump]]&lt;br /&gt;
***[[Software Shortcuts]] [[vvvv]] [[vj]]&lt;br /&gt;
***[[Photoshop Javascript]] .js&lt;br /&gt;
***[[7550A Hp Plotter]]&lt;br /&gt;
***[[Color (Aces, LUTs, Gamma etc)]] wip&lt;br /&gt;
***[[OSL|OSLs]]&lt;br /&gt;
***[[AI]]&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=884</id>
		<title>Houdini 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=884"/>
		<updated>2025-11-20T13:57:14Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page was created when I learned Houdini, it might be old and wrong ! (looking at you, HOM expressions). I&amp;#039;ll try to add more info with the hindsight.&lt;br /&gt;
&lt;br /&gt;
== Where to Start ==&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s a ton more videos nowadays then when I learned houdini, and instead of pointing to a thousand websites (well, I do just afterwards in the [links]), I&amp;#039;ll simply point out that SideFX have a [https://www.sidefx.com/tutorials/ curated list of tutorials].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Links to stuff I&amp;#039;ve saved (could have been a webring. remember those?). This was started long ago, some sites might be down, let&amp;#039;s try to clean this up (2023 edit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Manuel and Moritz&amp;#039; &amp;#039;&amp;#039;&amp;#039;[http://www.entagma.com/ Entagma]&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re seeing this page, you probably know their soothing voice already !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Richard C Thomas&amp;#039;s [https://richardcthomas.com/tab-tools website]&amp;#039;&amp;#039;&amp;#039;, loads of cool stuff! &lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Junichiro Horikawa&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.youtube.com/c/JunichiroHorikawa/videos experiments], he&amp;#039;s also in the Houdini subreddit.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Webb Hinton&amp;#039;&amp;#039;&amp;#039;&amp;#039;s wicked-ass [https://github.com/wyhinton/AwesomeHoudini AwesomeHoudini github] page with &amp;#039;&amp;#039;loads&amp;#039;&amp;#039; of ressources about/for Houdini&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Juraj Tomori&amp;#039;&amp;#039;&amp;#039;&amp;#039;s gitgub [https://jtomori.github.io/vex_tutorial/ vex tutorial/code] and [https://www.youtube.com/@seals77/videos youtube] channel&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Howiem&amp;#039;s&amp;#039;&amp;#039;&amp;#039; [http://howiem.com/wordpress/ blog] with code, CHOPs and various hardware hacking.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Houdini Gubbins&amp;#039;&amp;#039;&amp;#039; [https://houdinigubbins.wordpress.com/ technical blog]. Doesn&amp;#039;t share HIPs, but goes through his ideas, thought processes and implements research papers. Super inspiring stuff !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Matt Estela&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.tokeru.com/cgwiki/Main_Page cgwiki], aka Our Houdini Jesus :° -- if you landed on my wiki you&amp;#039;ve probably seen him. Super nice ressource and eve nicer dude, gives away his files too.&lt;br /&gt;
&lt;br /&gt;
* Henry Foster, known as &amp;#039;&amp;#039;&amp;#039;Toadstorm&amp;#039;&amp;#039;&amp;#039; has a [http://www.toadstorm.com/blog/ blog], and also created [https://www.motionoperators.com/ MOPs], which tries to make Motion Design life more like Cinema4D (ie friendlier)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;John Kunz&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://wiki.johnkunz.com/index.php?title=Main_Page wiki], and associated [https://www.youtube.com/@JohnKunz/videos youtube] account with top Notch recorded streams.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Jake Rice&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://jakerice.design/blog/ blog] (not updated recently)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Probiner&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [http://probiner.xyz/logs/ website] (not updated much anymore)&lt;br /&gt;
&lt;br /&gt;
* Eetu Martola&amp;#039;s [https://dailyhip.wordpress.com/ blog] (last update 2021)&lt;br /&gt;
&lt;br /&gt;
* Sam Hancock&amp;#039;s [https://ihoudini.blogspot.fr/ blogspot] (last update 2020 but interesting articles nonetheless)&lt;br /&gt;
&lt;br /&gt;
* Technische Universität Berlin&amp;#039;s [http://dgd.service.tu-berlin.de/wordpress/houdini/ wordpress] &amp;#039;for Mathematicians&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rich Lord&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.richlord.com/ website], full of downloadable goodies (creatures made with constraints!) It had disappeared from the web glad to see it&amp;#039;s back up.&lt;br /&gt;
&lt;br /&gt;
* https://hakeemadam.info/procedural-tools&lt;br /&gt;
&lt;br /&gt;
* -------- TODO sort the rest of the list --------------&lt;br /&gt;
&lt;br /&gt;
* https://www.enoni.de/wp/&lt;br /&gt;
&lt;br /&gt;
* https://www.nicholas-taylor.com/&lt;br /&gt;
&lt;br /&gt;
* https://sites.google.com/site/fujitarium/Houdini/useful-expressions-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
* https://houdinigubbins.wordpress.com&lt;br /&gt;
&lt;br /&gt;
* http://pepefx.blogspot.fr/&lt;br /&gt;
&lt;br /&gt;
* http://www.deborahrfowler.com/HoudiniResources/HoudiniTipsAndTricks.html&lt;br /&gt;
&lt;br /&gt;
* http://www.preset.de/2007/0711/lorenz/&lt;br /&gt;
&lt;br /&gt;
* http://www.3daet.com/cat/27/houdini/&lt;br /&gt;
&lt;br /&gt;
* Python: http://wiki.dreamsteep.com/Pythonhoudini&lt;br /&gt;
&lt;br /&gt;
* https://www.andynicholas.com&lt;br /&gt;
&lt;br /&gt;
* http://dansportfolio.com/wordpress/&lt;br /&gt;
&lt;br /&gt;
* https://houdininote.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* http://lab.ikoon.cz/&lt;br /&gt;
&lt;br /&gt;
* https://lewisinthelandofmachines.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* https://www.keatonwilliamson.com/houdini&lt;br /&gt;
&lt;br /&gt;
* https://www.xuanprada.com/&lt;br /&gt;
&lt;br /&gt;
* Attributes cheatsheet: http://mrkunz.com/blog/08_22_2018_VEX_Wrangle_Cheat_Sheet.html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Nifty links ===&lt;br /&gt;
&lt;br /&gt;
* Discord servers: [https://discord.gg/723NGrShdm Think Procedural]  and Houdini&amp;amp;Chill (it&amp;#039;s invite based, but worth it if you&amp;#039;re worth it); plus it has system to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;[http://fx-td.com/houdiniandchill/ publish the best (most liked) posts]&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; on the discord&lt;br /&gt;
&lt;br /&gt;
* https://www.reddit.com/r/Houdini/ I&amp;#039;m there daily and I like the fact that answers there will be crawlable by search engines (contrary to discord servers).&lt;br /&gt;
&lt;br /&gt;
* [https://mbaadsgaard.com/portfolio/laplacian-eigenvector-plugin-for-houdini/ HDK tutorial] on eigenvectors&lt;br /&gt;
&lt;br /&gt;
* https://inria.hal.science/hal-02541299/file/DeformerElastic-rigid_paper.pdf paper to implement&lt;br /&gt;
&lt;br /&gt;
=== Plug-ins/tools ===&lt;br /&gt;
&lt;br /&gt;
* To link: MOPs qLib OD &lt;br /&gt;
&lt;br /&gt;
* https://momme.gumroad.com&lt;br /&gt;
&lt;br /&gt;
* https://github.com/pedohorse (including RBF hda)&lt;br /&gt;
&lt;br /&gt;
== Flips ==&lt;br /&gt;
Basic setup:&lt;br /&gt;
&lt;br /&gt;
 http://i.imgur.com/cJpBIsk.png&lt;br /&gt;
&lt;br /&gt;
* better quality: more points (smaller point separatation) or decrease grid scale&lt;br /&gt;
* reseed particle in flipsolver: nice for droplets but /!\ particle IDs change&lt;br /&gt;
&lt;br /&gt;
==Expressions Vex &amp;amp; General Syntax==&lt;br /&gt;
[[Houdini_VEX]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$NPT number of points&lt;br /&gt;
$CEX $CEY $CEZ centroid&lt;br /&gt;
$FF frame number &lt;br /&gt;
$T time&lt;br /&gt;
$PT point&lt;br /&gt;
$CR $CB $CG color&lt;br /&gt;
&lt;br /&gt;
$OS use node name (in groups for ex)&lt;br /&gt;
&lt;br /&gt;
$CY copy number in copy node, use for placement w/o having to stamp&lt;br /&gt;
&lt;br /&gt;
`$OS`_`$OBJID` give objects nynamic names &lt;br /&gt;
opdigits($NAME)&lt;br /&gt;
&lt;br /&gt;
opinputpath(&amp;quot;sopnode&amp;quot;,inputN) get the incoming connection (0, 1...)&lt;br /&gt;
op:`opinputpath(&amp;quot;../&amp;quot;,1)`      ---&amp;gt; get full path of input 2 of parent&lt;br /&gt;
nprims(&amp;quot;../sort1&amp;quot;)       ----&amp;gt; number of primitives&lt;br /&gt;
$TEMP/houdiniCache/simdata.`padzero(4,if($F&amp;gt;70,70,$F))`.simdata   -----&amp;gt; read from cache, hold at a certain frame&lt;br /&gt;
&lt;br /&gt;
$OS.`substr(chs(&amp;quot;camera&amp;quot;),rindex(chs(&amp;quot;camera&amp;quot;), &amp;quot;/&amp;quot;)+1,200)`      ------&amp;gt; get the name of the camera if it&amp;#039;s a complicated name with lots of / / / &lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
in vex no uppercase:&lt;br /&gt;
v@Cd&lt;br /&gt;
i@id&lt;br /&gt;
set()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Print to console with precision===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
printf(&amp;quot;Point %*.*g\n&amp;quot;, 10, 10, @value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Expressions dump ===&lt;br /&gt;
On a switch; switches between inputs if first input is an empty vdb (fix for Maxwell&amp;#039;s shitty VDB implementation) - the second input being an &amp;#039;empty&amp;#039; but existing vdb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(prim(opinputpath(&amp;quot;.&amp;quot;,0),0,&amp;quot;file_voxel_count&amp;quot;,0)!=0,1,0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Shortcuts==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
general / viewport&lt;br /&gt;
---------------------&lt;br /&gt;
ctrl-b: full window&lt;br /&gt;
h: center&lt;br /&gt;
d: display options&lt;br /&gt;
p: show params&lt;br /&gt;
space-shift-h: center on object&lt;br /&gt;
h: center on grid/frame current selection&lt;br /&gt;
w: wireframe&lt;br /&gt;
left/right arrows =prev/next frame&lt;br /&gt;
ctrl-left arrow: go to first frame&lt;br /&gt;
&lt;br /&gt;
node editor&lt;br /&gt;
---------------------&lt;br /&gt;
u/i: up down hierachy&lt;br /&gt;
c: set color&lt;br /&gt;
ctrl-click on node blue bit: becomes purple = final output&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
animation&lt;br /&gt;
---------------------&lt;br /&gt;
alt on param boxes = keyframe&lt;br /&gt;
alt on param name = key x y z&lt;br /&gt;
&lt;br /&gt;
chops:&lt;br /&gt;
---------------------&lt;br /&gt;
d show points on curve&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
graph editor:&lt;br /&gt;
---------------------&lt;br /&gt;
shift-lmb: access graph editor&lt;br /&gt;
up down arrows: play forward&lt;br /&gt;
left right: prev next frame&lt;br /&gt;
v-h: zoom vertically/horizontally&lt;br /&gt;
j: show whole timeline&lt;br /&gt;
k: key all&lt;br /&gt;
g: group keys &lt;br /&gt;
&lt;br /&gt;
* drag &amp;amp; drop parameters&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
vopsop&lt;br /&gt;
----------------------&lt;br /&gt;
r: reverseinputs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POPNET==&lt;br /&gt;
* birthgroup: group of particles that were just borne, lasts 1 frame&lt;br /&gt;
&lt;br /&gt;
==DOPNET==&lt;br /&gt;
* can inherit params&lt;br /&gt;
* 3 solvers, bullet = speed&lt;br /&gt;
* &amp;#039;activate&amp;#039; node to do bullet time&lt;br /&gt;
* shelf tool always update latest created dopnet (&amp;#039;set always&amp;#039;)&lt;br /&gt;
* clones for active object = active creation with modulo&lt;br /&gt;
&lt;br /&gt;
== General == &lt;br /&gt;
* drag and drop nodes to get path names&lt;br /&gt;
&lt;br /&gt;
=== Color Schemes ===&lt;br /&gt;
* Red: out geo&lt;br /&gt;
* Purple: VOP_name&lt;br /&gt;
* Yellow: creation/merge&lt;br /&gt;
* Blue: POP DOP&lt;br /&gt;
* Light Green: Pre split&lt;br /&gt;
&lt;br /&gt;
==Volumes==&lt;br /&gt;
&lt;br /&gt;
* Volume from inside as well as surface:&lt;br /&gt;
&lt;br /&gt;
* add a &amp;#039;volume from points&amp;#039; &amp;gt; &amp;#039; stamp points &amp;#039; before volume object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* with volume dops: use curl noise --&amp;gt; 4d  with vector4 to do a time noise&lt;br /&gt;
&lt;br /&gt;
* displace noise: use shader, dig into &amp;#039;fireball&amp;#039; shader&lt;br /&gt;
&lt;br /&gt;
== Wtf ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
${OS}_new_pieces&lt;br /&gt;
pillar_?   (group)&lt;br /&gt;
@Cd&lt;br /&gt;
$VALUE&lt;br /&gt;
vector vexcode&lt;br /&gt;
`interpreted text`&lt;br /&gt;
op:opinputpath&lt;br /&gt;
chramp(values)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== rendering ==&lt;br /&gt;
* get motion blur on objects : geometry velocity motion blur&lt;br /&gt;
* plug stuff in a premade mantra surface (displace along normal, diffuse etc...)&lt;br /&gt;
&lt;br /&gt;
== Chops ==&lt;br /&gt;
* right click anim channels to add motion effects&lt;br /&gt;
* expressions: $I = ptnum, for curves&lt;br /&gt;
* object merge to grab channels&lt;br /&gt;
* multiply: order is important&lt;br /&gt;
* lookup: timeshift&lt;br /&gt;
* at Geo level, to get chop value: chop(&amp;quot;../CHOPNET/out/ty0&amp;quot;) or chopf&lt;br /&gt;
* recording mouse keyboard: keyboard - mouse -&amp;gt; record, hit &amp;#039;&amp;#039;&amp;#039;scroll lock&amp;#039;&amp;#039;&amp;#039; to disable shortcuts&lt;br /&gt;
&lt;br /&gt;
==l-systems==&lt;br /&gt;
http://algorithmicbotany.org/papers/&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proba: &amp;#039;:&amp;#039;&lt;br /&gt;
rules&lt;br /&gt;
function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==nodes==&lt;br /&gt;
&lt;br /&gt;
point replicate : multiply points&lt;br /&gt;
&lt;br /&gt;
attribute promote : transfer from point &amp;lt;&amp;gt; primitives&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Render Stuff and Cache Geo and Alembic ==&lt;br /&gt;
&lt;br /&gt;
* wedge: change node values an filenames with $WEDGENUM&lt;br /&gt;
&lt;br /&gt;
* fetch: renders specific ROPs, can be daisy-chained with a merge and (node to node)&lt;br /&gt;
&lt;br /&gt;
* multiple shapes in maya with alembic: in Houdini, need a primitive with strings called &amp;#039;name&amp;#039;, the names will be shape names, in the alembic exporter, make sur to choose Partition Mode: Use Combination Of Transform/Shape Node and choose previously created &amp;#039;name&amp;#039; as Attribute (also Ogawa format - ?)&lt;br /&gt;
&lt;br /&gt;
== Fur/hair ==&lt;br /&gt;
* put RBD solver before Wire solver in merge otherwise = bug&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
&lt;br /&gt;
[[Houdini_Python]]&lt;br /&gt;
&lt;br /&gt;
= Howto&amp;#039;s / Tricks =&lt;br /&gt;
* Wedges and HQUEUE:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/OjEpvpv.png&lt;br /&gt;
&lt;br /&gt;
* stop stepping: disable integer in frame options, add timeblend&lt;br /&gt;
&lt;br /&gt;
* using &amp;#039;rest&amp;#039; to get more interesting fractures&lt;br /&gt;
&lt;br /&gt;
* restpos: can be used in SHOPS to get object space&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/pZLGdrn.jpg&lt;br /&gt;
&lt;br /&gt;
* instances: instancepoint() w/ full point instancing --&amp;gt; packed disk primitives = refs = light&lt;br /&gt;
&lt;br /&gt;
* instances materials: &amp;quot;declare materials&amp;gt;declare all shops&amp;quot; to export shaders to instances (add using paramet interface window)&lt;br /&gt;
&lt;br /&gt;
* triangulate a whole object cleanly using foreach primitive &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/0KZYipI.jpg&lt;br /&gt;
&lt;br /&gt;
* On crashes: load with &amp;#039;Manual&amp;#039; and hit escape so nodes aren&amp;#039;t read (if file is corrupt for ever)&lt;br /&gt;
&lt;br /&gt;
* Blend between slowed down continuous mesh:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/eduhgBs.png&lt;br /&gt;
&lt;br /&gt;
* RBD Point Object from sequence from [http://forums.odforce.net/topic/16560-rbd-point-object-problem/?do=findComment&amp;amp;comment=101371 odforce]:&lt;br /&gt;
** add int attr on points (call it &amp;#039;shape&amp;#039;)&lt;br /&gt;
** write RBDs to disk&lt;br /&gt;
** in RBD point object override point value geometry path: /path/to/geo.*.bgeo&lt;br /&gt;
** &amp;#039;allow editing of contents&amp;#039; in RBD point object, dive inside fin &amp;#039;sopgeo2&amp;#039;, add a stamp OBJID, $OBJID, disable &amp;#039;Use External SOP&amp;#039;&lt;br /&gt;
** dive inside sopgeo, add a read file, and replace * from RBD point object to proper file path: `strreplace(chs(&amp;quot;../../geopath&amp;quot;), &amp;quot;*&amp;quot;, point(&amp;quot;/obj/gridscatter/OUT&amp;quot;, stamp(&amp;quot;..&amp;quot;, &amp;quot;OBJID&amp;quot;, -1), &amp;quot;shape&amp;quot;, 0))`&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Parenting: at object level or using &amp;#039;Rivet&amp;#039; for deforming geo&lt;br /&gt;
&lt;br /&gt;
* Group according to mesh connectivity: use &amp;#039;Island 1&amp;#039; &amp;#039;Island 2&amp;#039; etc... &lt;br /&gt;
&lt;br /&gt;
* If you get &amp;#039;unable to initialize UV rendering module with camera&amp;#039; when baking textures, add a new cam to your scene (cam1)&lt;br /&gt;
&lt;br /&gt;
* If you are using maxwell and alembic (abc) files to render particles (realflow plug-in)  you NEED a vector v and int id attributes otherwise it won&amp;#039;t render&lt;br /&gt;
&lt;br /&gt;
* Fluid simulation: collision reversed ? Use a vdb with a static solver, and reverse the vdb with a volume wrangle @surface *= -1&lt;br /&gt;
&lt;br /&gt;
[[Category:Houdini]]&lt;br /&gt;
&lt;br /&gt;
* Why is my motion blur not working ? You need &amp;#039;v&amp;#039; and to check &amp;#039;geometry velocity blur&amp;#039; on your object&lt;br /&gt;
&lt;br /&gt;
* I still have a hard time with vex code rotations/matrices even with matt estela&amp;#039;s wiki so VOP quicktip to rotate normals around an edge:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/RcrtESH.png&lt;br /&gt;
&lt;br /&gt;
=Specific Forum/Blog/Wiki articles=&lt;br /&gt;
===Modeling===&lt;br /&gt;
* [https://www.sidefx.com/forum/topic/44816/| Remeshing with external software] (quads, instant mesh) - lkruel&lt;br /&gt;
===Vex===&lt;br /&gt;
* [http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#Example:_Rotation| Understanding rotations] ([http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#More_on_rotation:_Orient.2C_Quaternions.2C_Matricies.2C_Offsets.2C_stuff| 2]) in houdini.... (vex, quaternions) - mestela&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Python&amp;diff=883</id>
		<title>Python</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Python&amp;diff=883"/>
		<updated>2025-11-19T11:01:15Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* AI Generated: YT downloader + Audio+Video Merger with FFMPEG */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Image sequence scanner ===&lt;br /&gt;
Vibe coded with Gemini. No human coding necessary.&lt;br /&gt;
Will look in a config.txt file (which will be created if it doesn&amp;#039;t exist) for a folder and scan and return a json file of the results. Loop until it is told to stop. &lt;br /&gt;
This is to be used for an image sequence imported in different software (most notably AE).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import json&lt;br /&gt;
import re&lt;br /&gt;
import time&lt;br /&gt;
from datetime import datetime&lt;br /&gt;
&lt;br /&gt;
class ConfigFileNotFoundError(FileNotFoundError):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Custom exception raised when the configuration file is missing.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    pass&lt;br /&gt;
&lt;br /&gt;
class ImageSequenceScanner:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    A tool to scan a directory recursively for image sequences. &lt;br /&gt;
    It now includes a filter to skip sequences shorter than a minimum length.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    # Constants for config file entries&lt;br /&gt;
    CONFIG_ROOT_DIR = &amp;quot;root_dir&amp;quot;&lt;br /&gt;
    CONFIG_FREQUENCY = &amp;quot;frequency&amp;quot;&lt;br /&gt;
    CONFIG_SHUTDOWN = &amp;quot;shutdown&amp;quot;&lt;br /&gt;
    CONFIG_LAST_SCAN = &amp;quot;last_scan&amp;quot;&lt;br /&gt;
    CONFIG_MIN_LENGTH = &amp;quot;min_sequence_length&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, json_path, config_path, default_root_dir):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        Initialize the scanner. Reads the root directory and frequency from config.&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        self.json_path = json_path&lt;br /&gt;
        self.config_path = config_path&lt;br /&gt;
        self.default_root_dir = default_root_dir&lt;br /&gt;
        &lt;br /&gt;
        # 🚨 Immediate Config File Check&lt;br /&gt;
        if not os.path.exists(self.config_path):&lt;br /&gt;
            raise ConfigFileNotFoundError(&lt;br /&gt;
                f&amp;quot;\n--- ERROR: Configuration file not found at &amp;#039;{self.config_path}&amp;#039; ---\n&amp;quot;&lt;br /&gt;
                &amp;quot;The script cannot start without a configuration file.&amp;quot;&lt;br /&gt;
            )&lt;br /&gt;
        &lt;br /&gt;
        # Read initial settings&lt;br /&gt;
        config = self._read_config()&lt;br /&gt;
        &lt;br /&gt;
        self.root_dir = config.get(self.CONFIG_ROOT_DIR, self.default_root_dir)&lt;br /&gt;
        self.current_scan_frequency = config.get(self.CONFIG_FREQUENCY, 60)&lt;br /&gt;
        self.priority_folder = config.get(&amp;quot;priority_folder&amp;quot;)&lt;br /&gt;
        self.should_shutdown = config.get(self.CONFIG_SHUTDOWN, False)&lt;br /&gt;
        # New: Minimum sequence length, default is 3&lt;br /&gt;
        self.min_sequence_length = config.get(self.CONFIG_MIN_LENGTH, 3) &lt;br /&gt;
&lt;br /&gt;
        self.sequence_data = self._load_json()&lt;br /&gt;
        self.last_scan_times = {} # For aggressive scanning&lt;br /&gt;
&lt;br /&gt;
        print(f&amp;quot;Root Scan Directory: {self.root_dir}&amp;quot;)&lt;br /&gt;
        print(f&amp;quot;Initial Scan Frequency: {self.current_scan_frequency} seconds&amp;quot;)&lt;br /&gt;
        print(f&amp;quot;Minimum Sequence Length: {self.min_sequence_length} images&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # --- Configuration and Data Management ---&lt;br /&gt;
&lt;br /&gt;
    def _load_json(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Load existing sequence data from the JSON file.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if os.path.exists(self.json_path):&lt;br /&gt;
            try:&lt;br /&gt;
                with open(self.json_path, &amp;#039;r&amp;#039;) as f:&lt;br /&gt;
                    return json.load(f)&lt;br /&gt;
            except json.JSONDecodeError:&lt;br /&gt;
                print(&amp;quot;Warning: JSON file is corrupted or empty. Starting with empty data.&amp;quot;)&lt;br /&gt;
                return {}&lt;br /&gt;
        return {}&lt;br /&gt;
&lt;br /&gt;
    def _save_json(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Save the updated sequence data to the JSON file.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        with open(self.json_path, &amp;#039;w&amp;#039;) as f:&lt;br /&gt;
            json.dump(self.sequence_data, f, indent=4)&lt;br /&gt;
        print(f&amp;quot;[{datetime.now().strftime(&amp;#039;%H:%M:%S&amp;#039;)}] JSON updated: {self.json_path}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    def _normalize_path(self, path):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Standardizes a path by expanding user and making it absolute.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        return os.path.abspath(os.path.expanduser(path))&lt;br /&gt;
&lt;br /&gt;
    def _read_config(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Reads config file, supporting all settings.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        config = {&lt;br /&gt;
            self.CONFIG_FREQUENCY: 60, &lt;br /&gt;
            self.CONFIG_ROOT_DIR: self.default_root_dir, &lt;br /&gt;
            &amp;quot;priority_folder&amp;quot;: None, &lt;br /&gt;
            self.CONFIG_SHUTDOWN: False,&lt;br /&gt;
            self.CONFIG_LAST_SCAN: &amp;quot;Never&amp;quot;,&lt;br /&gt;
            self.CONFIG_MIN_LENGTH: 3 # Default minimum length&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        try:&lt;br /&gt;
            with open(self.config_path, &amp;#039;r&amp;#039;) as f:&lt;br /&gt;
                lines = f.readlines()&lt;br /&gt;
&lt;br /&gt;
            for line in lines:&lt;br /&gt;
                line = line.strip()&lt;br /&gt;
                if not line or line.startswith(&amp;#039;#&amp;#039;):&lt;br /&gt;
                    continue&lt;br /&gt;
&lt;br /&gt;
                if &amp;#039;=&amp;#039; in line:&lt;br /&gt;
                    key, value = line.split(&amp;#039;=&amp;#039;, 1)&lt;br /&gt;
                    key = key.strip().lower()&lt;br /&gt;
                    value = value.strip()&lt;br /&gt;
                    &lt;br /&gt;
                    if key == self.CONFIG_FREQUENCY:&lt;br /&gt;
                        match_s = re.match(r&amp;#039;(\d+)s$&amp;#039;, value.lower())&lt;br /&gt;
                        match_m = re.match(r&amp;#039;(\d+)m(in)?$&amp;#039;, value.lower())&lt;br /&gt;
                        if match_s: config[self.CONFIG_FREQUENCY] = max(1, int(match_s.group(1)))&lt;br /&gt;
                        elif match_m: config[self.CONFIG_FREQUENCY] = max(60, int(match_m.group(1)) * 60)&lt;br /&gt;
                    &lt;br /&gt;
                    elif key == self.CONFIG_ROOT_DIR:&lt;br /&gt;
                        potential_path = self._normalize_path(value)&lt;br /&gt;
                        if os.path.isdir(potential_path):&lt;br /&gt;
                            config[self.CONFIG_ROOT_DIR] = potential_path&lt;br /&gt;
                    &lt;br /&gt;
                    elif key == self.CONFIG_SHUTDOWN:&lt;br /&gt;
                        if value.lower() == &amp;quot;true&amp;quot;:&lt;br /&gt;
                            config[self.CONFIG_SHUTDOWN] = True&lt;br /&gt;
                    &lt;br /&gt;
                    elif key == self.CONFIG_LAST_SCAN:&lt;br /&gt;
                        config[self.CONFIG_LAST_SCAN] = value&lt;br /&gt;
                    &lt;br /&gt;
                    elif key == self.CONFIG_MIN_LENGTH:&lt;br /&gt;
                        try:&lt;br /&gt;
                            config[self.CONFIG_MIN_LENGTH] = max(1, int(value)) # Minimum 1&lt;br /&gt;
                        except ValueError:&lt;br /&gt;
                            pass&lt;br /&gt;
                            &lt;br /&gt;
                else: # Check for standalone priority folder path&lt;br /&gt;
                    potential_path = self._normalize_path(line)&lt;br /&gt;
                    if os.path.isdir(potential_path):&lt;br /&gt;
                            config[&amp;quot;priority_folder&amp;quot;] = potential_path&lt;br /&gt;
                    elif os.path.isdir(os.path.join(self.root_dir, line)):&lt;br /&gt;
                            config[&amp;quot;priority_folder&amp;quot;] = os.path.join(self.root_dir, line)&lt;br /&gt;
            &lt;br /&gt;
            return config&lt;br /&gt;
        &lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            print(f&amp;quot;Error reading config file contents: {e}. Using default settings.&amp;quot;)&lt;br /&gt;
            return config&lt;br /&gt;
&lt;br /&gt;
    def _write_config_updates(self, priority_cleared=False):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Rewrites the config file to update last_scan time and clear priority/shutdown flags.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        new_last_scan_time = datetime.now().strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        try:&lt;br /&gt;
            new_lines = []&lt;br /&gt;
            &lt;br /&gt;
            with open(self.config_path, &amp;#039;r&amp;#039;) as f:&lt;br /&gt;
                lines = f.readlines()&lt;br /&gt;
            &lt;br /&gt;
            has_root = False&lt;br /&gt;
            has_freq = False&lt;br /&gt;
            has_last_scan = False&lt;br /&gt;
            has_shutdown = False&lt;br /&gt;
            has_min_length = False&lt;br /&gt;
            &lt;br /&gt;
            normalized_priority = self._normalize_path(self.priority_folder) if self.priority_folder else None&lt;br /&gt;
&lt;br /&gt;
            for line in lines:&lt;br /&gt;
                stripped_line = line.strip()&lt;br /&gt;
                key = stripped_line.split(&amp;#039;=&amp;#039;, 1)[0].strip().lower() if &amp;#039;=&amp;#039; in stripped_line else None&lt;br /&gt;
                &lt;br /&gt;
                # Skip the priority folder line if it was just scanned&lt;br /&gt;
                if priority_cleared:&lt;br /&gt;
                    if normalized_priority and (self._normalize_path(stripped_line) == normalized_priority or \&lt;br /&gt;
                        os.path.isdir(os.path.join(self.root_dir, stripped_line)) and self._normalize_path(os.path.join(self.root_dir, stripped_line)) == normalized_priority):&lt;br /&gt;
                         continue&lt;br /&gt;
                &lt;br /&gt;
                # Update lines with dynamic values&lt;br /&gt;
                if key == self.CONFIG_LAST_SCAN:&lt;br /&gt;
                    new_lines.append(f&amp;quot;{self.CONFIG_LAST_SCAN}={new_last_scan_time}\n&amp;quot;)&lt;br /&gt;
                    has_last_scan = True&lt;br /&gt;
                elif key == self.CONFIG_SHUTDOWN:&lt;br /&gt;
                    new_lines.append(f&amp;quot;{self.CONFIG_SHUTDOWN}=false\n&amp;quot;)&lt;br /&gt;
                    has_shutdown = True&lt;br /&gt;
                elif key == self.CONFIG_ROOT_DIR:&lt;br /&gt;
                    new_lines.append(f&amp;quot;{self.CONFIG_ROOT_DIR}={self.root_dir}\n&amp;quot;)&lt;br /&gt;
                    has_root = True&lt;br /&gt;
                elif key == self.CONFIG_FREQUENCY:&lt;br /&gt;
                    new_lines.append(f&amp;quot;{self.CONFIG_FREQUENCY}={self.current_scan_frequency}s\n&amp;quot;)&lt;br /&gt;
                    has_freq = True&lt;br /&gt;
                elif key == self.CONFIG_MIN_LENGTH:&lt;br /&gt;
                     new_lines.append(f&amp;quot;{self.CONFIG_MIN_LENGTH}={self.min_sequence_length}\n&amp;quot;)&lt;br /&gt;
                     has_min_length = True&lt;br /&gt;
                else:&lt;br /&gt;
                    new_lines.append(line)&lt;br /&gt;
            &lt;br /&gt;
            # Ensure mandatory lines are present if they were missing&lt;br /&gt;
            if not has_freq: new_lines.append(f&amp;quot;{self.CONFIG_FREQUENCY}={self.current_scan_frequency}s\n&amp;quot;)&lt;br /&gt;
            if not has_root: new_lines.append(f&amp;quot;{self.CONFIG_ROOT_DIR}={self.root_dir}\n&amp;quot;)&lt;br /&gt;
            if not has_last_scan: new_lines.append(f&amp;quot;{self.CONFIG_LAST_SCAN}={new_last_scan_time}\n&amp;quot;)&lt;br /&gt;
            if not has_shutdown: new_lines.append(f&amp;quot;{self.CONFIG_SHUTDOWN}=false\n&amp;quot;)&lt;br /&gt;
            if not has_min_length: new_lines.append(f&amp;quot;{self.CONFIG_MIN_LENGTH}={self.min_sequence_length}\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            with open(self.config_path, &amp;#039;w&amp;#039;) as f:&lt;br /&gt;
                f.writelines(new_lines)&lt;br /&gt;
            &lt;br /&gt;
            self.priority_folder = None&lt;br /&gt;
            self.should_shutdown = False&lt;br /&gt;
            &lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            print(f&amp;quot;Error updating config file: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # --- Scanning Logic ---&lt;br /&gt;
    &lt;br /&gt;
    def _is_aggressively_scan(self, dirpath):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Checks modification time for aggressive scanning.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        try:&lt;br /&gt;
            current_mtime = os.path.getmtime(dirpath)&lt;br /&gt;
        except OSError:&lt;br /&gt;
            return False &lt;br /&gt;
&lt;br /&gt;
        last_mtime = self.last_scan_times.get(dirpath, 0)&lt;br /&gt;
        modified = current_mtime &amp;gt; last_mtime&lt;br /&gt;
        self.last_scan_times[dirpath] = current_mtime&lt;br /&gt;
        return modified&lt;br /&gt;
&lt;br /&gt;
    def _detect_gaps(self, frames):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Analyzes a sorted list of frame numbers and identifies missing ranges (gaps).&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if not frames: return []&lt;br /&gt;
&lt;br /&gt;
        missing_ranges = []&lt;br /&gt;
        expected_frame = frames[0]&lt;br /&gt;
&lt;br /&gt;
        for frame in frames:&lt;br /&gt;
            if frame &amp;gt; expected_frame:&lt;br /&gt;
                gap_start = expected_frame&lt;br /&gt;
                gap_end = frame - 1&lt;br /&gt;
                &lt;br /&gt;
                missing_ranges.append({&lt;br /&gt;
                    &amp;quot;start&amp;quot;: gap_start,&lt;br /&gt;
                    &amp;quot;end&amp;quot;: gap_end,&lt;br /&gt;
                    &amp;quot;count&amp;quot;: gap_end - gap_start + 1&lt;br /&gt;
                })&lt;br /&gt;
            &lt;br /&gt;
            expected_frame = frame + 1&lt;br /&gt;
&lt;br /&gt;
        return missing_ranges&lt;br /&gt;
&lt;br /&gt;
    def scan_directory(self, target_dir):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;Scans a specific directory and its subfolders, including length filtering.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;Scanning target: {target_dir}&amp;quot;)&lt;br /&gt;
        data_updated = False&lt;br /&gt;
        SEQUENCE_RE = re.compile(r&amp;#039;(.+)\.(\d+)\.(.+)$&amp;#039;)&lt;br /&gt;
        &lt;br /&gt;
        if not os.path.isdir(target_dir):&lt;br /&gt;
            print(f&amp;quot;Error: Target directory not found: {target_dir}&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
        &lt;br /&gt;
        sequences_found_in_scan = set()&lt;br /&gt;
        &lt;br /&gt;
        for dirpath, dirnames, filenames in os.walk(target_dir):&lt;br /&gt;
            if not filenames:&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            full_path_key = os.path.abspath(dirpath)&lt;br /&gt;
            aggressive_scan = self._is_aggressively_scan(dirpath)&lt;br /&gt;
            &lt;br /&gt;
            # --- Optimization: Skip file processing if no changes ---&lt;br /&gt;
            if not aggressive_scan and full_path_key in self.last_scan_times:&lt;br /&gt;
                for seq_id, seq_info in self.sequence_data.items():&lt;br /&gt;
                    if os.path.abspath(seq_info.get(&amp;#039;path&amp;#039;)) == full_path_key and seq_info.get(&amp;#039;deleted&amp;#039;) is not True:&lt;br /&gt;
                        sequences_found_in_scan.add(seq_id)&lt;br /&gt;
                continue &lt;br /&gt;
&lt;br /&gt;
            sequences = {} &lt;br /&gt;
            for filename in filenames:&lt;br /&gt;
                match = SEQUENCE_RE.match(filename)&lt;br /&gt;
                if match:&lt;br /&gt;
                    base_name_prefix = match.group(1)&lt;br /&gt;
                    try:&lt;br /&gt;
                        frame_number = int(match.group(2))&lt;br /&gt;
                    except ValueError:&lt;br /&gt;
                        continue&lt;br /&gt;
                        &lt;br /&gt;
                    extension = match.group(3)&lt;br /&gt;
                    padding = len(match.group(2))&lt;br /&gt;
                    sequence_key = f&amp;quot;{base_name_prefix}{&amp;#039;#&amp;#039; * padding}.{extension}&amp;quot;&lt;br /&gt;
                    full_file_path = os.path.join(dirpath, filename)&lt;br /&gt;
                    &lt;br /&gt;
                    if sequence_key not in sequences:&lt;br /&gt;
                        sequences[sequence_key] = []&lt;br /&gt;
                    &lt;br /&gt;
                    sequences[sequence_key].append((frame_number, full_file_path))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            # --- Step 2: Determine sequence info, timestamps, and gaps ---&lt;br /&gt;
            for seq_key, frame_list in sequences.items():&lt;br /&gt;
                &lt;br /&gt;
                # 🚨 NEW: Filter out sequences shorter than the minimum length&lt;br /&gt;
                if len(frame_list) &amp;lt; self.min_sequence_length:&lt;br /&gt;
                    print(f&amp;quot;Skipping sequence &amp;#039;{seq_key}&amp;#039; (Count: {len(frame_list)}, Min: {self.min_sequence_length})&amp;quot;)&lt;br /&gt;
                    continue&lt;br /&gt;
                &lt;br /&gt;
                frame_list.sort(key=lambda x: x[0])&lt;br /&gt;
                frames = [f[0] for f in frame_list]&lt;br /&gt;
                &lt;br /&gt;
                start_frame = frames[0]&lt;br /&gt;
                end_frame = frames[-1]&lt;br /&gt;
                &lt;br /&gt;
                # Gap Detection&lt;br /&gt;
                missing_frames = self._detect_gaps(frames)&lt;br /&gt;
                total_expected_frames = end_frame - start_frame + 1&lt;br /&gt;
                &lt;br /&gt;
                # Last modified timestamp&lt;br /&gt;
                last_frame_path = frame_list[-1][1] &lt;br /&gt;
                try:&lt;br /&gt;
                    last_mtime_float = os.path.getmtime(last_frame_path)&lt;br /&gt;
                    last_mtime_str = datetime.fromtimestamp(last_mtime_float).strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;)&lt;br /&gt;
                except OSError:&lt;br /&gt;
                    last_mtime_str = &amp;quot;N/A&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                full_sequence_id = os.path.join(full_path_key, seq_key)&lt;br /&gt;
                sequences_found_in_scan.add(full_sequence_id)&lt;br /&gt;
&lt;br /&gt;
                is_new = full_sequence_id not in self.sequence_data&lt;br /&gt;
                is_changed = not is_new and (&lt;br /&gt;
                    self.sequence_data[full_sequence_id].get(&amp;#039;start&amp;#039;) != start_frame or &lt;br /&gt;
                    self.sequence_data[full_sequence_id].get(&amp;#039;end&amp;#039;) != end_frame or&lt;br /&gt;
                    self.sequence_data[full_sequence_id].get(&amp;#039;deleted&amp;#039;) is True or&lt;br /&gt;
                    len(self.sequence_data[full_sequence_id].get(&amp;#039;missing_frames&amp;#039;, [])) != len(missing_frames)&lt;br /&gt;
                )&lt;br /&gt;
                &lt;br /&gt;
                if is_new or is_changed:&lt;br /&gt;
                    &lt;br /&gt;
                    new_info = {&lt;br /&gt;
                        &amp;quot;path&amp;quot;: full_path_key,&lt;br /&gt;
                        &amp;quot;name&amp;quot;: seq_key,&lt;br /&gt;
                        &amp;quot;start&amp;quot;: start_frame,&lt;br /&gt;
                        &amp;quot;end&amp;quot;: end_frame,&lt;br /&gt;
                        &amp;quot;count&amp;quot;: len(frames),&lt;br /&gt;
                        &amp;quot;total_expected_frames&amp;quot;: total_expected_frames,&lt;br /&gt;
                        &amp;quot;missing_frames_count&amp;quot;: len(missing_frames),&lt;br /&gt;
                        &amp;quot;missing_frames&amp;quot;: missing_frames,&lt;br /&gt;
                        &amp;quot;last_modified_timestamp&amp;quot;: last_mtime_str,&lt;br /&gt;
                        &amp;quot;deleted&amp;quot;: False,&lt;br /&gt;
                        &amp;quot;deletion_timestamp&amp;quot;: None&lt;br /&gt;
                    }&lt;br /&gt;
                    self.sequence_data[full_sequence_id] = new_info&lt;br /&gt;
                    data_updated = True&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        # --- Step 3: Deletion Detection (Tombstone Record) ---&lt;br /&gt;
        keys_to_delete_from_data = [] # Track items that were previously marked as sequences but now fail the length check&lt;br /&gt;
        &lt;br /&gt;
        for seq_id, seq_info in self.sequence_data.items():&lt;br /&gt;
            &lt;br /&gt;
            if seq_id.startswith(self.root_dir):&lt;br /&gt;
                &lt;br /&gt;
                if seq_info.get(&amp;#039;deleted&amp;#039;) is not True:&lt;br /&gt;
                    if seq_id not in sequences_found_in_scan:&lt;br /&gt;
                        # Sequence was found in the data, but is missing on disk or failed the length check in Step 2.&lt;br /&gt;
                        &lt;br /&gt;
                        # We must check if the sequence was truly deleted or just filtered out.&lt;br /&gt;
                        # For simplicity and safety in a background script, we assume if it&amp;#039;s missing from sequences_found_in_scan, &lt;br /&gt;
                        # it&amp;#039;s either deleted OR too short to qualify now. We mark it deleted/obsolete anyway.&lt;br /&gt;
&lt;br /&gt;
                        self.sequence_data[seq_id][&amp;#039;deleted&amp;#039;] = True&lt;br /&gt;
                        self.sequence_data[seq_id][&amp;#039;deletion_timestamp&amp;#039;] = datetime.now().strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;)&lt;br /&gt;
                        &lt;br /&gt;
                        # Clear run-time data for deleted entry&lt;br /&gt;
                        self.sequence_data[seq_id][&amp;#039;count&amp;#039;] = 0&lt;br /&gt;
                        self.sequence_data[seq_id][&amp;#039;total_expected_frames&amp;#039;] = 0&lt;br /&gt;
                        self.sequence_data[seq_id][&amp;#039;missing_frames_count&amp;#039;] = 0&lt;br /&gt;
                        self.sequence_data[seq_id][&amp;#039;missing_frames&amp;#039;] = []&lt;br /&gt;
                        &lt;br /&gt;
                        data_updated = True&lt;br /&gt;
                        print(f&amp;quot;Detected deletion/obsolescence: {seq_info[&amp;#039;name&amp;#039;]}&amp;quot;)&lt;br /&gt;
                    &lt;br /&gt;
        &lt;br /&gt;
        # --- Final Save ---&lt;br /&gt;
        if data_updated:&lt;br /&gt;
            self._save_json()&lt;br /&gt;
        else:&lt;br /&gt;
            print(f&amp;quot;[{datetime.now().strftime(&amp;#039;%H:%M:%S&amp;#039;)}] Scan of {os.path.basename(target_dir)} finished. No changes detected.&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
    def start_background_scan(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;The main loop for continuous background operation.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        print(f&amp;quot;--- Starting Image Sequence Scanner ---&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        while True:&lt;br /&gt;
            try:&lt;br /&gt;
                # 1. Check for configuration updates&lt;br /&gt;
                config = self._read_config()&lt;br /&gt;
                &lt;br /&gt;
                self.current_scan_frequency = config.get(self.CONFIG_FREQUENCY, 60)&lt;br /&gt;
                self.priority_folder = config.get(&amp;quot;priority_folder&amp;quot;)&lt;br /&gt;
                self.should_shutdown = config.get(self.CONFIG_SHUTDOWN, False)&lt;br /&gt;
                self.min_sequence_length = config.get(self.CONFIG_MIN_LENGTH, 3)&lt;br /&gt;
&lt;br /&gt;
                new_root = config.get(self.CONFIG_ROOT_DIR, self.default_root_dir)&lt;br /&gt;
                if new_root != self.root_dir:&lt;br /&gt;
                    print(f&amp;quot;Root directory changed from {self.root_dir} to {new_root}.&amp;quot;)&lt;br /&gt;
                    self.root_dir = new_root&lt;br /&gt;
                &lt;br /&gt;
                # 2. Check for shutdown command&lt;br /&gt;
                if self.should_shutdown:&lt;br /&gt;
                    print(&amp;quot;\n*** SHUTDOWN COMMAND DETECTED IN CONFIG. Exiting. ***&amp;quot;)&lt;br /&gt;
                    self._write_config_updates()&lt;br /&gt;
                    break&lt;br /&gt;
                &lt;br /&gt;
                priority_scan_executed = False&lt;br /&gt;
                # 3. Check and perform PRIORITY scan&lt;br /&gt;
                if self.priority_folder and os.path.isdir(self.priority_folder):&lt;br /&gt;
                    self.scan_directory(self.priority_folder)&lt;br /&gt;
                    priority_scan_executed = True&lt;br /&gt;
                &lt;br /&gt;
                # 4. Perform REGULAR scan&lt;br /&gt;
                if os.path.isdir(self.root_dir):&lt;br /&gt;
                    self.scan_directory(self.root_dir)&lt;br /&gt;
                else:&lt;br /&gt;
                    print(f&amp;quot;Warning: Main root directory not found: {self.root_dir}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
                # 5. Update config file (last scan time and clear flags)&lt;br /&gt;
                self._write_config_updates(priority_cleared=priority_scan_executed)&lt;br /&gt;
&lt;br /&gt;
                # 6. Pause for the configured time&lt;br /&gt;
                time.sleep(self.current_scan_frequency)&lt;br /&gt;
                &lt;br /&gt;
            except KeyboardInterrupt:&lt;br /&gt;
                print(&amp;quot;\n--- Scanner stopped by user. ---&amp;quot;)&lt;br /&gt;
                break&lt;br /&gt;
            except Exception as e:&lt;br /&gt;
                print(f&amp;quot;An error occurred during the loop: {e}. Waiting 10s before retry.&amp;quot;)&lt;br /&gt;
                time.sleep(10) &lt;br /&gt;
&lt;br /&gt;
# --- Configuration and Execution ---&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;#039;__main__&amp;#039;:&lt;br /&gt;
    # Determine the directory where this script is located&lt;br /&gt;
    SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))&lt;br /&gt;
    &lt;br /&gt;
    # Define paths relative to the script&amp;#039;s directory&lt;br /&gt;
    DEFAULT_ROOT_DIR = os.path.join(SCRIPT_DIR, &amp;#039;data_to_scan&amp;#039;) &lt;br /&gt;
    OUTPUT_JSON_PATH = os.path.join(SCRIPT_DIR, &amp;#039;sequences.json&amp;#039;)&lt;br /&gt;
    CONFIG_FILE_PATH = os.path.join(SCRIPT_DIR, &amp;#039;config.txt&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    # Setup Environment&lt;br /&gt;
    os.makedirs(DEFAULT_ROOT_DIR, exist_ok=True)&lt;br /&gt;
    &lt;br /&gt;
    # Example: Create the config file if it doesn&amp;#039;t exist (to make the script runnable out-of-the-box)&lt;br /&gt;
    if not os.path.exists(CONFIG_FILE_PATH):&lt;br /&gt;
         print(f&amp;quot;Creating default config file: {CONFIG_FILE_PATH}&amp;quot;)&lt;br /&gt;
         with open(CONFIG_FILE_PATH, &amp;#039;w&amp;#039;) as f:&lt;br /&gt;
            f.write(&amp;quot;# Configuration for Image Sequence Scanner\n&amp;quot;)&lt;br /&gt;
            f.write(f&amp;quot;root_dir={DEFAULT_ROOT_DIR}\n&amp;quot;)&lt;br /&gt;
            f.write(&amp;quot;frequency=10s\n&amp;quot;)&lt;br /&gt;
            f.write(&amp;quot;shutdown=false\n&amp;quot;)&lt;br /&gt;
            f.write(&amp;quot;last_scan=Never\n&amp;quot;)&lt;br /&gt;
            f.write(&amp;quot;min_sequence_length=3\n&amp;quot;) # New default setting&lt;br /&gt;
&lt;br /&gt;
    # Instantiate and Start the Scanner&lt;br /&gt;
    try:&lt;br /&gt;
        scanner = ImageSequenceScanner(OUTPUT_JSON_PATH, CONFIG_FILE_PATH, DEFAULT_ROOT_DIR)&lt;br /&gt;
        scanner.start_background_scan()&lt;br /&gt;
    except ConfigFileNotFoundError as e:&lt;br /&gt;
        print(e)&lt;br /&gt;
        print(&amp;quot;\nACTION REQUIRED: Please create the configuration file and restart the scanner.&amp;quot;)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        print(f&amp;quot;An unhandled critical error occurred: {e}&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== AI Generated: YT downloader + Audio+Video Merger with FFMPEG ===&lt;br /&gt;
Scary how well this works.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import yt_dlp&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
def download_highest_resolution_mp4(channel_url: str, output_path: str = &amp;#039;./downloads&amp;#039;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Downloads the 720p resolution MP4 video from a YouTube live channel.&lt;br /&gt;
    Ensures each video is downloaded only once (yt-dlp handles skipping existing files).&lt;br /&gt;
&lt;br /&gt;
    Args:&lt;br /&gt;
        channel_url (str): The URL of the YouTube live channel.&lt;br /&gt;
        output_path (str): The directory where the video will be saved.&lt;br /&gt;
                           Defaults to &amp;#039;./downloads&amp;#039;.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # Ensure the output directory exists&lt;br /&gt;
    if not os.path.exists(output_path):&lt;br /&gt;
        os.makedirs(output_path)&lt;br /&gt;
        print(f&amp;quot;Created output directory: {output_path}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # yt-dlp options&lt;br /&gt;
    ydl_opts = {&lt;br /&gt;
        # Prioritize MP4, specifically targeting 720p height&lt;br /&gt;
        &amp;#039;format&amp;#039;: &amp;#039;bestvideo[ext=mp4][height=720]+bestaudio[ext=m4a]/best[ext=mp4][height=720]/best[height=720]&amp;#039;,&lt;br /&gt;
        &amp;#039;outtmpl&amp;#039;: os.path.join(output_path, &amp;#039;%(title)s.%(ext)s&amp;#039;), # Output template for filename&lt;br /&gt;
        &amp;#039;merge_output_format&amp;#039;: &amp;#039;mp4&amp;#039;, # Ensure final merged file is MP4&lt;br /&gt;
        &amp;#039;noplaylist&amp;#039;: True, # Do not download entire playlist if it&amp;#039;s a channel URL&lt;br /&gt;
        &amp;#039;restrictfilenames&amp;#039;: True, # Keep filenames simple&lt;br /&gt;
        &amp;#039;progress_hooks&amp;#039;: [lambda d: print(f&amp;quot;Downloading: {d[&amp;#039;filename&amp;#039;]} - {d[&amp;#039;_percent_str&amp;#039;]} of {d[&amp;#039;_total_bytes_str&amp;#039;] or d[&amp;#039;_total_bytes_estimate_str&amp;#039;] or &amp;#039;unknown size&amp;#039;} at {d[&amp;#039;_speed_str&amp;#039;] or &amp;#039;unknown speed&amp;#039;}&amp;quot;) if d[&amp;#039;status&amp;#039;] == &amp;#039;downloading&amp;#039; else None],&lt;br /&gt;
        &amp;#039;verbose&amp;#039;: False, # Set to True for more detailed output from yt-dlp&lt;br /&gt;
        &amp;#039;quiet&amp;#039;: False, # Set to True to suppress most output&lt;br /&gt;
        &amp;#039;ignoreerrors&amp;#039;: True, # Ignore errors for individual videos (e.g., if a specific format isn&amp;#039;t available)&lt;br /&gt;
        &amp;#039;download_archive&amp;#039;: os.path.join(output_path, &amp;#039;downloaded_videos.txt&amp;#039;), # Keep track of downloaded videos&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:&lt;br /&gt;
            print(f&amp;quot;\nAttempting to download from channel: {channel_url}&amp;quot;)&lt;br /&gt;
            # Extract information and download the latest video from the channel&lt;br /&gt;
            # yt-dlp automatically handles finding the latest video for a channel URL&lt;br /&gt;
            info_dict = ydl.extract_info(channel_url, download=True)&lt;br /&gt;
&lt;br /&gt;
            if info_dict:&lt;br /&gt;
                # If it&amp;#039;s a playlist/channel, info_dict might contain &amp;#039;entries&amp;#039;&lt;br /&gt;
                # We are interested in the single video downloaded or the latest one&lt;br /&gt;
                if &amp;#039;entries&amp;#039; in info_dict and info_dict[&amp;#039;entries&amp;#039;]:&lt;br /&gt;
                    # Assuming the first entry is the one downloaded or the most relevant&lt;br /&gt;
                    downloaded_title = info_dict[&amp;#039;entries&amp;#039;][0].get(&amp;#039;title&amp;#039;, &amp;#039;N/A&amp;#039;)&lt;br /&gt;
                    downloaded_ext = info_dict[&amp;#039;entries&amp;#039;][0].get(&amp;#039;ext&amp;#039;, &amp;#039;mp4&amp;#039;)&lt;br /&gt;
                    print(f&amp;quot;\nSuccessfully downloaded: {downloaded_title}.{downloaded_ext}&amp;quot;)&lt;br /&gt;
                else:&lt;br /&gt;
                    downloaded_title = info_dict.get(&amp;#039;title&amp;#039;, &amp;#039;N/A&amp;#039;)&lt;br /&gt;
                    downloaded_ext = info_dict.get(&amp;#039;ext&amp;#039;, &amp;#039;mp4&amp;#039;)&lt;br /&gt;
                    print(f&amp;quot;\nSuccessfully downloaded: {downloaded_title}.{downloaded_ext}&amp;quot;)&lt;br /&gt;
            else:&lt;br /&gt;
                print(&amp;quot;No video information found or downloaded.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        print(f&amp;quot;\nAn error occurred: {e}&amp;quot;)&lt;br /&gt;
        print(&amp;quot;Please ensure the channel URL is correct and accessible.&amp;quot;)&lt;br /&gt;
        print(&amp;quot;Also, check your internet connection and yt-dlp installation.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    # --- Configuration ---&lt;br /&gt;
    # Replace with the actual YouTube live channel URL&lt;br /&gt;
    # Example: &amp;#039;https://www.youtube.com/@NASA&amp;#039; or &amp;#039;https://www.youtube.com/channel/UCk8GzjMWMl_J_8P_J3P_J3A&amp;#039;&lt;br /&gt;
    youtube_channel_url = &amp;quot;https://www.youtube.com/@JunichiroHorikawa/streams&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    # Specify the directory where you want to save the downloaded videos&lt;br /&gt;
    download_directory = &amp;quot;./youtube_downloads&amp;quot;&lt;br /&gt;
    # -------------------&lt;br /&gt;
&lt;br /&gt;
    if &amp;quot;YOUR_YOUTUBE_LIVE_CHANNEL_URL_HERE&amp;quot; in youtube_channel_url:&lt;br /&gt;
        print(&amp;quot;Please replace &amp;#039;YOUR_YOUTUBE_LIVE_CHANNEL_URL_HERE&amp;#039; with the actual YouTube live channel URL.&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        download_highest_resolution_mp4(youtube_channel_url, download_directory)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FFMPEG merger&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import subprocess&lt;br /&gt;
&lt;br /&gt;
def merge_video_audio(input_directory: str, output_directory: str, ffmpeg_path: str):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Merges MP4 video files (ending with f136.mp4) with M4A audio files (ending with f140.m4a)&lt;br /&gt;
    from a specified input directory into a new output directory using FFmpeg.&lt;br /&gt;
&lt;br /&gt;
    Args:&lt;br /&gt;
        input_directory (str): The directory containing the video and audio files.&lt;br /&gt;
        output_directory (str): The directory where the merged video files will be saved.&lt;br /&gt;
        ffmpeg_path (str): The full path to the FFmpeg executable.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    if not os.path.exists(input_directory):&lt;br /&gt;
        print(f&amp;quot;Error: Input directory &amp;#039;{input_directory}&amp;#039; does not exist.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    if not os.path.exists(output_directory):&lt;br /&gt;
        os.makedirs(output_directory)&lt;br /&gt;
        print(f&amp;quot;Created output directory: {output_directory}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    # List all files in the input directory&lt;br /&gt;
    files = os.listdir(input_directory)&lt;br /&gt;
&lt;br /&gt;
    # Separate video and audio files based on their suffixes&lt;br /&gt;
    video_files = {f.replace(&amp;#039;.f136.mp4&amp;#039;, &amp;#039;&amp;#039;): f for f in files if f.endswith(&amp;#039;.f136.mp4&amp;#039;)}&lt;br /&gt;
    audio_files = {f.replace(&amp;#039;.f140.m4a&amp;#039;, &amp;#039;&amp;#039;): f for f in files if f.endswith(&amp;#039;.f140.m4a&amp;#039;)}&lt;br /&gt;
&lt;br /&gt;
    merged_count = 0&lt;br /&gt;
    skipped_count = 0&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\nSearching for video and audio pairs in &amp;#039;{input_directory}&amp;#039;...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for base_name, video_filename in video_files.items():&lt;br /&gt;
        if base_name in audio_files:&lt;br /&gt;
            audio_filename = audio_files[base_name]&lt;br /&gt;
&lt;br /&gt;
            video_path = os.path.join(input_directory, video_filename)&lt;br /&gt;
            audio_path = os.path.join(input_directory, audio_filename)&lt;br /&gt;
            output_filename = f&amp;quot;{base_name}.mp4&amp;quot; # Merged file will be a clean MP4&lt;br /&gt;
            output_path = os.path.join(output_directory, output_filename)&lt;br /&gt;
&lt;br /&gt;
            if os.path.exists(output_path):&lt;br /&gt;
                print(f&amp;quot;Skipping &amp;#039;{output_filename}&amp;#039;: Already exists in output directory.&amp;quot;)&lt;br /&gt;
                skipped_count += 1&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            print(f&amp;quot;\nMerging &amp;#039;{video_filename}&amp;#039; with &amp;#039;{audio_filename}&amp;#039; into &amp;#039;{output_filename}&amp;#039;...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # FFmpeg command to merge video and audio without re-encoding&lt;br /&gt;
            # -i for input file, -c:v copy for copying video stream, -c:a copy for copying audio stream&lt;br /&gt;
            # -map 0:v:0 to select video stream from first input, -map 1:a:0 to select audio stream from second input&lt;br /&gt;
            command = [&lt;br /&gt;
                ffmpeg_path,&lt;br /&gt;
                &amp;#039;-i&amp;#039;, video_path,&lt;br /&gt;
                &amp;#039;-i&amp;#039;, audio_path,&lt;br /&gt;
                &amp;#039;-c:v&amp;#039;, &amp;#039;copy&amp;#039;,&lt;br /&gt;
                &amp;#039;-c:a&amp;#039;, &amp;#039;copy&amp;#039;,&lt;br /&gt;
                &amp;#039;-map&amp;#039;, &amp;#039;0:v:0&amp;#039;, # Map video stream from first input&lt;br /&gt;
                &amp;#039;-map&amp;#039;, &amp;#039;1:a:0&amp;#039;, # Map audio stream from second input&lt;br /&gt;
                output_path&lt;br /&gt;
            ]&lt;br /&gt;
&lt;br /&gt;
            try:&lt;br /&gt;
                # Run the FFmpeg command&lt;br /&gt;
                # capture_output=True to suppress ffmpeg&amp;#039;s console output in the main script&amp;#039;s console&lt;br /&gt;
                # text=True to decode stdout/stderr as text&lt;br /&gt;
                process = subprocess.run(command, capture_output=True, text=True, check=True)&lt;br /&gt;
                print(f&amp;quot;Successfully merged &amp;#039;{output_filename}&amp;#039;.&amp;quot;)&lt;br /&gt;
                merged_count += 1&lt;br /&gt;
            except subprocess.CalledProcessError as e:&lt;br /&gt;
                print(f&amp;quot;Error merging &amp;#039;{output_filename}&amp;#039;:&amp;quot;)&lt;br /&gt;
                print(f&amp;quot;  Command: {&amp;#039; &amp;#039;.join(e.cmd)}&amp;quot;)&lt;br /&gt;
                print(f&amp;quot;  Return Code: {e.returncode}&amp;quot;)&lt;br /&gt;
                print(f&amp;quot;  STDOUT: {e.stdout}&amp;quot;)&lt;br /&gt;
                print(f&amp;quot;  STDERR: {e.stderr}&amp;quot;)&lt;br /&gt;
            except FileNotFoundError:&lt;br /&gt;
                print(f&amp;quot;Error: FFmpeg not found at &amp;#039;{ffmpeg_path}&amp;#039;. Please ensure the path is correct.&amp;quot;)&lt;br /&gt;
                return&lt;br /&gt;
            except Exception as e:&lt;br /&gt;
                print(f&amp;quot;An unexpected error occurred during merging &amp;#039;{output_filename}&amp;#039;: {e}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            print(f&amp;quot;Warning: No matching audio file found for video &amp;#039;{video_filename}&amp;#039;. Skipping.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;\n--- Merging Summary ---&amp;quot;)&lt;br /&gt;
    print(f&amp;quot;Total files processed: {len(video_files)}&amp;quot;)&lt;br /&gt;
    print(f&amp;quot;Successfully merged: {merged_count}&amp;quot;)&lt;br /&gt;
    print(f&amp;quot;Skipped (already exists): {skipped_count}&amp;quot;)&lt;br /&gt;
    print(f&amp;quot;Files with no matching audio: {len(video_files) - (merged_count + skipped_count)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    # --- Configuration ---&lt;br /&gt;
    # IMPORTANT: Replace with the actual path to your FFmpeg executable.&lt;br /&gt;
    # On Windows, it might be something like &amp;#039;C:\\ffmpeg\\bin\\ffmpeg.exe&amp;#039;&lt;br /&gt;
    # On macOS/Linux, it might be &amp;#039;/usr/local/bin/ffmpeg&amp;#039; or &amp;#039;/usr/bin/ffmpeg&amp;#039;&lt;br /&gt;
    # You can download FFmpeg from https://ffmpeg.org/download.html&lt;br /&gt;
    FFMPEG_EXECUTABLE_PATH = &amp;quot;C:\\Users\\mbernadat\\Downloads\\exe\\ffmpeg.exe&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    # Directory where your .f136.mp4 and .f140.m4a files are located&lt;br /&gt;
    INPUT_VIDEOS_DIR = &amp;quot;./youtube_downloads&amp;quot; # Assuming this is where the previous script downloads&lt;br /&gt;
    # Directory where the merged .mp4 files will be saved&lt;br /&gt;
    OUTPUT_MERGED_DIR = &amp;quot;./merged_videos&amp;quot;&lt;br /&gt;
    # -------------------&lt;br /&gt;
&lt;br /&gt;
    if &amp;quot;YOUR_FFMPEG_PATH_HERE&amp;quot; in FFMPEG_EXECUTABLE_PATH:&lt;br /&gt;
        print(&amp;quot;Please replace &amp;#039;YOUR_FFMPEG_PATH_HERE&amp;#039; with the actual path to your FFmpeg executable.&amp;quot;)&lt;br /&gt;
        print(&amp;quot;Example for Windows: &amp;#039;C:\\ffmpeg\\bin\\ffmpeg.exe&amp;#039;&amp;quot;)&lt;br /&gt;
        print(&amp;quot;Example for macOS/Linux: &amp;#039;/usr/local/bin/ffmpeg&amp;#039;&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        merge_video_audio(INPUT_VIDEOS_DIR, OUTPUT_MERGED_DIR, FFMPEG_EXECUTABLE_PATH)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== interencheres ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import re, os, uuid, datetime, sys, signal&lt;br /&gt;
from selenium import webdriver &lt;br /&gt;
from selenium.webdriver.chrome.service import Service&lt;br /&gt;
from selenium.webdriver.common.by import By&lt;br /&gt;
from selenium.common.exceptions import NoSuchElementException&lt;br /&gt;
from selenium.webdriver.remote.remote_connection import LOGGER as seleniumLogger&lt;br /&gt;
import logging&lt;br /&gt;
from subprocess import CREATE_NO_WINDOW&lt;br /&gt;
&lt;br /&gt;
from time import sleep &lt;br /&gt;
import urllib.request&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
## variables &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
service = Service(&amp;#039;Q:/WORK/TEMP/python/chromedriver_win32/chromedriver.exe&amp;#039;)&lt;br /&gt;
service.creationflags = CREATE_NO_WINDOW&lt;br /&gt;
subfolder = &amp;#039;encheres&amp;#039;&lt;br /&gt;
looptime = 5&lt;br /&gt;
&lt;br /&gt;
## defs&lt;br /&gt;
&lt;br /&gt;
def log(text,sameline=False):&lt;br /&gt;
	if sameline:&lt;br /&gt;
		print(text,end = &amp;#039;\r&amp;#039;)&lt;br /&gt;
	else:&lt;br /&gt;
		print(text)&lt;br /&gt;
&lt;br /&gt;
def sigint_handler(signal, frame):&lt;br /&gt;
    log(&amp;#039;KeyboardInterrupt is caught&amp;#039;)&lt;br /&gt;
    driver.quit()&lt;br /&gt;
    sys.exit(0)&lt;br /&gt;
&lt;br /&gt;
signal.signal(signal.SIGINT, sigint_handler)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def touch(fname, times=None):&lt;br /&gt;
	Path(fname).mkdir(parents=True, exist_ok=True)&lt;br /&gt;
&lt;br /&gt;
def textappend(file,text):&lt;br /&gt;
	with open(file, &amp;#039;a&amp;#039;,encoding=&amp;#039;utf-8-sig&amp;#039;) as record:&lt;br /&gt;
		record.write(text+&amp;#039;\n&amp;#039;)&lt;br /&gt;
def download(url,savedir):&lt;br /&gt;
	touch(savedir)&lt;br /&gt;
	uid = uuid.uuid4().hex&lt;br /&gt;
	urllib.request.urlretrieve(url, savedir+&amp;#039;/&amp;#039;+uid+&amp;#039;.jpg&amp;#039;)&lt;br /&gt;
	return uid&lt;br /&gt;
def out(driver):&lt;br /&gt;
	driver.quit()&lt;br /&gt;
	exit()&lt;br /&gt;
&lt;br /&gt;
def createHtml(textfile):&lt;br /&gt;
	htmlfile = textfile+&amp;#039;.html&amp;#039;&lt;br /&gt;
	if &amp;#039;.txt&amp;#039; in htmlfile:&lt;br /&gt;
		try:&lt;br /&gt;
			os.remove(htmlfile)&lt;br /&gt;
		except OSError:&lt;br /&gt;
			log(&amp;#039;cannot delete&amp;#039;+ htmlfile)&lt;br /&gt;
			pass&lt;br /&gt;
&lt;br /&gt;
	switch = 0&lt;br /&gt;
	colors = [&amp;#039;#F4F4F4&amp;#039;,&amp;#039;#FBFBFB&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
	html = &amp;#039;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;table&amp;gt;&amp;#039;&lt;br /&gt;
	with open(textfile, &amp;#039;r&amp;#039;,encoding=&amp;#039;utf-8-sig&amp;#039;) as records:&lt;br /&gt;
		page = records.readlines()&lt;br /&gt;
		for item in page:&lt;br /&gt;
			switch = not switch&lt;br /&gt;
			L = item.split(&amp;#039; | &amp;#039;)&lt;br /&gt;
			image = &amp;#039;&amp;lt;img style=&amp;quot;max-height:200px;max-width:200px&amp;quot; src=&amp;quot;images/&amp;#039;+L[0]+&amp;#039;.jpg&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
			infos = L[3]+&amp;#039;&amp;lt;br/&amp;gt;\n\t&amp;lt;br/&amp;gt;\n\t&amp;#039;+L[2]+&amp;#039;: &amp;#039;+L[4]&lt;br /&gt;
			html += &amp;#039;&amp;lt;tr style=&amp;quot;background-color:{color}&amp;quot;&amp;gt;\n\t&amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;{image}&amp;lt;/td&amp;gt;\n\t&amp;lt;td valign=&amp;quot;top&amp;quot; style=&amp;quot;padding:10px&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;{price}&amp;lt;/strong&amp;gt;&amp;lt;br/&amp;gt;\n\t{infos}\t&amp;lt;/td&amp;gt;\n&amp;lt;/tr&amp;gt;\n&amp;#039;.format(image=image,price=L[1],infos=infos,color=colors[switch])&lt;br /&gt;
	html += &amp;#039;&amp;lt;/table&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#039;&lt;br /&gt;
	try:&lt;br /&gt;
		textappend(htmlfile,html)&lt;br /&gt;
		log(&amp;#039;writing html file&amp;#039;)&lt;br /&gt;
	except:&lt;br /&gt;
		log(&amp;#039;error writing html file&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def writeOut(driver,previousprice,previousiteminfos,filepath,looptime):&lt;br /&gt;
	outstring = uid+&amp;#039; | &amp;#039;+previousprice+&amp;#039; | &amp;#039;+&amp;#039; | &amp;#039;.join(previousiteminfos);&lt;br /&gt;
	log(outstring)&lt;br /&gt;
	textappend(filepath,outstring)&lt;br /&gt;
	createHtml(filepath)&lt;br /&gt;
	log(&amp;#039;looping every &amp;#039;+str(looptime)+&amp;#039;s&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
url = &amp;#039;&amp;#039;&lt;br /&gt;
try:&lt;br /&gt;
	url = sys.argv[1]&lt;br /&gt;
except:&lt;br /&gt;
	log(&amp;#039;url needed as argument&amp;#039;)&lt;br /&gt;
	exit()&lt;br /&gt;
&lt;br /&gt;
savepath = os.path.dirname(os.path.realpath(__file__))+&amp;#039;/&amp;#039;+subfolder&lt;br /&gt;
fileNameDate = datetime.datetime.now().strftime(&amp;quot;%y%m%d_%H-%M&amp;quot;)&lt;br /&gt;
filepath = savepath+&amp;#039;/&amp;#039;+fileNameDate+&amp;#039;_&amp;#039;+url.split(&amp;#039;/&amp;#039;)[3]+&amp;#039;_&amp;#039;+url.split(&amp;#039;/&amp;#039;)[4]+&amp;#039;.txt&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
log(&amp;#039;loading headless chrome...&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
seleniumLogger.setLevel(logging.WARNING)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
options = webdriver.ChromeOptions()&lt;br /&gt;
#options.add_experimental_option(&amp;#039;prefs&amp;#039;, prefs)&lt;br /&gt;
options.add_argument(&amp;#039;--headless&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--no-sandbox&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--disable-gpu&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--window-size=1280x1696&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--user-data-dir=/tmp/user-data&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--hide-scrollbars&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--service-log-path=&amp;#039;+os.devnull)&lt;br /&gt;
options.add_argument(&amp;#039;--enable-logging&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--log-level=3&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--silent&amp;#039;);&lt;br /&gt;
options.add_argument(&amp;#039;--v=99&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--single-process&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--data-path=/tmp/data-path&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--ignore-certificate-errors&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--homedir=/tmp&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;--disk-cache-dir=/tmp/cache-dir&amp;#039;)&lt;br /&gt;
options.add_argument(&amp;#039;user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
driver = webdriver.Chrome(service=service,options=options)&lt;br /&gt;
&lt;br /&gt;
isWaitingToStart = True&lt;br /&gt;
isOn = True&lt;br /&gt;
&lt;br /&gt;
url = driver.get(url) &lt;br /&gt;
&lt;br /&gt;
log(&amp;#039;loading &amp;#039; + driver.current_url) #sanity check&lt;br /&gt;
log(&amp;#039;writing to &amp;#039;+filepath)&lt;br /&gt;
# figure our dom elements&lt;br /&gt;
&lt;br /&gt;
# is it started or over, if not wait&lt;br /&gt;
&lt;br /&gt;
while isWaitingToStart:&lt;br /&gt;
	try:&lt;br /&gt;
		items = driver.find_elements(By.CLASS_NAME, &amp;#039;wrapper-join-sale-button&amp;#039;)&lt;br /&gt;
		for item in items:&lt;br /&gt;
			if item.text == &amp;#039;Cette vente est terminée&amp;#039;:&lt;br /&gt;
				log(&amp;#039;over&amp;#039;)&lt;br /&gt;
				out(driver)&lt;br /&gt;
&lt;br /&gt;
			elif &amp;#039;La vente commence dans&amp;#039; in item.text:&lt;br /&gt;
				log(&amp;#039;waiting to start...&amp;#039;)&lt;br /&gt;
				sleep(100)&lt;br /&gt;
			else:&lt;br /&gt;
				isWaitingToStart = False&lt;br /&gt;
				log(&amp;#039;starting&amp;#039;)&lt;br /&gt;
				break&lt;br /&gt;
	except NoSuchElementException:&lt;br /&gt;
		print(&amp;#039;No wrapper-join-sale-button&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# assume it&amp;#039;s on&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
	priceitem = driver.find_element(By.CLASS_NAME, &amp;#039;text-h5&amp;#039;)&lt;br /&gt;
	items = driver.find_elements(By.CLASS_NAME, &amp;quot;text-h5&amp;quot;)&lt;br /&gt;
	for item in items: &lt;br /&gt;
		if &amp;#039;€&amp;#039; in item.text:&lt;br /&gt;
			priceitem = item&lt;br /&gt;
			break&lt;br /&gt;
except NoSuchElementException:&lt;br /&gt;
	log(&amp;#039;No text-h5 found&amp;#039;)&lt;br /&gt;
	out(driver)&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
	itemdiv = driver.find_element(By.CLASS_NAME, &amp;#039;current-item&amp;#039;)&lt;br /&gt;
except NoSuchElementException:&lt;br /&gt;
	log(&amp;#039;No current-item&amp;#039;)&lt;br /&gt;
	out(driver)&lt;br /&gt;
&lt;br /&gt;
# turn off video if it&amp;#039;s on&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
	driver.execute_script(&amp;#039;document.getElementById(&amp;quot;streaming-subscriber&amp;quot;).pause();&amp;#039;)&lt;br /&gt;
	log(&amp;#039;video paused&amp;#039;)&lt;br /&gt;
except:&lt;br /&gt;
	log(&amp;#039;No video stream found&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
previousprice = &amp;#039;none&amp;#039;&lt;br /&gt;
previousitem = &amp;#039;none&amp;#039;&lt;br /&gt;
previousiteminfos = []&lt;br /&gt;
previousimage = &amp;#039;none&amp;#039;&lt;br /&gt;
uid = &amp;#039;none&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# loop until we break&lt;br /&gt;
&lt;br /&gt;
while isOn:&lt;br /&gt;
&lt;br /&gt;
	# price and/or stop if finished&lt;br /&gt;
	try:&lt;br /&gt;
		findprice = driver.find_element(By.CLASS_NAME, &amp;#039;text-h5&amp;#039;)&lt;br /&gt;
		if not findprice:&lt;br /&gt;
			isOn = False&lt;br /&gt;
			log(&amp;#039;finished, stopping&amp;#039;)&lt;br /&gt;
			out(driver)&lt;br /&gt;
		fetchprice = priceitem.text&lt;br /&gt;
		fetchprice = &amp;quot;&amp;quot;.join(fetchprice.split()) # remove weird space characters&lt;br /&gt;
	except:&lt;br /&gt;
		log(&amp;#039;price error, most likely finished&amp;#039;)&lt;br /&gt;
		writeOut(driver,previousprice,previousiteminfos,filepath,looptime)&lt;br /&gt;
		#createHtml(filepath)&lt;br /&gt;
		out(driver)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	# current item description and image&lt;br /&gt;
	try:&lt;br /&gt;
		currentiteminfos = itemdiv.text.splitlines()&lt;br /&gt;
		currentitem = currentiteminfos[0]&lt;br /&gt;
	except:&lt;br /&gt;
		log(&amp;#039;item error&amp;#039;)&lt;br /&gt;
		out(driver)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	#fetch image&lt;br /&gt;
&lt;br /&gt;
	try:&lt;br /&gt;
		rgx = r&amp;#039;background-image: url\(&amp;quot;(.*)&amp;quot;&amp;#039;&lt;br /&gt;
		images = itemdiv.find_elements(By.CLASS_NAME, &amp;#039;v-image__image&amp;#039;)&lt;br /&gt;
		for image in images:&lt;br /&gt;
			style = image.get_attribute(&amp;#039;style&amp;#039;)&lt;br /&gt;
			result = re.search(rgx, style)&lt;br /&gt;
			if result:&lt;br /&gt;
				currentimage = result.groups(0)[0]&lt;br /&gt;
		&lt;br /&gt;
		if currentimage != previousimage and previousimage != &amp;#039;none&amp;#039;:&lt;br /&gt;
			uid = download(previousimage,savepath+&amp;#039;/images&amp;#039;)&lt;br /&gt;
			log(&amp;#039;new image downloaded&amp;#039;)&lt;br /&gt;
			previousimage = currentimage&lt;br /&gt;
	except:&lt;br /&gt;
		log(&amp;#039;image error&amp;#039;)&lt;br /&gt;
		out(driver)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	# if things have changed, it means the previous item bid is over&lt;br /&gt;
&lt;br /&gt;
	if (currentitem != previousitem ) and previousprice != &amp;#039;none&amp;#039;:&lt;br /&gt;
		writeOut(driver,previousprice,previousiteminfos,filepath,looptime)&lt;br /&gt;
		#outstring = uid+&amp;#039; | &amp;#039;+previousprice+&amp;#039; | &amp;#039;+&amp;#039; | &amp;#039;.join(previousiteminfos);&lt;br /&gt;
		#log(outstring)&lt;br /&gt;
		#textappend(filepath,outstring)&lt;br /&gt;
		#createHtml(filepath)&lt;br /&gt;
		#log(&amp;#039;looping every &amp;#039;+str(looptime)+&amp;#039;s&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
		try:&lt;br /&gt;
			driver.execute_script(&amp;#039;document.getElementById(&amp;quot;streaming-subscriber&amp;quot;).pause();&amp;#039;)&lt;br /&gt;
			log(&amp;#039;video paused&amp;#039;)&lt;br /&gt;
		except:&lt;br /&gt;
			log(&amp;#039;no video stream found&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if previousprice != fetchprice:&lt;br /&gt;
		log(previousprice+&amp;#039; &amp;#039;,True)&lt;br /&gt;
&lt;br /&gt;
	if fetchprice != &amp;#039;-- €&amp;#039;:&lt;br /&gt;
		previousprice = fetchprice&lt;br /&gt;
&lt;br /&gt;
	previousitem = currentitem&lt;br /&gt;
	previousiteminfos = currentiteminfos&lt;br /&gt;
	previousimage = currentimage&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	sleep(looptime)&lt;br /&gt;
&lt;br /&gt;
driver.quit()&lt;br /&gt;
log(&amp;#039;script finished&amp;#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
def touch(fname, times=None):&lt;br /&gt;
	Path(fname).mkdir(parents=True, exist_ok=True)&lt;br /&gt;
def textappend(file,text):&lt;br /&gt;
	with open(file, &amp;#039;a&amp;#039;,encoding=&amp;#039;utf-8-sig&amp;#039;) as record:&lt;br /&gt;
		record.write(text+&amp;#039;\n&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
subfolder = &amp;#039;encheres&amp;#039;&lt;br /&gt;
savepath = os.path.dirname(os.path.realpath(__file__))+&amp;#039;/&amp;#039;+subfolder&lt;br /&gt;
txtfile = savepath+&amp;#039;/220217_12-11_materiels-professionnels_vehicules-et-materiel-304020.txt&amp;#039;&lt;br /&gt;
&lt;br /&gt;
def createHtml(textfile):&lt;br /&gt;
	htmlfile = textfile+&amp;#039;.html&amp;#039;&lt;br /&gt;
	if &amp;#039;.txt&amp;#039; in htmlfile:&lt;br /&gt;
		try:&lt;br /&gt;
			os.remove(htmlfile)&lt;br /&gt;
		except OSError:&lt;br /&gt;
			#log(&amp;#039;cannot delete&amp;#039;+ htmlfile)&lt;br /&gt;
			pass&lt;br /&gt;
&lt;br /&gt;
	switch = 0&lt;br /&gt;
	colors = [&amp;#039;#F4F4F4&amp;#039;,&amp;#039;#FBFBFB&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
	html = &amp;#039;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;table&amp;gt;&amp;#039;&lt;br /&gt;
	with open(textfile, &amp;#039;r&amp;#039;,encoding=&amp;#039;utf-8-sig&amp;#039;) as records:&lt;br /&gt;
		page = records.readlines()&lt;br /&gt;
		for item in page:&lt;br /&gt;
			switch = not switch&lt;br /&gt;
			L = item.split(&amp;#039; | &amp;#039;)&lt;br /&gt;
			image = &amp;#039;&amp;lt;img style=&amp;quot;max-height:200px;max-width:200px&amp;quot; src=&amp;quot;images/&amp;#039;+L[0]+&amp;#039;.jpg&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
			infos = L[3]+&amp;#039;&amp;lt;br/&amp;gt;\n\t&amp;lt;br/&amp;gt;\n\t&amp;#039;+L[2]+&amp;#039;: &amp;#039;+L[4]&lt;br /&gt;
			html += &amp;#039;&amp;lt;tr style=&amp;quot;background-color:{color}&amp;quot;&amp;gt;\n\t&amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;{image}&amp;lt;/td&amp;gt;\n\t&amp;lt;td valign=&amp;quot;top&amp;quot; style=&amp;quot;padding:10px&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;{price}&amp;lt;/strong&amp;gt;&amp;lt;br/&amp;gt;\n\t{infos}\t&amp;lt;/td&amp;gt;\n&amp;lt;/tr&amp;gt;\n&amp;#039;.format(image=image,price=L[1],infos=infos,color=colors[switch])&lt;br /&gt;
	html += &amp;#039;&amp;lt;/table&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#039;&lt;br /&gt;
	try:&lt;br /&gt;
		textappend(htmlfile,html)&lt;br /&gt;
		log(&amp;#039;writing html file&amp;#039;)&lt;br /&gt;
	except:&lt;br /&gt;
		log(&amp;#039;error writing html file&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
createHtml(txtfile)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Radios scraping ===&lt;br /&gt;
todo: web&amp;lt;&amp;gt;PI interface&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import json&lt;br /&gt;
import urllib.request&lt;br /&gt;
import urllib.parse&lt;br /&gt;
import os&lt;br /&gt;
import datetime&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#todo: make sure tail works without opening ressource entirely&lt;br /&gt;
&lt;br /&gt;
path = os.path.dirname(os.path.realpath(__file__))&lt;br /&gt;
#path = os.getcwd()&lt;br /&gt;
sep = &amp;quot; | &amp;quot;&lt;br /&gt;
date = datetime.datetime.now().strftime(&amp;quot;%y%m%d&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def touch(fname, times=None):&lt;br /&gt;
	with open(fname, &amp;#039;a&amp;#039;):&lt;br /&gt;
		os.utime(fname,times)&lt;br /&gt;
&lt;br /&gt;
def tail(f, lines=1, _buffer=4098):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Tail a file and get X lines from the end&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	lines_found = []&lt;br /&gt;
	block_counter = -1&lt;br /&gt;
	file = open(f,&amp;#039;r&amp;#039;)&lt;br /&gt;
	while len(lines_found) &amp;lt; lines:&lt;br /&gt;
		try:&lt;br /&gt;
			file.seek(block_counter * _buffer, os.SEEK_END)&lt;br /&gt;
		except IOError:  # either file is too small, or too many lines requested&lt;br /&gt;
			file.seek(0)&lt;br /&gt;
			lines_found = file.readlines()&lt;br /&gt;
			break&lt;br /&gt;
	lines_found = file.readlines()&lt;br /&gt;
	block_counter -= 1&lt;br /&gt;
	return lines_found[-lines:]&lt;br /&gt;
	file.close()&lt;br /&gt;
&lt;br /&gt;
def getlastlines(file,n):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039; https://stackoverflow.com/questions/46258499/how-to-read-the-last-line-of-a-file-in-python &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	with open(file, &amp;#039;r&amp;#039;) as f:&lt;br /&gt;
		lines = f.readlines()&lt;br /&gt;
		return lines[-n:]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#####################&lt;br /&gt;
#                   #&lt;br /&gt;
# RADIO MEUH        #&lt;br /&gt;
#                   #&lt;br /&gt;
#####################&lt;br /&gt;
&lt;br /&gt;
url = &amp;quot;https://www.radiomeuh.com/player/rtdata/tracks.json&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#localfile = path+&amp;quot;/home/bernie/tools/loop/data/playlist.txt&amp;quot;&lt;br /&gt;
localfile = path+&amp;quot;/data/playlist_meuh.txt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
touch(localfile)&lt;br /&gt;
&lt;br /&gt;
lastlines = getlastlines(localfile,10)&lt;br /&gt;
&lt;br /&gt;
content = urllib.request.urlopen(url)&lt;br /&gt;
data = json.loads(content.read())&lt;br /&gt;
for el in reversed(data):&lt;br /&gt;
	record = (&amp;quot;{date}{sep}{time}{sep}{artist}{sep}{titre}&amp;quot;.format(sep=sep,date=date,time=el[&amp;#039;time&amp;#039;],artist=el[&amp;#039;artist&amp;#039;],titre=el[&amp;#039;titre&amp;#039;]))&lt;br /&gt;
	artisttitre = str(el[&amp;#039;artist&amp;#039;]+sep+el[&amp;#039;titre&amp;#039;])&lt;br /&gt;
	if len(lastlines) &amp;lt; 2:&lt;br /&gt;
		lastlines = [&amp;#039;0&amp;#039;,&amp;#039;0&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
	#if (record != lastlines[0].strip()) and (record != lastlines[1].strip()):&lt;br /&gt;
	if not any(artisttitre in s for s in lastlines):&lt;br /&gt;
		with open(localfile, &amp;#039;a&amp;#039;,encoding=&amp;#039;utf-8-sig&amp;#039;) as recordlist:&lt;br /&gt;
			recordlist.write(record+&amp;#039;\n&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
#####################&lt;br /&gt;
#                   #&lt;br /&gt;
# ELLEBORE          #&lt;br /&gt;
#                   #&lt;br /&gt;
#####################&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
url = &amp;#039;https://www.radio-ellebore.com/wp-admin/admin-ajax.php&amp;#039;&lt;br /&gt;
&lt;br /&gt;
localfile = path+&amp;quot;/data/playlist_ellebore.txt&amp;quot;&lt;br /&gt;
touch(localfile)&lt;br /&gt;
&lt;br /&gt;
lastline = getlastlines(localfile,1)&lt;br /&gt;
&lt;br /&gt;
if len(lastline)==0:&lt;br /&gt;
&lt;br /&gt;
	lastline = [&amp;#039;0 | 0 | 0 | 0&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
lastlog = lastline[0].strip().split(&amp;#039; | &amp;#039;)&lt;br /&gt;
&lt;br /&gt;
data = {&amp;#039;action&amp;#039;:&amp;#039;get_stream_title&amp;#039;,&amp;#039;stream&amp;#039;:&amp;#039;https://ellebore.ice.infomaniak.ch/ellebore-high.aac&amp;#039;}&lt;br /&gt;
data = urllib.parse.urlencode(data).encode()&lt;br /&gt;
hdr = { &amp;#039;Content-Type&amp;#039;:&amp;#039;application/x-www-form-urlencoded; charset=UTF-8&amp;#039; }&lt;br /&gt;
&lt;br /&gt;
req = urllib.request.Request(url, data=data, headers=hdr,method=&amp;#039;POST&amp;#039;)&lt;br /&gt;
response = urllib.request.urlopen(req)&lt;br /&gt;
result = response.read().decode(&amp;#039;utf-8&amp;#039;) &lt;br /&gt;
result = result.split(&amp;#039; - &amp;#039;)&lt;br /&gt;
&lt;br /&gt;
time = datetime.datetime.now().strftime(&amp;quot;%H:%M:%S&amp;quot;)&lt;br /&gt;
record = (&amp;quot;{date}{sep}{time}{sep}{artist}{sep}{titre}&amp;quot;.format(sep=sep,date=date,time=time,artist=result[0].strip(),titre=result[1].strip()))&lt;br /&gt;
&lt;br /&gt;
if lastlog[-1].strip() == &amp;#039;|&amp;#039;:&lt;br /&gt;
	lastlog = [&amp;#039;a&amp;#039;,&amp;#039;b&amp;#039;,&amp;#039;c&amp;#039;,&amp;#039;d&amp;#039;]&lt;br /&gt;
if str(result[0]+result[1]) != str(lastlog[2]+lastlog[3]) or lastline[0] == &amp;#039;0 | 0 | 0 | 0&amp;#039;:&lt;br /&gt;
	with open(localfile, &amp;#039;a&amp;#039;,encoding=&amp;#039;utf-8-sig&amp;#039;) as recordlist:&lt;br /&gt;
		recordlist.write(record+&amp;#039;\n&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
#le djam https://www.djamradio.com/actions/infos.php&lt;br /&gt;
#https://www.djamradio.com/actions/retrieve.php&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create a chart with frame size and render time ===&lt;br /&gt;
&lt;br /&gt;
py2&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/RBa7yJc.png&lt;br /&gt;
&lt;br /&gt;
Requires matplotlib to make charts, runs on right-clicking a file part of a sequence usign the windows sendto&amp;gt;menu (that you can edit by doing &amp;lt;code&amp;gt;Win-R &amp;gt; shell:sendto&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
python -m pip install -U pip&lt;br /&gt;
python -m pip install -U matplotlib&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Creates a chart with render time and frame size &amp;#039;&amp;#039;&amp;#039;using file creation time&amp;#039;&amp;#039;&amp;#039; (so as it was pointed out a little useless for multi-machine renders). Useful if you want to get a glimpse of simulation frame times&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os, sys, re, time&lt;br /&gt;
&lt;br /&gt;
import matplotlib.pyplot as plt&lt;br /&gt;
import numpy as np&lt;br /&gt;
&lt;br /&gt;
def sequence(file):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a file path, return a dictionnary with [directory, filename (without number and extension), start frame, end frame, padding, extension]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	# given  C:/path/the_sequence_0033.jpg&lt;br /&gt;
	# like so [&amp;#039;c:/path/&amp;#039;,&amp;#039;the_sequence_&amp;#039;,1,100,4,&amp;#039;.jpg&amp;#039;]&lt;br /&gt;
&lt;br /&gt;
	if os.path.isfile(file):&lt;br /&gt;
		reg = r&amp;#039;^(.+?)([0-9]+)\.([.a-zA-Z]{1,7})$&amp;#039;&lt;br /&gt;
		match = re.match(reg, file,re.IGNORECASE)&lt;br /&gt;
		if match:&lt;br /&gt;
			#return target&lt;br /&gt;
			newReg = r&amp;#039;(&amp;#039;+os.path.basename(match.groups()[0])+&amp;#039;)(\d*)\.(&amp;#039;+match.groups()[2]+&amp;#039;)&amp;#039;&lt;br /&gt;
						&lt;br /&gt;
			#bit convoluted but it will help me pick the first image of sequence that matches selection&lt;br /&gt;
			filelist = []&lt;br /&gt;
			target = os.path.dirname(file)&lt;br /&gt;
			for f in os.listdir(target):&lt;br /&gt;
				match = re.match(newReg, f,re.IGNORECASE)&lt;br /&gt;
				if match:&lt;br /&gt;
					filelist.append(match.groups())&lt;br /&gt;
			return [ target , filelist[0][0] , int(filelist[0][1]) , int(filelist[-1][1]) , len(filelist[0][1]) , filelist[0][2] ]&lt;br /&gt;
&lt;br /&gt;
sequence = sequence(sys.argv[1])&lt;br /&gt;
&lt;br /&gt;
start = sequence[2]&lt;br /&gt;
end = sequence[3]&lt;br /&gt;
&lt;br /&gt;
sequenceInfos = []&lt;br /&gt;
&lt;br /&gt;
for n in range(start,end+1):&lt;br /&gt;
	file = &amp;#039;{folder}/{file}{number}.{extension}&amp;#039;.format( folder = sequence[0], file = sequence[1], number = str(n).zfill( sequence[4] ), extension = sequence[5] )&lt;br /&gt;
	&lt;br /&gt;
	file = os.path.abspath(file)&lt;br /&gt;
	creationTime = os.path.getctime(file)&lt;br /&gt;
	size = os.path.getsize(file)&lt;br /&gt;
&lt;br /&gt;
	sequenceInfos.append([file,creationTime,size])&lt;br /&gt;
&lt;br /&gt;
frames = list(range(start, end + 1))&lt;br /&gt;
times = [ x[1] for x in sequenceInfos ] #creation times&lt;br /&gt;
&lt;br /&gt;
times_temp = times.copy()&lt;br /&gt;
&lt;br /&gt;
for i in range(1,len(times)):&lt;br /&gt;
	times_temp[i] = times[i] - times[i-1]&lt;br /&gt;
&lt;br /&gt;
times_temp[0] = times_temp[1]&lt;br /&gt;
times = times_temp.copy()&lt;br /&gt;
&lt;br /&gt;
sizes = [ x[2] / 1024.0 / 1024.0 for x in sequenceInfos ]&lt;br /&gt;
&lt;br /&gt;
fig, ax1 = plt.subplots()&lt;br /&gt;
&lt;br /&gt;
color = &amp;#039;tab:red&amp;#039;&lt;br /&gt;
ax1.set_xlabel(&amp;#039;frames&amp;#039;)&lt;br /&gt;
ax1.set_ylabel(&amp;#039;Frame Time (s)&amp;#039;, color=color)&lt;br /&gt;
ax1.plot(frames, times, color=color)&lt;br /&gt;
ax1.tick_params(axis=&amp;#039;y&amp;#039;, labelcolor=color)&lt;br /&gt;
ax1.set_ylim(bottom=0)&lt;br /&gt;
ax2 = ax1.twinx()  &lt;br /&gt;
&lt;br /&gt;
color = &amp;#039;tab:blue&amp;#039;&lt;br /&gt;
ax2.set_ylabel(&amp;#039;Size (mb)&amp;#039;, color=color)  # we already handled the x-label with ax1&lt;br /&gt;
ax2.plot(frames, sizes, color=color)&lt;br /&gt;
ax2.tick_params(axis=&amp;#039;y&amp;#039;, labelcolor=color)&lt;br /&gt;
&lt;br /&gt;
ax2.set_ylim(bottom=0)&lt;br /&gt;
fig.tight_layout()  &lt;br /&gt;
&lt;br /&gt;
plt.grid()&lt;br /&gt;
plt.show()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Houdini remote killswitch ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import urllib.request&lt;br /&gt;
import os&lt;br /&gt;
import time&lt;br /&gt;
from random import random&lt;br /&gt;
&lt;br /&gt;
while True:&lt;br /&gt;
    url = &amp;quot;https://berniebernie.fr/tools/stamp/up.txt&amp;quot;&lt;br /&gt;
    url += &amp;quot;?&amp;quot;+str(random())&lt;br /&gt;
    contents = urllib.request.urlopen(url).read()&lt;br /&gt;
    contents = contents.decode()&lt;br /&gt;
&lt;br /&gt;
    if contents==&amp;#039;kill&amp;#039;:&lt;br /&gt;
        os.system(&amp;#039;taskkill /F /IM &amp;quot;houdini*&amp;quot;&amp;#039;)&lt;br /&gt;
        print(&amp;#039;killed&amp;#039;)&lt;br /&gt;
        break&lt;br /&gt;
    else:&lt;br /&gt;
        print(url)&lt;br /&gt;
        print(contents)&lt;br /&gt;
        time.sleep(30)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Create a folder with today&amp;#039;s date===&lt;br /&gt;
https://i.imgur.com/YEG2kLI.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import time&lt;br /&gt;
from datetime import date&lt;br /&gt;
&lt;br /&gt;
today = date.today()&lt;br /&gt;
folder = today.strftime(&amp;quot;%Y_%m_%d&amp;quot;)&lt;br /&gt;
folder = os.getcwd()+&amp;#039;/&amp;#039;+folder&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
	os.mkdir(folder)&lt;br /&gt;
	print(&amp;quot;creating &amp;quot;+folder)&lt;br /&gt;
except:&lt;br /&gt;
	print(folder+ &amp;quot; exists or failed&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
time.sleep(.8)  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Find the name of the cloest matching color ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def colorName(r,g,b):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;returns the name of the closest color from the list using distance from r,g,b so might no be super precise :) &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	colors = {&lt;br /&gt;
	&amp;#039;AliceBlue&amp;#039;:[240,248,255],&lt;br /&gt;
	&amp;#039;AntiqueWhite&amp;#039;:[250,235,215],&lt;br /&gt;
	&amp;#039;Aqua&amp;#039;:[0,255,255],&lt;br /&gt;
	&amp;#039;Aquamarine&amp;#039;:[127,255,212],&lt;br /&gt;
	&amp;#039;Azure&amp;#039;:[240,255,255],&lt;br /&gt;
	&amp;#039;Beige&amp;#039;:[245,245,220],&lt;br /&gt;
	&amp;#039;Bisque&amp;#039;:[255,228,196],&lt;br /&gt;
	&amp;#039;Black&amp;#039;:[0,0,0],&lt;br /&gt;
	&amp;#039;BlanchedAlmond&amp;#039;:[255,235,205],&lt;br /&gt;
	&amp;#039;Blue&amp;#039;:[0,0,255],&lt;br /&gt;
	&amp;#039;BlueViolet&amp;#039;:[138,43,226],&lt;br /&gt;
	&amp;#039;Brown&amp;#039;:[165,42,42],&lt;br /&gt;
	&amp;#039;BurlyWood&amp;#039;:[222,184,135],&lt;br /&gt;
	&amp;#039;CadetBlue&amp;#039;:[95,158,160],&lt;br /&gt;
	&amp;#039;Chartreuse&amp;#039;:[127,255,0],&lt;br /&gt;
	&amp;#039;Chocolate&amp;#039;:[210,105,30],&lt;br /&gt;
	&amp;#039;Coral&amp;#039;:[255,127,80],&lt;br /&gt;
	&amp;#039;CornflowerBlue&amp;#039;:[100,149,237],&lt;br /&gt;
	&amp;#039;Cornsilk&amp;#039;:[255,248,220],&lt;br /&gt;
	&amp;#039;Crimson&amp;#039;:[220,20,60],&lt;br /&gt;
	&amp;#039;Cyan&amp;#039;:[0,255,255],&lt;br /&gt;
	&amp;#039;DarkBlue&amp;#039;:[0,0,139],&lt;br /&gt;
	&amp;#039;DarkCyan&amp;#039;:[0,139,139],&lt;br /&gt;
	&amp;#039;DarkGoldenRod&amp;#039;:[184,134,11],&lt;br /&gt;
	&amp;#039;DarkGray&amp;#039;:[169,169,169],&lt;br /&gt;
	&amp;#039;DarkGrey&amp;#039;:[169,169,169],&lt;br /&gt;
	&amp;#039;DarkGreen&amp;#039;:[0,100,0],&lt;br /&gt;
	&amp;#039;DarkKhaki&amp;#039;:[189,183,107],&lt;br /&gt;
	&amp;#039;DarkMagenta&amp;#039;:[139,0,139],&lt;br /&gt;
	&amp;#039;DarkOliveGreen&amp;#039;:[85,107,47],&lt;br /&gt;
	&amp;#039;DarkOrange&amp;#039;:[255,140,0],&lt;br /&gt;
	&amp;#039;DarkOrchid&amp;#039;:[153,50,204],&lt;br /&gt;
	&amp;#039;DarkRed&amp;#039;:[139,0,0],&lt;br /&gt;
	&amp;#039;DarkSalmon&amp;#039;:[233,150,122],&lt;br /&gt;
	&amp;#039;DarkSeaGreen&amp;#039;:[143,188,143],&lt;br /&gt;
	&amp;#039;DarkSlateBlue&amp;#039;:[72,61,139],&lt;br /&gt;
	&amp;#039;DarkSlateGray&amp;#039;:[47,79,79],&lt;br /&gt;
	&amp;#039;DarkSlateGrey&amp;#039;:[47,79,79],&lt;br /&gt;
	&amp;#039;DarkTurquoise&amp;#039;:[0,206,209],&lt;br /&gt;
	&amp;#039;DarkViolet&amp;#039;:[148,0,211],&lt;br /&gt;
	&amp;#039;DeepPink&amp;#039;:[255,20,147],&lt;br /&gt;
	&amp;#039;DeepSkyBlue&amp;#039;:[0,191,255],&lt;br /&gt;
	&amp;#039;DimGray&amp;#039;:[105,105,105],&lt;br /&gt;
	&amp;#039;DimGrey&amp;#039;:[105,105,105],&lt;br /&gt;
	&amp;#039;DodgerBlue&amp;#039;:[30,144,255],&lt;br /&gt;
	&amp;#039;FireBrick&amp;#039;:[178,34,34],&lt;br /&gt;
	&amp;#039;FloralWhite&amp;#039;:[255,250,240],&lt;br /&gt;
	&amp;#039;ForestGreen&amp;#039;:[34,139,34],&lt;br /&gt;
	&amp;#039;Fuchsia&amp;#039;:[255,0,255],&lt;br /&gt;
	&amp;#039;Gainsboro&amp;#039;:[220,220,220],&lt;br /&gt;
	&amp;#039;GhostWhite&amp;#039;:[248,248,255],&lt;br /&gt;
	&amp;#039;Gold&amp;#039;:[255,215,0],&lt;br /&gt;
	&amp;#039;GoldenRod&amp;#039;:[218,165,32],&lt;br /&gt;
	&amp;#039;Gray&amp;#039;:[128,128,128],&lt;br /&gt;
	&amp;#039;Grey&amp;#039;:[128,128,128],&lt;br /&gt;
	&amp;#039;Green&amp;#039;:[0,128,0],&lt;br /&gt;
	&amp;#039;GreenYellow&amp;#039;:[173,255,47],&lt;br /&gt;
	&amp;#039;HoneyDew&amp;#039;:[240,255,240],&lt;br /&gt;
	&amp;#039;HotPink&amp;#039;:[255,105,180],&lt;br /&gt;
	&amp;#039;IndianRed &amp;#039;:[205,92,92],&lt;br /&gt;
	&amp;#039;Indigo &amp;#039;:[75,0,130],&lt;br /&gt;
	&amp;#039;Ivory&amp;#039;:[255,255,240],&lt;br /&gt;
	&amp;#039;Khaki&amp;#039;:[240,230,140],&lt;br /&gt;
	&amp;#039;Lavender&amp;#039;:[230,230,250],&lt;br /&gt;
	&amp;#039;LavenderBlush&amp;#039;:[255,240,245],&lt;br /&gt;
	&amp;#039;LawnGreen&amp;#039;:[124,252,0],&lt;br /&gt;
	&amp;#039;LemonChiffon&amp;#039;:[255,250,205],&lt;br /&gt;
	&amp;#039;LightBlue&amp;#039;:[173,216,230],&lt;br /&gt;
	&amp;#039;LightCoral&amp;#039;:[240,128,128],&lt;br /&gt;
	&amp;#039;LightCyan&amp;#039;:[224,255,255],&lt;br /&gt;
	&amp;#039;LightGoldenRodYellow&amp;#039;:[250,250,210],&lt;br /&gt;
	&amp;#039;LightGray&amp;#039;:[211,211,211],&lt;br /&gt;
	&amp;#039;LightGrey&amp;#039;:[211,211,211],&lt;br /&gt;
	&amp;#039;LightGreen&amp;#039;:[144,238,144],&lt;br /&gt;
	&amp;#039;LightPink&amp;#039;:[255,182,193],&lt;br /&gt;
	&amp;#039;LightSalmon&amp;#039;:[255,160,122],&lt;br /&gt;
	&amp;#039;LightSeaGreen&amp;#039;:[32,178,170],&lt;br /&gt;
	&amp;#039;LightSkyBlue&amp;#039;:[135,206,250],&lt;br /&gt;
	&amp;#039;LightSlateGray&amp;#039;:[119,136,153],&lt;br /&gt;
	&amp;#039;LightSlateGrey&amp;#039;:[119,136,153],&lt;br /&gt;
	&amp;#039;LightSteelBlue&amp;#039;:[176,196,222],&lt;br /&gt;
	&amp;#039;LightYellow&amp;#039;:[255,255,224],&lt;br /&gt;
	&amp;#039;Lime&amp;#039;:[0,255,0],&lt;br /&gt;
	&amp;#039;LimeGreen&amp;#039;:[50,205,50],&lt;br /&gt;
	&amp;#039;Linen&amp;#039;:[250,240,230],&lt;br /&gt;
	&amp;#039;Magenta&amp;#039;:[255,0,255],&lt;br /&gt;
	&amp;#039;Maroon&amp;#039;:[128,0,0],&lt;br /&gt;
	&amp;#039;MediumAquaMarine&amp;#039;:[102,205,170],&lt;br /&gt;
	&amp;#039;MediumBlue&amp;#039;:[0,0,205],&lt;br /&gt;
	&amp;#039;MediumOrchid&amp;#039;:[186,85,211],&lt;br /&gt;
	&amp;#039;MediumPurple&amp;#039;:[147,112,219],&lt;br /&gt;
	&amp;#039;MediumSeaGreen&amp;#039;:[60,179,113],&lt;br /&gt;
	&amp;#039;MediumSlateBlue&amp;#039;:[123,104,238],&lt;br /&gt;
	&amp;#039;MediumSpringGreen&amp;#039;:[0,250,154],&lt;br /&gt;
	&amp;#039;MediumTurquoise&amp;#039;:[72,209,204],&lt;br /&gt;
	&amp;#039;MediumVioletRed&amp;#039;:[199,21,133],&lt;br /&gt;
	&amp;#039;MidnightBlue&amp;#039;:[25,25,112],&lt;br /&gt;
	&amp;#039;MintCream&amp;#039;:[245,255,250],&lt;br /&gt;
	&amp;#039;MistyRose&amp;#039;:[255,228,225],&lt;br /&gt;
	&amp;#039;Moccasin&amp;#039;:[255,228,181],&lt;br /&gt;
	&amp;#039;NavajoWhite&amp;#039;:[255,222,173],&lt;br /&gt;
	&amp;#039;Navy&amp;#039;:[0,0,128],&lt;br /&gt;
	&amp;#039;OldLace&amp;#039;:[253,245,230],&lt;br /&gt;
	&amp;#039;Olive&amp;#039;:[128,128,0],&lt;br /&gt;
	&amp;#039;OliveDrab&amp;#039;:[107,142,35],&lt;br /&gt;
	&amp;#039;Orange&amp;#039;:[255,165,0],&lt;br /&gt;
	&amp;#039;OrangeRed&amp;#039;:[255,69,0],&lt;br /&gt;
	&amp;#039;Orchid&amp;#039;:[218,112,214],&lt;br /&gt;
	&amp;#039;PaleGoldenRod&amp;#039;:[238,232,170],&lt;br /&gt;
	&amp;#039;PaleGreen&amp;#039;:[152,251,152],&lt;br /&gt;
	&amp;#039;PaleTurquoise&amp;#039;:[175,238,238],&lt;br /&gt;
	&amp;#039;PaleVioletRed&amp;#039;:[219,112,147],&lt;br /&gt;
	&amp;#039;PapayaWhip&amp;#039;:[255,239,213],&lt;br /&gt;
	&amp;#039;PeachPuff&amp;#039;:[255,218,185],&lt;br /&gt;
	&amp;#039;Peru&amp;#039;:[205,133,63],&lt;br /&gt;
	&amp;#039;Pink&amp;#039;:[255,192,203],&lt;br /&gt;
	&amp;#039;Plum&amp;#039;:[221,160,221],&lt;br /&gt;
	&amp;#039;PowderBlue&amp;#039;:[176,224,230],&lt;br /&gt;
	&amp;#039;Purple&amp;#039;:[128,0,128],&lt;br /&gt;
	&amp;#039;RebeccaPurple&amp;#039;:[102,51,153],&lt;br /&gt;
	&amp;#039;Red&amp;#039;:[255,0,0],&lt;br /&gt;
	&amp;#039;RosyBrown&amp;#039;:[188,143,143],&lt;br /&gt;
	&amp;#039;RoyalBlue&amp;#039;:[65,105,225],&lt;br /&gt;
	&amp;#039;SaddleBrown&amp;#039;:[139,69,19],&lt;br /&gt;
	&amp;#039;Salmon&amp;#039;:[250,128,114],&lt;br /&gt;
	&amp;#039;SandyBrown&amp;#039;:[244,164,96],&lt;br /&gt;
	&amp;#039;SeaGreen&amp;#039;:[46,139,87],&lt;br /&gt;
	&amp;#039;SeaShell&amp;#039;:[255,245,238],&lt;br /&gt;
	&amp;#039;Sienna&amp;#039;:[160,82,45],&lt;br /&gt;
	&amp;#039;Silver&amp;#039;:[192,192,192],&lt;br /&gt;
	&amp;#039;SkyBlue&amp;#039;:[135,206,235],&lt;br /&gt;
	&amp;#039;SlateBlue&amp;#039;:[106,90,205],&lt;br /&gt;
	&amp;#039;SlateGray&amp;#039;:[112,128,144],&lt;br /&gt;
	&amp;#039;SlateGrey&amp;#039;:[112,128,144],&lt;br /&gt;
	&amp;#039;Snow&amp;#039;:[255,250,250],&lt;br /&gt;
	&amp;#039;SpringGreen&amp;#039;:[0,255,127],&lt;br /&gt;
	&amp;#039;SteelBlue&amp;#039;:[70,130,180],&lt;br /&gt;
	&amp;#039;Tan&amp;#039;:[210,180,140],&lt;br /&gt;
	&amp;#039;Teal&amp;#039;:[0,128,128],&lt;br /&gt;
	&amp;#039;Thistle&amp;#039;:[216,191,216],&lt;br /&gt;
	&amp;#039;Tomato&amp;#039;:[255,99,71],&lt;br /&gt;
	&amp;#039;Turquoise&amp;#039;:[64,224,208],&lt;br /&gt;
	&amp;#039;Violet&amp;#039;:[238,130,238],&lt;br /&gt;
	&amp;#039;Wheat&amp;#039;:[245,222,179],&lt;br /&gt;
	&amp;#039;White&amp;#039;:[255,255,255],&lt;br /&gt;
	&amp;#039;WhiteSmoke&amp;#039;:[245,245,245],&lt;br /&gt;
	&amp;#039;Yellow&amp;#039;:[255,255,0],&lt;br /&gt;
	&amp;#039;YellowGreen&amp;#039;:[154,205,50]&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	testColor = [25,245,1]&lt;br /&gt;
	closestColor = &amp;#039;None&amp;#039;&lt;br /&gt;
	diff = 99999999&lt;br /&gt;
	for color in colors:&lt;br /&gt;
		d = pow(colors[color][0] - r,2) &lt;br /&gt;
		d = d + pow(colors[color][1] - g,2)&lt;br /&gt;
		d = d + pow(colors[color][2] - b,2)&lt;br /&gt;
		if min(diff,d) == d:&lt;br /&gt;
			diff = d&lt;br /&gt;
			closestColor = color&lt;br /&gt;
&lt;br /&gt;
	return closestColor&lt;br /&gt;
&lt;br /&gt;
print(colorName(254,50,23))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== List all objs in directory and subdirectories to text file ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os &lt;br /&gt;
dir_path = os.path.dirname(os.path.realpath(__file__))&lt;br /&gt;
listfilename = &amp;#039;objs_list.txt&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
objlist = []&lt;br /&gt;
for root, dirs, files in os.walk(dir_path):&lt;br /&gt;
    for file in files:&lt;br /&gt;
        if file.lower().endswith(&amp;#039;.obj&amp;#039;):&lt;br /&gt;
        	objlist.append(os.path.join(root, file))&lt;br /&gt;
   &lt;br /&gt;
print(&amp;#039;Writing &amp;#039;+str(len(objlist))+&amp;#039;to &amp;#039;+dir_path+&amp;#039;/&amp;#039;+listfilename)&lt;br /&gt;
&lt;br /&gt;
with open(dir_path+&amp;#039;/&amp;#039;+listfilename, &amp;#039;w&amp;#039;) as file_handler:&lt;br /&gt;
    for item in objlist:&lt;br /&gt;
        file_handler.write(&amp;quot;{}\n&amp;quot;.format(item))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== elections 2017 scraping ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# # -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
from urllib import urlopen&lt;br /&gt;
import re&lt;br /&gt;
import urlparse&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
baseUrl = &amp;#039;http://elections.interieur.gouv.fr/presidentielle-2017/&amp;#039;&lt;br /&gt;
townUrlsFile = os.path.dirname(os.path.realpath(__file__))+&amp;#039;/town_urls.txt&amp;#039;&lt;br /&gt;
#print townUrlsFile&lt;br /&gt;
&lt;br /&gt;
def getTownUrlsList():&lt;br /&gt;
  with open(townUrlsFile, &amp;#039;w&amp;#039;) as fid:&lt;br /&gt;
    count = 0&lt;br /&gt;
    townlist = []&lt;br /&gt;
    #get the page&lt;br /&gt;
    page = urlopen(baseUrl+&amp;#039;index.html&amp;#039;)&lt;br /&gt;
    page_content_HTML = page.read()&lt;br /&gt;
&lt;br /&gt;
    #grab the list&lt;br /&gt;
    start = &amp;#039;selected&amp;gt;Choisir un département&amp;lt;/option&amp;gt;&amp;#039;&lt;br /&gt;
    end = &amp;#039;&amp;lt;/select&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p class=&amp;quot;clic-carte&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
    departement_HTML = (page_content_HTML.split(start))[1].split(end)[0]&lt;br /&gt;
&lt;br /&gt;
    #iterate through departments (options)&lt;br /&gt;
    options = re.findall(r&amp;#039;&amp;lt;option value=&amp;quot;(.*)&amp;quot;&amp;gt;(.*)&amp;lt;/option&amp;gt;&amp;#039;,departement_HTML,re.M)&lt;br /&gt;
    for option in options:&lt;br /&gt;
      &lt;br /&gt;
      #get the page&lt;br /&gt;
      page = urlopen(baseUrl+option[0])&lt;br /&gt;
      page_content_HTML = page.read()&lt;br /&gt;
      &lt;br /&gt;
      #grab the list of town letters&lt;br /&gt;
      start = &amp;#039;initiale&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&amp;#039;&lt;br /&gt;
      end = &amp;#039;\xa0\n\t\t\t&amp;lt;hr&amp;gt;\n&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;\n&amp;lt;div class=&amp;quot;row-fluid pub-index-communes&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
      town_letters_HTML = (page_content_HTML.split(start))[1].split(end)[0]&lt;br /&gt;
      &lt;br /&gt;
      #iterate through town letters (A = all towns with A in this departement etc...)&lt;br /&gt;
      town_letters = re.findall(r&amp;#039;&amp;lt;a href=&amp;quot;../../(.*)&amp;quot;&amp;gt;(.*)&amp;lt;/a&amp;gt;&amp;#039;,town_letters_HTML,re.M)&lt;br /&gt;
      for town_letter in town_letters:&lt;br /&gt;
	page = urlopen(baseUrl+town_letter[0])&lt;br /&gt;
	page_content_HTML = page.read()&lt;br /&gt;
	&lt;br /&gt;
      &lt;br /&gt;
	#grab the list of towns&lt;br /&gt;
	start = &amp;#039;tableau-communes&amp;quot;&amp;gt;&amp;lt;tbody&amp;gt;&amp;#039;&lt;br /&gt;
	end = &amp;#039;&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;\n&amp;lt;br&amp;gt;\n&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;\n&amp;lt;/div&amp;gt;\n&amp;lt;div class=&amp;quot;row-fluid pub-bas&amp;quot;&amp;gt;\n&amp;lt;div class=&amp;quot;span5&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
	towns_HTML = (page_content_HTML.split(start))[1].split(end)[0]&lt;br /&gt;
	&lt;br /&gt;
	#print towns_HTML&lt;br /&gt;
	towns = re.findall(r&amp;#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;../../(.*)&amp;quot;&amp;gt;(.*)&amp;lt;/a&amp;gt;&amp;#039;,towns_HTML,re.M)      &lt;br /&gt;
	#iterate through towns&lt;br /&gt;
	for town in towns:&lt;br /&gt;
	  currentTown = option[1]+&amp;#039;|&amp;#039;+town[1]+&amp;#039;|&amp;#039;+baseUrl+town[0]&lt;br /&gt;
	  #townlist.append()&lt;br /&gt;
	  fid.write(currentTown+&amp;#039;\n&amp;#039;)&lt;br /&gt;
	  count = count + 1&lt;br /&gt;
	print count&lt;br /&gt;
&lt;br /&gt;
  #return townlist&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#fid.write(&amp;#039;\n&amp;#039;.join(getTownUrlsList()))&lt;br /&gt;
#fid.close()  &lt;br /&gt;
getTownUrlsList()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# # -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
from urllib import urlopen&lt;br /&gt;
import re&lt;br /&gt;
import urlparse&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
page = urlopen(&amp;#039;http://elections.interieur.gouv.fr/presidentielle-2017/011/075/index.html&amp;#039;)&lt;br /&gt;
page_content_HTML = page.read()&lt;br /&gt;
&lt;br /&gt;
#grab the list of town letters&lt;br /&gt;
if &amp;#039;arrondissement&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&amp;#039; in page_content_HTML:&lt;br /&gt;
  start = &amp;#039;arrondissement&amp;lt;/i&amp;gt;&amp;lt;/p&amp;gt;&amp;#039;&lt;br /&gt;
  end = &amp;#039;&amp;lt;div class=&amp;quot;row-fluid pub-resultats-entete&amp;#039;&lt;br /&gt;
else:&lt;br /&gt;
  start = &amp;#039;initiale&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&amp;#039;&lt;br /&gt;
  end = &amp;#039;\xa0\n\t\t\t&amp;lt;hr&amp;gt;\n&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;\n&amp;lt;div class=&amp;quot;row-fluid pub-index-communes&amp;quot;&amp;gt;&amp;#039;&lt;br /&gt;
town_letters_HTML = (page_content_HTML.split(start))[1].split(end)[0]&lt;br /&gt;
&lt;br /&gt;
for arrondissement in town_letters_HTML.split(&amp;#039;&amp;lt;/a&amp;gt; &amp;lt;a href&amp;#039;):&lt;br /&gt;
  print arrondissement.split(&amp;#039;../../&amp;#039;)[1].split(&amp;#039;&amp;quot;&amp;gt;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
#town_letters = re.findall(r&amp;#039;&amp;lt;a href=&amp;quot;../../(.*)&amp;quot;&amp;gt;(.*)&amp;lt;/a&amp;gt;&amp;#039;,town_letters_HTML,re.M)&lt;br /&gt;
#for town_letter in town_letters:&lt;br /&gt;
  #print town_letter&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== colorlovers color scraping ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# # -*- coding: utf-8 -*-&lt;br /&gt;
&lt;br /&gt;
from urllib.request import urlopen&lt;br /&gt;
import os&lt;br /&gt;
import codecs&lt;br /&gt;
import math&lt;br /&gt;
import time&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def grabCL(n):&lt;br /&gt;
	url = &amp;quot;http://www.colourlovers.com/ajax/browse-palettes/_page_&amp;quot;+str(n)+&amp;quot;?section=most-loved&amp;amp;period=all-time&amp;amp;view=meta&amp;amp;channelID=0&amp;quot;&lt;br /&gt;
	page = urlopen(url)&lt;br /&gt;
	page_content = page.read()&lt;br /&gt;
	&lt;br /&gt;
	with open(&amp;#039;Z:/BERNIE/vvvv/palettes/cl/output&amp;#039;+str(n)+&amp;#039;.txt&amp;#039;, &amp;#039;w&amp;#039;) as fid:&lt;br /&gt;
		fid.write(str(page_content))&lt;br /&gt;
		fid.close()&lt;br /&gt;
&lt;br /&gt;
def parseCL(n):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039; disgusting code to parse webpage because i can&amp;#039;t figure out beautifulsoup &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
	output = &amp;quot;&amp;quot;&lt;br /&gt;
	titles = []&lt;br /&gt;
	with open(&amp;#039;Z:/BERNIE/vvvv/palettes/cl/output&amp;#039;+str(n)+&amp;#039;.txt&amp;#039;, &amp;#039;r&amp;#039;) as fid:&lt;br /&gt;
		for line in fid:&lt;br /&gt;
			tokens = line.split(&amp;quot;&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;&amp;lt;div class=\&amp;quot;left username\&amp;quot;&amp;quot;)&lt;br /&gt;
			p = len(tokens)&lt;br /&gt;
			for i in range(p):&lt;br /&gt;
&lt;br /&gt;
				tokensTitle = tokens[i].split(&amp;quot;\&amp;quot;&amp;gt;&amp;quot;)&lt;br /&gt;
				titles.append(tokensTitle[-1])&lt;br /&gt;
&lt;br /&gt;
			#get colors&lt;br /&gt;
			lines = line.split(&amp;quot;&amp;lt;span class=\&amp;quot;c\&amp;quot; style=\&amp;quot;width: &amp;quot;)&lt;br /&gt;
&lt;br /&gt;
			j = 1&lt;br /&gt;
			while j&amp;lt;len(lines):&lt;br /&gt;
				&lt;br /&gt;
				#print(titles[(int((j-1)/10))])&lt;br /&gt;
				output += &amp;quot;\n&amp;quot;+str(int((j-1)/10)+(n-1)*15)+&amp;quot; &amp;quot;+titles[(int((j-1)/10))].replace(&amp;quot;\\&amp;quot;, &amp;quot;&amp;quot;)+&amp;quot;\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
				for k in range(5):&lt;br /&gt;
					curline = lines[j+k]&lt;br /&gt;
					widthTokens = curline.split(&amp;quot;px; height: 50px;&amp;quot;)&lt;br /&gt;
					width = widthTokens[0]&lt;br /&gt;
					colorTokens = curline.split(&amp;quot;;\&amp;quot;&amp;gt;&amp;lt;span class=\&amp;quot;s\&amp;quot; style=\&amp;quot;margin-top: 45px;\&amp;quot;&amp;gt;&amp;quot;)&lt;br /&gt;
					color = colorTokens[0][-7:]&lt;br /&gt;
					&lt;br /&gt;
					colorString = color+&amp;quot; &amp;quot;+str(float(width)/560)[:6]&lt;br /&gt;
					&lt;br /&gt;
					#print(colorString)&lt;br /&gt;
					output += colorString+&amp;quot;\n&amp;quot;&lt;br /&gt;
				#output += &amp;quot;\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
				j = j + 10&lt;br /&gt;
	return output&lt;br /&gt;
	&lt;br /&gt;
def scrapeCL(startPage, endPage, waitInSeconds):&lt;br /&gt;
	for i in range(startPage, endPage+1):&lt;br /&gt;
		grabCL(i)&lt;br /&gt;
		out = parseCL(i)&lt;br /&gt;
		#print(str(i))&lt;br /&gt;
		with open(&amp;quot;Z:/BERNIE/vvvv/palettes/colors_cl.txt&amp;quot;, &amp;quot;a&amp;quot;) as myfile:&lt;br /&gt;
			myfile.write(out)&lt;br /&gt;
		print(&amp;quot;Page &amp;quot;+str(i)+&amp;quot; grabbed... &amp;quot;+str(i*15)+ &amp;quot; records&amp;quot;)&lt;br /&gt;
		time.sleep(waitInSeconds)&lt;br /&gt;
&lt;br /&gt;
scrapeCL(1,270,1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Raytracer===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from PIL import Image&lt;br /&gt;
from math import sqrt&lt;br /&gt;
&lt;br /&gt;
imwidth = 640&lt;br /&gt;
imheight = 480&lt;br /&gt;
im = Image.new(&amp;quot;RGB&amp;quot;,(imwidth,imheight),&amp;quot;black&amp;quot;)&lt;br /&gt;
#im = Image.open(&amp;quot;lolmonkey.jpg&amp;quot;)&lt;br /&gt;
#im.show()&lt;br /&gt;
#im.save(&amp;quot;hellwrld.png&amp;quot;,&amp;quot;PNG&amp;quot;)&lt;br /&gt;
#NUMPY&lt;br /&gt;
&lt;br /&gt;
mysphere = [[0,0,0],200]&lt;br /&gt;
#myray = [[0,0,10],[0,0,-1]]&lt;br /&gt;
&lt;br /&gt;
def intersectRaySphere(ray,sphere):&lt;br /&gt;
	A = ray[1][0]*ray[1][0] + ray[1][1]*ray[1][1] + ray[1][2]*ray[1][2]&lt;br /&gt;
	B = 2.0 * (ray[1][0]*(ray[0][0]-sphere[0][0]) + ray[1][1]*(ray[0][1]-sphere[0][1]) + ray[1][2]*(ray[0][2]-sphere[0][2]))&lt;br /&gt;
	C = (ray[0][0]-sphere[0][0])*(ray[0][0]-sphere[0][0]) + (ray[0][1]-sphere[0][1])*(ray[0][1]-sphere[0][1]) + (ray[0][2]-sphere[0][2])*(ray[0][2]-sphere[0][2]) - sphere[1]&lt;br /&gt;
&lt;br /&gt;
	delta = B*B - 4.0*A*C&lt;br /&gt;
	&lt;br /&gt;
	results = []&lt;br /&gt;
	if(delta==0):&lt;br /&gt;
		results.append(-B/(2.0*A))&lt;br /&gt;
	if(delta&amp;gt;0):&lt;br /&gt;
		results.append((-B+(sqrt(delta)))/(2.0*A))&lt;br /&gt;
		results.append((-B-(sqrt(delta)))/(2.0*A))&lt;br /&gt;
	&lt;br /&gt;
	points = []&lt;br /&gt;
	for t in results:&lt;br /&gt;
		points.append([ray[0][0] + t*ray[1][0], ray[0][1] + t*ray[1][1],	ray[0][2] + t*ray[1][2]])&lt;br /&gt;
		&lt;br /&gt;
	if(len(points)==2):&lt;br /&gt;
		if(points[0]&amp;gt;points[1]):&lt;br /&gt;
			points = points[1]&lt;br /&gt;
		else:&lt;br /&gt;
			points = points[0]&lt;br /&gt;
	return points&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
def mag(vec):&lt;br /&gt;
	return sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);&lt;br /&gt;
&lt;br /&gt;
def cross(vec1,vec2):&lt;br /&gt;
	return [vec1[1]*vec2[2]-vec1[2]*vec2[1], vec1[2]*vec2[0]-vec1[0]*vec2[2], vec1[0]*vec2[1]-vec1[1]*vec2[0]]&lt;br /&gt;
	&lt;br /&gt;
def normalize(vec):&lt;br /&gt;
	return [vec[0]/mag(vec),vec[1]/mag(vec),vec[2]/mag(vec)]&lt;br /&gt;
	&lt;br /&gt;
def dot(vec1,vec2):&lt;br /&gt;
	return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]&lt;br /&gt;
&lt;br /&gt;
def pixToPoint(i,j,width,height,xPixSize,yPixSize,center,u,v):	&lt;br /&gt;
	cu = (float(2*i+1)/(2*xPixSize)-.5)*width&lt;br /&gt;
	cv = (float(2*j+1)/(2*yPixSize)-.5)*height&lt;br /&gt;
	x = center[0]+cu*u[0]+cv*v[0]&lt;br /&gt;
	y = center[1]+cu*u[1]+cv*v[1]&lt;br /&gt;
	z = center[2]+cu*u[2]+cv*v[2]	&lt;br /&gt;
	#print [x,y,z]&lt;br /&gt;
	#print [i,j]&lt;br /&gt;
	#print [cu,cv]&lt;br /&gt;
	return [x,y,z]&lt;br /&gt;
&lt;br /&gt;
lookat = [0,0,0]	&lt;br /&gt;
eye = [100,100,100]&lt;br /&gt;
f = 10&lt;br /&gt;
upvector = [0,1,0] &lt;br /&gt;
viewplaneW = imwidth/2&lt;br /&gt;
viewplaneH = imheight/2&lt;br /&gt;
&lt;br /&gt;
EA = [lookat[0]-eye[0],lookat[1]-eye[1],lookat[2]-eye[2]]&lt;br /&gt;
lenEA = mag(EA)&lt;br /&gt;
normEA = [EA[0]/lenEA,EA[1]/lenEA,EA[2]/lenEA]&lt;br /&gt;
center = [EA[0]+normEA[0]*f, EA[1]+normEA[1]*f, EA[2]+normEA[2]*f]&lt;br /&gt;
&lt;br /&gt;
w = normEA&lt;br /&gt;
u = normalize(cross(upvector,w))&lt;br /&gt;
v = normalize(cross(u,w))&lt;br /&gt;
#print(cross([1,0,0],[0,1,0]))&lt;br /&gt;
&lt;br /&gt;
light = [0,0,100]&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
#print intersectRaySphere(myray,mysphere)&lt;br /&gt;
&lt;br /&gt;
for x in range(imwidth):&lt;br /&gt;
	for y in range(imheight):&lt;br /&gt;
		#myray = [[x,y,-10],[0,0,1]]&lt;br /&gt;
		point = pixToPoint(x,y,imwidth,imheight,viewplaneW,viewplaneH,center,u,v)&lt;br /&gt;
		ray = [point,[point[0]-eye[0] , point[1]-eye[1] , point[2]-eye[2]]]&lt;br /&gt;
		if(len(intersectRaySphere(ray,mysphere))):&lt;br /&gt;
			n = normalize([point[0]-mysphere[0][0], point[1]-mysphere[0][1],point[2]-mysphere[0][2]])&lt;br /&gt;
			i = normalize([light[0]-point[0], light[1]-point[1],light[2]-point[2]])&lt;br /&gt;
			costheta = dot(n,i)&lt;br /&gt;
			#if(costheta&amp;lt;0):&lt;br /&gt;
			#	costheta=0&lt;br /&gt;
			#color = int(costheta*255)&lt;br /&gt;
&lt;br /&gt;
			#print n[0]&lt;br /&gt;
			#print costheta&lt;br /&gt;
			#print color&lt;br /&gt;
			im.putpixel((x,y),(int(-n[0]*255),int(-n[1]*255),int(-n[2]*255)))&lt;br /&gt;
			#im.putpixel((x,y),(255,255,0))&lt;br /&gt;
#im.show()&lt;br /&gt;
im.save(&amp;quot;sphr_&amp;quot;+str(mysphere[1])+&amp;quot;.png&amp;quot;,&amp;quot;PNG&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===File Handling Dandelion===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os&lt;br /&gt;
import re&lt;br /&gt;
def listdirs(folder):&lt;br /&gt;
    return [d for d in os.listdir(folder) if os.path.isdir(os.path.join(folder, d))]&lt;br /&gt;
&lt;br /&gt;
paths = &amp;#039;N:/01_OUT&amp;#039;&lt;br /&gt;
&lt;br /&gt;
pattern = &amp;#039;GB\d+_SC\d+.*_T\d+&amp;#039;&lt;br /&gt;
#text = &amp;#039;GB45_SC34_T3&amp;#039;&lt;br /&gt;
#match = re.search(pattern, text)&lt;br /&gt;
#print match&lt;br /&gt;
f = open(&amp;#039;N:/01_OUT/summary.html&amp;#039;, &amp;#039;w&amp;#039;)&lt;br /&gt;
#f.write(&amp;#039;0123456789abcdef&amp;#039;)&lt;br /&gt;
count = 0&lt;br /&gt;
for dir in listdirs(paths):&lt;br /&gt;
	f.write(&amp;quot;&amp;lt;hr&amp;gt;\n\n&amp;lt;/br&amp;gt;&amp;lt;/br&amp;gt;&amp;quot;+dir+&amp;quot;&amp;lt;/br&amp;gt;&amp;quot;)&lt;br /&gt;
	subdir = listdirs(paths+&amp;quot;/&amp;quot;+dir)&lt;br /&gt;
	for takes in subdir:&lt;br /&gt;
		tk = listdirs(paths+&amp;quot;/&amp;quot;+dir+&amp;quot;/&amp;quot;+takes)&lt;br /&gt;
		#if(len(tk)&amp;gt;4):&lt;br /&gt;
			#f.write(&amp;quot;\n&amp;quot;+paths+&amp;quot;/&amp;quot;+dir+&amp;quot;/&amp;quot;+takes)&lt;br /&gt;
		f.write(&amp;quot;\n&amp;quot;+paths+&amp;quot;/&amp;quot;+dir+&amp;quot;/&amp;quot;+takes+&amp;quot;&amp;lt;/br&amp;gt;&amp;quot;)&lt;br /&gt;
		for take in tk:&lt;br /&gt;
			match = re.search(pattern,take)&lt;br /&gt;
			if(match != &amp;quot;None&amp;quot;):&lt;br /&gt;
				count+=1&lt;br /&gt;
				if(count % 2 == 1):&lt;br /&gt;
					c = &amp;quot;#fbfbfb&amp;quot;;&lt;br /&gt;
				else:&lt;br /&gt;
					c = &amp;quot;#eeeeee&amp;quot;;&lt;br /&gt;
				f.write(&amp;quot;\n&amp;lt;div style=&amp;#039;background-color:&amp;quot;+c+&amp;quot;;&amp;#039;&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;input type=checkbox name=\&amp;quot;&amp;quot;+(paths+&amp;quot;/&amp;quot;+dir+&amp;quot;/&amp;quot;+takes)+&amp;quot;\&amp;quot; CHECKED&amp;gt;&amp;quot;+take+&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;)&lt;br /&gt;
				print take&lt;br /&gt;
				#print takes+&amp;quot;: &amp;quot;+str(len(tk))&lt;br /&gt;
				#print take+&amp;quot; (&amp;quot;+str(len(tk))+&amp;quot;)&amp;quot;&lt;br /&gt;
f.close();&lt;br /&gt;
raw_input(&amp;quot;-&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#for dir in os.listdir(path):&lt;br /&gt;
#	for subdir in os.listdir(path+&amp;quot;/&amp;quot;+dir):&lt;br /&gt;
#		takes = os.listdir(path+&amp;quot;/&amp;quot;+dir+&amp;quot;/&amp;quot;+subdir)&lt;br /&gt;
#		directories=[d for d in os.listdir(path+&amp;quot;/&amp;quot;+dir+&amp;quot;/&amp;quot;+subdir) if os.path.isdir(d)]&lt;br /&gt;
#		#print subdir+&amp;quot;:&amp;quot;+str(len(takes))&lt;br /&gt;
#		print directories&lt;br /&gt;
#&lt;br /&gt;
#raw_input(&amp;quot;Press ENTER to exit&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Stupid_Questions&amp;diff=882</id>
		<title>Houdini Stupid Questions</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Stupid_Questions&amp;diff=882"/>
		<updated>2025-11-13T13:39:36Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* How do I slide a curve along a curve ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes I struggle with stupid stuff, I&amp;#039;ll answer to my own questions here. If you&amp;#039;re here, you were probably brought in by Google!&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m still learning as I go so if you see something that&amp;#039;s downright wrong or silly, email me! bernie at berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
Jeez I need to sort these&lt;br /&gt;
&lt;br /&gt;
==== How do I transfer attributes without attribtransfer ? ====&lt;br /&gt;
Sometimes attributes transfer just... Doesn&amp;#039;t work right. I use good&amp;#039;ole xyzdistprimuv:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//transfers UVs from source 1 to source 0 using closest surface UV&lt;br /&gt;
i@prim;&lt;br /&gt;
v@uv;&lt;br /&gt;
xyzdist(1, @P, @prim,@uv);&lt;br /&gt;
@uv = primuv(1,&amp;quot;uv&amp;quot;,i@prim,@uv);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== How do I slide a curve along a curve ? ==== &lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/t9sm2Uj.gif https://i.imgur.com/ZXdtLOy.jpeg&lt;br /&gt;
&lt;br /&gt;
Working on snakes ? If you use carve, you&amp;#039;ll notice you end up with weird bits where the tips &amp;#039;pop&amp;#039;, what you would like is to slide the curve along another curve, but smoothly. In comes xyzdist and primuv (check out [https://www.toadstorm.com/blog/?p=465 Toadstorm&amp;#039;s page]). The advantage is that you will get a real, smooth sliding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//input 0 is the carved/small curve&lt;br /&gt;
//input 1 is the full, longer curve&lt;br /&gt;
&lt;br /&gt;
i@prim;&lt;br /&gt;
v@uv;&lt;br /&gt;
//figure out where i am on the original curve&lt;br /&gt;
xyzdist(1, @P, @prim,@uv);&lt;br /&gt;
&lt;br /&gt;
//animate the uv.x position, blissfully ignore what happens when we arrive at the end of curve&lt;br /&gt;
@uv.x += (@Time*.1)%1;&lt;br /&gt;
&lt;br /&gt;
//move along&lt;br /&gt;
@P = primuv(1,&amp;quot;P&amp;quot;,i@prim,@uv);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== How can I carve curves with an attribute ? ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/U56M7Ho.png&lt;br /&gt;
&lt;br /&gt;
The carve sop works great, but afaik you can&amp;#039;t use a local variable to carve multiple curves differently short of using a foreach loop. &lt;br /&gt;
&lt;br /&gt;
Thankfully (in more recent versions of houdini?), you can use a prebuilt function found in &amp;#039;&amp;#039;&amp;#039;$HFS/vex/include/groom.h&amp;#039;&amp;#039;&amp;#039;: &amp;#039;&amp;#039;void adjustPrimLength(const int geo, prim; const float currentlength, targetlength)&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In practice it&amp;#039;s fairly easy to use!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;groom.h&amp;gt;&lt;br /&gt;
float newperim = @perimeter*sin(@Time+@primnum/3.0)+1; //notice the 3.0 cause&amp;#039; i&amp;#039;m lazy to cast floats&lt;br /&gt;
adjustPrimLength(0, @primnum, @perimeter, newperim);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Get the end/start points of primitive curves====&lt;br /&gt;
You can use curveu and fetch 0.0 or 1.0 or use this one-liner&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#in a point wrangle&lt;br /&gt;
i@group_tip = vertexprimindex(0,@vtxnum) == 0;&lt;br /&gt;
i@group_end = vertexprimindex(0,@vtxnum) == primvertexcount(0,vertexprim(0,@vtxnum))-1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== How do I snap objects to objects ====&lt;br /&gt;
&lt;br /&gt;
Works in obj mode, not sop mode, use &amp;#039;;&amp;#039; (Orientatio Picking)&lt;br /&gt;
&lt;br /&gt;
Great tutorial here (hipflask) https://www.youtube.com/watch?v=Zh6Q6r9LQlA&lt;br /&gt;
&lt;br /&gt;
==== Shifting Keyframes in Animation Editor like in Maya ====&lt;br /&gt;
It&amp;#039;s a small frustration but to shift frames to later you can write &amp;#039;&amp;#039;&amp;#039;+10&amp;#039;&amp;#039;&amp;#039; frames&lt;br /&gt;
But if you want to shift frames to an earlier time you have to use &amp;#039;&amp;#039;&amp;#039;+-10&amp;#039;&amp;#039;&amp;#039; not -10&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/Dm05N8o.png&lt;br /&gt;
&lt;br /&gt;
==== How do I scale a packed primitive without unpacking it ? ====&lt;br /&gt;
&lt;br /&gt;
Code stolen from https://brendandawes.com/blog/scaling-packed-primitives-with-vex-in-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector scale = ch(&amp;#039;newScale&amp;#039;);&lt;br /&gt;
matrix3 trn = primintrinsic(0, &amp;quot;transform&amp;quot;, @primnum);&lt;br /&gt;
matrix scalem = maketransform(0, 0, {0,0,0}, {0,0,0}, scale, @P);&lt;br /&gt;
trn *= matrix3(scalem);&lt;br /&gt;
setprimintrinsic(0, &amp;quot;transform&amp;quot;, @primnum, trn);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== How do I embed textures in my Houdini .hip file ? ====&lt;br /&gt;
&lt;br /&gt;
I don&amp;#039;t really recommend it, as like the stash SOP it will increase your file size, but sometimes you want no dependencies. I know Rich Lord did something similar in his [https://www.richlord.com/getmyfiles nifty example files].&lt;br /&gt;
&lt;br /&gt;
Anyhow, the trick I used (there might be an easier method, IDK), is to add null Subnetwork node to my scene then right-click &amp;gt; &amp;#039;&amp;#039;&amp;#039;Digital Asset &amp;gt; Create New&amp;#039;&amp;#039;&amp;#039; but &amp;#039;&amp;#039;embed&amp;#039;&amp;#039; it in the current hip file instead of saving it externally.&lt;br /&gt;
&lt;br /&gt;
I right click the asset &amp;#039;&amp;#039;: Type Properties &amp;gt; Extra Files &amp;gt; Add Textures Files&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Then in my SOP nodes or other contexts (in my case a Network Image with a random cat), refer to the image by going to the internal HDA path; in the file browser I got to: &amp;#039;&amp;#039;&amp;#039;opdef:/Object/myEmbeddedHdaName&amp;#039;&amp;#039;&amp;#039; and it shows the images:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/8yxaDXS.png&lt;br /&gt;
&lt;br /&gt;
==== How can I rotate a velocity volume/VDB and have it work for advection ? ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/e82MuuN.gif&lt;br /&gt;
&lt;br /&gt;
Thought it would work straight outta the box but I had an issue with a rotated volume, so here&amp;#039;s how I fixed it &amp;#039;&amp;#039;before&amp;#039;&amp;#039; doing my transform:&lt;br /&gt;
&lt;br /&gt;
(I&amp;#039;m not sure how it works with a normal volume), if you have a vdb you can either use the &amp;#039;&amp;#039;&amp;#039;Primitive Properties&amp;#039;&amp;#039;&amp;#039; then Volumes&amp;gt;VDB&amp;gt;Vector Type set to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Displacement/Velocity/Acceleration&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;, or if you&amp;#039;re merging vel.* to a single vec3s volume using &amp;#039;&amp;#039;&amp;#039;VDB Vector Merge&amp;#039;&amp;#039;&amp;#039; make sure you set it to &amp;#039;&amp;#039;Displacement/Velocity/Acceleration&amp;#039;&amp;#039; here too. You can also use the &amp;#039;&amp;#039;&amp;#039;Labs Transform Properties&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Then do your rotate (or scale) and you will have rotated and scaled velocites (in my example, I&amp;#039;m advecting particles with VDB).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;edit:&amp;#039;&amp;#039; as usual Matt Estela touches on this topic (attribute types) on [https://www.tokeru.com/cgwiki/index.php?title=JoyOfVex17#Transform_operations_and_attribute_types Tokeru]:&lt;br /&gt;
&lt;br /&gt;
==== How do I work on two objects that don&amp;#039;t have the same number of points/prims ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/S9K9I9y.png&lt;br /&gt;
&lt;br /&gt;
Happens to me a lot, you are used to using @ptnum or @elemnum to work on several streams of the same initial object, but in the process you&amp;#039;ve deleted faces or points and code doesn&amp;#039;t work. Thankfully idtoprim and point are fast:&lt;br /&gt;
&lt;br /&gt;
Prior to branching your nodes, set &amp;lt;code&amp;gt;i@id=@elemnum&amp;lt;/code&amp;gt;, then use [https://www.sidefx.com/docs/houdini/vex/functions/idtoprim.html idtroprim]&lt;br /&gt;
&lt;br /&gt;
In the screenshot above, I run code to color my faces if they&amp;#039;ve been deleted in the second incoming stream&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(idtoprim(1,@id)==-1){&lt;br /&gt;
    @Cd *= 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== How do I export a tracked camera from After Effects to Houdini ? ====&lt;br /&gt;
&amp;#039;&amp;#039;edit:&amp;#039;&amp;#039; kinda broken, will try to work on something if i have time&lt;br /&gt;
&lt;br /&gt;
Use this code by Howiem:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
H &amp;gt; AE: https://gist.github.com/howiemnet/a3d7b9f283c9227f7fa2f355db89a5cf&lt;br /&gt;
&lt;br /&gt;
AE &amp;gt; H: https://gist.github.com/howiemnet/8784cf04568c849271730965eaf35159&lt;br /&gt;
&lt;br /&gt;
https://www.sidefx.com/forum/topic/53681/?page=1#post-241106&lt;br /&gt;
&lt;br /&gt;
==== Where can I find all the Houdini Environment Variables ? ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Help &amp;gt; About &amp;gt; Show Details&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1Tm7aCP.png&lt;br /&gt;
&lt;br /&gt;
==== How do I extract the transforms of my rigid body simulation objects ? ====&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s say I just want to grab the transforms of my simulation dop opbjects (using points), and reapply them to another object.&lt;br /&gt;
&lt;br /&gt;
I use the intrinsic &amp;lt;code&amp;gt;packedfulltransform&amp;lt;/code&amp;gt; so that I don&amp;#039;t have to bother with weird orientations&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
4@xform = primintrinsic(0, &amp;quot;packedfulltransform&amp;quot;, i@ptnum);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Then use &amp;#039;&amp;#039;&amp;#039;Transform By Attribute&amp;#039;&amp;#039;&amp;#039; sop to apply the transform. Will provide screenshots !&lt;br /&gt;
&lt;br /&gt;
===== How do I transform instance points ? =====&lt;br /&gt;
&lt;br /&gt;
hindsight edit: this feels super cumbersome. If you have packed objects you should simply be able to move,rotate, scale with &amp;#039;edit&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
More than once have I had to copy and move around objects manually but felt that simply merging them was a waste of ressources as it could be a simple instance that is moved around. &lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s a pain in the ass though because in SOPs transforms don&amp;#039;t seem to apply orient and pscale attributes. So without too much packedtransform trickery à la Matt Estela there is a way to extract transform using, well, &amp;#039;&amp;#039;extracttransform&amp;#039;&amp;#039;. &lt;br /&gt;
&lt;br /&gt;
It basically takes an object sitting straight and plump in the center of the world, a second object (sharing the same number of points and the same name attribute) moved and scaled around in the world, and returns a point with the &amp;lt;code&amp;gt;@P, @orient,@pscale&amp;lt;/code&amp;gt; attributes, all you need (with &amp;lt;code&amp;gt;s@instance = &amp;quot;/obj/myPigHead&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/lXsetYU.png&lt;br /&gt;
&lt;br /&gt;
==== How do you loop seamlessly using the Carve SOP ? ====&lt;br /&gt;
Using modulo 1, it&amp;#039;s easy to keep values between 0 and 1 in the Carve sop, so you can have animation like &amp;lt;code&amp;gt;( @Time * 0.1 + 0.1) % 1&amp;lt;/code&amp;gt;, however you often end up with a &amp;#039;jump&amp;#039; when the first U or second U reach 0 or 1. The trick is to toggle on and off the bottom extract parameter so that it reverses the extraction when you have the &amp;#039;jump&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/JmN0FJB.gif&lt;br /&gt;
&lt;br /&gt;
I used a simple &amp;lt;code&amp;gt;ch(&amp;#039;domainu1&amp;#039;)&amp;gt;ch(&amp;#039;domainu2&amp;#039;)&amp;lt;/code&amp;gt; (and switch it around for the second toggle) in the &amp;#039;Keep Inside&amp;#039; and &amp;#039;Keep Outside&amp;#039; expressions.&lt;br /&gt;
&lt;br /&gt;
==== How do I disable / remove pin to animation in Vellum ? ====&lt;br /&gt;
&lt;br /&gt;
If you use SOP level Vellum, dive inside solver and since I&amp;#039;m not a super big fan of the &amp;quot;vellum constraint property&amp;quot;, I use a geometry wrangle to remove the constraint prims:&lt;br /&gt;
&lt;br /&gt;
* set wrangle to primitives&lt;br /&gt;
* in the data binding change &amp;#039;&amp;#039;Geometry&amp;#039;&amp;#039; to &amp;#039;&amp;#039;ConstraintGeometry&amp;#039;&amp;#039; &lt;br /&gt;
* add your code, in my example it deletes geometry if frame&amp;gt;35 &amp;#039;&amp;#039;&amp;#039;and&amp;#039;&amp;#039;&amp;#039; the name of the constraint (&amp;#039;&amp;#039;constraint_tag&amp;#039;&amp;#039;) corresponds to the one I named in SOPs: &amp;#039;&amp;#039;pinToAnimationCstr&amp;#039;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/X4cAkVB.png&lt;br /&gt;
&lt;br /&gt;
==== Adding a keyframe to a parameter with python ====&lt;br /&gt;
You need to create the keyframe, then set it (bind it) to your parm. The doc is kind of mysterious about it but the easiest way to understand how it works it to call the &amp;lt;code&amp;gt;asCode()&amp;lt;/code&amp;gt; on your parm. I wrote a little snippet to extract code [https://berniebernie.fr/wiki/Houdini_Python#Selected_node_as_python_code here]&lt;br /&gt;
&lt;br /&gt;
Do that by simply dragging and dropping you parm into a python shell and adding asCode after it like so:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/p5RsOHd.gif&lt;br /&gt;
&lt;br /&gt;
And it&amp;#039;ll output something along the lines of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hou_parm = hou_node.parm(&amp;quot;camswitch&amp;quot;)&lt;br /&gt;
hou_parm.lock(False)&lt;br /&gt;
hou_parm.deleteAllKeyframes()&lt;br /&gt;
hou_parm.set(1)&lt;br /&gt;
hou_parm.setAutoscope(True)&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
hou_keyframe = hou.Keyframe()&lt;br /&gt;
hou_keyframe.setTime(7.958333333333333)&lt;br /&gt;
hou_keyframe.setValue(3)&lt;br /&gt;
hou_keyframe.setSlope(0)&lt;br /&gt;
hou_keyframe.setInSlope(0)&lt;br /&gt;
hou_keyframe.useSlope(False)&lt;br /&gt;
hou_keyframe.setAccel(0)&lt;br /&gt;
hou_keyframe.useAccel(False)&lt;br /&gt;
hou_keyframe.interpretAccelAsRatio(False)&lt;br /&gt;
hou_keyframe.setExpression(&amp;quot;constant()&amp;quot;, hou.exprLanguage.Hscript)&lt;br /&gt;
hou_parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====How do I detach a floating window for my parameters====&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s a nice feature I like from maya, I can&amp;#039;t seem to find it in the vanilla version of houdini however it is written in the docs: https://www.sidefx.com/docs/houdini/hom/hou/Desktop.html#createFloatingPaneTab&lt;br /&gt;
You can follow the instructions of [[Houdini Python#Auto-add_frame_offset_parameter|Auto Add Frame Offset Parameter]] to add a custom menu to your gear button (edit parameter interface), but instead of using &amp;lt;code&amp;gt;PARMmenu.xml&amp;lt;/code&amp;gt; you&amp;#039;ll have to add it to &amp;lt;code&amp;gt;ParmGearMenu.xml&amp;lt;/code&amp;gt;. I&amp;#039;ve also noticed that the hscript command &amp;lt;code&amp;gt;menurefresh&amp;lt;/code&amp;gt; doesn&amp;#039;t work with refreshing when you are editing your interface. FYI&lt;br /&gt;
&lt;br /&gt;
====Frame offset of a file sequence with padding====&lt;br /&gt;
You need padzero to match the padding of your image sequence like so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:/path/to/filesequence.`padzero(4, $F + 10)`.png&lt;br /&gt;
# at frame 1, returns D:/path/to/filesequence.0011.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, having it done so many times, I added a small tool to my inventory: [[Houdini Python#Auto-add_frame_offset_parameter|Auto Add Frame Offset Parameter]]&lt;br /&gt;
&lt;br /&gt;
====How do I view all Houdini icons ?====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;19.5 update&amp;#039;&amp;#039;&amp;#039;: broken ? Will investigate later.&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
So you want a pretty icon for your awesome HDA ? &lt;br /&gt;
You can open up the help file and replace whatever is there by /icons. It will load page with all available .svg icons.&lt;br /&gt;
&lt;br /&gt;
It is super slow to load though (extracting from a zip ? using a raspberry pi to host the help server?) so if you plan to do it more than once&lt;br /&gt;
I recommend opening it up in your browser and saving the page locally.&lt;br /&gt;
&lt;br /&gt;
Once you&amp;#039;ve found your icon hover over it to find the name and you can put it in the icon field of your HDA by using &amp;lt;code&amp;gt;CATEGORY_iconname&amp;lt;/code&amp;gt; so in my screenshot it would be &amp;lt;code&amp;gt;BUTTONS_bundle_set_selected&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/Ezx76xl.png&lt;br /&gt;
&lt;br /&gt;
====How do I add a custom OTL/HDA directory ====&lt;br /&gt;
I like to keep experimental HDAs in a folder that I can share across using google drive / dropbox/ whatever. &lt;br /&gt;
I&amp;#039;m not super fond of the packages/env handling as I feel it&amp;#039;s confusing for now but anyhow.&lt;br /&gt;
&lt;br /&gt;
This adds to $HOUDINI_OTLSCAN_PATH&lt;br /&gt;
&lt;br /&gt;
add to a json file in your packages folder&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;env&amp;quot;: [&lt;br /&gt;
    	{&lt;br /&gt;
    		&amp;quot;HOUDINI_OTLSCAN_PATH&amp;quot;: &amp;quot;G:/My Drive/OTL&amp;quot;&lt;br /&gt;
    	},&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Solo toggling a light in the IPR View ?====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#place in a shelf with a shortcut, I chose Ctrl-Alt-s as it was free. Works with Arnold&lt;br /&gt;
#hit it a second time to remove the solo light&lt;br /&gt;
ipr = next(x for x in hou.ui.currentPaneTabs() if x.type().name() == &amp;#039;IPRViewer&amp;#039;)&lt;br /&gt;
soloParm = ipr.ropNode().parm(&amp;#039;sololight&amp;#039;)&lt;br /&gt;
if soloParm.eval() != &amp;#039;&amp;#039;:&lt;br /&gt;
    soloParm.set(&amp;#039;&amp;#039;)&lt;br /&gt;
else:&lt;br /&gt;
    soloParm.set(&amp;quot; &amp;quot;.join([n.path() for n in hou.selectedNodes()]))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====How do I layout nodes nicely ?====&lt;br /&gt;
* A + drag in a direction with mouse: align nodes&lt;br /&gt;
* shift + drag node: move all nodes above current selected one&lt;br /&gt;
* ctrl + drag node: move nodes below&lt;br /&gt;
&lt;br /&gt;
====How do I change a transforms rotation axis easily in the viewport?====&lt;br /&gt;
Can&amp;#039;t be done &amp;#039;easily&amp;#039; AFAIK, you can however right click the manipulator tool to change axis pivots&lt;br /&gt;
====How do i bake cameras in houdini====&lt;br /&gt;
If you need keyframes, i recommend doing the easier bakeanimation way. Create a ropnet, add a bake animation, fetch source and target and hit render&lt;br /&gt;
&lt;br /&gt;
But using CHOPs&lt;br /&gt;
&lt;br /&gt;
* in a chop network, drop down an object node, grab your object that has parents or whatever as target object, nothing as reference object, and full transform or rotation and scale&lt;br /&gt;
* connect to the output an export node, choose your object to bake to, and in the path add &amp;#039;&amp;#039;&amp;#039;t[xyz] r[xyz]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* click the 2nd button of export node (orange &amp;#039;export&amp;#039; button) and it will connect the chop to your target object (channels will be orange)&lt;br /&gt;
https://i.imgur.com/TWWvxYg.png&lt;br /&gt;
* or use `chop(&amp;#039;path/to/chop/channelx&amp;#039;)` to fetch chop&lt;br /&gt;
* Right click your parm, &amp;lt;code&amp;gt;Keyframes&amp;gt;Bake Keyframes&amp;lt;/code&amp;gt; (I almost always use &amp;lt;code&amp;gt;Override Range from Selected Segments&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Bake, Disable Export Flags&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
==== How do I copy &amp;#039;stamp&amp;#039; different objects with Copy to Points ====&lt;br /&gt;
&lt;br /&gt;
I kind of brute-forced each time with a foreach loop but I&amp;#039;m a big dummy apparently all you need to do is have the same variable (a random int from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;number of objects - 1&amp;lt;/code&amp;gt;) on your 2nd input as it is on the copy to points Sop &amp;#039;Piece Attribute&amp;#039; which defaults to &amp;lt;code&amp;gt;@name&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/fUZiclu.png&lt;br /&gt;
&lt;br /&gt;
====How do I rotate around an axis when using copy to points====&lt;br /&gt;
Matt Estela has [http://www.tokeru.com/cgwiki/index.php?title=JoyOfVex17#Day_17B thorough] explanations which I need to read each time I do this.&lt;br /&gt;
Olivier Jeannel has video where he does it [https://vimeo.com/207626604 in VOPs]&lt;br /&gt;
&lt;br /&gt;
For simple stuff you can often use @N and @up, but there&amp;#039;s more control in using a quaternion @orient attribute:&lt;br /&gt;
&lt;br /&gt;
Setting up the orient to match what you would do with @N and @up:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@orient =  quaternion(maketransform(@N,@up));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rotating around an axis at a certain angle (in degrees):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector axis = chv(&amp;#039;axis&amp;#039;);&lt;br /&gt;
float angle = radians(ch(&amp;#039;angle&amp;#039;));&lt;br /&gt;
vector4 q = quaternion(angle, axis);&lt;br /&gt;
@orient =  qmultiply(@orient,q);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can &amp;#039;&amp;#039;&amp;#039;cumulate&amp;#039;&amp;#039;&amp;#039; orient transforms:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;video controls&amp;gt;&lt;br /&gt;
&amp;lt;source src=&amp;quot;http://mlkdesign.free.fr/dump/zzzzz.mp4&amp;quot; type=&amp;quot;video/mp4&amp;quot;&amp;gt;&lt;br /&gt;
Your browser does not support the video tag.&lt;br /&gt;
&amp;lt;/video&amp;gt; &lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TBD: doing it by moving @P (ie without copytopoints)&lt;br /&gt;
&lt;br /&gt;
====How do I pin an object to an another in Geometry/SOP context ?====&lt;br /&gt;
&lt;br /&gt;
* (use point deform with a single point?)&lt;br /&gt;
&lt;br /&gt;
* probably xyzdistance i&amp;#039;ll write about it later: http://www.toadstorm.com/blog/?p=465&lt;br /&gt;
&lt;br /&gt;
====How do I get the start/end frame of an alembic read in python?====&lt;br /&gt;
&lt;br /&gt;
    from _alembic_hom_extensions import alembicTimeRange as abctr&lt;br /&gt;
    startFrame,endFrame = abctr(&amp;#039;F:/filepathto/alembic.abc&amp;#039;)&lt;br /&gt;
    print startFrame,endFrame&lt;br /&gt;
====Why doesn&amp;#039;t my grain wire solve work?====&lt;br /&gt;
Check that you have:&lt;br /&gt;
* @restlength on the curves (prims)&lt;br /&gt;
* in the dopnetwork, &amp;#039;&amp;#039;Emission type: All Geometry&amp;#039;&amp;#039;&lt;br /&gt;
* each point needs to have @targetstiffness and an attr to drive the &amp;#039;animated&amp;#039; point (@targetweight for instance) -- then in a pop wrangle:&lt;br /&gt;
    if (@targetweight==1) {&lt;br /&gt;
     v@targetP=point(0,&amp;#039;P&amp;#039;,@id);&lt;br /&gt;
    }&lt;br /&gt;
====Why does my RBD bullet sim jump/explode on first frame====&lt;br /&gt;
* Sometimes due to interpenetration solving. Make sure nothing collides before hand (duh). Usually inter-penetration solving &amp;#039;spreads&amp;#039; Objects around, but the sim shouldn&amp;#039;t explode&lt;br /&gt;
* I&amp;#039;ve also found out that if I use a random &amp;#039;orientation&amp;#039; attribute it goes to shit, instead I use &amp;#039;rot&amp;#039;&lt;br /&gt;
* If I use orientation (vec4) to setup randomness of objects, make sure the attribute is deleted if you copy pack&amp;amp;instance (ie: attr rand (orient) &amp;gt; copy to points (pack&amp;amp;instance) &amp;gt; delete attr (orient)) because the orientation is already stored in the packed intrinsics.&lt;br /&gt;
* Sometimes tiny masses will also make the objects skittish, also built-in drag/airresist sometimes goes to Inf or -Inf energy == shit&lt;br /&gt;
Edit: More reasons/details&lt;br /&gt;
* NaNs in sims can apparently cause this. Make sure you&amp;#039;re not using airresist/popwind (perhaps this is broken on my side at work)which seems to fuck up a lot of things.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GT7Yi6c.gif&lt;br /&gt;
&lt;br /&gt;
====How do I scale manipulators viewport size (like +/- in maya)?====&lt;br /&gt;
/ and *  (be warned that + and - change smooth mesh preview like 1 and 3 in maya)&lt;br /&gt;
====How do I scale uniformely using the gizmo ?====&lt;br /&gt;
* http://www.sidefx.com/docs/houdini/basics/handles.html&lt;br /&gt;
* basically, moving an arrowhead scales uniformely, moving the rest of the handles scales non-uniformely&lt;br /&gt;
====How do I interpolate motion between still frames====&lt;br /&gt;
I might be missing something super simple because this seems unreasonably complicated&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;edit:&amp;#039;&amp;#039; I was probably missing the simple &amp;#039;&amp;#039;&amp;#039;Timeblend&amp;#039;&amp;#039;&amp;#039; node. I&amp;#039;ll leave this up; who knows.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/v0BqBWl.png&lt;br /&gt;
&lt;br /&gt;
* The trick is to use floor($FF) in the timeshift rather than $F otherwise you get frame jumps on half-frames&lt;br /&gt;
* So 1 timeshift with floor($FF), one with floor($FF)+1, plus a blendshape with $FF-floor($FF)&lt;br /&gt;
&lt;br /&gt;
Slightly more complex example where I had objects not necessarily sharing the same ptnum (but with a unique Id attribute) -- and a need to lerp orients too&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int pt = findattribval(1, &amp;quot;point&amp;quot;, &amp;quot;id&amp;quot;, @id);&lt;br /&gt;
i@id2 = pt; &lt;br /&gt;
if(pt == -1){&lt;br /&gt;
    @Cd = {1,0,0};&lt;br /&gt;
}else{&lt;br /&gt;
    vector p2 = point(1,&amp;quot;P&amp;quot;,pt);&lt;br /&gt;
    vector4 orient = point(1,&amp;quot;orient&amp;quot;,pt);&lt;br /&gt;
    @P = lerp(@P,p2,@Frame%1);&lt;br /&gt;
    @orient = lerp(@orient,orient,@Frame%1);&lt;br /&gt;
    @Cd = @Cd;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====How do I instance a node (reference copy)====&lt;br /&gt;
* ctrl-shift-alt drag nodes&lt;br /&gt;
&lt;br /&gt;
====Improve VDB viewport resolution====&lt;br /&gt;
&lt;br /&gt;
Volume visualization, even at maximum viewport resolution won&amp;#039;t give a nice viewport display if your vdbs are very sparse (&amp;#039;pockets&amp;#039; of volumes). So, for preview purposes you can use &amp;#039;VDB Segment by Connectivity&amp;#039; to re-split the vdb into smaller vdbs which will display properly (be warned that this can slow things down to a crawl)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/naWaiA1.gif&lt;br /&gt;
&lt;br /&gt;
In my case I toggled off &amp;#039;use color&amp;#039; and &amp;#039;append names&amp;#039; so that my volume visualization color still works. Also you can turn off &amp;#039;smooth wire shaded&amp;#039; to remove bounding boxes. And if you&amp;#039;re happy with the preview, Flipbook==Approved Render :D&lt;br /&gt;
&lt;br /&gt;
====How do I combine (easily) loads of vdbs====&lt;br /&gt;
&lt;br /&gt;
Thanks to esteemed friend&amp;amp;colleague Charles&lt;br /&gt;
&lt;br /&gt;
* Make sure &amp;#039;Flatten all B into A is on and your vdb (source A) is the same rez&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/qcuClGv.png&lt;br /&gt;
&lt;br /&gt;
====How do I activate/fill vdbs cells (voxels)====&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to fill your volume vdb with empty data to do stuff with it.&lt;br /&gt;
&lt;br /&gt;
* Use vdb activate on itself to &amp;#039;fill up&amp;#039;, uncheck use value&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/02x5N2T.png&lt;br /&gt;
&lt;br /&gt;
===How do I cleanly retrieve vertex info to point (like laying layout UVs in world space)===&lt;br /&gt;
&lt;br /&gt;
* Use vertex split ! https://www.youtube.com/watch?v=F2pwzyQ1oc4&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/3td2zkw.png&lt;br /&gt;
&lt;br /&gt;
===How do I timestamp my outputs===&lt;br /&gt;
&lt;br /&gt;
You can use the a houdini variable: &amp;lt;code&amp;gt;`strreplace(strreplace($_HIP_SAVETIME,&amp;#039;:&amp;#039;,&amp;#039;-&amp;#039;),&amp;#039; &amp;#039;,&amp;#039;_&amp;#039;)`&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/LtTSp3i.png&lt;br /&gt;
&lt;br /&gt;
Or you can go dirty with sytem calls, on windows and my locale (france) the following works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
`system(&amp;quot;cmd /C echo %date:~6,4%%date:~3,2%%date:~0,2%&amp;quot;)`&lt;br /&gt;
//evaluates to 20200917 for today the 17 of september 2020&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More modern python, works as a multiline expression if you have the return statement:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;import time;return time.strftime(&amp;quot;%Y-%m-%d_%Hh%M&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
#returns: 2022-09-08_13h29&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===How do I get pre and post infinity animation with keyframes===&lt;br /&gt;
&lt;br /&gt;
* In the Animation Editor, pick channels and Alt-E (channel properties): &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/T8tD5E6.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I refer to a relative SOP in vex===&lt;br /&gt;
The syntax can be confusing, but I think this works, when using the &amp;#039;point&amp;#039; (or similar) functions. You must have a string with op in it: &amp;#039;&amp;#039;&amp;#039;&amp;quot;op:chs(&amp;#039;parm&amp;#039;)&amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/HZ5ML7p.png&lt;br /&gt;
&lt;br /&gt;
===Rounding numbers in Vex===&lt;br /&gt;
&lt;br /&gt;
Is really annoying. Save yourself the trouble and worst case use pythonexpression.&lt;br /&gt;
Floating point issues, sprintf doesn&amp;#039;t really want to work as it seems. Anyways:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//before, shows 1.610000000000000001 or simila&lt;br /&gt;
//after, shows 1.61&lt;br /&gt;
`pythonexprs(&amp;quot;&amp;#039;%0.2f&amp;#039; % &amp;quot; + ch(&amp;quot;../parameter&amp;quot;))`&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Creating a linear pre-roll===&lt;br /&gt;
&lt;br /&gt;
Freeze the two first frames of animation and interpolate to get your pre-roll. Volume (rotations) are lost but I&amp;#039;m not smart enough to do non-linear interpolation yet.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/rD3M4Ms.gif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Exporting Houdini instances to Maya (via particles) ===&lt;br /&gt;
&lt;br /&gt;
Fuck this shit. It can&amp;#039;t be done. I lost two days of my life, I&amp;#039;ll post back if I manage it but maya is a fucking nightmare and its low price is the only thing keeping people from buying Houdini.&lt;br /&gt;
&lt;br /&gt;
Posting my research if another poor soul has to do this. We wanted to take instanced animation from H to maya keeping rotation and scale&lt;br /&gt;
&lt;br /&gt;
First, here&amp;#039;s things I haven&amp;#039;t tried&lt;br /&gt;
&lt;br /&gt;
* using H engine in maya. Way too expensive to put on the farm.&lt;br /&gt;
* Using Filmbox Crate .abc importer/exporter to get point info in maya. My colleagues don&amp;#039;t seem to have gone far with it.&lt;br /&gt;
* Using old weird bgeoclassic importer. &lt;br /&gt;
&lt;br /&gt;
What &amp;#039;&amp;#039;seemed&amp;#039;&amp;#039; to work was using MASH instancing BUT&lt;br /&gt;
&lt;br /&gt;
* Using face instancing (distribution) seems to euler flip. (ie exporting thousands of simple triangles from Houdini). Scale works fine. Tried LOADs of combinations.&lt;br /&gt;
* Using edge instancing (distributions) seems to keep rotation fine (if you have a houdini triangle that fits the requirements -- and skip one out of 3 edges, ie instance nulls). Scaling only works in one axis.&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s &amp;lt;del&amp;gt;2018&amp;lt;/del&amp;gt; &amp;lt;del&amp;gt;2019&amp;lt;/del&amp;gt; 2020 Autodesk. Step up your fucking game.&lt;br /&gt;
&lt;br /&gt;
=== Motion blur on changing topology from Houdini to Maya and Redshift ===&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s not clear from the doc. If you have a flip sim or something funky that changes at each frame, here&amp;#039;s how to get velocity showing up in Redshift from Maya:&lt;br /&gt;
&lt;br /&gt;
/!\ We had a pretty nasty bug (crashes) with redshift when using end-of-frame motion blur instead of center-of-frame.&lt;br /&gt;
&lt;br /&gt;
In Houdini:&lt;br /&gt;
&lt;br /&gt;
* change your &amp;#039;&amp;#039;&amp;#039;v&amp;#039;&amp;#039;&amp;#039; to &amp;#039;&amp;#039;&amp;#039;Cd&amp;#039;&amp;#039;&amp;#039; (from what I gather it needs to be a vector with R,G,B not X,Y,Z or 0,1,2) using a wrangle &lt;br /&gt;
* Promote &amp;#039;&amp;#039;&amp;#039;Cd&amp;#039;&amp;#039;&amp;#039; to vertices&lt;br /&gt;
* Change its name (optional, I chose myVel in examples) delete original v (optional)&lt;br /&gt;
* Export with alembic ROP (no need to turn on &amp;#039;motion blur&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/YXR2F28.png&lt;br /&gt;
&lt;br /&gt;
In Maya:&lt;br /&gt;
&lt;br /&gt;
* Import abc cache (optional: lament on how unstable maya is when you reload an .abc cache). If your mesh comes in coloured it&amp;#039;s a good sign.&lt;br /&gt;
* In the &amp;#039;&amp;#039;&amp;#039;shape&amp;#039;&amp;#039;&amp;#039; attributes of your abc, choose the vertex color attribute from Houdini for velocity&lt;br /&gt;
* Make sure your have moblur turned on in your render options and you should be good !&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/DJfNWqL.png&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Windows_batch&amp;diff=881</id>
		<title>Windows batch</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Windows_batch&amp;diff=881"/>
		<updated>2025-11-12T14:16:09Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Sequence of images to h264 / h265 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Add list of filenames to comma separated text file ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
del f.txt&lt;br /&gt;
set file=f.txt&lt;br /&gt;
for /f &amp;quot;delims=&amp;quot; %%a in (&amp;#039;dir /b/s *.pdf&amp;#039;) do (&lt;br /&gt;
    echo | set /p=%%~na, &amp;gt;&amp;gt; f.txt&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Right Click to add folder to system PATH (windows) ===&lt;br /&gt;
* Save this as a batch file somewhere (without spaces in folder+file name! I think. Fuck Windows!)&lt;br /&gt;
* Shortcut to it in the sendto folder (to go directly to the sendto folder, write shell:sendto in Win-R (run) menu)&lt;br /&gt;
* Edit the shortcut advanced properties to run as Administrator&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo _____envs_________________________________________________________________&lt;br /&gt;
for %%G in (&amp;quot;%path:;=&amp;quot; &amp;quot;%&amp;quot;) do @echo %%G&lt;br /&gt;
echo __________________________________________________________________________&lt;br /&gt;
echo adding %1 to path&lt;br /&gt;
&lt;br /&gt;
set NEW_PATH=%1&lt;br /&gt;
set NEW_PATH=%NEW_PATH:&amp;quot;=%&lt;br /&gt;
set NEW_PATH_VAR=&amp;quot;%PATH%;%NEW_PATH%&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SET /P M=1 to add to user PATH, 2 for system PATH, 3 for Windows Dialog, 4 to cancel : &lt;br /&gt;
IF %M%==1 GOTO userV&lt;br /&gt;
IF %M%==2 GOTO systemV&lt;br /&gt;
IF %M%==3 GOTO windowsD&lt;br /&gt;
IF %M%==4 GOTO EOF&lt;br /&gt;
&lt;br /&gt;
:userV&lt;br /&gt;
setx PATH %NEW_PATH_VAR%&lt;br /&gt;
goto EOF&lt;br /&gt;
:systemV&lt;br /&gt;
setx /M PATH %NEW_PATH_VAR%&lt;br /&gt;
goto EOF&lt;br /&gt;
:windowsD&lt;br /&gt;
rundll32.exe sysdm.cpl,EditEnvironmentVariables&lt;br /&gt;
goto EOF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Parse folder with image sequence(s) to mp4 videos with ffmpeg (tested on windows) ===&lt;br /&gt;
==== Sequence of images to h264 / h265 ====&lt;br /&gt;
https://i.imgur.com/U8ZZm6u.gif&lt;br /&gt;
&lt;br /&gt;
* should be py2/3 compatible, haven&amp;#039;t tried a lot of cases&lt;br /&gt;
* sometimes python (.py) files won&amp;#039;t work directly in the shell:sendto folder. You instead need to create a .bat file and point to a python executable like so (remember to add the current file variable if your python script needs it like I do)&lt;br /&gt;
* Will ask for a &amp;#039;&amp;#039;&amp;#039;ffmpeg&amp;#039;&amp;#039;&amp;#039; exe on first run, saves the path as a pickle in your userprofile.&lt;br /&gt;
* Dec23 update: removed tkinter and uses powershell for file location. It&amp;#039;s win only for now anyways. Also updated the .bat file to use whatever windows uses for .py files.&lt;br /&gt;
* Dec23 update2: due to WindowsLogic© you can&amp;#039;t use &amp;quot;start&amp;quot; to launch a .py file with arguments reliably. It&amp;#039;s better to point to a python.exe + flags. Sucks. Solution can be to pass 1 argument as the new window title and retrieve it using some nasty code: https://stackoverflow.com/a/58355052 but I this is getting ridiculous&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
&amp;quot;C:\Program Files\Blender Foundation\Blender 3.6\3.6\python\bin\python.exe&amp;quot; %~dp0/h264.py &amp;quot;%~f1&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# the h264.py file&lt;br /&gt;
#&lt;br /&gt;
# windows, save into sendto folder so you can access with right click &amp;gt; send to&lt;br /&gt;
# to access the folder, write shell:sendto in Win-R (run) menu&lt;br /&gt;
&lt;br /&gt;
debug = True&lt;br /&gt;
&lt;br /&gt;
#older/other options, things to note, I moved to h265/hvec because I had malloc issues. 64bit problem ? RAM problems ? I dunno.&lt;br /&gt;
&lt;br /&gt;
#ffmpegcommand = &amp;#039;-r 25 -start_number {} -i &amp;quot;{}&amp;quot; -c:v libx264 -b 5000k -pix_fmt yuv420p -hide_banner -loglevel panic -stats &amp;quot;{}&amp;quot;&amp;#039; #uses the first frame figured out. Doesnt work well with non padded sequences&lt;br /&gt;
#motion jpeg&lt;br /&gt;
#ffmpegcommand = &amp;#039;-r 25 -start_number {} -i &amp;quot;{}&amp;quot; -c:v mjpeg -qscale:v 1 -vendor ap10 -pix_fmt yuvj422p -hide_banner -loglevel panic -stats &amp;quot;{}&amp;quot;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
ffmpegcommand = &amp;#039;-r 25 -start_number {} -i &amp;quot;{}&amp;quot; -c:v libx265 -b 5000k -pix_fmt yuv420p -hide_banner -loglevel panic -stats &amp;quot;{}&amp;quot;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
if debug:&lt;br /&gt;
	ffmpegcommand = &amp;#039;-r 25 -start_number {} -i &amp;quot;{}&amp;quot; -c:v libx265 -b 5000k -pix_fmt yuv420p -hide_banner -loglevel warning -stats &amp;quot;{}&amp;quot;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
extension = &amp;#039;.mov&amp;#039;&lt;br /&gt;
reg = r&amp;#039;^(.+?)([0-9]+)\.(png|exr|jpg)$&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def grabfile():&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;TY stackoverflow&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	PS_Commands = &amp;quot;Add-Type -AssemblyName System.Windows.Forms;&amp;quot;&lt;br /&gt;
	PS_Commands += &amp;quot;$fileBrowser = New-Object System.Windows.Forms.OpenFileDialog;&amp;quot;&lt;br /&gt;
	PS_Commands += &amp;quot;$Null = $fileBrowser.ShowDialog();&amp;quot;&lt;br /&gt;
	PS_Commands += &amp;quot;echo $fileBrowser.FileName&amp;quot;&lt;br /&gt;
	file_path = subprocess.run([&amp;quot;powershell.exe&amp;quot;, PS_Commands], stdout=subprocess.PIPE)&lt;br /&gt;
	file_path = file_path.stdout.decode()&lt;br /&gt;
	file_path = file_path.rstrip()&lt;br /&gt;
	if file_path:&lt;br /&gt;
		return file_path&lt;br /&gt;
	else:&lt;br /&gt;
		return False&lt;br /&gt;
&lt;br /&gt;
import sys, os, pickle, subprocess&lt;br /&gt;
from os.path import expanduser&lt;br /&gt;
import re&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#get ffmpeg exe, nor error checking, save in a pickle in home folder if found&lt;br /&gt;
&lt;br /&gt;
picklefp = expanduser(&amp;#039;~&amp;#039;)+&amp;#039;/.ffmpegpref.pckl&amp;#039;&lt;br /&gt;
pickle_data = &amp;#039;&amp;#039;&lt;br /&gt;
try:&lt;br /&gt;
    with open(picklefp, &amp;#039;rb&amp;#039;) as f:&lt;br /&gt;
        pickle_data = pickle.load(f)&lt;br /&gt;
except UnicodeDecodeError as e:&lt;br /&gt;
    with open(picklefp, &amp;#039;rb&amp;#039;) as f:&lt;br /&gt;
        pickle_data = pickle.load(f, encoding=&amp;#039;latin1&amp;#039;)&lt;br /&gt;
except Exception as e:&lt;br /&gt;
    print(&amp;#039;No path to an ffmpeg.exe encoder found in the following preference file. Choose one: &amp;#039;, e)&lt;br /&gt;
&lt;br /&gt;
if not pickle_data:&lt;br /&gt;
	file_path = grabfile()&lt;br /&gt;
	if file_path:&lt;br /&gt;
		f = open(picklefp,&amp;#039;wb&amp;#039;)&lt;br /&gt;
		file_path = &amp;#039;&amp;quot;&amp;#039;+file_path+&amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
		pickle.dump(file_path, f)&lt;br /&gt;
		pickle_data = file_path&lt;br /&gt;
		f.close()&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No ffmpeg.exe chosen. Press key to exit&amp;#039;)&lt;br /&gt;
		try:&lt;br /&gt;
			raw_input()&lt;br /&gt;
		except:&lt;br /&gt;
			input()&lt;br /&gt;
		sys.exit()&lt;br /&gt;
&lt;br /&gt;
#get the argument/flag, exit if none is given&lt;br /&gt;
try:&lt;br /&gt;
	target = sys.argv[1]&lt;br /&gt;
except:&lt;br /&gt;
	print(&amp;#039;Python file needs a file or folder as an argument. Press key to exit&amp;#039;)&lt;br /&gt;
	try:&lt;br /&gt;
		raw_input()&lt;br /&gt;
	except:&lt;br /&gt;
		input()&lt;br /&gt;
	sys.exit()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#figure out image sequence(s) from file or folder, expect path/to/file/(whatever)(digits).(jpg/png/exr)&lt;br /&gt;
&lt;br /&gt;
filelist = []&lt;br /&gt;
if os.path.isdir(target):&lt;br /&gt;
	for f in sorted(os.listdir(target)):&lt;br /&gt;
		match = re.match(reg, f,re.IGNORECASE)&lt;br /&gt;
		if match:&lt;br /&gt;
			filelist.append(match.groups())&lt;br /&gt;
&lt;br /&gt;
if os.path.isfile(target):&lt;br /&gt;
	match = re.match(reg, target,re.IGNORECASE)&lt;br /&gt;
	if match:&lt;br /&gt;
		target = os.path.dirname(target)&lt;br /&gt;
		newReg = &amp;#039;(&amp;#039;+os.path.basename(match.groups()[0])+&amp;#039;)(\d*)\.(&amp;#039;+match.groups()[2]+&amp;#039;)&amp;#039;&lt;br /&gt;
		#bit convoluted but it will help me pick the first image of sequence that matches selection&lt;br /&gt;
		sortedFiles = os.listdir(target)&lt;br /&gt;
		sortedFiles.sort()&lt;br /&gt;
		for f in sortedFiles:&lt;br /&gt;
			match = re.match(newReg, f,re.IGNORECASE)&lt;br /&gt;
			if match:&lt;br /&gt;
				filelist.append(match.groups())&lt;br /&gt;
&lt;br /&gt;
sequences = []&lt;br /&gt;
imagename = &amp;#039;&amp;#039;&lt;br /&gt;
for image in filelist:&lt;br /&gt;
	#grab only first image, if you have multiple sequences in a single folder&lt;br /&gt;
	if image[0] != imagename:&lt;br /&gt;
		sequences.append(image)&lt;br /&gt;
		imagename = image[0]&lt;br /&gt;
for sequence in sequences:&lt;br /&gt;
	if debug:&lt;br /&gt;
		print(&amp;quot;DEBUG | file: {} | start: {} (?) | ext: {}&amp;quot;.format(sequence[0],sequence[1],sequence[2]))&lt;br /&gt;
&lt;br /&gt;
	#a pesky ffmpeg &amp;#039;bug&amp;#039;, if you use non padded digits in your sequence you can have %d but if you have padded numbers you _need_ %##d&lt;br /&gt;
	if len(sequence[1]) &amp;gt; 1:&lt;br /&gt;
		imagePath = target+&amp;#039;/&amp;#039;+sequence[0]+&amp;#039;%0&amp;#039;+str(len(sequence[1]))+&amp;#039;d.&amp;#039;+sequence[2]&lt;br /&gt;
	else:&lt;br /&gt;
		imagePath = target+&amp;#039;/&amp;#039;+sequence[0]+&amp;#039;%d.&amp;#039;+sequence[2]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	startFrame = int(sequence[1])&lt;br /&gt;
	videoPath = target+&amp;#039;/&amp;#039;+sequence[0].rstrip(&amp;#039;._-&amp;#039;)+extension&lt;br /&gt;
	if sequence[2].lower() == &amp;#039;exr&amp;#039;:&lt;br /&gt;
		#add linear to sRGB color space&lt;br /&gt;
		pickle_data += &amp;quot; -apply_trc iec61966_2_1&amp;quot;&lt;br /&gt;
	command = pickle_data+&amp;#039; &amp;#039;+ffmpegcommand.format(startFrame,imagePath,videoPath)&lt;br /&gt;
	#command = pickle_data+&amp;#039; &amp;#039;+ffmpegcommand.format(imagePath,videoPath)&lt;br /&gt;
	if debug:&lt;br /&gt;
		print(&amp;quot;DEBUG | &amp;quot;+command)&lt;br /&gt;
	return_code = subprocess.call(command, shell=True)  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if debug:&lt;br /&gt;
	print(&amp;quot;DEBUG | paused. Press a key to quit&amp;quot;)&lt;br /&gt;
	try:&lt;br /&gt;
		raw_input()&lt;br /&gt;
	except:&lt;br /&gt;
		input()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Video to sequence of images with FFMPEG ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6AQLmiA.gif&lt;br /&gt;
&lt;br /&gt;
* Add .bat to the sendto folder (to go directly to the sendto folder, write shell:sendto in Win-R (run) menu)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
C:\Users\pathtoffmpeg\ffmpeg.exe -i %1 -vsync 0 %1_%%04d.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or more complete python.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# windows, save into sendto folder so you can access with right click &amp;gt; send to&lt;br /&gt;
# to access the folder, write shell:sendto in Win-R (run) menu&lt;br /&gt;
&lt;br /&gt;
ffmpegcommand = &amp;#039;-i &amp;quot;{}&amp;quot; -hide_banner -loglevel panic -stats &amp;quot;{}.%04d.jpg&amp;quot;&amp;#039;&lt;br /&gt;
sequence_ext = &amp;quot;_seq&amp;quot;&lt;br /&gt;
import sys, os, pickle, subprocess&lt;br /&gt;
from pprint import pprint&lt;br /&gt;
from os.path import expanduser&lt;br /&gt;
import re&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def grabfile():&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;TY stackoverflow&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	PS_Commands = &amp;quot;Add-Type -AssemblyName System.Windows.Forms;&amp;quot;&lt;br /&gt;
	PS_Commands += &amp;quot;$fileBrowser = New-Object System.Windows.Forms.OpenFileDialog;&amp;quot;&lt;br /&gt;
	PS_Commands += &amp;quot;$Null = $fileBrowser.ShowDialog();&amp;quot;&lt;br /&gt;
	PS_Commands += &amp;quot;echo $fileBrowser.FileName&amp;quot;&lt;br /&gt;
	file_path = subprocess.run([&amp;quot;powershell.exe&amp;quot;, PS_Commands], stdout=subprocess.PIPE)&lt;br /&gt;
	file_path = file_path.stdout.decode()&lt;br /&gt;
	file_path = file_path.rstrip()&lt;br /&gt;
	if file_path:&lt;br /&gt;
		return file_path&lt;br /&gt;
	else:&lt;br /&gt;
		return False&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#get ffmpeg exe, slight error checking, save in a pickle in home folder if found&lt;br /&gt;
&lt;br /&gt;
picklefp = expanduser(&amp;#039;~&amp;#039;)+&amp;#039;/.ffmpegpref.pckl&amp;#039;&lt;br /&gt;
pickle_data = &amp;#039;&amp;#039;&lt;br /&gt;
try:&lt;br /&gt;
    with open(picklefp, &amp;#039;rb&amp;#039;) as f:&lt;br /&gt;
        pickle_data = pickle.load(f)&lt;br /&gt;
except UnicodeDecodeError as e:&lt;br /&gt;
    with open(picklefp, &amp;#039;rb&amp;#039;) as f:&lt;br /&gt;
        pickle_data = pickle.load(f, encoding=&amp;#039;latin1&amp;#039;)&lt;br /&gt;
except Exception as e:&lt;br /&gt;
    print(&amp;#039;No path to an ffmpeg.exe encoder found in the following preference file. Choose one: &amp;#039;, e)&lt;br /&gt;
&lt;br /&gt;
if not pickle_data:&lt;br /&gt;
	file_path = grabfile()&lt;br /&gt;
	if file_path:&lt;br /&gt;
		f = open(picklefp,&amp;#039;wb&amp;#039;)&lt;br /&gt;
		file_path = &amp;#039;&amp;quot;&amp;#039;+file_path+&amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
		pickle.dump(file_path, f)&lt;br /&gt;
		pickle_data = file_path&lt;br /&gt;
		f.close()&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No ffmpeg.exe chosen. Press key to exit&amp;#039;)&lt;br /&gt;
		try:&lt;br /&gt;
			raw_input()&lt;br /&gt;
		except:&lt;br /&gt;
			input()&lt;br /&gt;
		sys.exit()&lt;br /&gt;
&lt;br /&gt;
#get the argument/flag, exit if none is given&lt;br /&gt;
try:&lt;br /&gt;
	targets = sys.argv&lt;br /&gt;
except:&lt;br /&gt;
	print(&amp;#039;Python file needs a file or folder as an argument. Press key to exit&amp;#039;)&lt;br /&gt;
	try:&lt;br /&gt;
		raw_input()&lt;br /&gt;
	except:&lt;br /&gt;
		input()&lt;br /&gt;
	sys.exit()&lt;br /&gt;
&lt;br /&gt;
for elm in targets[1:]:&lt;br /&gt;
	if os.path.isfile(elm):&lt;br /&gt;
		dir = os.path.dirname(elm)+&amp;#039;/&amp;#039;+os.path.basename(elm)+sequence_ext&lt;br /&gt;
		if not os.path.exists(dir):&lt;br /&gt;
			os.makedirs(dir)&lt;br /&gt;
		filename = dir+&amp;#039;/&amp;#039;+os.path.basename(elm)+sequence_ext&lt;br /&gt;
		command = pickle_data+&amp;#039; &amp;#039;+ffmpegcommand.format(elm,filename)&lt;br /&gt;
		#print(command)&lt;br /&gt;
		return_code = subprocess.call(command, shell=True)  &lt;br /&gt;
		&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
try:&lt;br /&gt;
	raw_input()&lt;br /&gt;
except:&lt;br /&gt;
	input()&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
CMBLine tips: http://www.dostips.com/DtCodeSnippets.php&lt;br /&gt;
&lt;br /&gt;
=== Create gif from sequence with imagemagick ===&lt;br /&gt;
Use with filemenutools and imagemagick (with IM&amp;#039;s path in the system/env path)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Dim arr(4)&lt;br /&gt;
q = chr(34)&lt;br /&gt;
l = 0&lt;br /&gt;
arg1 = Wscript.Arguments(0)&lt;br /&gt;
Set re = new regexp&lt;br /&gt;
re.IgnoreCase = True&lt;br /&gt;
re.Global = True&lt;br /&gt;
re.Pattern = &amp;quot;(.*)([_|\.]\d*)\.([a-zA-Z]{2,4})&amp;quot;&lt;br /&gt;
  Set matches = re.Execute(arg1)&lt;br /&gt;
  If matches.Count &amp;gt; 0 Then&lt;br /&gt;
    Set match = matches(0)&lt;br /&gt;
    l = l + 1&lt;br /&gt;
    arr(l) = match.Value&lt;br /&gt;
    If match.SubMatches.Count &amp;gt; 0 Then&lt;br /&gt;
      For I = 0 To match.SubMatches.Count-1&lt;br /&gt;
              l = l + 1&lt;br /&gt;
              arr(l) = match.SubMatches(I)&lt;br /&gt;
      Next&lt;br /&gt;
      originalSeq = arr(2)+Left(arr(3),1)+&amp;quot;*.&amp;quot;+arr(4)&lt;br /&gt;
      gifname = arr(2)+Left(arr(3),1)+&amp;quot;.gif&amp;quot;&lt;br /&gt;
      fname=InputBox(&amp;quot;Length of frames in milliseconds&amp;quot;)&lt;br /&gt;
      If fname &amp;lt;&amp;gt; &amp;quot;&amp;quot; Then&lt;br /&gt;
         Dim objShell&lt;br /&gt;
         Set objShell = WScript.CreateObject (&amp;quot;WScript.shell&amp;quot;)&lt;br /&gt;
	 cmdVar = &amp;quot;convert -delay &amp;quot;&amp;amp; fname &amp;amp; &amp;quot; -loop 0 &amp;quot; &amp;amp; q &amp;amp; originalSeq &amp;amp; q &amp;amp; &amp;quot; &amp;quot; &amp;amp; q &amp;amp; gifname &amp;amp; q&lt;br /&gt;
         objShell.run &amp;quot;cmd /K &amp;quot;+cmdVar+&amp;quot; &amp;amp; echo. &amp;amp; echo ------------------FINISHED------------ &amp;amp; Pause &amp;amp; Exit&amp;quot;&lt;br /&gt;
      End If&lt;br /&gt;
    End If&lt;br /&gt;
  Else&lt;br /&gt;
    msgbox &amp;quot;Not a sequence (format: path/name_0001.ext or name.01.ext )&amp;quot;, 0, &amp;quot;sequence processing&amp;quot;&lt;br /&gt;
  End If&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Render sequence from shell ===&lt;br /&gt;
http://i.imgur.com/ZB4Lu.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Dim arr(4)&lt;br /&gt;
l = 0&lt;br /&gt;
arg1 = Wscript.Arguments(0)&lt;br /&gt;
Set re = new regexp&lt;br /&gt;
re.IgnoreCase = True&lt;br /&gt;
re.Global = True&lt;br /&gt;
re.Pattern = &amp;quot;(.*)([_|\.]\d*)\.([a-zA-Z]{2,4})&amp;quot;&lt;br /&gt;
  Set matches = re.Execute(arg1)&lt;br /&gt;
  If matches.Count &amp;gt; 0 Then&lt;br /&gt;
    Set match = matches(0)&lt;br /&gt;
    l = l + 1&lt;br /&gt;
    arr(l) = match.Value&lt;br /&gt;
    If match.SubMatches.Count &amp;gt; 0 Then&lt;br /&gt;
      For I = 0 To match.SubMatches.Count-1&lt;br /&gt;
              l = l + 1&lt;br /&gt;
              arr(l) = match.SubMatches(I)&lt;br /&gt;
      Next&lt;br /&gt;
    End If&lt;br /&gt;
  Else&lt;br /&gt;
    msgbox &amp;quot;No match&amp;quot;, 0, &amp;quot;sequence processing&amp;quot;&lt;br /&gt;
  End If&lt;br /&gt;
MsgBox arr(2)+Left(arr(3),1)+&amp;quot;%0&amp;quot;+CStr(Len(arr(3))-1)+&amp;quot;d.&amp;quot;+arr(4)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Find % of space left on drives (WIN)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
strComputer = &amp;quot;.&amp;quot;&lt;br /&gt;
Set objWMIService = GetObject(&amp;quot;winmgmts:&amp;quot; _&lt;br /&gt;
    &amp;amp; &amp;quot;{impersonationLevel=impersonate}!\\&amp;quot; &amp;amp; strComputer &amp;amp; &amp;quot;\root\cimv2&amp;quot;)&lt;br /&gt;
Set colDisks = objWMIService.ExecQuery _&lt;br /&gt;
    (&amp;quot;Select * from Win32_LogicalDisk Where DriveType = 3&amp;quot;)&lt;br /&gt;
For Each objDisk in colDisks&lt;br /&gt;
    intFreeSpace = objDisk.FreeSpace&lt;br /&gt;
    intTotalSpace = objDisk.Size&lt;br /&gt;
    pctFreeSpace = intFreeSpace / intTotalSpace&lt;br /&gt;
    Wscript.Echo objDisk.DeviceID, FormatPercent(pctFreeSpace)&lt;br /&gt;
Next&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Windows text 2 speech ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Option Explicit&lt;br /&gt;
Dim caca&lt;br /&gt;
caca = WScript.Arguments(0)&lt;br /&gt;
CreateObject(&amp;quot;SAPI.SpVoice&amp;quot;).Speak caca&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Wake on lan freebox: http://michauko.org/blog/2007/07/03/wake-on-lan-et-freebox/comment-page-1/#comment-2709&lt;br /&gt;
===Powershell remove empty directories===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function DeleteEmptyDirectories {&lt;br /&gt;
  param([string] $root)&lt;br /&gt;
&lt;br /&gt;
  [System.IO.Directory]::GetDirectories(&amp;quot;$root&amp;quot;) |&lt;br /&gt;
    % {&lt;br /&gt;
      DeleteEmptyDirectories &amp;quot;$_&amp;quot;;&lt;br /&gt;
      if ([System.IO.Directory]::GetFileSystemEntries(&amp;quot;$_&amp;quot;).Length -eq 0) {&lt;br /&gt;
        Write-Output &amp;quot;Removing $_&amp;quot;;&lt;br /&gt;
        Remove-Item -Force &amp;quot;$_&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DeleteEmptyDirectories &amp;quot;P:\Path\to\wherever&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== EXR rezip===&lt;br /&gt;
http://www.fevrierdorian.com/blog/public/billets/2011_03_08_images_exr_compo_lent/OpenImageIO_bin.7z&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if [%1]==[] goto :eof&lt;br /&gt;
:loop&lt;br /&gt;
C:\Users\BUFFALO\Desktop\OpenImageIO_bin\iconvert.exe -v --compression zips --scanline %1 %1&lt;br /&gt;
shift&lt;br /&gt;
if not [%1]==[] goto loop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== render sequence to movie ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//works on png files and plays fine&lt;br /&gt;
ffmpeg -f image2 -i fileName.%4d.png -r 25 -vf &amp;quot;scale=1280:trunc(ow/a/vsub)*vsub&amp;quot; -y fileName.mp4 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
http://x264.nl/&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//doesn&amp;#039;t really work well&lt;br /&gt;
x264 -i &amp;quot;sequence%04d.png&amp;quot; &amp;quot;output.mp4&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== wait until file is created and do something ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
echo waiting...&lt;br /&gt;
:start&lt;br /&gt;
PING 1.1.1.1 -n 1 -w 1000 &amp;gt;NUL&lt;br /&gt;
if EXIST test.txt start calc &amp;amp; goto :eof&lt;br /&gt;
goto start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== loop some app forever ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
:loopme&lt;br /&gt;
PING 1.1.1.1 -n 1 -w 2000 &amp;gt;NUL&lt;br /&gt;
tasklist /fi &amp;quot;imagename eq maya.exe&amp;quot; 2&amp;gt; NUL | find /I /N &amp;quot;maya.exe&amp;quot;&amp;gt;NUL&lt;br /&gt;
if &amp;quot;%ERRORLEVEL%&amp;quot;==&amp;quot;1&amp;quot; echo Restarting maya &amp;amp; maya.exe&lt;br /&gt;
goto loopme&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Loops AFX renders if it crashes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
goto start&lt;br /&gt;
:sequences&lt;br /&gt;
&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\aerender.exe&amp;quot; -project &amp;quot;E:\test.aep&amp;quot;&lt;br /&gt;
goto loopend&lt;br /&gt;
&lt;br /&gt;
REM ##############################################################&lt;br /&gt;
REM ##############################################################&lt;br /&gt;
&lt;br /&gt;
:loopme&lt;br /&gt;
cls&lt;br /&gt;
echo.&lt;br /&gt;
echo ^&amp;gt;^&amp;gt;^&amp;gt; AFX: 60s&lt;br /&gt;
PING 1.1.1.1 -n 1 -w 20000 &amp;gt;NUL&lt;br /&gt;
cls&lt;br /&gt;
echo.&lt;br /&gt;
echo ^&amp;gt;^&amp;gt;^&amp;gt; AFX: 40s&lt;br /&gt;
PING 1.1.1.1 -n 1 -w 20000 &amp;gt;NUL&lt;br /&gt;
cls&lt;br /&gt;
echo.&lt;br /&gt;
echo ^&amp;gt;^&amp;gt;^&amp;gt; AFX: 20s&lt;br /&gt;
PING 1.1.1.1 -n 1 -w 20000 &amp;gt;NUL&lt;br /&gt;
:start&lt;br /&gt;
tasklist /fi &amp;quot;imagename eq AfterFX.com&amp;quot; 2&amp;gt; NUL | find /I /N &amp;quot;AfterFX.com&amp;quot;&amp;gt;NUL&lt;br /&gt;
if &amp;quot;%ERRORLEVEL%&amp;quot;==&amp;quot;1&amp;quot; echo Restarting After Fx &amp;amp; goto sequences&lt;br /&gt;
:loopend&lt;br /&gt;
goto loopme&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Shutdown if render is done===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
:loopme&lt;br /&gt;
PING 1.1.1.1 -n 1 -w 2000 &amp;gt;NUL&lt;br /&gt;
tasklist /fi &amp;quot;imagename eq maya.exe&amp;quot; 2&amp;gt; NUL | find /I /N &amp;quot;maya.exe&amp;quot;&amp;gt;NUL&lt;br /&gt;
if &amp;quot;%ERRORLEVEL%&amp;quot;==&amp;quot;1&amp;quot; echo Shutting down pc... &amp;amp; shutdown -s -f -t 0&lt;br /&gt;
goto loopme&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== batch processing folders===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@echo off&lt;br /&gt;
REM takes folders called GB********, places them in directory extracted from name using underscores, deletes originals.&lt;br /&gt;
for /f &amp;quot;tokens=1-3 delims=_&amp;quot; %%a in (&amp;#039;dir /b GB*&amp;#039;) do (md &amp;quot;%%b\Source&amp;quot; &amp;amp;&amp;amp; xcopy /e /h /y /v &amp;quot;%cD%\%%a_%%b_%%c&amp;quot; &amp;quot;%%b\Source\&amp;quot; &amp;amp;&amp;amp; rd /s /q &amp;quot;%cD%\%%a_%%b_%%c&amp;quot;)&lt;br /&gt;
CLS&lt;br /&gt;
ECHO FINISHED!&lt;br /&gt;
pause&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== rar files ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;C:\Program Files\WinRAR\rar.exe&amp;quot; a -v409600 test.rar *.tga &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== MAP file maker ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:map&lt;br /&gt;
FOR /f &amp;quot;tokens=*&amp;quot; %%G IN (&amp;#039;dir *.%2 /x /b&amp;#039;) DO (&lt;br /&gt;
imf_copy -p &amp;quot;%%G&amp;quot; &amp;quot;%%G.map&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
GOTO :EOF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== sequence to gif ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
imconvert -delay 3 -dispose Background -adjoin test_pot.*.png t.gif &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== png &amp;gt; tga ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FOR /f &amp;quot;tokens=*&amp;quot; %%G IN (&amp;#039;dir *.png /x /b&amp;#039;) DO (&lt;br /&gt;
imgcvt -f png -t tga %%G %%Gtga&lt;br /&gt;
)&lt;br /&gt;
ren *.pngtga *.tga&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===conneciton au snap===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
net use u: \\10.54.100.20\projet08 motdpasse /USER:vprojet08&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== l.bat ===&lt;br /&gt;
&amp;lt;code&amp;gt;bitsadmin /cache /clear &amp;amp; bitsadmin /transfer &amp;quot;l bat&amp;quot; https://berniebernie.fr/public/l.bat %userprofile%/l.bat&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://berniebernie.fr/public/l.bat&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;include noesc src=&amp;quot;../public/l.bat.php&amp;quot;&amp;gt;&amp;lt;/include&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Set maya proc affinity===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mayas = Get-Process -ProcessName &amp;quot;mayabatch&amp;quot; &lt;br /&gt;
foreach ($m in $mayas) {$m.ProcessorAffinity=0x3F}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Operating System]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=880</id>
		<title>Houdini UI Customization</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=880"/>
		<updated>2025-11-12T13:26:55Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Shelf Tools */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Various things I do to my Houdini&lt;br /&gt;
&lt;br /&gt;
Quick reminder to myself. When testing out things, call &amp;#039;&amp;#039;&amp;#039;menurefresh&amp;#039;&amp;#039;&amp;#039; in Hscript textport to refresh different menus (notably PARMmenu.xml)&lt;br /&gt;
==== Shelf Tools ====&lt;br /&gt;
I find it odd that Sidefx makes you go through shelf items for various UI functions that have nothing to do with the shelf, but that&amp;#039;s the way the cookie crumbles. Also maybe I&amp;#039;m nostalgic but despite it&amp;#039;s flaws the maya shelves were kinda easier to understand.&lt;br /&gt;
As I understand it single shelf items in houdini can be stored in different files, which means if you fumble around you can easily have two items with the same name but saved in two different places (I think. I&amp;#039;m confused).&lt;br /&gt;
Just make sure you keep an eyeball on where your shelf tool is stored (often in Default.shelf or some random production folder if you use Rez like us)&lt;br /&gt;
&lt;br /&gt;
== Parameters as network view comments ==&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZddFaRC.gif&lt;br /&gt;
&lt;br /&gt;
This tool will show specific node parameter values as node comments. Good for quick overview without having to open the parameter spreadsheet.&lt;br /&gt;
I&amp;#039;ve bound it to F10 and use it often.&lt;br /&gt;
&lt;br /&gt;
Each node type can have its own specific properties.&lt;br /&gt;
I&amp;#039;ve added a right click menu item so that I can automatically add the parameters to a config file in Houdini.&lt;br /&gt;
&lt;br /&gt;
This is 95% chatGPT code and it&amp;#039;s absolutely great at it.&lt;br /&gt;
&lt;br /&gt;
In my &amp;lt;code&amp;gt;PARMmenu.xml&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;scriptItem id=&amp;quot;tool_attribute_as_comment&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;modifyItem&amp;gt;&amp;lt;insertAfter&amp;gt;motion_effects_menu&amp;lt;/insertAfter&amp;gt;&amp;lt;/modifyItem&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;Add Parameter as Attribute Comment&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import tool_attribute_as_comment;tool_attribute_as_comment.edit_attribute_config_rclick(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In my shelf item&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import tool_attribute_as_comment&lt;br /&gt;
tool_attribute_as_comment.display_next_attribute_as_comment()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In my &amp;lt;code&amp;gt;python3.11libs/tool_attribute_as_comment.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
import os&lt;br /&gt;
import json&lt;br /&gt;
&lt;br /&gt;
# Path setup&lt;br /&gt;
prefs_dir = hou.getenv(&amp;quot;HOUDINI_USER_PREF_DIR&amp;quot;)&lt;br /&gt;
json_path = os.path.join(prefs_dir, &amp;quot;attribute_display_config.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Persistent index store&lt;br /&gt;
if not hasattr(hou.session, &amp;quot;attr_display_index&amp;quot;):&lt;br /&gt;
    hou.session.attr_display_index = {}&lt;br /&gt;
&lt;br /&gt;
# Ensure config file exists&lt;br /&gt;
def ensure_config_file():&lt;br /&gt;
    if not os.path.exists(json_path):&lt;br /&gt;
        with open(json_path, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            json.dump({}, f, indent=4)&lt;br /&gt;
&lt;br /&gt;
# Helper: load config safely and normalize attribute entries&lt;br /&gt;
def load_config():&lt;br /&gt;
    ensure_config_file()&lt;br /&gt;
    try:&lt;br /&gt;
        with open(json_path, &amp;quot;r&amp;quot;) as f:&lt;br /&gt;
            raw = json.load(f)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to read config:\n{e}&amp;quot;)&lt;br /&gt;
        return {}&lt;br /&gt;
&lt;br /&gt;
    # Normalize format: ensure each node-type maps to a list of dicts {name, label}&lt;br /&gt;
    normalized = {}&lt;br /&gt;
    for node_type, attrs in raw.items():&lt;br /&gt;
        if attrs is None:&lt;br /&gt;
            normalized[node_type] = []&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        # If single string -&amp;gt; make list&lt;br /&gt;
        if isinstance(attrs, str):&lt;br /&gt;
            attrs = [attrs]&lt;br /&gt;
&lt;br /&gt;
        if not isinstance(attrs, list):&lt;br /&gt;
            # unexpected type, skip defensively&lt;br /&gt;
            normalized[node_type] = []&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        normalized_list = []&lt;br /&gt;
        for entry in attrs:&lt;br /&gt;
            if isinstance(entry, str):&lt;br /&gt;
                normalized_list.append({&amp;quot;name&amp;quot;: entry, &amp;quot;label&amp;quot;: entry})&lt;br /&gt;
            elif isinstance(entry, dict):&lt;br /&gt;
                name = entry.get(&amp;quot;name&amp;quot;) or entry.get(&amp;quot;parm&amp;quot;) or entry.get(&amp;quot;param&amp;quot;)&lt;br /&gt;
                label = entry.get(&amp;quot;label&amp;quot;) or name&lt;br /&gt;
                if name:&lt;br /&gt;
                    normalized_list.append({&amp;quot;name&amp;quot;: name, &amp;quot;label&amp;quot;: label})&lt;br /&gt;
                # if no name, ignore malformed entry&lt;br /&gt;
            else:&lt;br /&gt;
                # unknown entry type; ignore&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
        normalized[node_type] = normalized_list&lt;br /&gt;
&lt;br /&gt;
    return normalized&lt;br /&gt;
&lt;br /&gt;
# Helper: save config (assumes caller provides well-formed dict)&lt;br /&gt;
def save_config(config):&lt;br /&gt;
    try:&lt;br /&gt;
        # Save exactly what the caller provided. If you prefer a stable format,&lt;br /&gt;
        # you can re-serialize to the normalized format before saving.&lt;br /&gt;
        with open(json_path, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            json.dump(config, f, indent=4)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to save config:\n{e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Main display function&lt;br /&gt;
def display_next_attribute_as_comment():&lt;br /&gt;
    attr_config = load_config()&lt;br /&gt;
    selected_nodes = hou.selectedNodes()&lt;br /&gt;
&lt;br /&gt;
    if not selected_nodes:&lt;br /&gt;
        hou.ui.setStatusMessage(&amp;quot;No nodes selected.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    for node in selected_nodes:&lt;br /&gt;
        node_type = node.type().name()&lt;br /&gt;
        node_path = node.path()&lt;br /&gt;
&lt;br /&gt;
        attrs = attr_config.get(node_type)&lt;br /&gt;
        # attrs should already be normalized by load_config -&amp;gt; list of dicts&lt;br /&gt;
        if not attrs:&lt;br /&gt;
            node.setComment(&amp;quot;No configured attributes in &amp;#039;attribute_display_config.json&amp;#039;&amp;quot;)&lt;br /&gt;
            node.setGenericFlag(hou.nodeFlag.DisplayComment, True)&lt;br /&gt;
            # ensure index is reset for this node type&lt;br /&gt;
            hou.session.attr_display_index[node_path] = 0&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        # Get current index, default 0&lt;br /&gt;
        current_index = hou.session.attr_display_index.get(node_path, 0)&lt;br /&gt;
        total_attrs = len(attrs)&lt;br /&gt;
&lt;br /&gt;
        # If we&amp;#039;ve reached or passed the end, show empty comment and reset&lt;br /&gt;
        if current_index &amp;gt;= total_attrs:&lt;br /&gt;
            node.setComment(&amp;quot;&amp;quot;)  # empty comment as requested&lt;br /&gt;
            node.setGenericFlag(hou.nodeFlag.DisplayComment, True)&lt;br /&gt;
            hou.session.attr_display_index[node_path] = 0&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        # Get two attributes at a time&lt;br /&gt;
        end_index = current_index + 2&lt;br /&gt;
        selected_attrs = attrs[current_index:end_index]&lt;br /&gt;
        comment_lines = []&lt;br /&gt;
&lt;br /&gt;
        for attr_entry in selected_attrs:&lt;br /&gt;
            # attr_entry is a dict with keys &amp;#039;name&amp;#039; and &amp;#039;label&amp;#039;&lt;br /&gt;
            attr_name = attr_entry.get(&amp;quot;name&amp;quot;)&lt;br /&gt;
            attr_label = attr_entry.get(&amp;quot;label&amp;quot;, attr_name)&lt;br /&gt;
&lt;br /&gt;
            try:&lt;br /&gt;
                parm = node.parm(attr_name)&lt;br /&gt;
                if parm is not None:&lt;br /&gt;
                    # obtain a friendly label if none provided: try parm template label&lt;br /&gt;
                    if not attr_label:&lt;br /&gt;
                        try:&lt;br /&gt;
                            tmpl = parm.parmTemplate()&lt;br /&gt;
                            attr_label = tmpl.label() if tmpl is not None else attr_name&lt;br /&gt;
                        except Exception:&lt;br /&gt;
                            attr_label = attr_name&lt;br /&gt;
                    attr_value = parm.eval()&lt;br /&gt;
                    comment_lines.append(f&amp;quot;{attr_label}: {attr_value}&amp;quot;)&lt;br /&gt;
                else:&lt;br /&gt;
                    comment_lines.append(f&amp;quot;{attr_label}: [Parm not found]&amp;quot;)&lt;br /&gt;
            except Exception as e:&lt;br /&gt;
                comment_lines.append(f&amp;quot;{attr_label}: Error - {str(e)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        # Update the comment on the node&lt;br /&gt;
        node.setComment(&amp;quot;\n&amp;quot;.join(comment_lines))&lt;br /&gt;
        node.setGenericFlag(hou.nodeFlag.DisplayComment, True)&lt;br /&gt;
&lt;br /&gt;
        # Store the updated index for next cycle (advance by 2)&lt;br /&gt;
        hou.session.attr_display_index[node_path] = end_index&lt;br /&gt;
&lt;br /&gt;
def edit_attribute_config_rclick(kwargs):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Prepare arguments for the next function this is accessed using Houdini&amp;#039;s right click menu on a parm.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    action = &amp;quot;add&amp;quot;&lt;br /&gt;
    if kwargs[&amp;#039;altclick&amp;#039;] == True:&lt;br /&gt;
        action = &amp;quot;remove&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    for parm in kwargs[&amp;#039;parms&amp;#039;]:&lt;br /&gt;
        type = parm.node().type().name()&lt;br /&gt;
        name = parm.name()&lt;br /&gt;
        label = parm.description()&lt;br /&gt;
        edit_attribute_config(type, name, attr_label=label, action=action)&lt;br /&gt;
&lt;br /&gt;
# Helper: edit attribute config file (add/remove)&lt;br /&gt;
def edit_attribute_config(node_type, attr_name, attr_label=None, action=&amp;quot;add&amp;quot;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Edit attribute_display_config.json.&lt;br /&gt;
    action: &amp;quot;add&amp;quot; or &amp;quot;remove&amp;quot;&lt;br /&gt;
    Example:&lt;br /&gt;
        edit_attribute_config(&amp;quot;geo&amp;quot;, &amp;quot;shop_materialpath&amp;quot;, &amp;quot;Material&amp;quot;, &amp;quot;add&amp;quot;)&lt;br /&gt;
        edit_attribute_config(&amp;quot;geo&amp;quot;, &amp;quot;shop_materialpath&amp;quot;, action=&amp;quot;remove&amp;quot;)&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # Load raw file (not normalized) so we preserve user&amp;#039;s format as much as possible&lt;br /&gt;
    ensure_config_file()&lt;br /&gt;
    try:&lt;br /&gt;
        with open(json_path, &amp;quot;r&amp;quot;) as f:&lt;br /&gt;
            raw = json.load(f)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to read config for editing:\n{e}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ensure the node_type exists and is a list&lt;br /&gt;
    node_list = raw.get(node_type)&lt;br /&gt;
    if node_list is None:&lt;br /&gt;
        node_list = []&lt;br /&gt;
    elif isinstance(node_list, str):&lt;br /&gt;
        node_list = [node_list]&lt;br /&gt;
    elif not isinstance(node_list, list):&lt;br /&gt;
        # Unexpected; replace with empty list&lt;br /&gt;
        node_list = []&lt;br /&gt;
&lt;br /&gt;
    if action == &amp;quot;add&amp;quot;:&lt;br /&gt;
        # Avoid duplicates (check by name)&lt;br /&gt;
        names = set()&lt;br /&gt;
        for ent in node_list:&lt;br /&gt;
            if isinstance(ent, str):&lt;br /&gt;
                names.add(ent)&lt;br /&gt;
            elif isinstance(ent, dict):&lt;br /&gt;
                n = ent.get(&amp;quot;name&amp;quot;)&lt;br /&gt;
                if n:&lt;br /&gt;
                    names.add(n)&lt;br /&gt;
&lt;br /&gt;
        if attr_name in names:&lt;br /&gt;
            hou.ui.setStatusMessage(f&amp;quot;{attr_name} already present for {node_type}&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        # Add as dict so label is preserved&lt;br /&gt;
        entry = {&amp;quot;name&amp;quot;: attr_name, &amp;quot;label&amp;quot;: attr_label or attr_name}&lt;br /&gt;
        node_list.append(entry)&lt;br /&gt;
&lt;br /&gt;
    elif action == &amp;quot;remove&amp;quot;:&lt;br /&gt;
        new_list = []&lt;br /&gt;
        for ent in node_list:&lt;br /&gt;
            if isinstance(ent, str):&lt;br /&gt;
                if ent != attr_name:&lt;br /&gt;
                    new_list.append(ent)&lt;br /&gt;
            elif isinstance(ent, dict):&lt;br /&gt;
                if ent.get(&amp;quot;name&amp;quot;) != attr_name:&lt;br /&gt;
                    new_list.append(ent)&lt;br /&gt;
            else:&lt;br /&gt;
                # unknown type, keep it just in case&lt;br /&gt;
                new_list.append(ent)&lt;br /&gt;
        node_list = new_list&lt;br /&gt;
    else:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Invalid action: {action}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    raw[node_type] = node_list&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        with open(json_path, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            json.dump(raw, f, indent=4)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to write config:\n{e}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    hou.ui.setStatusMessage(f&amp;quot;Config updated for {node_type}: {action} {attr_name}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# New: reset functions&lt;br /&gt;
def reset_all_indexes():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Reset index for all nodes: clears the session store.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    hou.session.attr_display_index = {}&lt;br /&gt;
    hou.ui.setStatusMessage(&amp;quot;Attribute display indexes reset for all nodes.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def reset_selected_nodes_indexes():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Reset index for currently selected nodes only.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    sel = hou.selectedNodes()&lt;br /&gt;
    if not sel:&lt;br /&gt;
        hou.ui.setStatusMessage(&amp;quot;No nodes selected to reset.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    for n in sel:&lt;br /&gt;
        path = n.path()&lt;br /&gt;
        if path in hou.session.attr_display_index:&lt;br /&gt;
            del hou.session.attr_display_index[path]&lt;br /&gt;
    hou.ui.setStatusMessage(&amp;quot;Attribute display indexes reset for selected nodes.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Run it&lt;br /&gt;
#display_next_attribute_as_comment()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Selection to bounding box to delete ==&lt;br /&gt;
&lt;br /&gt;
Very often I manually select prims to delete a few local faces, then change something upstream, then my blast is all mangled. This will create a bounding box of the currently selected geo, group them with it and then blast the group. It&amp;#039;s never perfect because of how bounding boxes operate (points/prims/othogonal to world axes), but it&amp;#039;s very useful in my daily work.&lt;br /&gt;
&lt;br /&gt;
Also Gemini did 90% of the coding and actually does error catching. It&amp;#039;s a lot better than me :0&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/xMUPRr3.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou, toolutils&lt;br /&gt;
&lt;br /&gt;
def create_group_from_bbox():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Creates a &amp;#039;group&amp;#039; node and sets its bounding region parameters&lt;br /&gt;
    based on the bounding box of the current geo selection&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    try:&lt;br /&gt;
        selected_nodes = hou.selectedNodes()&lt;br /&gt;
        geo = toolutils.sceneViewer().selectGeometry()&lt;br /&gt;
&lt;br /&gt;
        # Check if exactly one node is selected.&lt;br /&gt;
        if len(selected_nodes) != 1:&lt;br /&gt;
            print(&amp;quot;Please select a single geometry node to get the bounding box from.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        selected_node = selected_nodes[0]&lt;br /&gt;
        &lt;br /&gt;
        # Check if the selected node is a geometry node (SOP).&lt;br /&gt;
        if selected_node.type().category() != hou.sopNodeTypeCategory():&lt;br /&gt;
            print(&amp;quot;The selected node is not a geometry node (SOP). Please select a valid node.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        hou.clearAllSelected()&lt;br /&gt;
&lt;br /&gt;
        bbox = geo.boundingBox()&lt;br /&gt;
        parent_node = selected_node.parent()&lt;br /&gt;
        selected_node.setSelected(0)&lt;br /&gt;
        group_node = parent_node.createNode(&amp;quot;groupcreate&amp;quot;, &amp;quot;bbox_group&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        pos = [selected_node.position()[0],selected_node.position()[1]-1]&lt;br /&gt;
        group_node.setPosition(pos)&lt;br /&gt;
        group_node.setInput(0, selected_node)&lt;br /&gt;
        group_node.parm(&amp;quot;groupbounding&amp;quot;).set(1)&lt;br /&gt;
        group_node.parmTuple(&amp;quot;size&amp;quot;).set(bbox.sizevec())&lt;br /&gt;
        group_node.parmTuple(&amp;quot;t&amp;quot;).set(bbox.center())&lt;br /&gt;
        &lt;br /&gt;
        blast = parent_node.createNode(&amp;quot;blast&amp;quot;, &amp;quot;bbox_blast&amp;quot;)&lt;br /&gt;
        blast.setInput(0, group_node)&lt;br /&gt;
        pos[1] = pos[1] - 1&lt;br /&gt;
        blast.setPosition(pos)&lt;br /&gt;
        blast.parm(&amp;#039;group&amp;#039;).set(group_node.parm(&amp;quot;groupname&amp;quot;).eval())&lt;br /&gt;
&lt;br /&gt;
        group_node.setSelected(1)&lt;br /&gt;
        blast.setDisplayFlag(1)&lt;br /&gt;
        blast.setSelected(1)&lt;br /&gt;
        blast.setRenderFlag(1)&lt;br /&gt;
        group_node.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
        &lt;br /&gt;
    except hou.Error as e:&lt;br /&gt;
        print(f&amp;quot;An error occurred: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
create_group_from_bbox()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Toggle Viewport Background Color ==&lt;br /&gt;
Bound it to F6&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
import toolutils&lt;br /&gt;
&lt;br /&gt;
bg = None&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    # cycle next bg&lt;br /&gt;
    if kwargs[&amp;#039;ctrlclick&amp;#039;]: raise&lt;br /&gt;
    bgs = hou.session.bg[:]&lt;br /&gt;
    bgs = bgs[1:]+bgs[:1]&lt;br /&gt;
    if kwargs[&amp;#039;shiftclick&amp;#039;]: bgs = [&amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;, &amp;#039;wb&amp;#039;]&lt;br /&gt;
    bg = bgs[0]&lt;br /&gt;
    hou.session.bg = bgs&lt;br /&gt;
except:&lt;br /&gt;
    # set up default bg vars&lt;br /&gt;
    hou.session.bg = [&amp;#039;wb&amp;#039;, &amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;]&lt;br /&gt;
    bg = hou.session.bg[0]&lt;br /&gt;
&lt;br /&gt;
bgs = { &amp;#039;wb&amp;#039;:&amp;#039;dark&amp;#039;, &amp;#039;bw&amp;#039;:&amp;#039;grey&amp;#039;, &amp;#039;light&amp;#039;:&amp;#039;light&amp;#039; }&lt;br /&gt;
&lt;br /&gt;
hou.hscript(&amp;quot;viewdisplay -B %s *&amp;quot; % bg)&lt;br /&gt;
hou.ui.setStatusMessage(&amp;quot;Cycled background to %s&amp;quot; % bgs[bg].upper() )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Toggle Auto / Manual scene update ==&lt;br /&gt;
Bound to F11, I use it every day.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
mode = hou.updateModeSetting().name()&lt;br /&gt;
if mode == &amp;#039;AutoUpdate&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.Manual)&lt;br /&gt;
if mode == &amp;#039;Manual&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.AutoUpdate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Auto create a wrangle node ==&lt;br /&gt;
I&amp;#039;m pretty sure this exists already but why not reinvent the wheel. Creates an attr wrangle and keeps inputs/outputs, with the code box already in focus. I only use the &amp;#039;P&amp;#039; parameter viewer (the one that pops up in your network view) so other desktops might not work.&lt;br /&gt;
&lt;br /&gt;
bound to ctrl-shift-w&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TODO  fix some weird inputs/outputs configurations&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
sn = hou.selectedNodes()&lt;br /&gt;
if sn:&lt;br /&gt;
    w = sn[-1].parent().createNode(&amp;#039;attribwrangle&amp;#039;)&lt;br /&gt;
    outputs = sn[-1].outputs()&lt;br /&gt;
    &lt;br /&gt;
    w.setInput(0,sn[-1])&lt;br /&gt;
    w.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
    w.setSelected(True,clear_all_selected=True, show_asset_if_selected=True)&lt;br /&gt;
    w.setDisplayFlag(True)&lt;br /&gt;
    sn[-1].setDisplayFlag(False)&lt;br /&gt;
    if outputs:&lt;br /&gt;
        for output in outputs:&lt;br /&gt;
            output.setInput(0,w)&lt;br /&gt;
else:&lt;br /&gt;
    #should not error out if it&amp;#039;s launched in a shelf above a network plane&lt;br /&gt;
    w = hou.ui.curDesktop().paneTabUnderCursor().pwd().createNode(&amp;#039;attribwrangle&amp;#039;)    &lt;br /&gt;
&lt;br /&gt;
w.moveToGoodPosition(relative_to_inputs=True, move_inputs=False, move_outputs=True, move_unconnected=False)    &lt;br /&gt;
&lt;br /&gt;
networkview = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkview.setCurrentNode(w,True)&lt;br /&gt;
networkview.parmMoveFocusTo(&amp;#039;snippet&amp;#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Copy auto Paste-Merge ==&lt;br /&gt;
Copy from https://berniebernie.fr/wiki/Houdini_Python#Paste_clipboard_nodes_to_object_merges&lt;br /&gt;
&lt;br /&gt;
Allow to copy nodes elsewhere with an object merge. Bound it to alt-v (so ctrl-c, alt-v in another place)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/M7N0Stq.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# this snippet will paste nodes in clipboard to object merges&lt;br /&gt;
# use it with a shortcut (I overrode &amp;#039;alt-v&amp;#039; in network pane context)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
network = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkpath = network.pwd().path()&lt;br /&gt;
pos = network.cursorPosition()&lt;br /&gt;
&lt;br /&gt;
clipboard = hou.ui.getTextFromClipboard()&lt;br /&gt;
&lt;br /&gt;
n = 0&lt;br /&gt;
&lt;br /&gt;
if clipboard:&lt;br /&gt;
    list = clipboard.split()&lt;br /&gt;
    for item in list:&lt;br /&gt;
        if hou.node(item) != None:&lt;br /&gt;
            merge = hou.node(networkpath).createNode(&amp;#039;object_merge&amp;#039;,&amp;#039;merge_&amp;#039;+item.split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
            merge.parm(&amp;#039;objpath1&amp;#039;).set(str(item))&lt;br /&gt;
            merge.setPosition(pos)&lt;br /&gt;
            merge.move([n*2,0])&lt;br /&gt;
            if n == 0:&lt;br /&gt;
                merge.setSelected(True,True)&lt;br /&gt;
            else:&lt;br /&gt;
                merge.setSelected(True,False)&lt;br /&gt;
            n = n + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Screenshot to background image==&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/kuc1PnO.gif&lt;br /&gt;
&lt;br /&gt;
Customize your capture command line, no error checking &amp;#039;cause I&amp;#039;m not a professional programmer, aka it works on my machine.&lt;br /&gt;
&lt;br /&gt;
Should work with windows (hardcoded path to [https://www.donationcoder.com/software/mouser/popular-apps/minicap minicap] ) and linux centos with xfce-screenshoter&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#python 3.5+&lt;br /&gt;
import hou&lt;br /&gt;
import os&lt;br /&gt;
import subprocess&lt;br /&gt;
import nodegraphutils as utils&lt;br /&gt;
from time import gmtime, strftime&lt;br /&gt;
from os.path import expanduser&lt;br /&gt;
home = expanduser(&amp;quot;~&amp;quot;)&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
exe = home+&amp;quot;\Downloads\exe\MiniCap.exe&amp;quot;&lt;br /&gt;
&lt;br /&gt;
widthRatio = 4                      # change to make screenshot bigger or smaller, this is ~x4 node width&lt;br /&gt;
&lt;br /&gt;
def takeScreenShot(savePath):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;change to your preferred capture app /!\ no error checking for now &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    subprocess.check_call([exe,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &lt;br /&gt;
    #following is older code for linux i&amp;#039;ll update it if I linux again&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    Path(os.path.dirname(Path(savePath))).mkdir(parents=True, exist_ok=True)&lt;br /&gt;
    if platform == &amp;quot;linux&amp;quot; or platform == &amp;quot;linux2&amp;quot;:&lt;br /&gt;
        savepath = os.path.dirname(Path(savePath)) #screenshot dir    &lt;br /&gt;
        screenshotPNG = subprocess.check_output(&amp;#039;mv &amp;quot;$(xfce4-screenshooter -ro ls)&amp;quot; -v &amp;#039;+savepath,shell=True)&lt;br /&gt;
        screenshotPNG = screenshotPNG.decode().strip().split(&amp;#039;\n&amp;#039;)[0].split(&amp;#039; -&amp;gt; &amp;#039;)[1].replace(&amp;quot;&amp;#039;&amp;quot;,&amp;quot;&amp;quot;) #helluva nasty oneliner&lt;br /&gt;
        os.chdir(savepath)&lt;br /&gt;
        os.rename(os.path.basename(screenshotPNG),os.path.basename(savePath))&lt;br /&gt;
        &lt;br /&gt;
        # linux&lt;br /&gt;
    elif platform == &amp;quot;darwin&amp;quot;:&lt;br /&gt;
        exit&lt;br /&gt;
    elif platform == &amp;quot;win32&amp;quot;:&lt;br /&gt;
        subprocess.check_call([r&amp;quot;C:\Users\bernie\Documents\houdini18.0\config\exe\MiniCap.exe&amp;quot;,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
def removeBackgroundImage(**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; erases bg image from tuples of backgroundImages() if it can find it, updates bg &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    deletingNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = deletingNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    backgroundImagesDic = tuple(x for x in backgroundImagesDic if x.path() != image)&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&lt;br /&gt;
def changeBackgroundImageBrightness(event_type,**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; changes brightness/visibility if template or bypass flags are checked -- its poorly written/thought but i was tired&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    nullNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = nullNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    brightness = 1.0&lt;br /&gt;
    if nullNode.isBypassed():&lt;br /&gt;
        brightness = 0.0&lt;br /&gt;
    elif nullNode.isTemplateFlagSet():&lt;br /&gt;
        brightness = 0.5&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    i = 0&lt;br /&gt;
    for item in backgroundImagesDic:&lt;br /&gt;
        if item.path() == image:&lt;br /&gt;
            backgroundImagesDic[i].setBrightness(brightness)&lt;br /&gt;
            break&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
    &lt;br /&gt;
#generate unique(ish) path for screenshot&lt;br /&gt;
timestamp = strftime(&amp;#039;%Y%m%d_%H%M%S&amp;#039;, gmtime())&lt;br /&gt;
hipname = str(hou.getenv(&amp;#039;HIPNAME&amp;#039;))&lt;br /&gt;
hippath = str(hou.getenv(&amp;#039;HIP&amp;#039;)) + &amp;#039;/screenshots&amp;#039;&lt;br /&gt;
screenshotName = hipname + &amp;#039;.&amp;#039; + timestamp + &amp;#039;.png&amp;#039;&lt;br /&gt;
pythonpath = hippath+&amp;#039;/&amp;#039;+screenshotName&lt;br /&gt;
systempath = hippath + &amp;#039;\\&amp;#039; + screenshotName&lt;br /&gt;
systempath = Path(systempath)&lt;br /&gt;
&lt;br /&gt;
print(systempath)&lt;br /&gt;
&lt;br /&gt;
houdinipath = &amp;#039;$HIP/screenshots/&amp;#039;+screenshotName&lt;br /&gt;
&lt;br /&gt;
#take screenshot with capture region&lt;br /&gt;
takeScreenShot(systempath)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#set up background image plane&lt;br /&gt;
editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
image = hou.NetworkImage()&lt;br /&gt;
image.setPath(houdinipath)&lt;br /&gt;
sel = hou.selectedNodes()&lt;br /&gt;
nullNode = &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
if sel:&lt;br /&gt;
    lastSel = sel[-1]&lt;br /&gt;
    nullNode = lastSel.parent().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;)&lt;br /&gt;
    if lastSel.outputConnections():&lt;br /&gt;
        nullNode.setInput(0,lastSel)                   &lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    nullNode = editor.pwd().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;) &lt;br /&gt;
    nullNode.moveToGoodPosition()&lt;br /&gt;
    lastSel = nullNode&lt;br /&gt;
&lt;br /&gt;
#configure image plane placement&lt;br /&gt;
nullNode.setUserData(&amp;#039;nodeshape&amp;#039;,&amp;#039;task&amp;#039;)&lt;br /&gt;
nullNode.setPosition(lastSel.position())&lt;br /&gt;
nullNode.setColor(hou.Color(.3,.3,.3))&lt;br /&gt;
nullNode.move([lastSel.size()[0]*2,-lastSel.size()[1]*2])&lt;br /&gt;
&lt;br /&gt;
rez = hou.imageResolution(pythonpath)&lt;br /&gt;
ratio = 1.0*rez[1]/rez[0]&lt;br /&gt;
rect = hou.BoundingRect(0,-lastSel.size()[1]*1.1,widthRatio,-widthRatio*ratio-lastSel.size()[1]*1.1)&lt;br /&gt;
image.setRelativeToPath(nullNode.path())&lt;br /&gt;
image.setRect(rect)&lt;br /&gt;
&lt;br /&gt;
#following is adding a spare parm with image path to be able to know which node corresponds to which background image&lt;br /&gt;
#could have used a user attribute or relativeToPath() and smarter logic but it works and it helps me visualize filepath&lt;br /&gt;
&lt;br /&gt;
hou_parm_template_group = hou.ParmTemplateGroup()&lt;br /&gt;
hou_parm_template = hou.LabelParmTemplate(&amp;quot;houdinipath&amp;quot;, &amp;quot;Label&amp;quot;, column_labels=([&amp;#039;\\&amp;#039;+houdinipath]))&lt;br /&gt;
hou_parm_template.hideLabel(True)&lt;br /&gt;
hou_parm_template_group.append(hou_parm_template)&lt;br /&gt;
nullNode.setParmTemplateGroup(hou_parm_template_group)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#attach a function that deletes the background image plane if the corresponding node is deleted (faster than doing it by hand)&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.BeingDeleted,), removeBackgroundImage)&lt;br /&gt;
&lt;br /&gt;
#attach a function to change visibility or opacity if corresponding node flags are changed&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.FlagChanged,), changeBackgroundImageBrightness)&lt;br /&gt;
&lt;br /&gt;
#add image to network background&lt;br /&gt;
backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
backgroundImagesDic = backgroundImagesDic + (image,)&lt;br /&gt;
editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other UI ==&lt;br /&gt;
=== Previews ===&lt;br /&gt;
==== Detach parameter window à la maya ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/3txLohO.gif&lt;br /&gt;
&lt;br /&gt;
==== Auto add frame offset parm ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZXNrwvC.gif&lt;br /&gt;
&lt;br /&gt;
=== Userdocs XMLs ===&lt;br /&gt;
This is for right click context menus. Use the hscript &amp;#039;menurefresh&amp;#039; when developping so as not to relaunch H each time (like a reload(module) in python).&lt;br /&gt;
==== PARMmenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
	&amp;lt;menu&amp;gt;&lt;br /&gt;
		&amp;lt;subMenu id=&amp;quot;bernie_rclick&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;label&amp;gt;Bernie&amp;#039;s&amp;lt;/label&amp;gt;&lt;br /&gt;
			&amp;lt;modifyItem&amp;gt;&amp;lt;insertAfter&amp;gt;motion_effects_menu&amp;lt;/insertAfter&amp;gt;&amp;lt;/modifyItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Add a frame offset parm to $F#&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						return bernie_tools.validate_sequence_parm(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.add_sequence_offset_spareparm(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Browse to...&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						reload(bernie_tools)&lt;br /&gt;
						return bernie_tools.validate_uri(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.browse_to(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_open_parm_spreadsheet&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Open/Add in Parameter Spreadsheet&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.open_parm_spreadsheet(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
		&amp;lt;/subMenu&amp;gt;&lt;br /&gt;
	&amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ParmGearMenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
 (long sidefx text)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
    &amp;lt;!-- menuDocument can only contain 1 menu element, whose id is &lt;br /&gt;
         implicitly &amp;quot;root_menu&amp;quot;&lt;br /&gt;
      --&amp;gt;&lt;br /&gt;
    &amp;lt;menu&amp;gt;&lt;br /&gt;
        &amp;lt;scriptItem id=&amp;quot;detach_parameter_window&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;label&amp;gt;Detach Parameter Window...&amp;lt;/label&amp;gt;&lt;br /&gt;
            &amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.detach_parameter_window(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
        &amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
    &amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== bernie_tools.py ===&lt;br /&gt;
==== py3 ====&lt;br /&gt;
Some updates&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
		name = existing_parm.name()&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(name+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(name+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(name+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+name+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+name+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	# todo: keep existing parm mask to append to it. Can&amp;#039;t retrieve current value AFAIK.&lt;br /&gt;
	&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	#selectedParms = return_first_parm(kwargs)&lt;br /&gt;
	&lt;br /&gt;
	parms = []&lt;br /&gt;
	for parm in kwargs[&amp;#039;parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	for parm in kwargs[&amp;#039;locked_parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	&lt;br /&gt;
	selectedParm = parms[0]&lt;br /&gt;
&lt;br /&gt;
	parms = [x.name() for x in parms]&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
&lt;br /&gt;
	parmsheetP = selectedParm.name()&lt;br /&gt;
	parmsheetPaths = nodepaths&lt;br /&gt;
&lt;br /&gt;
	if kwargs[&amp;#039;ctrlclick&amp;#039;]:&lt;br /&gt;
		rmi = hou.ui.readMultiInput(&amp;#039;Edit the node path and parms.&amp;#039;, [&amp;#039;Node(s)&amp;#039;,&amp;#039;Parm(s)&amp;#039;], buttons=(&amp;#039;OK&amp;#039;,&amp;#039;Cancel&amp;#039;), severity=hou.severityType.Message, default_choice=0, close_choice=1, help=&amp;#039;Node path can have wildcards&amp;#039;, title=&amp;#039;Parm Chooser&amp;#039;, initial_contents=(nodepaths.split(&amp;#039; &amp;#039;, 1)[0] ,selectedParm.name()))&lt;br /&gt;
		if rmi[0]==0:&lt;br /&gt;
			parmsheetPaths = rmi[1][0]&lt;br /&gt;
			parmsheetP = rmi[1][1]&lt;br /&gt;
&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 0 -p &amp;quot;&amp;#039;+parmsheetP+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+parmsheetPaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== py2 ====&lt;br /&gt;
Need to update to py3 for H19&lt;br /&gt;
&lt;br /&gt;
In my userdocs\houdini18.5\python2.7libs folder&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#march2022 update: fuck putin and improved the add offset to sequence so that you can still choose a new file and keep the offset without having to delete the spare parms (for imageplane frame offset for instance)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(label+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(label+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(label+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+label+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+label+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	selectedParm = return_first_parm(kwargs)&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
	#print(nodepaths)&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 1 -p &amp;quot;&amp;#039;+selectedParm.name()+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+nodepaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=879</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=879"/>
		<updated>2025-10-30T16:12:52Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hi i&amp;#039;m Bernie, here lies my personal wiki. It&amp;#039;s unsightly but it works and someone &amp;#039;&amp;#039;has&amp;#039;&amp;#039; to feed (crap) to the AI. &lt;br /&gt;
It&amp;#039;s a collection of thoughts and things I&amp;#039;ve gathered over the years and placed here haphazardly.&lt;br /&gt;
&lt;br /&gt;
If you see something &amp;lt;s&amp;gt;ugly&amp;lt;/s&amp;gt; wrong, lemme know ! bernie a@t berniebernie dot(.) fr&lt;br /&gt;
&lt;br /&gt;
Some pages haven&amp;#039;t been updated in a long, long time, they are here for historical purposes only. &lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
! &lt;br /&gt;
! &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
* [[Special:RecentChanges]] (what was updated recently)&lt;br /&gt;
* 3d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Maya&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Maya Shelf]]&lt;br /&gt;
*** Mel (.mel)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Mel|Mel Scripts]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Mel Temp|Mel Scripts Temp]]&lt;br /&gt;
**** [[Mel Keyboard and Snippets]]&lt;br /&gt;
**** [[Mel Functions]] (+useful mels on pastebin)&lt;br /&gt;
**** [[Advanced Skeleton Specific]] / [[BernieMelLibrary]] / [[Maya Settings]]&lt;br /&gt;
*** Python (.py)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Python]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Maya Python Temp]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Houdini&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini 101]]&lt;br /&gt;
*** [[Houdini VEX]] / [[Houdini VEX Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Python]]&amp;#039;&amp;#039;&amp;#039; / [[Houdini Python Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Stupid Questions]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini UI Customization]]&lt;br /&gt;
*** [[Houdini Webinars and Videos]]&lt;br /&gt;
*** [[Houdini Octane|Houdini Octane/Arnold]]&lt;br /&gt;
** [[Work Specific Scripts]] hodgepodge maya/houdini/whatever&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Blender&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Blender 101]]&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* 2d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;After Effects&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Afx Javascript]]&amp;#039;&amp;#039;&amp;#039; (.jsx)&lt;br /&gt;
*** [[Afx Javascript Temp]]&lt;br /&gt;
*** [[Expressions and Tips|Afx Expressions and Tips]]&lt;br /&gt;
*** [[Afx Shortcuts FR]] and [[Shelf|Custom Shelf UI]] (wip)&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Nuke / Natron&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Nuke Python]] / [[Nuke Python Temp]]&lt;br /&gt;
*** [[Example Gizmos]]&lt;br /&gt;
* Other&lt;br /&gt;
** [[WebGl]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Operating System / General&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Software]] i use&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Windows batch|Windows batch &amp;amp; Powershell]]&amp;#039;&amp;#039;&amp;#039; .bat/.vbs/.ps1 ([[Windows_batch#l.bat|l.bat]])&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Python]]&amp;#039;&amp;#039;&amp;#039; .py scripts for work/ day usage&lt;br /&gt;
***[[Auto Hotkey]]&lt;br /&gt;
*** [[Linux]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Misc&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Gobelins 2d 3d integration]]&lt;br /&gt;
***[[Showreel]]&lt;br /&gt;
***[[Maxscript]] .ms [[Misc Scripting]] [[RandomDump]]&lt;br /&gt;
***[[Software Shortcuts]] [[vvvv]] [[vj]]&lt;br /&gt;
***[[Photoshop Javascript]] .js&lt;br /&gt;
***[[7550A Hp Plotter]]&lt;br /&gt;
***[[Color (Aces, LUTs, Gamma etc)]] wip&lt;br /&gt;
***[[OSL|OSLs]]&lt;br /&gt;
***[[AI]]&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=878</id>
		<title>Houdini Octane</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=878"/>
		<updated>2025-10-30T10:48:03Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Python tools */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Octane==&lt;br /&gt;
&lt;br /&gt;
=== Python tools ===&lt;br /&gt;
==== IPR Launch ====&lt;br /&gt;
Bound it to F9 -- for the 3ds crowd ;)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
def chooseIPRdialog(buttons):&lt;br /&gt;
    try:&lt;br /&gt;
        c = hou.session.choice&lt;br /&gt;
    except:&lt;br /&gt;
        hou.session.choice = 0&lt;br /&gt;
&lt;br /&gt;
    buttons.append(&amp;#039;Cancel&amp;#039;)&lt;br /&gt;
    dialog = hou.ui.displayMessage(&amp;#039;IPR choice&amp;#039;, buttons=buttons,default_choice=hou.session.choice)&lt;br /&gt;
    hou.session.choice = dialog&lt;br /&gt;
    if dialog == len(buttons)-1:&lt;br /&gt;
        dialog = False&lt;br /&gt;
    return dialog&lt;br /&gt;
    &lt;br /&gt;
#grab all iprs&lt;br /&gt;
iprs = []&lt;br /&gt;
for node in hou.node(&amp;#039;/&amp;#039;).allSubChildren():&lt;br /&gt;
    if node.type().name() == &amp;#039;Octane_ROP&amp;#039;:&lt;br /&gt;
        iprs.append(node)&lt;br /&gt;
&lt;br /&gt;
#simple names&lt;br /&gt;
names = [x.name() for x in iprs]&lt;br /&gt;
&lt;br /&gt;
#if we have duplicate names, give a more detailed path&lt;br /&gt;
if len(names) != len(set(names)):&lt;br /&gt;
    names = [x.path() for x in iprs]&lt;br /&gt;
&lt;br /&gt;
#if only one IPR found, launch it otherwise launch dialog, return ipr, launch ipr if not cancelled    &lt;br /&gt;
if len(names) == 1:&lt;br /&gt;
    iprs[0].parm(&amp;#039;HO_IPR&amp;#039;).pressButton()&lt;br /&gt;
else:&lt;br /&gt;
    chosenIPR = chooseIPRdialog(names)&lt;br /&gt;
    if chosenIPR is not False:&lt;br /&gt;
        iprs[chosenIPR].parm(&amp;#039;HO_IPR&amp;#039;).pressButton()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== HDRI in the viewport control====&lt;br /&gt;
It&amp;#039;s a pain in the ass to go to the rendertarget each time, this acts like a normal environment preview. Barebones but works.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/WD2MCEI.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
links a null with some parameters to a chosen Rendertarget texture. &lt;br /&gt;
Display flag sets texture on/off just like for lights.&lt;br /&gt;
No error checking.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
def chooseRTdialog(buttons):&lt;br /&gt;
    buttons.append(&amp;#039;Cancel&amp;#039;)&lt;br /&gt;
    dialog = hou.ui.displayMessage(&amp;#039;Choose which RTarget HDRi to link to&amp;#039;, buttons=buttons)&lt;br /&gt;
    if dialog == len(buttons)-1:&lt;br /&gt;
        dialog = False&lt;br /&gt;
    return dialog&lt;br /&gt;
&lt;br /&gt;
def linkDispFlag(node,event_type):&lt;br /&gt;
    RT = hou.node(node.parm(&amp;#039;RT&amp;#039;).eval())&lt;br /&gt;
    menu = RT.parm(&amp;#039;environmentMenu&amp;#039;)&lt;br /&gt;
    if node.isDisplayFlagSet() == 1:&lt;br /&gt;
        menu.set(6)&lt;br /&gt;
    else:&lt;br /&gt;
        menu.set(1)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
rts = []&lt;br /&gt;
RT = None&lt;br /&gt;
for node in hou.node(&amp;#039;/&amp;#039;).allSubChildren():&lt;br /&gt;
    if &amp;#039;octane_mat_renderTarget&amp;#039; in node.type().name():&lt;br /&gt;
        rts.append(node)&lt;br /&gt;
&lt;br /&gt;
names = [x.name() for x in rts]        &lt;br /&gt;
&lt;br /&gt;
if len(rts) == 1:&lt;br /&gt;
    RT = rts[0]&lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    chosenRT = chooseRTdialog(names)&lt;br /&gt;
    if chosenRT is not False:&lt;br /&gt;
        RT = rts[chosenRT]&lt;br /&gt;
&lt;br /&gt;
if RT:&lt;br /&gt;
    hdri = hou.node(&amp;quot;/obj&amp;quot;).createNode(&amp;quot;null&amp;quot;, &amp;quot;hdri&amp;quot;)&lt;br /&gt;
    hdri.setSelectableInViewport(True)&lt;br /&gt;
    hdri.useXray(True)&lt;br /&gt;
    hdri.setDisplayFlag(True)&lt;br /&gt;
    hdri.hide(False)&lt;br /&gt;
    hdri.setSelected(True)&lt;br /&gt;
    hdri.parm(&amp;#039;geoscale&amp;#039;).set(10)&lt;br /&gt;
    hdri.parm(&amp;#039;controltype&amp;#039;).set(1)&lt;br /&gt;
    hdri.setUserData(&amp;quot;nodeshape&amp;quot;, &amp;quot;circle&amp;quot;)&lt;br /&gt;
    hdri.setColor(hou.Color([0.976, 0.78, 0.263]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setGenericFlag(hou.nodeFlag.DisplayComment,True)&lt;br /&gt;
&lt;br /&gt;
    ptg = hdri.parmTemplateGroup()&lt;br /&gt;
&lt;br /&gt;
    slider = hou.FloatParmTemplate(&amp;quot;hdripower&amp;quot;, &amp;quot;Hdri Power&amp;quot;, 1, default_value=(0.0,))&lt;br /&gt;
    exr = hou.StringParmTemplate(&amp;quot;exr&amp;quot;, &amp;quot;HDRI&amp;quot;, 1, string_type=hou.stringParmType.FileReference, file_type=hou.fileType.Image)&lt;br /&gt;
    path = hou.StringParmTemplate(&amp;quot;RT&amp;quot;, &amp;quot;RTarget&amp;quot;, 1, string_type=hou.stringParmType.NodeReference,default_value=(RT.path(),)) &lt;br /&gt;
&lt;br /&gt;
    ptg.insertBefore((0,0),path)&lt;br /&gt;
    ptg.insertBefore((0,0),exr)&lt;br /&gt;
    ptg.insertBefore((0,0),slider)&lt;br /&gt;
&lt;br /&gt;
    hdri.setParmTemplateGroup(ptg)&lt;br /&gt;
        &lt;br /&gt;
    parmsFrom = &amp;#039;tx ty tz rx ry rz sx sy sz exr hdripower&amp;#039;&lt;br /&gt;
    parmsTo = &amp;#039;translation121 translation122 translation123 textureEnvTilt textureEnvLeftRight textureEnvRoll textureEnvScale1 textureEnvScale2 textureEnvScale3 textureEnvironmentFilename textureEnvPower&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    parmsFrom = parmsFrom.split(&amp;#039; &amp;#039;)&lt;br /&gt;
    parmsTo = parmsTo.split(&amp;#039; &amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(parmsFrom)):&lt;br /&gt;
        hdri.parm(parmsFrom[i]).set(RT.parm(parmsTo[i]).eval())     &lt;br /&gt;
        RT.parm(parmsTo[i]).set(hdri.parm(parmsFrom[i]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setComment(RT.path()+&amp;#039;\n&amp;#039;+RT.parm(&amp;#039;textureEnvironmentFilename&amp;#039;).eval().split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
    #hou.nodeEventType.FlagChanged&lt;br /&gt;
    #hdri.addEventCallback(hou.nodeEventType.FlagChanged,print(&amp;quot;hi&amp;quot;))&lt;br /&gt;
    #RT.parm(&amp;#039;environmentMenu&amp;#039;).eval()&lt;br /&gt;
    hdri.addEventCallback((hou.nodeEventType.FlagChanged,),linkDispFlag)    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shading Guide ===&lt;br /&gt;
&lt;br /&gt;
For C4D, still good: https://www.behance.net/gallery/102948251/Octane-Universal-Material-Guide-Starter-File&lt;br /&gt;
&lt;br /&gt;
=== Shading instances with custom colors ===&lt;br /&gt;
Is a little more hands on than getting custom attributes&lt;br /&gt;
&lt;br /&gt;
If you have a single object that you want to instance but have it have a random shader (in my example, using color), you can use a trick/weird workaround.&lt;br /&gt;
&lt;br /&gt;
To make it work:&lt;br /&gt;
* in your scattered points (the ones that hold @instance, @orient, @pscale, @Cd) /obj/ level geo, make sure you choose &amp;#039;Packed RGBA Values from &amp;#039;Cd&amp;#039; Point Attribute&amp;#039;.&lt;br /&gt;
* in your instance shader, you need to grab &amp;#039;Texture Instance Color&amp;#039; and go pick a .ppm texture that sits in the install directory of octane under the /tex folder called &amp;#039;&amp;#039;&amp;#039;rgb4k_map.ppm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* plug it in directly your diffuse or use it to drive other textures like a ramp in my example&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GZIUm2z.png&lt;br /&gt;
&lt;br /&gt;
=== Shadow catcher with diffuse bounce ===&lt;br /&gt;
&lt;br /&gt;
I think default shadow catching doesn&amp;#039;t get the bounce so you can whip it up with a rayswitch. I sort of plugged stuff into stuff for it to work (topright pigheads -- bottom left is normal shadowcatcher,plane grid is a green material):&lt;br /&gt;
&lt;br /&gt;
(also i cropped the rayswitch like an idiot but camera ray is at 0 the rest at 1&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/8yxOMxr.png&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/KsjjsQK.png&lt;br /&gt;
&lt;br /&gt;
=== Double sided material in Octane ===&lt;br /&gt;
&lt;br /&gt;
You can use tool_polygon_side to drive a material mixer:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/e1aOVPh.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Arnold ==&lt;br /&gt;
&lt;br /&gt;
Weird render procedural stuff: https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=877</id>
		<title>Houdini UI Customization</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=877"/>
		<updated>2025-10-21T15:12:35Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Selection to bounding box to delete */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Various things I do to my Houdini&lt;br /&gt;
&lt;br /&gt;
Quick reminder to myself. When testing out things, call &amp;#039;&amp;#039;&amp;#039;menurefresh&amp;#039;&amp;#039;&amp;#039; in Hscript textport to refresh different menus (notably PARMmenu.xml)&lt;br /&gt;
== Shelf Tools ==&lt;br /&gt;
I find it odd that Sidefx makes you go through shelf items for various UI functions that have nothing to do with the shelf, but that&amp;#039;s the way the cookie crumbles. Also maybe I&amp;#039;m nostalgic but despite it&amp;#039;s flaws the maya shelves were kinda easier to understand.&lt;br /&gt;
As I understand it single shelf items in houdini can be stored in different files, which means if you fumble around you can easily have two items with the same name but saved in two different places (I think. I&amp;#039;m confused).&lt;br /&gt;
Just make sure you keep an eyeball on where your shelf tool is stored (often in Default.shelf or some random production folder if you use Rez like us)&lt;br /&gt;
&lt;br /&gt;
=== Parameters as network view comments===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZddFaRC.gif&lt;br /&gt;
&lt;br /&gt;
This tool will show specific node parameter values as node comments. Good for quick overview without having to open the parameter spreadsheet.&lt;br /&gt;
I&amp;#039;ve bound it to F10 and use it often.&lt;br /&gt;
&lt;br /&gt;
Each node type can have its own specific properties.&lt;br /&gt;
I&amp;#039;ve added a right click menu item so that I can automatically add the parameters to a config file in Houdini.&lt;br /&gt;
&lt;br /&gt;
This is 95% chatGPT code and it&amp;#039;s absolutely great at it.&lt;br /&gt;
&lt;br /&gt;
In my &amp;lt;code&amp;gt;PARMmenu.xml&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;scriptItem id=&amp;quot;tool_attribute_as_comment&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;modifyItem&amp;gt;&amp;lt;insertAfter&amp;gt;motion_effects_menu&amp;lt;/insertAfter&amp;gt;&amp;lt;/modifyItem&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;Add Parameter as Attribute Comment&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import tool_attribute_as_comment;tool_attribute_as_comment.edit_attribute_config_rclick(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In my shelf item&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import tool_attribute_as_comment&lt;br /&gt;
tool_attribute_as_comment.display_next_attribute_as_comment()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In my &amp;lt;code&amp;gt;python3.11libs/tool_attribute_as_comment.py&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
import os&lt;br /&gt;
import json&lt;br /&gt;
&lt;br /&gt;
# Path setup&lt;br /&gt;
prefs_dir = hou.getenv(&amp;quot;HOUDINI_USER_PREF_DIR&amp;quot;)&lt;br /&gt;
json_path = os.path.join(prefs_dir, &amp;quot;attribute_display_config.json&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Persistent index store&lt;br /&gt;
if not hasattr(hou.session, &amp;quot;attr_display_index&amp;quot;):&lt;br /&gt;
    hou.session.attr_display_index = {}&lt;br /&gt;
&lt;br /&gt;
# Ensure config file exists&lt;br /&gt;
def ensure_config_file():&lt;br /&gt;
    if not os.path.exists(json_path):&lt;br /&gt;
        with open(json_path, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            json.dump({}, f, indent=4)&lt;br /&gt;
&lt;br /&gt;
# Helper: load config safely and normalize attribute entries&lt;br /&gt;
def load_config():&lt;br /&gt;
    ensure_config_file()&lt;br /&gt;
    try:&lt;br /&gt;
        with open(json_path, &amp;quot;r&amp;quot;) as f:&lt;br /&gt;
            raw = json.load(f)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to read config:\n{e}&amp;quot;)&lt;br /&gt;
        return {}&lt;br /&gt;
&lt;br /&gt;
    # Normalize format: ensure each node-type maps to a list of dicts {name, label}&lt;br /&gt;
    normalized = {}&lt;br /&gt;
    for node_type, attrs in raw.items():&lt;br /&gt;
        if attrs is None:&lt;br /&gt;
            normalized[node_type] = []&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        # If single string -&amp;gt; make list&lt;br /&gt;
        if isinstance(attrs, str):&lt;br /&gt;
            attrs = [attrs]&lt;br /&gt;
&lt;br /&gt;
        if not isinstance(attrs, list):&lt;br /&gt;
            # unexpected type, skip defensively&lt;br /&gt;
            normalized[node_type] = []&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        normalized_list = []&lt;br /&gt;
        for entry in attrs:&lt;br /&gt;
            if isinstance(entry, str):&lt;br /&gt;
                normalized_list.append({&amp;quot;name&amp;quot;: entry, &amp;quot;label&amp;quot;: entry})&lt;br /&gt;
            elif isinstance(entry, dict):&lt;br /&gt;
                name = entry.get(&amp;quot;name&amp;quot;) or entry.get(&amp;quot;parm&amp;quot;) or entry.get(&amp;quot;param&amp;quot;)&lt;br /&gt;
                label = entry.get(&amp;quot;label&amp;quot;) or name&lt;br /&gt;
                if name:&lt;br /&gt;
                    normalized_list.append({&amp;quot;name&amp;quot;: name, &amp;quot;label&amp;quot;: label})&lt;br /&gt;
                # if no name, ignore malformed entry&lt;br /&gt;
            else:&lt;br /&gt;
                # unknown entry type; ignore&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
        normalized[node_type] = normalized_list&lt;br /&gt;
&lt;br /&gt;
    return normalized&lt;br /&gt;
&lt;br /&gt;
# Helper: save config (assumes caller provides well-formed dict)&lt;br /&gt;
def save_config(config):&lt;br /&gt;
    try:&lt;br /&gt;
        # Save exactly what the caller provided. If you prefer a stable format,&lt;br /&gt;
        # you can re-serialize to the normalized format before saving.&lt;br /&gt;
        with open(json_path, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            json.dump(config, f, indent=4)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to save config:\n{e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Main display function&lt;br /&gt;
def display_next_attribute_as_comment():&lt;br /&gt;
    attr_config = load_config()&lt;br /&gt;
    selected_nodes = hou.selectedNodes()&lt;br /&gt;
&lt;br /&gt;
    if not selected_nodes:&lt;br /&gt;
        hou.ui.setStatusMessage(&amp;quot;No nodes selected.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    for node in selected_nodes:&lt;br /&gt;
        node_type = node.type().name()&lt;br /&gt;
        node_path = node.path()&lt;br /&gt;
&lt;br /&gt;
        attrs = attr_config.get(node_type)&lt;br /&gt;
        # attrs should already be normalized by load_config -&amp;gt; list of dicts&lt;br /&gt;
        if not attrs:&lt;br /&gt;
            node.setComment(&amp;quot;No configured attributes in &amp;#039;attribute_display_config.json&amp;#039;&amp;quot;)&lt;br /&gt;
            node.setGenericFlag(hou.nodeFlag.DisplayComment, True)&lt;br /&gt;
            # ensure index is reset for this node type&lt;br /&gt;
            hou.session.attr_display_index[node_path] = 0&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        # Get current index, default 0&lt;br /&gt;
        current_index = hou.session.attr_display_index.get(node_path, 0)&lt;br /&gt;
        total_attrs = len(attrs)&lt;br /&gt;
&lt;br /&gt;
        # If we&amp;#039;ve reached or passed the end, show empty comment and reset&lt;br /&gt;
        if current_index &amp;gt;= total_attrs:&lt;br /&gt;
            node.setComment(&amp;quot;&amp;quot;)  # empty comment as requested&lt;br /&gt;
            node.setGenericFlag(hou.nodeFlag.DisplayComment, True)&lt;br /&gt;
            hou.session.attr_display_index[node_path] = 0&lt;br /&gt;
            continue&lt;br /&gt;
&lt;br /&gt;
        # Get two attributes at a time&lt;br /&gt;
        end_index = current_index + 2&lt;br /&gt;
        selected_attrs = attrs[current_index:end_index]&lt;br /&gt;
        comment_lines = []&lt;br /&gt;
&lt;br /&gt;
        for attr_entry in selected_attrs:&lt;br /&gt;
            # attr_entry is a dict with keys &amp;#039;name&amp;#039; and &amp;#039;label&amp;#039;&lt;br /&gt;
            attr_name = attr_entry.get(&amp;quot;name&amp;quot;)&lt;br /&gt;
            attr_label = attr_entry.get(&amp;quot;label&amp;quot;, attr_name)&lt;br /&gt;
&lt;br /&gt;
            try:&lt;br /&gt;
                parm = node.parm(attr_name)&lt;br /&gt;
                if parm is not None:&lt;br /&gt;
                    # obtain a friendly label if none provided: try parm template label&lt;br /&gt;
                    if not attr_label:&lt;br /&gt;
                        try:&lt;br /&gt;
                            tmpl = parm.parmTemplate()&lt;br /&gt;
                            attr_label = tmpl.label() if tmpl is not None else attr_name&lt;br /&gt;
                        except Exception:&lt;br /&gt;
                            attr_label = attr_name&lt;br /&gt;
                    attr_value = parm.eval()&lt;br /&gt;
                    comment_lines.append(f&amp;quot;{attr_label}: {attr_value}&amp;quot;)&lt;br /&gt;
                else:&lt;br /&gt;
                    comment_lines.append(f&amp;quot;{attr_label}: [Parm not found]&amp;quot;)&lt;br /&gt;
            except Exception as e:&lt;br /&gt;
                comment_lines.append(f&amp;quot;{attr_label}: Error - {str(e)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        # Update the comment on the node&lt;br /&gt;
        node.setComment(&amp;quot;\n&amp;quot;.join(comment_lines))&lt;br /&gt;
        node.setGenericFlag(hou.nodeFlag.DisplayComment, True)&lt;br /&gt;
&lt;br /&gt;
        # Store the updated index for next cycle (advance by 2)&lt;br /&gt;
        hou.session.attr_display_index[node_path] = end_index&lt;br /&gt;
&lt;br /&gt;
def edit_attribute_config_rclick(kwargs):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Prepare arguments for the next function this is accessed using Houdini&amp;#039;s right click menu on a parm.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    action = &amp;quot;add&amp;quot;&lt;br /&gt;
    if kwargs[&amp;#039;altclick&amp;#039;] == True:&lt;br /&gt;
        action = &amp;quot;remove&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    for parm in kwargs[&amp;#039;parms&amp;#039;]:&lt;br /&gt;
        type = parm.node().type().name()&lt;br /&gt;
        name = parm.name()&lt;br /&gt;
        label = parm.description()&lt;br /&gt;
        edit_attribute_config(type, name, attr_label=label, action=action)&lt;br /&gt;
&lt;br /&gt;
# Helper: edit attribute config file (add/remove)&lt;br /&gt;
def edit_attribute_config(node_type, attr_name, attr_label=None, action=&amp;quot;add&amp;quot;):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Edit attribute_display_config.json.&lt;br /&gt;
    action: &amp;quot;add&amp;quot; or &amp;quot;remove&amp;quot;&lt;br /&gt;
    Example:&lt;br /&gt;
        edit_attribute_config(&amp;quot;geo&amp;quot;, &amp;quot;shop_materialpath&amp;quot;, &amp;quot;Material&amp;quot;, &amp;quot;add&amp;quot;)&lt;br /&gt;
        edit_attribute_config(&amp;quot;geo&amp;quot;, &amp;quot;shop_materialpath&amp;quot;, action=&amp;quot;remove&amp;quot;)&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    # Load raw file (not normalized) so we preserve user&amp;#039;s format as much as possible&lt;br /&gt;
    ensure_config_file()&lt;br /&gt;
    try:&lt;br /&gt;
        with open(json_path, &amp;quot;r&amp;quot;) as f:&lt;br /&gt;
            raw = json.load(f)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to read config for editing:\n{e}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    # Ensure the node_type exists and is a list&lt;br /&gt;
    node_list = raw.get(node_type)&lt;br /&gt;
    if node_list is None:&lt;br /&gt;
        node_list = []&lt;br /&gt;
    elif isinstance(node_list, str):&lt;br /&gt;
        node_list = [node_list]&lt;br /&gt;
    elif not isinstance(node_list, list):&lt;br /&gt;
        # Unexpected; replace with empty list&lt;br /&gt;
        node_list = []&lt;br /&gt;
&lt;br /&gt;
    if action == &amp;quot;add&amp;quot;:&lt;br /&gt;
        # Avoid duplicates (check by name)&lt;br /&gt;
        names = set()&lt;br /&gt;
        for ent in node_list:&lt;br /&gt;
            if isinstance(ent, str):&lt;br /&gt;
                names.add(ent)&lt;br /&gt;
            elif isinstance(ent, dict):&lt;br /&gt;
                n = ent.get(&amp;quot;name&amp;quot;)&lt;br /&gt;
                if n:&lt;br /&gt;
                    names.add(n)&lt;br /&gt;
&lt;br /&gt;
        if attr_name in names:&lt;br /&gt;
            hou.ui.setStatusMessage(f&amp;quot;{attr_name} already present for {node_type}&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        # Add as dict so label is preserved&lt;br /&gt;
        entry = {&amp;quot;name&amp;quot;: attr_name, &amp;quot;label&amp;quot;: attr_label or attr_name}&lt;br /&gt;
        node_list.append(entry)&lt;br /&gt;
&lt;br /&gt;
    elif action == &amp;quot;remove&amp;quot;:&lt;br /&gt;
        new_list = []&lt;br /&gt;
        for ent in node_list:&lt;br /&gt;
            if isinstance(ent, str):&lt;br /&gt;
                if ent != attr_name:&lt;br /&gt;
                    new_list.append(ent)&lt;br /&gt;
            elif isinstance(ent, dict):&lt;br /&gt;
                if ent.get(&amp;quot;name&amp;quot;) != attr_name:&lt;br /&gt;
                    new_list.append(ent)&lt;br /&gt;
            else:&lt;br /&gt;
                # unknown type, keep it just in case&lt;br /&gt;
                new_list.append(ent)&lt;br /&gt;
        node_list = new_list&lt;br /&gt;
    else:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Invalid action: {action}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    raw[node_type] = node_list&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
        with open(json_path, &amp;quot;w&amp;quot;) as f:&lt;br /&gt;
            json.dump(raw, f, indent=4)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        hou.ui.setStatusMessage(f&amp;quot;Failed to write config:\n{e}&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
    hou.ui.setStatusMessage(f&amp;quot;Config updated for {node_type}: {action} {attr_name}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# New: reset functions&lt;br /&gt;
def reset_all_indexes():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Reset index for all nodes: clears the session store.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    hou.session.attr_display_index = {}&lt;br /&gt;
    hou.ui.setStatusMessage(&amp;quot;Attribute display indexes reset for all nodes.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def reset_selected_nodes_indexes():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Reset index for currently selected nodes only.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    sel = hou.selectedNodes()&lt;br /&gt;
    if not sel:&lt;br /&gt;
        hou.ui.setStatusMessage(&amp;quot;No nodes selected to reset.&amp;quot;)&lt;br /&gt;
        return&lt;br /&gt;
    for n in sel:&lt;br /&gt;
        path = n.path()&lt;br /&gt;
        if path in hou.session.attr_display_index:&lt;br /&gt;
            del hou.session.attr_display_index[path]&lt;br /&gt;
    hou.ui.setStatusMessage(&amp;quot;Attribute display indexes reset for selected nodes.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
# Run it&lt;br /&gt;
#display_next_attribute_as_comment()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Selection to bounding box to delete ===&lt;br /&gt;
&lt;br /&gt;
Very often I manually select prims to delete a few local faces, then change something upstream, then my blast is all mangled. This will create a bounding box of the currently selected geo, group them with it and then blast the group. It&amp;#039;s never perfect because of how bounding boxes operate (points/prims/othogonal to world axes), but it&amp;#039;s very useful in my daily work.&lt;br /&gt;
&lt;br /&gt;
Also Gemini did 90% of the coding and actually does error catching. It&amp;#039;s a lot better than me :0&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/xMUPRr3.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou, toolutils&lt;br /&gt;
&lt;br /&gt;
def create_group_from_bbox():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Creates a &amp;#039;group&amp;#039; node and sets its bounding region parameters&lt;br /&gt;
    based on the bounding box of the current geo selection&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    try:&lt;br /&gt;
        selected_nodes = hou.selectedNodes()&lt;br /&gt;
        geo = toolutils.sceneViewer().selectGeometry()&lt;br /&gt;
&lt;br /&gt;
        # Check if exactly one node is selected.&lt;br /&gt;
        if len(selected_nodes) != 1:&lt;br /&gt;
            print(&amp;quot;Please select a single geometry node to get the bounding box from.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        selected_node = selected_nodes[0]&lt;br /&gt;
        &lt;br /&gt;
        # Check if the selected node is a geometry node (SOP).&lt;br /&gt;
        if selected_node.type().category() != hou.sopNodeTypeCategory():&lt;br /&gt;
            print(&amp;quot;The selected node is not a geometry node (SOP). Please select a valid node.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        hou.clearAllSelected()&lt;br /&gt;
&lt;br /&gt;
        bbox = geo.boundingBox()&lt;br /&gt;
        parent_node = selected_node.parent()&lt;br /&gt;
        selected_node.setSelected(0)&lt;br /&gt;
        group_node = parent_node.createNode(&amp;quot;groupcreate&amp;quot;, &amp;quot;bbox_group&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        pos = [selected_node.position()[0],selected_node.position()[1]-1]&lt;br /&gt;
        group_node.setPosition(pos)&lt;br /&gt;
        group_node.setInput(0, selected_node)&lt;br /&gt;
        group_node.parm(&amp;quot;groupbounding&amp;quot;).set(1)&lt;br /&gt;
        group_node.parmTuple(&amp;quot;size&amp;quot;).set(bbox.sizevec())&lt;br /&gt;
        group_node.parmTuple(&amp;quot;t&amp;quot;).set(bbox.center())&lt;br /&gt;
        &lt;br /&gt;
        blast = parent_node.createNode(&amp;quot;blast&amp;quot;, &amp;quot;bbox_blast&amp;quot;)&lt;br /&gt;
        blast.setInput(0, group_node)&lt;br /&gt;
        pos[1] = pos[1] - 1&lt;br /&gt;
        blast.setPosition(pos)&lt;br /&gt;
        blast.parm(&amp;#039;group&amp;#039;).set(group_node.parm(&amp;quot;groupname&amp;quot;).eval())&lt;br /&gt;
&lt;br /&gt;
        group_node.setSelected(1)&lt;br /&gt;
        blast.setDisplayFlag(1)&lt;br /&gt;
        blast.setSelected(1)&lt;br /&gt;
        blast.setRenderFlag(1)&lt;br /&gt;
        group_node.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
        &lt;br /&gt;
    except hou.Error as e:&lt;br /&gt;
        print(f&amp;quot;An error occurred: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
create_group_from_bbox()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Toggle Viewport Background Color ===&lt;br /&gt;
Bound it to F6&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
import toolutils&lt;br /&gt;
&lt;br /&gt;
bg = None&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    # cycle next bg&lt;br /&gt;
    if kwargs[&amp;#039;ctrlclick&amp;#039;]: raise&lt;br /&gt;
    bgs = hou.session.bg[:]&lt;br /&gt;
    bgs = bgs[1:]+bgs[:1]&lt;br /&gt;
    if kwargs[&amp;#039;shiftclick&amp;#039;]: bgs = [&amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;, &amp;#039;wb&amp;#039;]&lt;br /&gt;
    bg = bgs[0]&lt;br /&gt;
    hou.session.bg = bgs&lt;br /&gt;
except:&lt;br /&gt;
    # set up default bg vars&lt;br /&gt;
    hou.session.bg = [&amp;#039;wb&amp;#039;, &amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;]&lt;br /&gt;
    bg = hou.session.bg[0]&lt;br /&gt;
&lt;br /&gt;
bgs = { &amp;#039;wb&amp;#039;:&amp;#039;dark&amp;#039;, &amp;#039;bw&amp;#039;:&amp;#039;grey&amp;#039;, &amp;#039;light&amp;#039;:&amp;#039;light&amp;#039; }&lt;br /&gt;
&lt;br /&gt;
hou.hscript(&amp;quot;viewdisplay -B %s *&amp;quot; % bg)&lt;br /&gt;
hou.ui.setStatusMessage(&amp;quot;Cycled background to %s&amp;quot; % bgs[bg].upper() )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Toggle Auto / Manual scene update ===&lt;br /&gt;
Bound to F11, I use it every day.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
mode = hou.updateModeSetting().name()&lt;br /&gt;
if mode == &amp;#039;AutoUpdate&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.Manual)&lt;br /&gt;
if mode == &amp;#039;Manual&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.AutoUpdate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Auto create a wrangle node ===&lt;br /&gt;
I&amp;#039;m pretty sure this exists already but why not reinvent the wheel. Creates an attr wrangle and keeps inputs/outputs, with the code box already in focus. I only use the &amp;#039;P&amp;#039; parameter viewer (the one that pops up in your network view) so other desktops might not work.&lt;br /&gt;
&lt;br /&gt;
bound to ctrl-shift-w&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TODO  fix some weird inputs/outputs configurations&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
sn = hou.selectedNodes()&lt;br /&gt;
if sn:&lt;br /&gt;
    w = sn[-1].parent().createNode(&amp;#039;attribwrangle&amp;#039;)&lt;br /&gt;
    outputs = sn[-1].outputs()&lt;br /&gt;
    &lt;br /&gt;
    w.setInput(0,sn[-1])&lt;br /&gt;
    w.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
    w.setSelected(True,clear_all_selected=True, show_asset_if_selected=True)&lt;br /&gt;
    w.setDisplayFlag(True)&lt;br /&gt;
    sn[-1].setDisplayFlag(False)&lt;br /&gt;
    if outputs:&lt;br /&gt;
        for output in outputs:&lt;br /&gt;
            output.setInput(0,w)&lt;br /&gt;
else:&lt;br /&gt;
    #should not error out if it&amp;#039;s launched in a shelf above a network plane&lt;br /&gt;
    w = hou.ui.curDesktop().paneTabUnderCursor().pwd().createNode(&amp;#039;attribwrangle&amp;#039;)    &lt;br /&gt;
&lt;br /&gt;
w.moveToGoodPosition(relative_to_inputs=True, move_inputs=False, move_outputs=True, move_unconnected=False)    &lt;br /&gt;
&lt;br /&gt;
networkview = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkview.setCurrentNode(w,True)&lt;br /&gt;
networkview.parmMoveFocusTo(&amp;#039;snippet&amp;#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Copy auto Paste-Merge ===&lt;br /&gt;
Copy from https://berniebernie.fr/wiki/Houdini_Python#Paste_clipboard_nodes_to_object_merges&lt;br /&gt;
&lt;br /&gt;
Allow to copy nodes elsewhere with an object merge. Bound it to alt-v (so ctrl-c, alt-v in another place)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/M7N0Stq.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# this snippet will paste nodes in clipboard to object merges&lt;br /&gt;
# use it with a shortcut (I overrode &amp;#039;alt-v&amp;#039; in network pane context)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
network = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkpath = network.pwd().path()&lt;br /&gt;
pos = network.cursorPosition()&lt;br /&gt;
&lt;br /&gt;
clipboard = hou.ui.getTextFromClipboard()&lt;br /&gt;
&lt;br /&gt;
n = 0&lt;br /&gt;
&lt;br /&gt;
if clipboard:&lt;br /&gt;
    list = clipboard.split()&lt;br /&gt;
    for item in list:&lt;br /&gt;
        if hou.node(item) != None:&lt;br /&gt;
            merge = hou.node(networkpath).createNode(&amp;#039;object_merge&amp;#039;,&amp;#039;merge_&amp;#039;+item.split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
            merge.parm(&amp;#039;objpath1&amp;#039;).set(str(item))&lt;br /&gt;
            merge.setPosition(pos)&lt;br /&gt;
            merge.move([n*2,0])&lt;br /&gt;
            if n == 0:&lt;br /&gt;
                merge.setSelected(True,True)&lt;br /&gt;
            else:&lt;br /&gt;
                merge.setSelected(True,False)&lt;br /&gt;
            n = n + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Screenshot to background image===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/kuc1PnO.gif&lt;br /&gt;
&lt;br /&gt;
Customize your capture command line, no error checking &amp;#039;cause I&amp;#039;m not a professional programmer, aka it works on my machine.&lt;br /&gt;
&lt;br /&gt;
Should work with windows (hardcoded path to [https://www.donationcoder.com/software/mouser/popular-apps/minicap minicap] ) and linux centos with xfce-screenshoter&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#python 3.5+&lt;br /&gt;
import hou&lt;br /&gt;
import os&lt;br /&gt;
import subprocess&lt;br /&gt;
import nodegraphutils as utils&lt;br /&gt;
from time import gmtime, strftime&lt;br /&gt;
from os.path import expanduser&lt;br /&gt;
home = expanduser(&amp;quot;~&amp;quot;)&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
exe = home+&amp;quot;\Downloads\exe\MiniCap.exe&amp;quot;&lt;br /&gt;
&lt;br /&gt;
widthRatio = 4                      # change to make screenshot bigger or smaller, this is ~x4 node width&lt;br /&gt;
&lt;br /&gt;
def takeScreenShot(savePath):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;change to your preferred capture app /!\ no error checking for now &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    subprocess.check_call([exe,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &lt;br /&gt;
    #following is older code for linux i&amp;#039;ll update it if I linux again&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    Path(os.path.dirname(Path(savePath))).mkdir(parents=True, exist_ok=True)&lt;br /&gt;
    if platform == &amp;quot;linux&amp;quot; or platform == &amp;quot;linux2&amp;quot;:&lt;br /&gt;
        savepath = os.path.dirname(Path(savePath)) #screenshot dir    &lt;br /&gt;
        screenshotPNG = subprocess.check_output(&amp;#039;mv &amp;quot;$(xfce4-screenshooter -ro ls)&amp;quot; -v &amp;#039;+savepath,shell=True)&lt;br /&gt;
        screenshotPNG = screenshotPNG.decode().strip().split(&amp;#039;\n&amp;#039;)[0].split(&amp;#039; -&amp;gt; &amp;#039;)[1].replace(&amp;quot;&amp;#039;&amp;quot;,&amp;quot;&amp;quot;) #helluva nasty oneliner&lt;br /&gt;
        os.chdir(savepath)&lt;br /&gt;
        os.rename(os.path.basename(screenshotPNG),os.path.basename(savePath))&lt;br /&gt;
        &lt;br /&gt;
        # linux&lt;br /&gt;
    elif platform == &amp;quot;darwin&amp;quot;:&lt;br /&gt;
        exit&lt;br /&gt;
    elif platform == &amp;quot;win32&amp;quot;:&lt;br /&gt;
        subprocess.check_call([r&amp;quot;C:\Users\bernie\Documents\houdini18.0\config\exe\MiniCap.exe&amp;quot;,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
def removeBackgroundImage(**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; erases bg image from tuples of backgroundImages() if it can find it, updates bg &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    deletingNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = deletingNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    backgroundImagesDic = tuple(x for x in backgroundImagesDic if x.path() != image)&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&lt;br /&gt;
def changeBackgroundImageBrightness(event_type,**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; changes brightness/visibility if template or bypass flags are checked -- its poorly written/thought but i was tired&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    nullNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = nullNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    brightness = 1.0&lt;br /&gt;
    if nullNode.isBypassed():&lt;br /&gt;
        brightness = 0.0&lt;br /&gt;
    elif nullNode.isTemplateFlagSet():&lt;br /&gt;
        brightness = 0.5&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    i = 0&lt;br /&gt;
    for item in backgroundImagesDic:&lt;br /&gt;
        if item.path() == image:&lt;br /&gt;
            backgroundImagesDic[i].setBrightness(brightness)&lt;br /&gt;
            break&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
    &lt;br /&gt;
#generate unique(ish) path for screenshot&lt;br /&gt;
timestamp = strftime(&amp;#039;%Y%m%d_%H%M%S&amp;#039;, gmtime())&lt;br /&gt;
hipname = str(hou.getenv(&amp;#039;HIPNAME&amp;#039;))&lt;br /&gt;
hippath = str(hou.getenv(&amp;#039;HIP&amp;#039;)) + &amp;#039;/screenshots&amp;#039;&lt;br /&gt;
screenshotName = hipname + &amp;#039;.&amp;#039; + timestamp + &amp;#039;.png&amp;#039;&lt;br /&gt;
pythonpath = hippath+&amp;#039;/&amp;#039;+screenshotName&lt;br /&gt;
systempath = hippath + &amp;#039;\\&amp;#039; + screenshotName&lt;br /&gt;
systempath = Path(systempath)&lt;br /&gt;
&lt;br /&gt;
print(systempath)&lt;br /&gt;
&lt;br /&gt;
houdinipath = &amp;#039;$HIP/screenshots/&amp;#039;+screenshotName&lt;br /&gt;
&lt;br /&gt;
#take screenshot with capture region&lt;br /&gt;
takeScreenShot(systempath)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#set up background image plane&lt;br /&gt;
editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
image = hou.NetworkImage()&lt;br /&gt;
image.setPath(houdinipath)&lt;br /&gt;
sel = hou.selectedNodes()&lt;br /&gt;
nullNode = &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
if sel:&lt;br /&gt;
    lastSel = sel[-1]&lt;br /&gt;
    nullNode = lastSel.parent().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;)&lt;br /&gt;
    if lastSel.outputConnections():&lt;br /&gt;
        nullNode.setInput(0,lastSel)                   &lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    nullNode = editor.pwd().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;) &lt;br /&gt;
    nullNode.moveToGoodPosition()&lt;br /&gt;
    lastSel = nullNode&lt;br /&gt;
&lt;br /&gt;
#configure image plane placement&lt;br /&gt;
nullNode.setUserData(&amp;#039;nodeshape&amp;#039;,&amp;#039;task&amp;#039;)&lt;br /&gt;
nullNode.setPosition(lastSel.position())&lt;br /&gt;
nullNode.setColor(hou.Color(.3,.3,.3))&lt;br /&gt;
nullNode.move([lastSel.size()[0]*2,-lastSel.size()[1]*2])&lt;br /&gt;
&lt;br /&gt;
rez = hou.imageResolution(pythonpath)&lt;br /&gt;
ratio = 1.0*rez[1]/rez[0]&lt;br /&gt;
rect = hou.BoundingRect(0,-lastSel.size()[1]*1.1,widthRatio,-widthRatio*ratio-lastSel.size()[1]*1.1)&lt;br /&gt;
image.setRelativeToPath(nullNode.path())&lt;br /&gt;
image.setRect(rect)&lt;br /&gt;
&lt;br /&gt;
#following is adding a spare parm with image path to be able to know which node corresponds to which background image&lt;br /&gt;
#could have used a user attribute or relativeToPath() and smarter logic but it works and it helps me visualize filepath&lt;br /&gt;
&lt;br /&gt;
hou_parm_template_group = hou.ParmTemplateGroup()&lt;br /&gt;
hou_parm_template = hou.LabelParmTemplate(&amp;quot;houdinipath&amp;quot;, &amp;quot;Label&amp;quot;, column_labels=([&amp;#039;\\&amp;#039;+houdinipath]))&lt;br /&gt;
hou_parm_template.hideLabel(True)&lt;br /&gt;
hou_parm_template_group.append(hou_parm_template)&lt;br /&gt;
nullNode.setParmTemplateGroup(hou_parm_template_group)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#attach a function that deletes the background image plane if the corresponding node is deleted (faster than doing it by hand)&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.BeingDeleted,), removeBackgroundImage)&lt;br /&gt;
&lt;br /&gt;
#attach a function to change visibility or opacity if corresponding node flags are changed&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.FlagChanged,), changeBackgroundImageBrightness)&lt;br /&gt;
&lt;br /&gt;
#add image to network background&lt;br /&gt;
backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
backgroundImagesDic = backgroundImagesDic + (image,)&lt;br /&gt;
editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other UI ==&lt;br /&gt;
=== Previews ===&lt;br /&gt;
==== Detach parameter window à la maya ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/3txLohO.gif&lt;br /&gt;
&lt;br /&gt;
==== Auto add frame offset parm ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZXNrwvC.gif&lt;br /&gt;
&lt;br /&gt;
=== Userdocs XMLs ===&lt;br /&gt;
This is for right click context menus. Use the hscript &amp;#039;menurefresh&amp;#039; when developping so as not to relaunch H each time (like a reload(module) in python).&lt;br /&gt;
==== PARMmenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
	&amp;lt;menu&amp;gt;&lt;br /&gt;
		&amp;lt;subMenu id=&amp;quot;bernie_rclick&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;label&amp;gt;Bernie&amp;#039;s&amp;lt;/label&amp;gt;&lt;br /&gt;
			&amp;lt;modifyItem&amp;gt;&amp;lt;insertAfter&amp;gt;motion_effects_menu&amp;lt;/insertAfter&amp;gt;&amp;lt;/modifyItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Add a frame offset parm to $F#&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						return bernie_tools.validate_sequence_parm(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.add_sequence_offset_spareparm(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Browse to...&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						reload(bernie_tools)&lt;br /&gt;
						return bernie_tools.validate_uri(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.browse_to(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_open_parm_spreadsheet&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Open/Add in Parameter Spreadsheet&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.open_parm_spreadsheet(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
		&amp;lt;/subMenu&amp;gt;&lt;br /&gt;
	&amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ParmGearMenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
 (long sidefx text)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
    &amp;lt;!-- menuDocument can only contain 1 menu element, whose id is &lt;br /&gt;
         implicitly &amp;quot;root_menu&amp;quot;&lt;br /&gt;
      --&amp;gt;&lt;br /&gt;
    &amp;lt;menu&amp;gt;&lt;br /&gt;
        &amp;lt;scriptItem id=&amp;quot;detach_parameter_window&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;label&amp;gt;Detach Parameter Window...&amp;lt;/label&amp;gt;&lt;br /&gt;
            &amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.detach_parameter_window(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
        &amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
    &amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== bernie_tools.py ===&lt;br /&gt;
==== py3 ====&lt;br /&gt;
Some updates&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
		name = existing_parm.name()&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(name+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(name+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(name+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+name+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+name+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	# todo: keep existing parm mask to append to it. Can&amp;#039;t retrieve current value AFAIK.&lt;br /&gt;
	&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	#selectedParms = return_first_parm(kwargs)&lt;br /&gt;
	&lt;br /&gt;
	parms = []&lt;br /&gt;
	for parm in kwargs[&amp;#039;parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	for parm in kwargs[&amp;#039;locked_parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	&lt;br /&gt;
	selectedParm = parms[0]&lt;br /&gt;
&lt;br /&gt;
	parms = [x.name() for x in parms]&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
&lt;br /&gt;
	parmsheetP = selectedParm.name()&lt;br /&gt;
	parmsheetPaths = nodepaths&lt;br /&gt;
&lt;br /&gt;
	if kwargs[&amp;#039;ctrlclick&amp;#039;]:&lt;br /&gt;
		rmi = hou.ui.readMultiInput(&amp;#039;Edit the node path and parms.&amp;#039;, [&amp;#039;Node(s)&amp;#039;,&amp;#039;Parm(s)&amp;#039;], buttons=(&amp;#039;OK&amp;#039;,&amp;#039;Cancel&amp;#039;), severity=hou.severityType.Message, default_choice=0, close_choice=1, help=&amp;#039;Node path can have wildcards&amp;#039;, title=&amp;#039;Parm Chooser&amp;#039;, initial_contents=(nodepaths.split(&amp;#039; &amp;#039;, 1)[0] ,selectedParm.name()))&lt;br /&gt;
		if rmi[0]==0:&lt;br /&gt;
			parmsheetPaths = rmi[1][0]&lt;br /&gt;
			parmsheetP = rmi[1][1]&lt;br /&gt;
&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 0 -p &amp;quot;&amp;#039;+parmsheetP+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+parmsheetPaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== py2 ====&lt;br /&gt;
Need to update to py3 for H19&lt;br /&gt;
&lt;br /&gt;
In my userdocs\houdini18.5\python2.7libs folder&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#march2022 update: fuck putin and improved the add offset to sequence so that you can still choose a new file and keep the offset without having to delete the spare parms (for imageplane frame offset for instance)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(label+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(label+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(label+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+label+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+label+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	selectedParm = return_first_parm(kwargs)&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
	#print(nodepaths)&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 1 -p &amp;quot;&amp;#039;+selectedParm.name()+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+nodepaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=876</id>
		<title>Houdini UI Customization</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=876"/>
		<updated>2025-10-21T13:44:25Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Shelf Tools */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Various things I do to my Houdini&lt;br /&gt;
&lt;br /&gt;
Quick reminder to myself. When testing out things, call &amp;#039;&amp;#039;&amp;#039;menurefresh&amp;#039;&amp;#039;&amp;#039; in Hscript textport to refresh different menus (notably PARMmenu.xml)&lt;br /&gt;
== Shelf Tools ==&lt;br /&gt;
I find it odd that Sidefx makes you go through shelf items for various UI functions that have nothing to do with the shelf, but that&amp;#039;s the way the cookie crumbles. Also maybe I&amp;#039;m nostalgic but despite it&amp;#039;s flaws the maya shelves were kinda easier to understand.&lt;br /&gt;
As I understand it single shelf items in houdini can be stored in different files, which means if you fumble around you can easily have two items with the same name but saved in two different places (I think. I&amp;#039;m confused).&lt;br /&gt;
Just make sure you keep an eyeball on where your shelf tool is stored (often in Default.shelf or some random production folder if you use Rez like us)&lt;br /&gt;
&lt;br /&gt;
=== Selection to bounding box to delete ===&lt;br /&gt;
&lt;br /&gt;
Very often I manually select prims to delete a few local faces, then change something upstream, then my blast is all mangled. This will create a bounding box of the currently selected geo, group them with it and then blast the group. It&amp;#039;s never perfect because of how bounding boxes operate (points/prims/othogonal to world axes), but it&amp;#039;s very useful in my daily work.&lt;br /&gt;
&lt;br /&gt;
Also Gemini did 90% of the coding and actually does error catching. It&amp;#039;s a lot better than me :0&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/xMUPRr3.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou, toolutils&lt;br /&gt;
&lt;br /&gt;
def create_group_from_bbox():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Creates a &amp;#039;group&amp;#039; node and sets its bounding region parameters&lt;br /&gt;
    based on the bounding box of the current geo selection&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    try:&lt;br /&gt;
        selected_nodes = hou.selectedNodes()&lt;br /&gt;
        geo = toolutils.sceneViewer().selectGeometry()&lt;br /&gt;
&lt;br /&gt;
        # Check if exactly one node is selected.&lt;br /&gt;
        if len(selected_nodes) != 1:&lt;br /&gt;
            print(&amp;quot;Please select a single geometry node to get the bounding box from.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        selected_node = selected_nodes[0]&lt;br /&gt;
        &lt;br /&gt;
        # Check if the selected node is a geometry node (SOP).&lt;br /&gt;
        if selected_node.type().category() != hou.sopNodeTypeCategory():&lt;br /&gt;
            print(&amp;quot;The selected node is not a geometry node (SOP). Please select a valid node.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        hou.clearAllSelected()&lt;br /&gt;
&lt;br /&gt;
        bbox = geo.boundingBox()&lt;br /&gt;
        parent_node = selected_node.parent()&lt;br /&gt;
        selected_node.setSelected(0)&lt;br /&gt;
        group_node = parent_node.createNode(&amp;quot;groupcreate&amp;quot;, &amp;quot;bbox_group&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        pos = [selected_node.position()[0],selected_node.position()[1]-1]&lt;br /&gt;
        group_node.setPosition(pos)&lt;br /&gt;
        group_node.setInput(0, selected_node)&lt;br /&gt;
        group_node.parm(&amp;quot;groupbounding&amp;quot;).set(1)&lt;br /&gt;
        group_node.parmTuple(&amp;quot;size&amp;quot;).set(bbox.sizevec())&lt;br /&gt;
        group_node.parmTuple(&amp;quot;t&amp;quot;).set(bbox.center())&lt;br /&gt;
        &lt;br /&gt;
        blast = parent_node.createNode(&amp;quot;blast&amp;quot;, &amp;quot;bbox_blast&amp;quot;)&lt;br /&gt;
        blast.setInput(0, group_node)&lt;br /&gt;
        pos[1] = pos[1] - 1&lt;br /&gt;
        blast.setPosition(pos)&lt;br /&gt;
        blast.parm(&amp;#039;group&amp;#039;).set(group_node.parm(&amp;quot;groupname&amp;quot;).eval())&lt;br /&gt;
&lt;br /&gt;
        group_node.setSelected(1)&lt;br /&gt;
        blast.setDisplayFlag(1)&lt;br /&gt;
        blast.setSelected(1)&lt;br /&gt;
        blast.setRenderFlag(1)&lt;br /&gt;
        group_node.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
        &lt;br /&gt;
    except hou.Error as e:&lt;br /&gt;
        print(f&amp;quot;An error occurred: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
create_group_from_bbox()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Toggle Viewport Background Color ===&lt;br /&gt;
Bound it to F6&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
import toolutils&lt;br /&gt;
&lt;br /&gt;
bg = None&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    # cycle next bg&lt;br /&gt;
    if kwargs[&amp;#039;ctrlclick&amp;#039;]: raise&lt;br /&gt;
    bgs = hou.session.bg[:]&lt;br /&gt;
    bgs = bgs[1:]+bgs[:1]&lt;br /&gt;
    if kwargs[&amp;#039;shiftclick&amp;#039;]: bgs = [&amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;, &amp;#039;wb&amp;#039;]&lt;br /&gt;
    bg = bgs[0]&lt;br /&gt;
    hou.session.bg = bgs&lt;br /&gt;
except:&lt;br /&gt;
    # set up default bg vars&lt;br /&gt;
    hou.session.bg = [&amp;#039;wb&amp;#039;, &amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;]&lt;br /&gt;
    bg = hou.session.bg[0]&lt;br /&gt;
&lt;br /&gt;
bgs = { &amp;#039;wb&amp;#039;:&amp;#039;dark&amp;#039;, &amp;#039;bw&amp;#039;:&amp;#039;grey&amp;#039;, &amp;#039;light&amp;#039;:&amp;#039;light&amp;#039; }&lt;br /&gt;
&lt;br /&gt;
hou.hscript(&amp;quot;viewdisplay -B %s *&amp;quot; % bg)&lt;br /&gt;
hou.ui.setStatusMessage(&amp;quot;Cycled background to %s&amp;quot; % bgs[bg].upper() )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Toggle Auto / Manual scene update ===&lt;br /&gt;
Bound to F11, I use it every day.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
mode = hou.updateModeSetting().name()&lt;br /&gt;
if mode == &amp;#039;AutoUpdate&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.Manual)&lt;br /&gt;
if mode == &amp;#039;Manual&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.AutoUpdate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Auto create a wrangle node ===&lt;br /&gt;
I&amp;#039;m pretty sure this exists already but why not reinvent the wheel. Creates an attr wrangle and keeps inputs/outputs, with the code box already in focus. I only use the &amp;#039;P&amp;#039; parameter viewer (the one that pops up in your network view) so other desktops might not work.&lt;br /&gt;
&lt;br /&gt;
bound to ctrl-shift-w&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TODO  fix some weird inputs/outputs configurations&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
sn = hou.selectedNodes()&lt;br /&gt;
if sn:&lt;br /&gt;
    w = sn[-1].parent().createNode(&amp;#039;attribwrangle&amp;#039;)&lt;br /&gt;
    outputs = sn[-1].outputs()&lt;br /&gt;
    &lt;br /&gt;
    w.setInput(0,sn[-1])&lt;br /&gt;
    w.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
    w.setSelected(True,clear_all_selected=True, show_asset_if_selected=True)&lt;br /&gt;
    w.setDisplayFlag(True)&lt;br /&gt;
    sn[-1].setDisplayFlag(False)&lt;br /&gt;
    if outputs:&lt;br /&gt;
        for output in outputs:&lt;br /&gt;
            output.setInput(0,w)&lt;br /&gt;
else:&lt;br /&gt;
    #should not error out if it&amp;#039;s launched in a shelf above a network plane&lt;br /&gt;
    w = hou.ui.curDesktop().paneTabUnderCursor().pwd().createNode(&amp;#039;attribwrangle&amp;#039;)    &lt;br /&gt;
&lt;br /&gt;
w.moveToGoodPosition(relative_to_inputs=True, move_inputs=False, move_outputs=True, move_unconnected=False)    &lt;br /&gt;
&lt;br /&gt;
networkview = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkview.setCurrentNode(w,True)&lt;br /&gt;
networkview.parmMoveFocusTo(&amp;#039;snippet&amp;#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Copy auto Paste-Merge ===&lt;br /&gt;
Copy from https://berniebernie.fr/wiki/Houdini_Python#Paste_clipboard_nodes_to_object_merges&lt;br /&gt;
&lt;br /&gt;
Allow to copy nodes elsewhere with an object merge. Bound it to alt-v (so ctrl-c, alt-v in another place)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/M7N0Stq.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# this snippet will paste nodes in clipboard to object merges&lt;br /&gt;
# use it with a shortcut (I overrode &amp;#039;alt-v&amp;#039; in network pane context)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
network = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkpath = network.pwd().path()&lt;br /&gt;
pos = network.cursorPosition()&lt;br /&gt;
&lt;br /&gt;
clipboard = hou.ui.getTextFromClipboard()&lt;br /&gt;
&lt;br /&gt;
n = 0&lt;br /&gt;
&lt;br /&gt;
if clipboard:&lt;br /&gt;
    list = clipboard.split()&lt;br /&gt;
    for item in list:&lt;br /&gt;
        if hou.node(item) != None:&lt;br /&gt;
            merge = hou.node(networkpath).createNode(&amp;#039;object_merge&amp;#039;,&amp;#039;merge_&amp;#039;+item.split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
            merge.parm(&amp;#039;objpath1&amp;#039;).set(str(item))&lt;br /&gt;
            merge.setPosition(pos)&lt;br /&gt;
            merge.move([n*2,0])&lt;br /&gt;
            if n == 0:&lt;br /&gt;
                merge.setSelected(True,True)&lt;br /&gt;
            else:&lt;br /&gt;
                merge.setSelected(True,False)&lt;br /&gt;
            n = n + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Screenshot to background image===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/kuc1PnO.gif&lt;br /&gt;
&lt;br /&gt;
Customize your capture command line, no error checking &amp;#039;cause I&amp;#039;m not a professional programmer, aka it works on my machine.&lt;br /&gt;
&lt;br /&gt;
Should work with windows (hardcoded path to [https://www.donationcoder.com/software/mouser/popular-apps/minicap minicap] ) and linux centos with xfce-screenshoter&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#python 3.5+&lt;br /&gt;
import hou&lt;br /&gt;
import os&lt;br /&gt;
import subprocess&lt;br /&gt;
import nodegraphutils as utils&lt;br /&gt;
from time import gmtime, strftime&lt;br /&gt;
from os.path import expanduser&lt;br /&gt;
home = expanduser(&amp;quot;~&amp;quot;)&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
exe = home+&amp;quot;\Downloads\exe\MiniCap.exe&amp;quot;&lt;br /&gt;
&lt;br /&gt;
widthRatio = 4                      # change to make screenshot bigger or smaller, this is ~x4 node width&lt;br /&gt;
&lt;br /&gt;
def takeScreenShot(savePath):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;change to your preferred capture app /!\ no error checking for now &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    subprocess.check_call([exe,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &lt;br /&gt;
    #following is older code for linux i&amp;#039;ll update it if I linux again&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    Path(os.path.dirname(Path(savePath))).mkdir(parents=True, exist_ok=True)&lt;br /&gt;
    if platform == &amp;quot;linux&amp;quot; or platform == &amp;quot;linux2&amp;quot;:&lt;br /&gt;
        savepath = os.path.dirname(Path(savePath)) #screenshot dir    &lt;br /&gt;
        screenshotPNG = subprocess.check_output(&amp;#039;mv &amp;quot;$(xfce4-screenshooter -ro ls)&amp;quot; -v &amp;#039;+savepath,shell=True)&lt;br /&gt;
        screenshotPNG = screenshotPNG.decode().strip().split(&amp;#039;\n&amp;#039;)[0].split(&amp;#039; -&amp;gt; &amp;#039;)[1].replace(&amp;quot;&amp;#039;&amp;quot;,&amp;quot;&amp;quot;) #helluva nasty oneliner&lt;br /&gt;
        os.chdir(savepath)&lt;br /&gt;
        os.rename(os.path.basename(screenshotPNG),os.path.basename(savePath))&lt;br /&gt;
        &lt;br /&gt;
        # linux&lt;br /&gt;
    elif platform == &amp;quot;darwin&amp;quot;:&lt;br /&gt;
        exit&lt;br /&gt;
    elif platform == &amp;quot;win32&amp;quot;:&lt;br /&gt;
        subprocess.check_call([r&amp;quot;C:\Users\bernie\Documents\houdini18.0\config\exe\MiniCap.exe&amp;quot;,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
def removeBackgroundImage(**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; erases bg image from tuples of backgroundImages() if it can find it, updates bg &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    deletingNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = deletingNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    backgroundImagesDic = tuple(x for x in backgroundImagesDic if x.path() != image)&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&lt;br /&gt;
def changeBackgroundImageBrightness(event_type,**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; changes brightness/visibility if template or bypass flags are checked -- its poorly written/thought but i was tired&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    nullNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = nullNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    brightness = 1.0&lt;br /&gt;
    if nullNode.isBypassed():&lt;br /&gt;
        brightness = 0.0&lt;br /&gt;
    elif nullNode.isTemplateFlagSet():&lt;br /&gt;
        brightness = 0.5&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    i = 0&lt;br /&gt;
    for item in backgroundImagesDic:&lt;br /&gt;
        if item.path() == image:&lt;br /&gt;
            backgroundImagesDic[i].setBrightness(brightness)&lt;br /&gt;
            break&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
    &lt;br /&gt;
#generate unique(ish) path for screenshot&lt;br /&gt;
timestamp = strftime(&amp;#039;%Y%m%d_%H%M%S&amp;#039;, gmtime())&lt;br /&gt;
hipname = str(hou.getenv(&amp;#039;HIPNAME&amp;#039;))&lt;br /&gt;
hippath = str(hou.getenv(&amp;#039;HIP&amp;#039;)) + &amp;#039;/screenshots&amp;#039;&lt;br /&gt;
screenshotName = hipname + &amp;#039;.&amp;#039; + timestamp + &amp;#039;.png&amp;#039;&lt;br /&gt;
pythonpath = hippath+&amp;#039;/&amp;#039;+screenshotName&lt;br /&gt;
systempath = hippath + &amp;#039;\\&amp;#039; + screenshotName&lt;br /&gt;
systempath = Path(systempath)&lt;br /&gt;
&lt;br /&gt;
print(systempath)&lt;br /&gt;
&lt;br /&gt;
houdinipath = &amp;#039;$HIP/screenshots/&amp;#039;+screenshotName&lt;br /&gt;
&lt;br /&gt;
#take screenshot with capture region&lt;br /&gt;
takeScreenShot(systempath)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#set up background image plane&lt;br /&gt;
editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
image = hou.NetworkImage()&lt;br /&gt;
image.setPath(houdinipath)&lt;br /&gt;
sel = hou.selectedNodes()&lt;br /&gt;
nullNode = &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
if sel:&lt;br /&gt;
    lastSel = sel[-1]&lt;br /&gt;
    nullNode = lastSel.parent().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;)&lt;br /&gt;
    if lastSel.outputConnections():&lt;br /&gt;
        nullNode.setInput(0,lastSel)                   &lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    nullNode = editor.pwd().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;) &lt;br /&gt;
    nullNode.moveToGoodPosition()&lt;br /&gt;
    lastSel = nullNode&lt;br /&gt;
&lt;br /&gt;
#configure image plane placement&lt;br /&gt;
nullNode.setUserData(&amp;#039;nodeshape&amp;#039;,&amp;#039;task&amp;#039;)&lt;br /&gt;
nullNode.setPosition(lastSel.position())&lt;br /&gt;
nullNode.setColor(hou.Color(.3,.3,.3))&lt;br /&gt;
nullNode.move([lastSel.size()[0]*2,-lastSel.size()[1]*2])&lt;br /&gt;
&lt;br /&gt;
rez = hou.imageResolution(pythonpath)&lt;br /&gt;
ratio = 1.0*rez[1]/rez[0]&lt;br /&gt;
rect = hou.BoundingRect(0,-lastSel.size()[1]*1.1,widthRatio,-widthRatio*ratio-lastSel.size()[1]*1.1)&lt;br /&gt;
image.setRelativeToPath(nullNode.path())&lt;br /&gt;
image.setRect(rect)&lt;br /&gt;
&lt;br /&gt;
#following is adding a spare parm with image path to be able to know which node corresponds to which background image&lt;br /&gt;
#could have used a user attribute or relativeToPath() and smarter logic but it works and it helps me visualize filepath&lt;br /&gt;
&lt;br /&gt;
hou_parm_template_group = hou.ParmTemplateGroup()&lt;br /&gt;
hou_parm_template = hou.LabelParmTemplate(&amp;quot;houdinipath&amp;quot;, &amp;quot;Label&amp;quot;, column_labels=([&amp;#039;\\&amp;#039;+houdinipath]))&lt;br /&gt;
hou_parm_template.hideLabel(True)&lt;br /&gt;
hou_parm_template_group.append(hou_parm_template)&lt;br /&gt;
nullNode.setParmTemplateGroup(hou_parm_template_group)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#attach a function that deletes the background image plane if the corresponding node is deleted (faster than doing it by hand)&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.BeingDeleted,), removeBackgroundImage)&lt;br /&gt;
&lt;br /&gt;
#attach a function to change visibility or opacity if corresponding node flags are changed&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.FlagChanged,), changeBackgroundImageBrightness)&lt;br /&gt;
&lt;br /&gt;
#add image to network background&lt;br /&gt;
backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
backgroundImagesDic = backgroundImagesDic + (image,)&lt;br /&gt;
editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other UI ==&lt;br /&gt;
=== Previews ===&lt;br /&gt;
==== Detach parameter window à la maya ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/3txLohO.gif&lt;br /&gt;
&lt;br /&gt;
==== Auto add frame offset parm ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZXNrwvC.gif&lt;br /&gt;
&lt;br /&gt;
=== Userdocs XMLs ===&lt;br /&gt;
This is for right click context menus. Use the hscript &amp;#039;menurefresh&amp;#039; when developping so as not to relaunch H each time (like a reload(module) in python).&lt;br /&gt;
==== PARMmenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
	&amp;lt;menu&amp;gt;&lt;br /&gt;
		&amp;lt;subMenu id=&amp;quot;bernie_rclick&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;label&amp;gt;Bernie&amp;#039;s&amp;lt;/label&amp;gt;&lt;br /&gt;
			&amp;lt;modifyItem&amp;gt;&amp;lt;insertAfter&amp;gt;motion_effects_menu&amp;lt;/insertAfter&amp;gt;&amp;lt;/modifyItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Add a frame offset parm to $F#&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						return bernie_tools.validate_sequence_parm(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.add_sequence_offset_spareparm(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Browse to...&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						reload(bernie_tools)&lt;br /&gt;
						return bernie_tools.validate_uri(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.browse_to(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_open_parm_spreadsheet&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Open/Add in Parameter Spreadsheet&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.open_parm_spreadsheet(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
		&amp;lt;/subMenu&amp;gt;&lt;br /&gt;
	&amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ParmGearMenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
 (long sidefx text)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
    &amp;lt;!-- menuDocument can only contain 1 menu element, whose id is &lt;br /&gt;
         implicitly &amp;quot;root_menu&amp;quot;&lt;br /&gt;
      --&amp;gt;&lt;br /&gt;
    &amp;lt;menu&amp;gt;&lt;br /&gt;
        &amp;lt;scriptItem id=&amp;quot;detach_parameter_window&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;label&amp;gt;Detach Parameter Window...&amp;lt;/label&amp;gt;&lt;br /&gt;
            &amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.detach_parameter_window(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
        &amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
    &amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== bernie_tools.py ===&lt;br /&gt;
==== py3 ====&lt;br /&gt;
Some updates&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
		name = existing_parm.name()&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(name+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(name+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(name+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+name+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+name+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	# todo: keep existing parm mask to append to it. Can&amp;#039;t retrieve current value AFAIK.&lt;br /&gt;
	&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	#selectedParms = return_first_parm(kwargs)&lt;br /&gt;
	&lt;br /&gt;
	parms = []&lt;br /&gt;
	for parm in kwargs[&amp;#039;parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	for parm in kwargs[&amp;#039;locked_parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	&lt;br /&gt;
	selectedParm = parms[0]&lt;br /&gt;
&lt;br /&gt;
	parms = [x.name() for x in parms]&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
&lt;br /&gt;
	parmsheetP = selectedParm.name()&lt;br /&gt;
	parmsheetPaths = nodepaths&lt;br /&gt;
&lt;br /&gt;
	if kwargs[&amp;#039;ctrlclick&amp;#039;]:&lt;br /&gt;
		rmi = hou.ui.readMultiInput(&amp;#039;Edit the node path and parms.&amp;#039;, [&amp;#039;Node(s)&amp;#039;,&amp;#039;Parm(s)&amp;#039;], buttons=(&amp;#039;OK&amp;#039;,&amp;#039;Cancel&amp;#039;), severity=hou.severityType.Message, default_choice=0, close_choice=1, help=&amp;#039;Node path can have wildcards&amp;#039;, title=&amp;#039;Parm Chooser&amp;#039;, initial_contents=(nodepaths.split(&amp;#039; &amp;#039;, 1)[0] ,selectedParm.name()))&lt;br /&gt;
		if rmi[0]==0:&lt;br /&gt;
			parmsheetPaths = rmi[1][0]&lt;br /&gt;
			parmsheetP = rmi[1][1]&lt;br /&gt;
&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 0 -p &amp;quot;&amp;#039;+parmsheetP+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+parmsheetPaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== py2 ====&lt;br /&gt;
Need to update to py3 for H19&lt;br /&gt;
&lt;br /&gt;
In my userdocs\houdini18.5\python2.7libs folder&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#march2022 update: fuck putin and improved the add offset to sequence so that you can still choose a new file and keep the offset without having to delete the spare parms (for imageplane frame offset for instance)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(label+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(label+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(label+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+label+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+label+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	selectedParm = return_first_parm(kwargs)&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
	#print(nodepaths)&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 1 -p &amp;quot;&amp;#039;+selectedParm.name()+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+nodepaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Stupid_Questions&amp;diff=875</id>
		<title>Houdini Stupid Questions</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Stupid_Questions&amp;diff=875"/>
		<updated>2025-10-15T16:31:19Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* How do I transform instance points ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes I struggle with stupid stuff, I&amp;#039;ll answer to my own questions here. If you&amp;#039;re here, you were probably brought in by Google!&lt;br /&gt;
&lt;br /&gt;
I&amp;#039;m still learning as I go so if you see something that&amp;#039;s downright wrong or silly, email me! bernie at berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
Jeez I need to sort these&lt;br /&gt;
&lt;br /&gt;
==== How do I slide a curve along a curve ? ==== &lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/t9sm2Uj.gif https://i.imgur.com/ZXdtLOy.jpeg&lt;br /&gt;
&lt;br /&gt;
Working on snakes ? If you use carve, you&amp;#039;ll notice you end up with weird bits where the tips &amp;#039;pop&amp;#039;, what you would like is to slide the curve along another curve, but smoothly. In comes xyzdist and primuv (check out [https://www.toadstorm.com/blog/?p=465 Toadstorm&amp;#039;s page]). The advantage is that you will get a real, smooth sliding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//input 0 is the carved/small curve&lt;br /&gt;
//input 1 is the full, longer curve&lt;br /&gt;
&lt;br /&gt;
i@prim;&lt;br /&gt;
v@uv;&lt;br /&gt;
//figure out where i am on the original curve&lt;br /&gt;
xyzdist(1, @P, @prim,@uv);&lt;br /&gt;
&lt;br /&gt;
//animate the uv.x position, blissfully ignore what happens when we arrive at the end of curve&lt;br /&gt;
@uv.x += (@Time*.1)%1;&lt;br /&gt;
&lt;br /&gt;
//move along&lt;br /&gt;
@P = primuv(1,&amp;quot;P&amp;quot;,i@prim,@uv);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== How can I carve curves with an attribute ? ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/U56M7Ho.png&lt;br /&gt;
&lt;br /&gt;
The carve sop works great, but afaik you can&amp;#039;t use a local variable to carve multiple curves differently short of using a foreach loop. &lt;br /&gt;
&lt;br /&gt;
Thankfully (in more recent versions of houdini?), you can use a prebuilt function found in &amp;#039;&amp;#039;&amp;#039;$HFS/vex/include/groom.h&amp;#039;&amp;#039;&amp;#039;: &amp;#039;&amp;#039;void adjustPrimLength(const int geo, prim; const float currentlength, targetlength)&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
In practice it&amp;#039;s fairly easy to use!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;groom.h&amp;gt;&lt;br /&gt;
float newperim = @perimeter*sin(@Time+@primnum/3.0)+1; //notice the 3.0 cause&amp;#039; i&amp;#039;m lazy to cast floats&lt;br /&gt;
adjustPrimLength(0, @primnum, @perimeter, newperim);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Get the end/start points of primitive curves====&lt;br /&gt;
You can use curveu and fetch 0.0 or 1.0 or use this one-liner&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#in a point wrangle&lt;br /&gt;
i@group_tip = vertexprimindex(0,@vtxnum) == 0;&lt;br /&gt;
i@group_end = vertexprimindex(0,@vtxnum) == primvertexcount(0,vertexprim(0,@vtxnum))-1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== How do I snap objects to objects ====&lt;br /&gt;
&lt;br /&gt;
Works in obj mode, not sop mode, use &amp;#039;;&amp;#039; (Orientatio Picking)&lt;br /&gt;
&lt;br /&gt;
Great tutorial here (hipflask) https://www.youtube.com/watch?v=Zh6Q6r9LQlA&lt;br /&gt;
&lt;br /&gt;
==== Shifting Keyframes in Animation Editor like in Maya ====&lt;br /&gt;
It&amp;#039;s a small frustration but to shift frames to later you can write &amp;#039;&amp;#039;&amp;#039;+10&amp;#039;&amp;#039;&amp;#039; frames&lt;br /&gt;
But if you want to shift frames to an earlier time you have to use &amp;#039;&amp;#039;&amp;#039;+-10&amp;#039;&amp;#039;&amp;#039; not -10&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/Dm05N8o.png&lt;br /&gt;
&lt;br /&gt;
==== How do I scale a packed primitive without unpacking it ? ====&lt;br /&gt;
&lt;br /&gt;
Code stolen from https://brendandawes.com/blog/scaling-packed-primitives-with-vex-in-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector scale = ch(&amp;#039;newScale&amp;#039;);&lt;br /&gt;
matrix3 trn = primintrinsic(0, &amp;quot;transform&amp;quot;, @primnum);&lt;br /&gt;
matrix scalem = maketransform(0, 0, {0,0,0}, {0,0,0}, scale, @P);&lt;br /&gt;
trn *= matrix3(scalem);&lt;br /&gt;
setprimintrinsic(0, &amp;quot;transform&amp;quot;, @primnum, trn);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== How do I embed textures in my Houdini .hip file ? ====&lt;br /&gt;
&lt;br /&gt;
I don&amp;#039;t really recommend it, as like the stash SOP it will increase your file size, but sometimes you want no dependencies. I know Rich Lord did something similar in his [https://www.richlord.com/getmyfiles nifty example files].&lt;br /&gt;
&lt;br /&gt;
Anyhow, the trick I used (there might be an easier method, IDK), is to add null Subnetwork node to my scene then right-click &amp;gt; &amp;#039;&amp;#039;&amp;#039;Digital Asset &amp;gt; Create New&amp;#039;&amp;#039;&amp;#039; but &amp;#039;&amp;#039;embed&amp;#039;&amp;#039; it in the current hip file instead of saving it externally.&lt;br /&gt;
&lt;br /&gt;
I right click the asset &amp;#039;&amp;#039;: Type Properties &amp;gt; Extra Files &amp;gt; Add Textures Files&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Then in my SOP nodes or other contexts (in my case a Network Image with a random cat), refer to the image by going to the internal HDA path; in the file browser I got to: &amp;#039;&amp;#039;&amp;#039;opdef:/Object/myEmbeddedHdaName&amp;#039;&amp;#039;&amp;#039; and it shows the images:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/8yxaDXS.png&lt;br /&gt;
&lt;br /&gt;
==== How can I rotate a velocity volume/VDB and have it work for advection ? ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/e82MuuN.gif&lt;br /&gt;
&lt;br /&gt;
Thought it would work straight outta the box but I had an issue with a rotated volume, so here&amp;#039;s how I fixed it &amp;#039;&amp;#039;before&amp;#039;&amp;#039; doing my transform:&lt;br /&gt;
&lt;br /&gt;
(I&amp;#039;m not sure how it works with a normal volume), if you have a vdb you can either use the &amp;#039;&amp;#039;&amp;#039;Primitive Properties&amp;#039;&amp;#039;&amp;#039; then Volumes&amp;gt;VDB&amp;gt;Vector Type set to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;Displacement/Velocity/Acceleration&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;, or if you&amp;#039;re merging vel.* to a single vec3s volume using &amp;#039;&amp;#039;&amp;#039;VDB Vector Merge&amp;#039;&amp;#039;&amp;#039; make sure you set it to &amp;#039;&amp;#039;Displacement/Velocity/Acceleration&amp;#039;&amp;#039; here too. You can also use the &amp;#039;&amp;#039;&amp;#039;Labs Transform Properties&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
Then do your rotate (or scale) and you will have rotated and scaled velocites (in my example, I&amp;#039;m advecting particles with VDB).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;edit:&amp;#039;&amp;#039; as usual Matt Estela touches on this topic (attribute types) on [https://www.tokeru.com/cgwiki/index.php?title=JoyOfVex17#Transform_operations_and_attribute_types Tokeru]:&lt;br /&gt;
&lt;br /&gt;
==== How do I work on two objects that don&amp;#039;t have the same number of points/prims ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/S9K9I9y.png&lt;br /&gt;
&lt;br /&gt;
Happens to me a lot, you are used to using @ptnum or @elemnum to work on several streams of the same initial object, but in the process you&amp;#039;ve deleted faces or points and code doesn&amp;#039;t work. Thankfully idtoprim and point are fast:&lt;br /&gt;
&lt;br /&gt;
Prior to branching your nodes, set &amp;lt;code&amp;gt;i@id=@elemnum&amp;lt;/code&amp;gt;, then use [https://www.sidefx.com/docs/houdini/vex/functions/idtoprim.html idtroprim]&lt;br /&gt;
&lt;br /&gt;
In the screenshot above, I run code to color my faces if they&amp;#039;ve been deleted in the second incoming stream&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(idtoprim(1,@id)==-1){&lt;br /&gt;
    @Cd *= 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== How do I export a tracked camera from After Effects to Houdini ? ====&lt;br /&gt;
&amp;#039;&amp;#039;edit:&amp;#039;&amp;#039; kinda broken, will try to work on something if i have time&lt;br /&gt;
&lt;br /&gt;
Use this code by Howiem:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
H &amp;gt; AE: https://gist.github.com/howiemnet/a3d7b9f283c9227f7fa2f355db89a5cf&lt;br /&gt;
&lt;br /&gt;
AE &amp;gt; H: https://gist.github.com/howiemnet/8784cf04568c849271730965eaf35159&lt;br /&gt;
&lt;br /&gt;
https://www.sidefx.com/forum/topic/53681/?page=1#post-241106&lt;br /&gt;
&lt;br /&gt;
==== Where can I find all the Houdini Environment Variables ? ====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;Help &amp;gt; About &amp;gt; Show Details&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/1Tm7aCP.png&lt;br /&gt;
&lt;br /&gt;
==== How do I extract the transforms of my rigid body simulation objects ? ====&lt;br /&gt;
&lt;br /&gt;
Let&amp;#039;s say I just want to grab the transforms of my simulation dop opbjects (using points), and reapply them to another object.&lt;br /&gt;
&lt;br /&gt;
I use the intrinsic &amp;lt;code&amp;gt;packedfulltransform&amp;lt;/code&amp;gt; so that I don&amp;#039;t have to bother with weird orientations&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
4@xform = primintrinsic(0, &amp;quot;packedfulltransform&amp;quot;, i@ptnum);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Then use &amp;#039;&amp;#039;&amp;#039;Transform By Attribute&amp;#039;&amp;#039;&amp;#039; sop to apply the transform. Will provide screenshots !&lt;br /&gt;
&lt;br /&gt;
===== How do I transform instance points ? =====&lt;br /&gt;
&lt;br /&gt;
hindsight edit: this feels super cumbersome. If you have packed objects you should simply be able to move,rotate, scale with &amp;#039;edit&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
More than once have I had to copy and move around objects manually but felt that simply merging them was a waste of ressources as it could be a simple instance that is moved around. &lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s a pain in the ass though because in SOPs transforms don&amp;#039;t seem to apply orient and pscale attributes. So without too much packedtransform trickery à la Matt Estela there is a way to extract transform using, well, &amp;#039;&amp;#039;extracttransform&amp;#039;&amp;#039;. &lt;br /&gt;
&lt;br /&gt;
It basically takes an object sitting straight and plump in the center of the world, a second object (sharing the same number of points and the same name attribute) moved and scaled around in the world, and returns a point with the &amp;lt;code&amp;gt;@P, @orient,@pscale&amp;lt;/code&amp;gt; attributes, all you need (with &amp;lt;code&amp;gt;s@instance = &amp;quot;/obj/myPigHead&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/lXsetYU.png&lt;br /&gt;
&lt;br /&gt;
==== How do you loop seamlessly using the Carve SOP ? ====&lt;br /&gt;
Using modulo 1, it&amp;#039;s easy to keep values between 0 and 1 in the Carve sop, so you can have animation like &amp;lt;code&amp;gt;( @Time * 0.1 + 0.1) % 1&amp;lt;/code&amp;gt;, however you often end up with a &amp;#039;jump&amp;#039; when the first U or second U reach 0 or 1. The trick is to toggle on and off the bottom extract parameter so that it reverses the extraction when you have the &amp;#039;jump&amp;#039;:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/JmN0FJB.gif&lt;br /&gt;
&lt;br /&gt;
I used a simple &amp;lt;code&amp;gt;ch(&amp;#039;domainu1&amp;#039;)&amp;gt;ch(&amp;#039;domainu2&amp;#039;)&amp;lt;/code&amp;gt; (and switch it around for the second toggle) in the &amp;#039;Keep Inside&amp;#039; and &amp;#039;Keep Outside&amp;#039; expressions.&lt;br /&gt;
&lt;br /&gt;
==== How do I disable / remove pin to animation in Vellum ? ====&lt;br /&gt;
&lt;br /&gt;
If you use SOP level Vellum, dive inside solver and since I&amp;#039;m not a super big fan of the &amp;quot;vellum constraint property&amp;quot;, I use a geometry wrangle to remove the constraint prims:&lt;br /&gt;
&lt;br /&gt;
* set wrangle to primitives&lt;br /&gt;
* in the data binding change &amp;#039;&amp;#039;Geometry&amp;#039;&amp;#039; to &amp;#039;&amp;#039;ConstraintGeometry&amp;#039;&amp;#039; &lt;br /&gt;
* add your code, in my example it deletes geometry if frame&amp;gt;35 &amp;#039;&amp;#039;&amp;#039;and&amp;#039;&amp;#039;&amp;#039; the name of the constraint (&amp;#039;&amp;#039;constraint_tag&amp;#039;&amp;#039;) corresponds to the one I named in SOPs: &amp;#039;&amp;#039;pinToAnimationCstr&amp;#039;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/X4cAkVB.png&lt;br /&gt;
&lt;br /&gt;
==== Adding a keyframe to a parameter with python ====&lt;br /&gt;
You need to create the keyframe, then set it (bind it) to your parm. The doc is kind of mysterious about it but the easiest way to understand how it works it to call the &amp;lt;code&amp;gt;asCode()&amp;lt;/code&amp;gt; on your parm. I wrote a little snippet to extract code [https://berniebernie.fr/wiki/Houdini_Python#Selected_node_as_python_code here]&lt;br /&gt;
&lt;br /&gt;
Do that by simply dragging and dropping you parm into a python shell and adding asCode after it like so:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/p5RsOHd.gif&lt;br /&gt;
&lt;br /&gt;
And it&amp;#039;ll output something along the lines of:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
hou_parm = hou_node.parm(&amp;quot;camswitch&amp;quot;)&lt;br /&gt;
hou_parm.lock(False)&lt;br /&gt;
hou_parm.deleteAllKeyframes()&lt;br /&gt;
hou_parm.set(1)&lt;br /&gt;
hou_parm.setAutoscope(True)&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
hou_keyframe = hou.Keyframe()&lt;br /&gt;
hou_keyframe.setTime(7.958333333333333)&lt;br /&gt;
hou_keyframe.setValue(3)&lt;br /&gt;
hou_keyframe.setSlope(0)&lt;br /&gt;
hou_keyframe.setInSlope(0)&lt;br /&gt;
hou_keyframe.useSlope(False)&lt;br /&gt;
hou_keyframe.setAccel(0)&lt;br /&gt;
hou_keyframe.useAccel(False)&lt;br /&gt;
hou_keyframe.interpretAccelAsRatio(False)&lt;br /&gt;
hou_keyframe.setExpression(&amp;quot;constant()&amp;quot;, hou.exprLanguage.Hscript)&lt;br /&gt;
hou_parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====How do I detach a floating window for my parameters====&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s a nice feature I like from maya, I can&amp;#039;t seem to find it in the vanilla version of houdini however it is written in the docs: https://www.sidefx.com/docs/houdini/hom/hou/Desktop.html#createFloatingPaneTab&lt;br /&gt;
You can follow the instructions of [[Houdini Python#Auto-add_frame_offset_parameter|Auto Add Frame Offset Parameter]] to add a custom menu to your gear button (edit parameter interface), but instead of using &amp;lt;code&amp;gt;PARMmenu.xml&amp;lt;/code&amp;gt; you&amp;#039;ll have to add it to &amp;lt;code&amp;gt;ParmGearMenu.xml&amp;lt;/code&amp;gt;. I&amp;#039;ve also noticed that the hscript command &amp;lt;code&amp;gt;menurefresh&amp;lt;/code&amp;gt; doesn&amp;#039;t work with refreshing when you are editing your interface. FYI&lt;br /&gt;
&lt;br /&gt;
====Frame offset of a file sequence with padding====&lt;br /&gt;
You need padzero to match the padding of your image sequence like so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
D:/path/to/filesequence.`padzero(4, $F + 10)`.png&lt;br /&gt;
# at frame 1, returns D:/path/to/filesequence.0011.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, having it done so many times, I added a small tool to my inventory: [[Houdini Python#Auto-add_frame_offset_parameter|Auto Add Frame Offset Parameter]]&lt;br /&gt;
&lt;br /&gt;
====How do I view all Houdini icons ?====&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039; &amp;#039;&amp;#039;&amp;#039;19.5 update&amp;#039;&amp;#039;&amp;#039;: broken ? Will investigate later.&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
So you want a pretty icon for your awesome HDA ? &lt;br /&gt;
You can open up the help file and replace whatever is there by /icons. It will load page with all available .svg icons.&lt;br /&gt;
&lt;br /&gt;
It is super slow to load though (extracting from a zip ? using a raspberry pi to host the help server?) so if you plan to do it more than once&lt;br /&gt;
I recommend opening it up in your browser and saving the page locally.&lt;br /&gt;
&lt;br /&gt;
Once you&amp;#039;ve found your icon hover over it to find the name and you can put it in the icon field of your HDA by using &amp;lt;code&amp;gt;CATEGORY_iconname&amp;lt;/code&amp;gt; so in my screenshot it would be &amp;lt;code&amp;gt;BUTTONS_bundle_set_selected&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/Ezx76xl.png&lt;br /&gt;
&lt;br /&gt;
====How do I add a custom OTL/HDA directory ====&lt;br /&gt;
I like to keep experimental HDAs in a folder that I can share across using google drive / dropbox/ whatever. &lt;br /&gt;
I&amp;#039;m not super fond of the packages/env handling as I feel it&amp;#039;s confusing for now but anyhow.&lt;br /&gt;
&lt;br /&gt;
This adds to $HOUDINI_OTLSCAN_PATH&lt;br /&gt;
&lt;br /&gt;
add to a json file in your packages folder&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;env&amp;quot;: [&lt;br /&gt;
    	{&lt;br /&gt;
    		&amp;quot;HOUDINI_OTLSCAN_PATH&amp;quot;: &amp;quot;G:/My Drive/OTL&amp;quot;&lt;br /&gt;
    	},&lt;br /&gt;
    ]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Solo toggling a light in the IPR View ?====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#place in a shelf with a shortcut, I chose Ctrl-Alt-s as it was free. Works with Arnold&lt;br /&gt;
#hit it a second time to remove the solo light&lt;br /&gt;
ipr = next(x for x in hou.ui.currentPaneTabs() if x.type().name() == &amp;#039;IPRViewer&amp;#039;)&lt;br /&gt;
soloParm = ipr.ropNode().parm(&amp;#039;sololight&amp;#039;)&lt;br /&gt;
if soloParm.eval() != &amp;#039;&amp;#039;:&lt;br /&gt;
    soloParm.set(&amp;#039;&amp;#039;)&lt;br /&gt;
else:&lt;br /&gt;
    soloParm.set(&amp;quot; &amp;quot;.join([n.path() for n in hou.selectedNodes()]))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====How do I layout nodes nicely ?====&lt;br /&gt;
* A + drag in a direction with mouse: align nodes&lt;br /&gt;
* shift + drag node: move all nodes above current selected one&lt;br /&gt;
* ctrl + drag node: move nodes below&lt;br /&gt;
&lt;br /&gt;
====How do I change a transforms rotation axis easily in the viewport?====&lt;br /&gt;
Can&amp;#039;t be done &amp;#039;easily&amp;#039; AFAIK, you can however right click the manipulator tool to change axis pivots&lt;br /&gt;
====How do i bake cameras in houdini====&lt;br /&gt;
If you need keyframes, i recommend doing the easier bakeanimation way. Create a ropnet, add a bake animation, fetch source and target and hit render&lt;br /&gt;
&lt;br /&gt;
But using CHOPs&lt;br /&gt;
&lt;br /&gt;
* in a chop network, drop down an object node, grab your object that has parents or whatever as target object, nothing as reference object, and full transform or rotation and scale&lt;br /&gt;
* connect to the output an export node, choose your object to bake to, and in the path add &amp;#039;&amp;#039;&amp;#039;t[xyz] r[xyz]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* click the 2nd button of export node (orange &amp;#039;export&amp;#039; button) and it will connect the chop to your target object (channels will be orange)&lt;br /&gt;
https://i.imgur.com/TWWvxYg.png&lt;br /&gt;
* or use `chop(&amp;#039;path/to/chop/channelx&amp;#039;)` to fetch chop&lt;br /&gt;
* Right click your parm, &amp;lt;code&amp;gt;Keyframes&amp;gt;Bake Keyframes&amp;lt;/code&amp;gt; (I almost always use &amp;lt;code&amp;gt;Override Range from Selected Segments&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Bake, Disable Export Flags&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
==== How do I copy &amp;#039;stamp&amp;#039; different objects with Copy to Points ====&lt;br /&gt;
&lt;br /&gt;
I kind of brute-forced each time with a foreach loop but I&amp;#039;m a big dummy apparently all you need to do is have the same variable (a random int from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;number of objects - 1&amp;lt;/code&amp;gt;) on your 2nd input as it is on the copy to points Sop &amp;#039;Piece Attribute&amp;#039; which defaults to &amp;lt;code&amp;gt;@name&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/fUZiclu.png&lt;br /&gt;
&lt;br /&gt;
====How do I rotate around an axis when using copy to points====&lt;br /&gt;
Matt Estela has [http://www.tokeru.com/cgwiki/index.php?title=JoyOfVex17#Day_17B thorough] explanations which I need to read each time I do this.&lt;br /&gt;
Olivier Jeannel has video where he does it [https://vimeo.com/207626604 in VOPs]&lt;br /&gt;
&lt;br /&gt;
For simple stuff you can often use @N and @up, but there&amp;#039;s more control in using a quaternion @orient attribute:&lt;br /&gt;
&lt;br /&gt;
Setting up the orient to match what you would do with @N and @up:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@orient =  quaternion(maketransform(@N,@up));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Rotating around an axis at a certain angle (in degrees):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector axis = chv(&amp;#039;axis&amp;#039;);&lt;br /&gt;
float angle = radians(ch(&amp;#039;angle&amp;#039;));&lt;br /&gt;
vector4 q = quaternion(angle, axis);&lt;br /&gt;
@orient =  qmultiply(@orient,q);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can &amp;#039;&amp;#039;&amp;#039;cumulate&amp;#039;&amp;#039;&amp;#039; orient transforms:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;video controls&amp;gt;&lt;br /&gt;
&amp;lt;source src=&amp;quot;http://mlkdesign.free.fr/dump/zzzzz.mp4&amp;quot; type=&amp;quot;video/mp4&amp;quot;&amp;gt;&lt;br /&gt;
Your browser does not support the video tag.&lt;br /&gt;
&amp;lt;/video&amp;gt; &lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TBD: doing it by moving @P (ie without copytopoints)&lt;br /&gt;
&lt;br /&gt;
====How do I pin an object to an another in Geometry/SOP context ?====&lt;br /&gt;
&lt;br /&gt;
* (use point deform with a single point?)&lt;br /&gt;
&lt;br /&gt;
* probably xyzdistance i&amp;#039;ll write about it later: http://www.toadstorm.com/blog/?p=465&lt;br /&gt;
&lt;br /&gt;
====How do I get the start/end frame of an alembic read in python?====&lt;br /&gt;
&lt;br /&gt;
    from _alembic_hom_extensions import alembicTimeRange as abctr&lt;br /&gt;
    startFrame,endFrame = abctr(&amp;#039;F:/filepathto/alembic.abc&amp;#039;)&lt;br /&gt;
    print startFrame,endFrame&lt;br /&gt;
====Why doesn&amp;#039;t my grain wire solve work?====&lt;br /&gt;
Check that you have:&lt;br /&gt;
* @restlength on the curves (prims)&lt;br /&gt;
* in the dopnetwork, &amp;#039;&amp;#039;Emission type: All Geometry&amp;#039;&amp;#039;&lt;br /&gt;
* each point needs to have @targetstiffness and an attr to drive the &amp;#039;animated&amp;#039; point (@targetweight for instance) -- then in a pop wrangle:&lt;br /&gt;
    if (@targetweight==1) {&lt;br /&gt;
     v@targetP=point(0,&amp;#039;P&amp;#039;,@id);&lt;br /&gt;
    }&lt;br /&gt;
====Why does my RBD bullet sim jump/explode on first frame====&lt;br /&gt;
* Sometimes due to interpenetration solving. Make sure nothing collides before hand (duh). Usually inter-penetration solving &amp;#039;spreads&amp;#039; Objects around, but the sim shouldn&amp;#039;t explode&lt;br /&gt;
* I&amp;#039;ve also found out that if I use a random &amp;#039;orientation&amp;#039; attribute it goes to shit, instead I use &amp;#039;rot&amp;#039;&lt;br /&gt;
* If I use orientation (vec4) to setup randomness of objects, make sure the attribute is deleted if you copy pack&amp;amp;instance (ie: attr rand (orient) &amp;gt; copy to points (pack&amp;amp;instance) &amp;gt; delete attr (orient)) because the orientation is already stored in the packed intrinsics.&lt;br /&gt;
* Sometimes tiny masses will also make the objects skittish, also built-in drag/airresist sometimes goes to Inf or -Inf energy == shit&lt;br /&gt;
Edit: More reasons/details&lt;br /&gt;
* NaNs in sims can apparently cause this. Make sure you&amp;#039;re not using airresist/popwind (perhaps this is broken on my side at work)which seems to fuck up a lot of things.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GT7Yi6c.gif&lt;br /&gt;
&lt;br /&gt;
====How do I scale manipulators viewport size (like +/- in maya)?====&lt;br /&gt;
/ and *  (be warned that + and - change smooth mesh preview like 1 and 3 in maya)&lt;br /&gt;
====How do I scale uniformely using the gizmo ?====&lt;br /&gt;
* http://www.sidefx.com/docs/houdini/basics/handles.html&lt;br /&gt;
* basically, moving an arrowhead scales uniformely, moving the rest of the handles scales non-uniformely&lt;br /&gt;
====How do I interpolate motion between still frames====&lt;br /&gt;
I might be missing something super simple because this seems unreasonably complicated&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;edit:&amp;#039;&amp;#039; I was probably missing the simple &amp;#039;&amp;#039;&amp;#039;Timeblend&amp;#039;&amp;#039;&amp;#039; node. I&amp;#039;ll leave this up; who knows.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/v0BqBWl.png&lt;br /&gt;
&lt;br /&gt;
* The trick is to use floor($FF) in the timeshift rather than $F otherwise you get frame jumps on half-frames&lt;br /&gt;
* So 1 timeshift with floor($FF), one with floor($FF)+1, plus a blendshape with $FF-floor($FF)&lt;br /&gt;
&lt;br /&gt;
Slightly more complex example where I had objects not necessarily sharing the same ptnum (but with a unique Id attribute) -- and a need to lerp orients too&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int pt = findattribval(1, &amp;quot;point&amp;quot;, &amp;quot;id&amp;quot;, @id);&lt;br /&gt;
i@id2 = pt; &lt;br /&gt;
if(pt == -1){&lt;br /&gt;
    @Cd = {1,0,0};&lt;br /&gt;
}else{&lt;br /&gt;
    vector p2 = point(1,&amp;quot;P&amp;quot;,pt);&lt;br /&gt;
    vector4 orient = point(1,&amp;quot;orient&amp;quot;,pt);&lt;br /&gt;
    @P = lerp(@P,p2,@Frame%1);&lt;br /&gt;
    @orient = lerp(@orient,orient,@Frame%1);&lt;br /&gt;
    @Cd = @Cd;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====How do I instance a node (reference copy)====&lt;br /&gt;
* ctrl-shift-alt drag nodes&lt;br /&gt;
&lt;br /&gt;
====Improve VDB viewport resolution====&lt;br /&gt;
&lt;br /&gt;
Volume visualization, even at maximum viewport resolution won&amp;#039;t give a nice viewport display if your vdbs are very sparse (&amp;#039;pockets&amp;#039; of volumes). So, for preview purposes you can use &amp;#039;VDB Segment by Connectivity&amp;#039; to re-split the vdb into smaller vdbs which will display properly (be warned that this can slow things down to a crawl)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/naWaiA1.gif&lt;br /&gt;
&lt;br /&gt;
In my case I toggled off &amp;#039;use color&amp;#039; and &amp;#039;append names&amp;#039; so that my volume visualization color still works. Also you can turn off &amp;#039;smooth wire shaded&amp;#039; to remove bounding boxes. And if you&amp;#039;re happy with the preview, Flipbook==Approved Render :D&lt;br /&gt;
&lt;br /&gt;
====How do I combine (easily) loads of vdbs====&lt;br /&gt;
&lt;br /&gt;
Thanks to esteemed friend&amp;amp;colleague Charles&lt;br /&gt;
&lt;br /&gt;
* Make sure &amp;#039;Flatten all B into A is on and your vdb (source A) is the same rez&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/qcuClGv.png&lt;br /&gt;
&lt;br /&gt;
====How do I activate/fill vdbs cells (voxels)====&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to fill your volume vdb with empty data to do stuff with it.&lt;br /&gt;
&lt;br /&gt;
* Use vdb activate on itself to &amp;#039;fill up&amp;#039;, uncheck use value&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/02x5N2T.png&lt;br /&gt;
&lt;br /&gt;
===How do I cleanly retrieve vertex info to point (like laying layout UVs in world space)===&lt;br /&gt;
&lt;br /&gt;
* Use vertex split ! https://www.youtube.com/watch?v=F2pwzyQ1oc4&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/3td2zkw.png&lt;br /&gt;
&lt;br /&gt;
===How do I timestamp my outputs===&lt;br /&gt;
&lt;br /&gt;
You can use the a houdini variable: &amp;lt;code&amp;gt;`strreplace(strreplace($_HIP_SAVETIME,&amp;#039;:&amp;#039;,&amp;#039;-&amp;#039;),&amp;#039; &amp;#039;,&amp;#039;_&amp;#039;)`&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/LtTSp3i.png&lt;br /&gt;
&lt;br /&gt;
Or you can go dirty with sytem calls, on windows and my locale (france) the following works:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
`system(&amp;quot;cmd /C echo %date:~6,4%%date:~3,2%%date:~0,2%&amp;quot;)`&lt;br /&gt;
//evaluates to 20200917 for today the 17 of september 2020&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
More modern python, works as a multiline expression if you have the return statement:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;import time;return time.strftime(&amp;quot;%Y-%m-%d_%Hh%M&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
#returns: 2022-09-08_13h29&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===How do I get pre and post infinity animation with keyframes===&lt;br /&gt;
&lt;br /&gt;
* In the Animation Editor, pick channels and Alt-E (channel properties): &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/T8tD5E6.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== How do I refer to a relative SOP in vex===&lt;br /&gt;
The syntax can be confusing, but I think this works, when using the &amp;#039;point&amp;#039; (or similar) functions. You must have a string with op in it: &amp;#039;&amp;#039;&amp;#039;&amp;quot;op:chs(&amp;#039;parm&amp;#039;)&amp;quot;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/HZ5ML7p.png&lt;br /&gt;
&lt;br /&gt;
===Rounding numbers in Vex===&lt;br /&gt;
&lt;br /&gt;
Is really annoying. Save yourself the trouble and worst case use pythonexpression.&lt;br /&gt;
Floating point issues, sprintf doesn&amp;#039;t really want to work as it seems. Anyways:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//before, shows 1.610000000000000001 or simila&lt;br /&gt;
//after, shows 1.61&lt;br /&gt;
`pythonexprs(&amp;quot;&amp;#039;%0.2f&amp;#039; % &amp;quot; + ch(&amp;quot;../parameter&amp;quot;))`&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Creating a linear pre-roll===&lt;br /&gt;
&lt;br /&gt;
Freeze the two first frames of animation and interpolate to get your pre-roll. Volume (rotations) are lost but I&amp;#039;m not smart enough to do non-linear interpolation yet.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/rD3M4Ms.gif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Exporting Houdini instances to Maya (via particles) ===&lt;br /&gt;
&lt;br /&gt;
Fuck this shit. It can&amp;#039;t be done. I lost two days of my life, I&amp;#039;ll post back if I manage it but maya is a fucking nightmare and its low price is the only thing keeping people from buying Houdini.&lt;br /&gt;
&lt;br /&gt;
Posting my research if another poor soul has to do this. We wanted to take instanced animation from H to maya keeping rotation and scale&lt;br /&gt;
&lt;br /&gt;
First, here&amp;#039;s things I haven&amp;#039;t tried&lt;br /&gt;
&lt;br /&gt;
* using H engine in maya. Way too expensive to put on the farm.&lt;br /&gt;
* Using Filmbox Crate .abc importer/exporter to get point info in maya. My colleagues don&amp;#039;t seem to have gone far with it.&lt;br /&gt;
* Using old weird bgeoclassic importer. &lt;br /&gt;
&lt;br /&gt;
What &amp;#039;&amp;#039;seemed&amp;#039;&amp;#039; to work was using MASH instancing BUT&lt;br /&gt;
&lt;br /&gt;
* Using face instancing (distribution) seems to euler flip. (ie exporting thousands of simple triangles from Houdini). Scale works fine. Tried LOADs of combinations.&lt;br /&gt;
* Using edge instancing (distributions) seems to keep rotation fine (if you have a houdini triangle that fits the requirements -- and skip one out of 3 edges, ie instance nulls). Scaling only works in one axis.&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s &amp;lt;del&amp;gt;2018&amp;lt;/del&amp;gt; &amp;lt;del&amp;gt;2019&amp;lt;/del&amp;gt; 2020 Autodesk. Step up your fucking game.&lt;br /&gt;
&lt;br /&gt;
=== Motion blur on changing topology from Houdini to Maya and Redshift ===&lt;br /&gt;
&lt;br /&gt;
It&amp;#039;s not clear from the doc. If you have a flip sim or something funky that changes at each frame, here&amp;#039;s how to get velocity showing up in Redshift from Maya:&lt;br /&gt;
&lt;br /&gt;
/!\ We had a pretty nasty bug (crashes) with redshift when using end-of-frame motion blur instead of center-of-frame.&lt;br /&gt;
&lt;br /&gt;
In Houdini:&lt;br /&gt;
&lt;br /&gt;
* change your &amp;#039;&amp;#039;&amp;#039;v&amp;#039;&amp;#039;&amp;#039; to &amp;#039;&amp;#039;&amp;#039;Cd&amp;#039;&amp;#039;&amp;#039; (from what I gather it needs to be a vector with R,G,B not X,Y,Z or 0,1,2) using a wrangle &lt;br /&gt;
* Promote &amp;#039;&amp;#039;&amp;#039;Cd&amp;#039;&amp;#039;&amp;#039; to vertices&lt;br /&gt;
* Change its name (optional, I chose myVel in examples) delete original v (optional)&lt;br /&gt;
* Export with alembic ROP (no need to turn on &amp;#039;motion blur&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/YXR2F28.png&lt;br /&gt;
&lt;br /&gt;
In Maya:&lt;br /&gt;
&lt;br /&gt;
* Import abc cache (optional: lament on how unstable maya is when you reload an .abc cache). If your mesh comes in coloured it&amp;#039;s a good sign.&lt;br /&gt;
* In the &amp;#039;&amp;#039;&amp;#039;shape&amp;#039;&amp;#039;&amp;#039; attributes of your abc, choose the vertex color attribute from Houdini for velocity&lt;br /&gt;
* Make sure your have moblur turned on in your render options and you should be good !&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/DJfNWqL.png&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Vvvv&amp;diff=874</id>
		<title>Vvvv</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Vvvv&amp;diff=874"/>
		<updated>2025-10-15T16:23:33Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;here be dragons, but I need to get back to VVVV &amp;amp; Touch!&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=873</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Main_Page&amp;diff=873"/>
		<updated>2025-10-15T16:21:37Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hi i&amp;#039;m Bernie, here lies my personal wiki. It&amp;#039;s unsightly but it works and someone &amp;#039;&amp;#039;has&amp;#039;&amp;#039; to feed (crap) to the AI. &lt;br /&gt;
It&amp;#039;s a collection of thoughts and things I&amp;#039;ve gathered over the years and placed here haphazardly.&lt;br /&gt;
&lt;br /&gt;
If you see something &amp;lt;s&amp;gt;ugly&amp;lt;/s&amp;gt; wrong, lemme know !&lt;br /&gt;
&lt;br /&gt;
Some pages haven&amp;#039;t been updated in a long, long time, they are here for historical purposes only. &lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
! &lt;br /&gt;
! &lt;br /&gt;
|- style=&amp;quot;vertical-align:top;&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
* [[Special:RecentChanges]] &lt;br /&gt;
* 3d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Maya&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Maya Shelf]]&lt;br /&gt;
*** Mel (.mel)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Mel|Mel Scripts]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Mel Temp|Mel Scripts Temp]]&lt;br /&gt;
**** [[Mel Keyboard and Snippets]]&lt;br /&gt;
**** [[Mel Functions]] (+useful mels on pastebin)&lt;br /&gt;
**** [[Advanced Skeleton Specific]] / [[BernieMelLibrary]] / [[Maya Settings]]&lt;br /&gt;
*** Python (.py)&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;[[Maya Python]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** [[Maya Python Temp]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Houdini&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini 101]]&lt;br /&gt;
*** [[Houdini VEX]] / [[Houdini VEX Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Python]]&amp;#039;&amp;#039;&amp;#039; / [[Houdini Python Temp]]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Houdini Stupid Questions]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Houdini UI Customization]]&lt;br /&gt;
*** [[Houdini Webinars and Videos]]&lt;br /&gt;
*** [[Houdini Octane|Houdini Octane/Arnold]]&lt;br /&gt;
** [[Work Specific Scripts]] hodgepodge maya/houdini/whatever&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Blender&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Blender 101]]&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
* 2d&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;After Effects&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Afx Javascript]]&amp;#039;&amp;#039;&amp;#039; (.jsx)&lt;br /&gt;
*** [[Afx Javascript Temp]]&lt;br /&gt;
*** [[Expressions and Tips|Afx Expressions and Tips]]&lt;br /&gt;
*** [[Afx Shortcuts FR]] and [[Shelf|Custom Shelf UI]] (wip)&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Nuke / Natron&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Nuke Python]] / [[Nuke Python Temp]]&lt;br /&gt;
*** [[Example Gizmos]]&lt;br /&gt;
* Other&lt;br /&gt;
** [[WebGl]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Operating System / General&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Software]] i use&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Windows batch|Windows batch &amp;amp; Powershell]]&amp;#039;&amp;#039;&amp;#039; .bat/.vbs/.ps1 ([[Windows_batch#l.bat|l.bat]])&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;[[Python]]&amp;#039;&amp;#039;&amp;#039; .py scripts for work/ day usage&lt;br /&gt;
***[[Auto Hotkey]]&lt;br /&gt;
*** [[Linux]]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Misc&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** [[Gobelins 2d 3d integration]]&lt;br /&gt;
***[[Showreel]]&lt;br /&gt;
***[[Maxscript]] .ms [[Misc Scripting]] [[RandomDump]]&lt;br /&gt;
***[[Software Shortcuts]] [[vvvv]] [[vj]]&lt;br /&gt;
***[[Photoshop Javascript]] .js&lt;br /&gt;
***[[7550A Hp Plotter]]&lt;br /&gt;
***[[Color (Aces, LUTs, Gamma etc)]] wip&lt;br /&gt;
***[[OSL|OSLs]]&lt;br /&gt;
***[[AI]]&lt;br /&gt;
&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=872</id>
		<title>Houdini Octane</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=872"/>
		<updated>2025-10-10T15:45:09Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* HDRI in the viewport control */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Octane==&lt;br /&gt;
&lt;br /&gt;
=== Python tools ===&lt;br /&gt;
==== HDRI in the viewport control====&lt;br /&gt;
It&amp;#039;s a pain in the ass to go to the rendertarget each time, this acts like a normal environment preview. Barebones but works.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/WD2MCEI.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
links a null with some parameters to a chosen Rendertarget texture. &lt;br /&gt;
Display flag sets texture on/off just like for lights.&lt;br /&gt;
No error checking.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
def chooseRTdialog(buttons):&lt;br /&gt;
    buttons.append(&amp;#039;Cancel&amp;#039;)&lt;br /&gt;
    dialog = hou.ui.displayMessage(&amp;#039;Choose which RTarget HDRi to link to&amp;#039;, buttons=buttons)&lt;br /&gt;
    if dialog == len(buttons)-1:&lt;br /&gt;
        dialog = False&lt;br /&gt;
    return dialog&lt;br /&gt;
&lt;br /&gt;
def linkDispFlag(node,event_type):&lt;br /&gt;
    RT = hou.node(node.parm(&amp;#039;RT&amp;#039;).eval())&lt;br /&gt;
    menu = RT.parm(&amp;#039;environmentMenu&amp;#039;)&lt;br /&gt;
    if node.isDisplayFlagSet() == 1:&lt;br /&gt;
        menu.set(6)&lt;br /&gt;
    else:&lt;br /&gt;
        menu.set(1)&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
rts = []&lt;br /&gt;
RT = None&lt;br /&gt;
for node in hou.node(&amp;#039;/&amp;#039;).allSubChildren():&lt;br /&gt;
    if &amp;#039;octane_mat_renderTarget&amp;#039; in node.type().name():&lt;br /&gt;
        rts.append(node)&lt;br /&gt;
&lt;br /&gt;
names = [x.name() for x in rts]        &lt;br /&gt;
&lt;br /&gt;
if len(rts) == 1:&lt;br /&gt;
    RT = rts[0]&lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    chosenRT = chooseRTdialog(names)&lt;br /&gt;
    if chosenRT is not False:&lt;br /&gt;
        RT = rts[chosenRT]&lt;br /&gt;
&lt;br /&gt;
if RT:&lt;br /&gt;
    hdri = hou.node(&amp;quot;/obj&amp;quot;).createNode(&amp;quot;null&amp;quot;, &amp;quot;hdri&amp;quot;)&lt;br /&gt;
    hdri.setSelectableInViewport(True)&lt;br /&gt;
    hdri.useXray(True)&lt;br /&gt;
    hdri.setDisplayFlag(True)&lt;br /&gt;
    hdri.hide(False)&lt;br /&gt;
    hdri.setSelected(True)&lt;br /&gt;
    hdri.parm(&amp;#039;geoscale&amp;#039;).set(10)&lt;br /&gt;
    hdri.parm(&amp;#039;controltype&amp;#039;).set(1)&lt;br /&gt;
    hdri.setUserData(&amp;quot;nodeshape&amp;quot;, &amp;quot;circle&amp;quot;)&lt;br /&gt;
    hdri.setColor(hou.Color([0.976, 0.78, 0.263]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setGenericFlag(hou.nodeFlag.DisplayComment,True)&lt;br /&gt;
&lt;br /&gt;
    ptg = hdri.parmTemplateGroup()&lt;br /&gt;
&lt;br /&gt;
    slider = hou.FloatParmTemplate(&amp;quot;hdripower&amp;quot;, &amp;quot;Hdri Power&amp;quot;, 1, default_value=(0.0,))&lt;br /&gt;
    exr = hou.StringParmTemplate(&amp;quot;exr&amp;quot;, &amp;quot;HDRI&amp;quot;, 1, string_type=hou.stringParmType.FileReference, file_type=hou.fileType.Image)&lt;br /&gt;
    path = hou.StringParmTemplate(&amp;quot;RT&amp;quot;, &amp;quot;RTarget&amp;quot;, 1, string_type=hou.stringParmType.NodeReference,default_value=(RT.path(),)) &lt;br /&gt;
&lt;br /&gt;
    ptg.insertBefore((0,0),path)&lt;br /&gt;
    ptg.insertBefore((0,0),exr)&lt;br /&gt;
    ptg.insertBefore((0,0),slider)&lt;br /&gt;
&lt;br /&gt;
    hdri.setParmTemplateGroup(ptg)&lt;br /&gt;
        &lt;br /&gt;
    parmsFrom = &amp;#039;tx ty tz rx ry rz sx sy sz exr hdripower&amp;#039;&lt;br /&gt;
    parmsTo = &amp;#039;translation121 translation122 translation123 textureEnvTilt textureEnvLeftRight textureEnvRoll textureEnvScale1 textureEnvScale2 textureEnvScale3 textureEnvironmentFilename textureEnvPower&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    parmsFrom = parmsFrom.split(&amp;#039; &amp;#039;)&lt;br /&gt;
    parmsTo = parmsTo.split(&amp;#039; &amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(parmsFrom)):&lt;br /&gt;
        hdri.parm(parmsFrom[i]).set(RT.parm(parmsTo[i]).eval())     &lt;br /&gt;
        RT.parm(parmsTo[i]).set(hdri.parm(parmsFrom[i]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setComment(RT.path()+&amp;#039;\n&amp;#039;+RT.parm(&amp;#039;textureEnvironmentFilename&amp;#039;).eval().split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
    #hou.nodeEventType.FlagChanged&lt;br /&gt;
    #hdri.addEventCallback(hou.nodeEventType.FlagChanged,print(&amp;quot;hi&amp;quot;))&lt;br /&gt;
    #RT.parm(&amp;#039;environmentMenu&amp;#039;).eval()&lt;br /&gt;
    hdri.addEventCallback((hou.nodeEventType.FlagChanged,),linkDispFlag)    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shading Guide ===&lt;br /&gt;
&lt;br /&gt;
For C4D, still good: https://www.behance.net/gallery/102948251/Octane-Universal-Material-Guide-Starter-File&lt;br /&gt;
&lt;br /&gt;
=== Shading instances with custom colors ===&lt;br /&gt;
Is a little more hands on than getting custom attributes&lt;br /&gt;
&lt;br /&gt;
If you have a single object that you want to instance but have it have a random shader (in my example, using color), you can use a trick/weird workaround.&lt;br /&gt;
&lt;br /&gt;
To make it work:&lt;br /&gt;
* in your scattered points (the ones that hold @instance, @orient, @pscale, @Cd) /obj/ level geo, make sure you choose &amp;#039;Packed RGBA Values from &amp;#039;Cd&amp;#039; Point Attribute&amp;#039;.&lt;br /&gt;
* in your instance shader, you need to grab &amp;#039;Texture Instance Color&amp;#039; and go pick a .ppm texture that sits in the install directory of octane under the /tex folder called &amp;#039;&amp;#039;&amp;#039;rgb4k_map.ppm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* plug it in directly your diffuse or use it to drive other textures like a ramp in my example&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GZIUm2z.png&lt;br /&gt;
&lt;br /&gt;
=== Shadow catcher with diffuse bounce ===&lt;br /&gt;
&lt;br /&gt;
I think default shadow catching doesn&amp;#039;t get the bounce so you can whip it up with a rayswitch. I sort of plugged stuff into stuff for it to work (topright pigheads -- bottom left is normal shadowcatcher,plane grid is a green material):&lt;br /&gt;
&lt;br /&gt;
(also i cropped the rayswitch like an idiot but camera ray is at 0 the rest at 1&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/8yxOMxr.png&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/KsjjsQK.png&lt;br /&gt;
&lt;br /&gt;
=== Double sided material in Octane ===&lt;br /&gt;
&lt;br /&gt;
You can use tool_polygon_side to drive a material mixer:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/e1aOVPh.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Arnold ==&lt;br /&gt;
&lt;br /&gt;
Weird render procedural stuff: https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=871</id>
		<title>Houdini Octane</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=871"/>
		<updated>2025-10-09T16:25:19Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* HDRI in the viewport control */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Octane==&lt;br /&gt;
&lt;br /&gt;
=== Python tools ===&lt;br /&gt;
==== HDRI in the viewport control====&lt;br /&gt;
It&amp;#039;s a pain in the ass to go to the rendertarget each time, this acts like a normal environment preview. Barebones but works.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/WD2MCEI.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
links a null with some parameters to a chosen Rendertarget texture. &lt;br /&gt;
Display flag sets texture on/off just like for lights.&lt;br /&gt;
No error checking.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
def chooseRTdialog(buttons):&lt;br /&gt;
    buttons.append(&amp;#039;Cancel&amp;#039;)&lt;br /&gt;
    dialog = hou.ui.displayMessage(&amp;#039;Choose which RTarget HDRi to link to&amp;#039;, buttons=buttons)&lt;br /&gt;
    if dialog == len(buttons)-1:&lt;br /&gt;
        dialog = False&lt;br /&gt;
    return dialog&lt;br /&gt;
&lt;br /&gt;
def linkDispFlag(node,event_type):&lt;br /&gt;
    RT = hou.node(node.parm(&amp;#039;RT&amp;#039;).eval())&lt;br /&gt;
    menu = RT.parm(&amp;#039;environmentMenu&amp;#039;)&lt;br /&gt;
    if node.isDisplayFlagSet() == 1:&lt;br /&gt;
        menu.set(6)&lt;br /&gt;
    else:&lt;br /&gt;
        menu.set(1)&lt;br /&gt;
   &lt;br /&gt;
rts = []&lt;br /&gt;
RT = None&lt;br /&gt;
for node in hou.node(&amp;#039;/&amp;#039;).allSubChildren():&lt;br /&gt;
    if &amp;#039;octane_mat_renderTarget&amp;#039; in node.type().name():&lt;br /&gt;
        rts.append(node)&lt;br /&gt;
&lt;br /&gt;
names = [x.name() for x in rts]        &lt;br /&gt;
        &lt;br /&gt;
if len(rts) == 1:&lt;br /&gt;
    RT = rts[0]&lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    chosenRT = chooseRTdialog(names)&lt;br /&gt;
    if chosenRT is not False:&lt;br /&gt;
        RT = chosenRT&lt;br /&gt;
&lt;br /&gt;
if RT:&lt;br /&gt;
    hdri = hou.node(&amp;quot;/obj&amp;quot;).createNode(&amp;quot;null&amp;quot;, &amp;quot;hdri&amp;quot;)&lt;br /&gt;
    hdri.setSelectableInViewport(True)&lt;br /&gt;
    hdri.useXray(True)&lt;br /&gt;
    hdri.setDisplayFlag(True)&lt;br /&gt;
    hdri.hide(False)&lt;br /&gt;
    hdri.setSelected(True)&lt;br /&gt;
    hdri.parm(&amp;#039;geoscale&amp;#039;).set(10)&lt;br /&gt;
    hdri.parm(&amp;#039;controltype&amp;#039;).set(1)&lt;br /&gt;
    hdri.setUserData(&amp;quot;nodeshape&amp;quot;, &amp;quot;circle&amp;quot;)&lt;br /&gt;
    hdri.setColor(hou.Color([0.976, 0.78, 0.263]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setGenericFlag(hou.nodeFlag.DisplayComment,True)&lt;br /&gt;
&lt;br /&gt;
    ptg = hdri.parmTemplateGroup()&lt;br /&gt;
&lt;br /&gt;
    slider = hou.FloatParmTemplate(&amp;quot;hdripower&amp;quot;, &amp;quot;Hdri Power&amp;quot;, 1, default_value=(0.0,))&lt;br /&gt;
    exr = hou.StringParmTemplate(&amp;quot;exr&amp;quot;, &amp;quot;HDRI&amp;quot;, 1, string_type=hou.stringParmType.FileReference, file_type=hou.fileType.Image)&lt;br /&gt;
    path = hou.StringParmTemplate(&amp;quot;RT&amp;quot;, &amp;quot;RTarget&amp;quot;, 1, string_type=hou.stringParmType.NodeReference,default_value=(RT.path(),)) &lt;br /&gt;
&lt;br /&gt;
    ptg.insertBefore((0,0),path)&lt;br /&gt;
    ptg.insertBefore((0,0),exr)&lt;br /&gt;
    ptg.insertBefore((0,0),slider)&lt;br /&gt;
&lt;br /&gt;
    hdri.setParmTemplateGroup(ptg)&lt;br /&gt;
        &lt;br /&gt;
    parmsFrom = &amp;#039;tx ty tz rx ry rz sx sy sz exr hdripower&amp;#039;&lt;br /&gt;
    parmsTo = &amp;#039;translation121 translation122 translation123 textureEnvTilt textureEnvLeftRight textureEnvRoll textureEnvScale1 textureEnvScale2 textureEnvScale3 textureEnvironmentFilename textureEnvPower&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    parmsFrom = parmsFrom.split(&amp;#039; &amp;#039;)&lt;br /&gt;
    parmsTo = parmsTo.split(&amp;#039; &amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(parmsFrom)):&lt;br /&gt;
        hdri.parm(parmsFrom[i]).set(RT.parm(parmsTo[i]).eval())     &lt;br /&gt;
        RT.parm(parmsTo[i]).set(hdri.parm(parmsFrom[i]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setComment(RT.path()+&amp;#039;\n&amp;#039;+RT.parm(&amp;#039;textureEnvironmentFilename&amp;#039;).eval().split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
    hdri.addEventCallback((hou.nodeEventType.FlagChanged,),linkDispFlag)    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shading Guide ===&lt;br /&gt;
&lt;br /&gt;
For C4D, still good: https://www.behance.net/gallery/102948251/Octane-Universal-Material-Guide-Starter-File&lt;br /&gt;
&lt;br /&gt;
=== Shading instances with custom colors ===&lt;br /&gt;
Is a little more hands on than getting custom attributes&lt;br /&gt;
&lt;br /&gt;
If you have a single object that you want to instance but have it have a random shader (in my example, using color), you can use a trick/weird workaround.&lt;br /&gt;
&lt;br /&gt;
To make it work:&lt;br /&gt;
* in your scattered points (the ones that hold @instance, @orient, @pscale, @Cd) /obj/ level geo, make sure you choose &amp;#039;Packed RGBA Values from &amp;#039;Cd&amp;#039; Point Attribute&amp;#039;.&lt;br /&gt;
* in your instance shader, you need to grab &amp;#039;Texture Instance Color&amp;#039; and go pick a .ppm texture that sits in the install directory of octane under the /tex folder called &amp;#039;&amp;#039;&amp;#039;rgb4k_map.ppm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* plug it in directly your diffuse or use it to drive other textures like a ramp in my example&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GZIUm2z.png&lt;br /&gt;
&lt;br /&gt;
=== Shadow catcher with diffuse bounce ===&lt;br /&gt;
&lt;br /&gt;
I think default shadow catching doesn&amp;#039;t get the bounce so you can whip it up with a rayswitch. I sort of plugged stuff into stuff for it to work (topright pigheads -- bottom left is normal shadowcatcher,plane grid is a green material):&lt;br /&gt;
&lt;br /&gt;
(also i cropped the rayswitch like an idiot but camera ray is at 0 the rest at 1&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/8yxOMxr.png&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/KsjjsQK.png&lt;br /&gt;
&lt;br /&gt;
=== Double sided material in Octane ===&lt;br /&gt;
&lt;br /&gt;
You can use tool_polygon_side to drive a material mixer:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/e1aOVPh.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Arnold ==&lt;br /&gt;
&lt;br /&gt;
Weird render procedural stuff: https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=870</id>
		<title>Houdini 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=870"/>
		<updated>2025-10-09T16:01:01Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page was created when I learned Houdini, it might be old and wrong ! (looking at you, HOM expressions). I&amp;#039;ll try to add more info with the hindsight.&lt;br /&gt;
&lt;br /&gt;
== Where to Start ==&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s a ton more videos nowadays then when I learned houdini, and instead of pointing to a thousand websites (well, I do just afterwards in the [links]), I&amp;#039;ll simply point out that SideFX have a [https://www.sidefx.com/tutorials/ curated list of tutorials].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Links to stuff I&amp;#039;ve saved (could have been a webring. remember those?). This was started long ago, some sites might be down, let&amp;#039;s try to clean this up (2023 edit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Manuel and Moritz&amp;#039; &amp;#039;&amp;#039;&amp;#039;[http://www.entagma.com/ Entagma]&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re seeing this page, you probably know their soothing voice already !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Richard C Thomas&amp;#039;s [https://richardcthomas.com/tab-tools website]&amp;#039;&amp;#039;&amp;#039;, loads of cool stuff! &lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Junichiro Horikawa&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.youtube.com/c/JunichiroHorikawa/videos experiments], he&amp;#039;s also in the Houdini subreddit.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Webb Hinton&amp;#039;&amp;#039;&amp;#039;&amp;#039;s wicked-ass [https://github.com/wyhinton/AwesomeHoudini AwesomeHoudini github] page with &amp;#039;&amp;#039;loads&amp;#039;&amp;#039; of ressources about/for Houdini&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Juraj Tomori&amp;#039;&amp;#039;&amp;#039;&amp;#039;s gitgub [https://jtomori.github.io/vex_tutorial/ vex tutorial/code] and [https://www.youtube.com/@seals77/videos youtube] channel&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Howiem&amp;#039;s&amp;#039;&amp;#039;&amp;#039; [http://howiem.com/wordpress/ blog] with code, CHOPs and various hardware hacking.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Houdini Gubbins&amp;#039;&amp;#039;&amp;#039; [https://houdinigubbins.wordpress.com/ technical blog]. Doesn&amp;#039;t share HIPs, but goes through his ideas, thought processes and implements research papers. Super inspiring stuff !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Matt Estela&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.tokeru.com/cgwiki/Main_Page cgwiki], aka Our Houdini Jesus :° -- if you landed on my wiki you&amp;#039;ve probably seen him. Super nice ressource and eve nicer dude, gives away his files too.&lt;br /&gt;
&lt;br /&gt;
* Henry Foster, known as &amp;#039;&amp;#039;&amp;#039;Toadstorm&amp;#039;&amp;#039;&amp;#039; has a [http://www.toadstorm.com/blog/ blog], and also created [https://www.motionoperators.com/ MOPs], which tries to make Motion Design life more like Cinema4D (ie friendlier)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;John Kunz&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://wiki.johnkunz.com/index.php?title=Main_Page wiki], and associated [https://www.youtube.com/@JohnKunz/videos youtube] account with top Notch recorded streams.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Jake Rice&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://jakerice.design/blog/ blog] (not updated recently)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Probiner&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [http://probiner.xyz/logs/ website] (not updated much anymore)&lt;br /&gt;
&lt;br /&gt;
* Eetu Martola&amp;#039;s [https://dailyhip.wordpress.com/ blog] (last update 2021)&lt;br /&gt;
&lt;br /&gt;
* Sam Hancock&amp;#039;s [https://ihoudini.blogspot.fr/ blogspot] (last update 2020 but interesting articles nonetheless)&lt;br /&gt;
&lt;br /&gt;
* Technische Universität Berlin&amp;#039;s [http://dgd.service.tu-berlin.de/wordpress/houdini/ wordpress] &amp;#039;for Mathematicians&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rich Lord&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.richlord.com/ website], full of downloadable goodies (creatures made with constraints!) It had disappeared from the web glad to see it&amp;#039;s back up.&lt;br /&gt;
&lt;br /&gt;
* https://hakeemadam.info/procedural-tools&lt;br /&gt;
&lt;br /&gt;
* -------- TODO sort the rest of the list --------------&lt;br /&gt;
&lt;br /&gt;
* https://www.nicholas-taylor.com/&lt;br /&gt;
&lt;br /&gt;
* https://sites.google.com/site/fujitarium/Houdini/useful-expressions-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
* https://houdinigubbins.wordpress.com&lt;br /&gt;
&lt;br /&gt;
* http://pepefx.blogspot.fr/&lt;br /&gt;
&lt;br /&gt;
* http://www.deborahrfowler.com/HoudiniResources/HoudiniTipsAndTricks.html&lt;br /&gt;
&lt;br /&gt;
* http://www.preset.de/2007/0711/lorenz/&lt;br /&gt;
&lt;br /&gt;
* http://www.3daet.com/cat/27/houdini/&lt;br /&gt;
&lt;br /&gt;
* Python: http://wiki.dreamsteep.com/Pythonhoudini&lt;br /&gt;
&lt;br /&gt;
* https://www.andynicholas.com&lt;br /&gt;
&lt;br /&gt;
* http://dansportfolio.com/wordpress/&lt;br /&gt;
&lt;br /&gt;
* https://houdininote.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* http://lab.ikoon.cz/&lt;br /&gt;
&lt;br /&gt;
* https://lewisinthelandofmachines.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* https://www.keatonwilliamson.com/houdini&lt;br /&gt;
&lt;br /&gt;
* https://www.xuanprada.com/&lt;br /&gt;
&lt;br /&gt;
* Attributes cheatsheet: http://mrkunz.com/blog/08_22_2018_VEX_Wrangle_Cheat_Sheet.html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Nifty links ===&lt;br /&gt;
&lt;br /&gt;
* Discord servers: [https://discord.gg/723NGrShdm Think Procedural]  and Houdini&amp;amp;Chill (it&amp;#039;s invite based, but worth it if you&amp;#039;re worth it); plus it has system to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;[http://fx-td.com/houdiniandchill/ publish the best (most liked) posts]&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; on the discord&lt;br /&gt;
&lt;br /&gt;
* https://www.reddit.com/r/Houdini/ I&amp;#039;m there daily and I like the fact that answers there will be crawlable by search engines (contrary to discord servers).&lt;br /&gt;
&lt;br /&gt;
* [https://mbaadsgaard.com/portfolio/laplacian-eigenvector-plugin-for-houdini/ HDK tutorial] on eigenvectors&lt;br /&gt;
&lt;br /&gt;
* https://inria.hal.science/hal-02541299/file/DeformerElastic-rigid_paper.pdf paper to implement&lt;br /&gt;
&lt;br /&gt;
=== Plug-ins/tools ===&lt;br /&gt;
&lt;br /&gt;
* To link: MOPs qLib OD &lt;br /&gt;
&lt;br /&gt;
* https://momme.gumroad.com&lt;br /&gt;
&lt;br /&gt;
* https://github.com/pedohorse (including RBF hda)&lt;br /&gt;
&lt;br /&gt;
== Flips ==&lt;br /&gt;
Basic setup:&lt;br /&gt;
&lt;br /&gt;
 http://i.imgur.com/cJpBIsk.png&lt;br /&gt;
&lt;br /&gt;
* better quality: more points (smaller point separatation) or decrease grid scale&lt;br /&gt;
* reseed particle in flipsolver: nice for droplets but /!\ particle IDs change&lt;br /&gt;
&lt;br /&gt;
==Expressions Vex &amp;amp; General Syntax==&lt;br /&gt;
[[Houdini_VEX]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$NPT number of points&lt;br /&gt;
$CEX $CEY $CEZ centroid&lt;br /&gt;
$FF frame number &lt;br /&gt;
$T time&lt;br /&gt;
$PT point&lt;br /&gt;
$CR $CB $CG color&lt;br /&gt;
&lt;br /&gt;
$OS use node name (in groups for ex)&lt;br /&gt;
&lt;br /&gt;
$CY copy number in copy node, use for placement w/o having to stamp&lt;br /&gt;
&lt;br /&gt;
`$OS`_`$OBJID` give objects nynamic names &lt;br /&gt;
opdigits($NAME)&lt;br /&gt;
&lt;br /&gt;
opinputpath(&amp;quot;sopnode&amp;quot;,inputN) get the incoming connection (0, 1...)&lt;br /&gt;
op:`opinputpath(&amp;quot;../&amp;quot;,1)`      ---&amp;gt; get full path of input 2 of parent&lt;br /&gt;
nprims(&amp;quot;../sort1&amp;quot;)       ----&amp;gt; number of primitives&lt;br /&gt;
$TEMP/houdiniCache/simdata.`padzero(4,if($F&amp;gt;70,70,$F))`.simdata   -----&amp;gt; read from cache, hold at a certain frame&lt;br /&gt;
&lt;br /&gt;
$OS.`substr(chs(&amp;quot;camera&amp;quot;),rindex(chs(&amp;quot;camera&amp;quot;), &amp;quot;/&amp;quot;)+1,200)`      ------&amp;gt; get the name of the camera if it&amp;#039;s a complicated name with lots of / / / &lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
in vex no uppercase:&lt;br /&gt;
v@Cd&lt;br /&gt;
i@id&lt;br /&gt;
set()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Print to console with precision===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
printf(&amp;quot;Point %*.*g\n&amp;quot;, 10, 10, @value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Expressions dump ===&lt;br /&gt;
On a switch; switches between inputs if first input is an empty vdb (fix for Maxwell&amp;#039;s shitty VDB implementation) - the second input being an &amp;#039;empty&amp;#039; but existing vdb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(prim(opinputpath(&amp;quot;.&amp;quot;,0),0,&amp;quot;file_voxel_count&amp;quot;,0)!=0,1,0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Shortcuts==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
general / viewport&lt;br /&gt;
---------------------&lt;br /&gt;
ctrl-b: full window&lt;br /&gt;
h: center&lt;br /&gt;
d: display options&lt;br /&gt;
p: show params&lt;br /&gt;
space-shift-h: center on object&lt;br /&gt;
h: center on grid/frame current selection&lt;br /&gt;
w: wireframe&lt;br /&gt;
left/right arrows =prev/next frame&lt;br /&gt;
ctrl-left arrow: go to first frame&lt;br /&gt;
&lt;br /&gt;
node editor&lt;br /&gt;
---------------------&lt;br /&gt;
u/i: up down hierachy&lt;br /&gt;
c: set color&lt;br /&gt;
ctrl-click on node blue bit: becomes purple = final output&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
animation&lt;br /&gt;
---------------------&lt;br /&gt;
alt on param boxes = keyframe&lt;br /&gt;
alt on param name = key x y z&lt;br /&gt;
&lt;br /&gt;
chops:&lt;br /&gt;
---------------------&lt;br /&gt;
d show points on curve&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
graph editor:&lt;br /&gt;
---------------------&lt;br /&gt;
shift-lmb: access graph editor&lt;br /&gt;
up down arrows: play forward&lt;br /&gt;
left right: prev next frame&lt;br /&gt;
v-h: zoom vertically/horizontally&lt;br /&gt;
j: show whole timeline&lt;br /&gt;
k: key all&lt;br /&gt;
g: group keys &lt;br /&gt;
&lt;br /&gt;
* drag &amp;amp; drop parameters&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
vopsop&lt;br /&gt;
----------------------&lt;br /&gt;
r: reverseinputs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POPNET==&lt;br /&gt;
* birthgroup: group of particles that were just borne, lasts 1 frame&lt;br /&gt;
&lt;br /&gt;
==DOPNET==&lt;br /&gt;
* can inherit params&lt;br /&gt;
* 3 solvers, bullet = speed&lt;br /&gt;
* &amp;#039;activate&amp;#039; node to do bullet time&lt;br /&gt;
* shelf tool always update latest created dopnet (&amp;#039;set always&amp;#039;)&lt;br /&gt;
* clones for active object = active creation with modulo&lt;br /&gt;
&lt;br /&gt;
== General == &lt;br /&gt;
* drag and drop nodes to get path names&lt;br /&gt;
&lt;br /&gt;
=== Color Schemes ===&lt;br /&gt;
* Red: out geo&lt;br /&gt;
* Purple: VOP_name&lt;br /&gt;
* Yellow: creation/merge&lt;br /&gt;
* Blue: POP DOP&lt;br /&gt;
* Light Green: Pre split&lt;br /&gt;
&lt;br /&gt;
==Volumes==&lt;br /&gt;
&lt;br /&gt;
* Volume from inside as well as surface:&lt;br /&gt;
&lt;br /&gt;
* add a &amp;#039;volume from points&amp;#039; &amp;gt; &amp;#039; stamp points &amp;#039; before volume object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* with volume dops: use curl noise --&amp;gt; 4d  with vector4 to do a time noise&lt;br /&gt;
&lt;br /&gt;
* displace noise: use shader, dig into &amp;#039;fireball&amp;#039; shader&lt;br /&gt;
&lt;br /&gt;
== Wtf ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
${OS}_new_pieces&lt;br /&gt;
pillar_?   (group)&lt;br /&gt;
@Cd&lt;br /&gt;
$VALUE&lt;br /&gt;
vector vexcode&lt;br /&gt;
`interpreted text`&lt;br /&gt;
op:opinputpath&lt;br /&gt;
chramp(values)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== rendering ==&lt;br /&gt;
* get motion blur on objects : geometry velocity motion blur&lt;br /&gt;
* plug stuff in a premade mantra surface (displace along normal, diffuse etc...)&lt;br /&gt;
&lt;br /&gt;
== Chops ==&lt;br /&gt;
* right click anim channels to add motion effects&lt;br /&gt;
* expressions: $I = ptnum, for curves&lt;br /&gt;
* object merge to grab channels&lt;br /&gt;
* multiply: order is important&lt;br /&gt;
* lookup: timeshift&lt;br /&gt;
* at Geo level, to get chop value: chop(&amp;quot;../CHOPNET/out/ty0&amp;quot;) or chopf&lt;br /&gt;
* recording mouse keyboard: keyboard - mouse -&amp;gt; record, hit &amp;#039;&amp;#039;&amp;#039;scroll lock&amp;#039;&amp;#039;&amp;#039; to disable shortcuts&lt;br /&gt;
&lt;br /&gt;
==l-systems==&lt;br /&gt;
http://algorithmicbotany.org/papers/&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proba: &amp;#039;:&amp;#039;&lt;br /&gt;
rules&lt;br /&gt;
function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==nodes==&lt;br /&gt;
&lt;br /&gt;
point replicate : multiply points&lt;br /&gt;
&lt;br /&gt;
attribute promote : transfer from point &amp;lt;&amp;gt; primitives&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Render Stuff and Cache Geo and Alembic ==&lt;br /&gt;
&lt;br /&gt;
* wedge: change node values an filenames with $WEDGENUM&lt;br /&gt;
&lt;br /&gt;
* fetch: renders specific ROPs, can be daisy-chained with a merge and (node to node)&lt;br /&gt;
&lt;br /&gt;
* multiple shapes in maya with alembic: in Houdini, need a primitive with strings called &amp;#039;name&amp;#039;, the names will be shape names, in the alembic exporter, make sur to choose Partition Mode: Use Combination Of Transform/Shape Node and choose previously created &amp;#039;name&amp;#039; as Attribute (also Ogawa format - ?)&lt;br /&gt;
&lt;br /&gt;
== Fur/hair ==&lt;br /&gt;
* put RBD solver before Wire solver in merge otherwise = bug&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
&lt;br /&gt;
[[Houdini_Python]]&lt;br /&gt;
&lt;br /&gt;
= Howto&amp;#039;s / Tricks =&lt;br /&gt;
* Wedges and HQUEUE:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/OjEpvpv.png&lt;br /&gt;
&lt;br /&gt;
* stop stepping: disable integer in frame options, add timeblend&lt;br /&gt;
&lt;br /&gt;
* using &amp;#039;rest&amp;#039; to get more interesting fractures&lt;br /&gt;
&lt;br /&gt;
* restpos: can be used in SHOPS to get object space&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/pZLGdrn.jpg&lt;br /&gt;
&lt;br /&gt;
* instances: instancepoint() w/ full point instancing --&amp;gt; packed disk primitives = refs = light&lt;br /&gt;
&lt;br /&gt;
* instances materials: &amp;quot;declare materials&amp;gt;declare all shops&amp;quot; to export shaders to instances (add using paramet interface window)&lt;br /&gt;
&lt;br /&gt;
* triangulate a whole object cleanly using foreach primitive &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/0KZYipI.jpg&lt;br /&gt;
&lt;br /&gt;
* On crashes: load with &amp;#039;Manual&amp;#039; and hit escape so nodes aren&amp;#039;t read (if file is corrupt for ever)&lt;br /&gt;
&lt;br /&gt;
* Blend between slowed down continuous mesh:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/eduhgBs.png&lt;br /&gt;
&lt;br /&gt;
* RBD Point Object from sequence from [http://forums.odforce.net/topic/16560-rbd-point-object-problem/?do=findComment&amp;amp;comment=101371 odforce]:&lt;br /&gt;
** add int attr on points (call it &amp;#039;shape&amp;#039;)&lt;br /&gt;
** write RBDs to disk&lt;br /&gt;
** in RBD point object override point value geometry path: /path/to/geo.*.bgeo&lt;br /&gt;
** &amp;#039;allow editing of contents&amp;#039; in RBD point object, dive inside fin &amp;#039;sopgeo2&amp;#039;, add a stamp OBJID, $OBJID, disable &amp;#039;Use External SOP&amp;#039;&lt;br /&gt;
** dive inside sopgeo, add a read file, and replace * from RBD point object to proper file path: `strreplace(chs(&amp;quot;../../geopath&amp;quot;), &amp;quot;*&amp;quot;, point(&amp;quot;/obj/gridscatter/OUT&amp;quot;, stamp(&amp;quot;..&amp;quot;, &amp;quot;OBJID&amp;quot;, -1), &amp;quot;shape&amp;quot;, 0))`&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Parenting: at object level or using &amp;#039;Rivet&amp;#039; for deforming geo&lt;br /&gt;
&lt;br /&gt;
* Group according to mesh connectivity: use &amp;#039;Island 1&amp;#039; &amp;#039;Island 2&amp;#039; etc... &lt;br /&gt;
&lt;br /&gt;
* If you get &amp;#039;unable to initialize UV rendering module with camera&amp;#039; when baking textures, add a new cam to your scene (cam1)&lt;br /&gt;
&lt;br /&gt;
* If you are using maxwell and alembic (abc) files to render particles (realflow plug-in)  you NEED a vector v and int id attributes otherwise it won&amp;#039;t render&lt;br /&gt;
&lt;br /&gt;
* Fluid simulation: collision reversed ? Use a vdb with a static solver, and reverse the vdb with a volume wrangle @surface *= -1&lt;br /&gt;
&lt;br /&gt;
[[Category:Houdini]]&lt;br /&gt;
&lt;br /&gt;
* Why is my motion blur not working ? You need &amp;#039;v&amp;#039; and to check &amp;#039;geometry velocity blur&amp;#039; on your object&lt;br /&gt;
&lt;br /&gt;
* I still have a hard time with vex code rotations/matrices even with matt estela&amp;#039;s wiki so VOP quicktip to rotate normals around an edge:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/RcrtESH.png&lt;br /&gt;
&lt;br /&gt;
=Specific Forum/Blog/Wiki articles=&lt;br /&gt;
===Modeling===&lt;br /&gt;
* [https://www.sidefx.com/forum/topic/44816/| Remeshing with external software] (quads, instant mesh) - lkruel&lt;br /&gt;
===Vex===&lt;br /&gt;
* [http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#Example:_Rotation| Understanding rotations] ([http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#More_on_rotation:_Orient.2C_Quaternions.2C_Matricies.2C_Offsets.2C_stuff| 2]) in houdini.... (vex, quaternions) - mestela&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=869</id>
		<title>Houdini Octane</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_Octane&amp;diff=869"/>
		<updated>2025-10-09T15:33:46Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Shading Guide */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Octane==&lt;br /&gt;
&lt;br /&gt;
=== Python tools ===&lt;br /&gt;
==== HDRI in the viewport control====&lt;br /&gt;
It&amp;#039;s a pain in the ass to go to the rendertarget each time, this acts like a normal environment preview. Barebones but works.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
links a null with some parameters to a chosen Rendertarget texture. &lt;br /&gt;
Display flag sets texture on/off just like for lights.&lt;br /&gt;
No error checking.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
def chooseRTdialog(buttons):&lt;br /&gt;
    buttons.append(&amp;#039;Cancel&amp;#039;)&lt;br /&gt;
    dialog = hou.ui.displayMessage(&amp;#039;Choose which RTarget HDRi to link to&amp;#039;, buttons=buttons)&lt;br /&gt;
    if dialog == len(buttons)-1:&lt;br /&gt;
        dialog = False&lt;br /&gt;
    return dialog&lt;br /&gt;
&lt;br /&gt;
def linkDispFlag(node,event_type):&lt;br /&gt;
    RT = hou.node(node.parm(&amp;#039;RT&amp;#039;).eval())&lt;br /&gt;
    menu = RT.parm(&amp;#039;environmentMenu&amp;#039;)&lt;br /&gt;
    if node.isDisplayFlagSet() == 1:&lt;br /&gt;
        menu.set(6)&lt;br /&gt;
    else:&lt;br /&gt;
        menu.set(1)&lt;br /&gt;
   &lt;br /&gt;
rts = []&lt;br /&gt;
RT = None&lt;br /&gt;
for node in hou.node(&amp;#039;/&amp;#039;).allSubChildren():&lt;br /&gt;
    if &amp;#039;octane_mat_renderTarget&amp;#039; in node.type().name():&lt;br /&gt;
        rts.append(node)&lt;br /&gt;
&lt;br /&gt;
names = [x.name() for x in rts]        &lt;br /&gt;
        &lt;br /&gt;
if len(rts) == 1:&lt;br /&gt;
    RT = rts[0]&lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    chosenRT = chooseRTdialog(names)&lt;br /&gt;
    if chosenRT is not False:&lt;br /&gt;
        RT = chosenRT&lt;br /&gt;
&lt;br /&gt;
if RT:&lt;br /&gt;
    hdri = hou.node(&amp;quot;/obj&amp;quot;).createNode(&amp;quot;null&amp;quot;, &amp;quot;hdri&amp;quot;)&lt;br /&gt;
    hdri.setSelectableInViewport(True)&lt;br /&gt;
    hdri.useXray(True)&lt;br /&gt;
    hdri.setDisplayFlag(True)&lt;br /&gt;
    hdri.hide(False)&lt;br /&gt;
    hdri.setSelected(True)&lt;br /&gt;
    hdri.parm(&amp;#039;geoscale&amp;#039;).set(10)&lt;br /&gt;
    hdri.parm(&amp;#039;controltype&amp;#039;).set(1)&lt;br /&gt;
    hdri.setUserData(&amp;quot;nodeshape&amp;quot;, &amp;quot;circle&amp;quot;)&lt;br /&gt;
    hdri.setColor(hou.Color([0.976, 0.78, 0.263]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setGenericFlag(hou.nodeFlag.DisplayComment,True)&lt;br /&gt;
&lt;br /&gt;
    ptg = hdri.parmTemplateGroup()&lt;br /&gt;
&lt;br /&gt;
    slider = hou.FloatParmTemplate(&amp;quot;hdripower&amp;quot;, &amp;quot;Hdri Power&amp;quot;, 1, default_value=(0.0,))&lt;br /&gt;
    exr = hou.StringParmTemplate(&amp;quot;exr&amp;quot;, &amp;quot;HDRI&amp;quot;, 1, string_type=hou.stringParmType.FileReference, file_type=hou.fileType.Image)&lt;br /&gt;
    path = hou.StringParmTemplate(&amp;quot;RT&amp;quot;, &amp;quot;RTarget&amp;quot;, 1, string_type=hou.stringParmType.NodeReference,default_value=(RT.path(),)) &lt;br /&gt;
&lt;br /&gt;
    ptg.insertBefore((0,0),path)&lt;br /&gt;
    ptg.insertBefore((0,0),exr)&lt;br /&gt;
    ptg.insertBefore((0,0),slider)&lt;br /&gt;
&lt;br /&gt;
    hdri.setParmTemplateGroup(ptg)&lt;br /&gt;
        &lt;br /&gt;
    parmsFrom = &amp;#039;tx ty tz rx ry rz sx sy sz exr hdripower&amp;#039;&lt;br /&gt;
    parmsTo = &amp;#039;translation121 translation122 translation123 textureEnvTilt textureEnvLeftRight textureEnvRoll textureEnvScale1 textureEnvScale2 textureEnvScale3 textureEnvironmentFilename textureEnvPower&amp;#039;&lt;br /&gt;
&lt;br /&gt;
    parmsFrom = parmsFrom.split(&amp;#039; &amp;#039;)&lt;br /&gt;
    parmsTo = parmsTo.split(&amp;#039; &amp;#039;)&lt;br /&gt;
&lt;br /&gt;
    for i in range(len(parmsFrom)):&lt;br /&gt;
        hdri.parm(parmsFrom[i]).set(RT.parm(parmsTo[i]).eval())     &lt;br /&gt;
        RT.parm(parmsTo[i]).set(hdri.parm(parmsFrom[i]))&lt;br /&gt;
    &lt;br /&gt;
    hdri.setComment(RT.path()+&amp;#039;\n&amp;#039;+RT.parm(&amp;#039;textureEnvironmentFilename&amp;#039;).eval().split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
    hdri.addEventCallback((hou.nodeEventType.FlagChanged,),linkDispFlag)    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Shading Guide ===&lt;br /&gt;
&lt;br /&gt;
For C4D, still good: https://www.behance.net/gallery/102948251/Octane-Universal-Material-Guide-Starter-File&lt;br /&gt;
&lt;br /&gt;
=== Shading instances with custom colors ===&lt;br /&gt;
Is a little more hands on than getting custom attributes&lt;br /&gt;
&lt;br /&gt;
If you have a single object that you want to instance but have it have a random shader (in my example, using color), you can use a trick/weird workaround.&lt;br /&gt;
&lt;br /&gt;
To make it work:&lt;br /&gt;
* in your scattered points (the ones that hold @instance, @orient, @pscale, @Cd) /obj/ level geo, make sure you choose &amp;#039;Packed RGBA Values from &amp;#039;Cd&amp;#039; Point Attribute&amp;#039;.&lt;br /&gt;
* in your instance shader, you need to grab &amp;#039;Texture Instance Color&amp;#039; and go pick a .ppm texture that sits in the install directory of octane under the /tex folder called &amp;#039;&amp;#039;&amp;#039;rgb4k_map.ppm&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* plug it in directly your diffuse or use it to drive other textures like a ramp in my example&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GZIUm2z.png&lt;br /&gt;
&lt;br /&gt;
=== Shadow catcher with diffuse bounce ===&lt;br /&gt;
&lt;br /&gt;
I think default shadow catching doesn&amp;#039;t get the bounce so you can whip it up with a rayswitch. I sort of plugged stuff into stuff for it to work (topright pigheads -- bottom left is normal shadowcatcher,plane grid is a green material):&lt;br /&gt;
&lt;br /&gt;
(also i cropped the rayswitch like an idiot but camera ray is at 0 the rest at 1&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/8yxOMxr.png&lt;br /&gt;
&lt;br /&gt;
https://imgur.com/KsjjsQK.png&lt;br /&gt;
&lt;br /&gt;
=== Double sided material in Octane ===&lt;br /&gt;
&lt;br /&gt;
You can use tool_polygon_side to drive a material mixer:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/e1aOVPh.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Arnold ==&lt;br /&gt;
&lt;br /&gt;
Weird render procedural stuff: https://arnoldsupport.com/2018/11/21/backdoor-setting-visibility/&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=868</id>
		<title>Expressions and Tips</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Expressions_and_Tips&amp;diff=868"/>
		<updated>2025-09-30T14:41:52Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Rand characters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[azerty AFX]] keyboard shortcuts &lt;br /&gt;
=== Somewhat automatic Contact Sheet ===&lt;br /&gt;
WIP. It will autolayout footage/comps and corresponding text layers (you&amp;#039;ll still need to ctrl c ctrl v position+scale expressions).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = &amp;#039;&amp;#039;;&lt;br /&gt;
list = [];&lt;br /&gt;
for(i=1;i&amp;lt;thisComp.numLayers;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	try{cur = L.source.name}catch(err){cur = false}&lt;br /&gt;
	list.push(cur?cur:null);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
list.join(&amp;#039;\n&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Converting RGB to CMYK in After Effects ===&lt;br /&gt;
Is a real pain in the buttocks. I know I&amp;#039;m not following any nifty color conversion algorithm but I just needed a way to use substractive colors on a project (fake print with halftone pattern). This was such a pain in the buttock that I&amp;#039;ll probably code a better plugin if I ever get the chance to.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/eL2vlqv.gif&lt;br /&gt;
&lt;br /&gt;
The way I went from RGB to C,M,Y. The settings are not all necessary (and also no K, I still need to fiddle with this to find the exact RGB match! I&amp;#039;ll reupload if I find a better solution).&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/iD9BZ24.jpeg&lt;br /&gt;
&lt;br /&gt;
=== Position from mask path ===&lt;br /&gt;
image tbd&lt;br /&gt;
&lt;br /&gt;
Takes the first and second frame of an effect to drive a position along a path. You can use this to drive the &amp;#039;write-on&amp;#039; effect if you don&amp;#039;t have 3d stroke.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = (time-key(1).time)/(key(2).time-key(1).time);&lt;br /&gt;
t = linear(t,0,1,0,1);&lt;br /&gt;
mask(1).maskPath.pointOnPath(t)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Loop animation before and after first and last keyframes===&lt;br /&gt;
&lt;br /&gt;
Should work with mask keyframes too, as opposed to loopOut/loopIn&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/6adNfdW.png&lt;br /&gt;
&lt;br /&gt;
Keep in mind that just like the normal loopOut, the last keyframe will always be replaced by the value of the first one, so you should have a sacrificial keyframe. Tried to have a one-liner but haven&amp;#039;t wrapped my head around it quite fully&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var offset = time&amp;lt;key(1).time?key(numKeys).time:key(1).time;&lt;br /&gt;
valueAtTime( (time - offset ) % ( key(numKeys).time - key(1).time ) + offset );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Display current layer name ===&lt;br /&gt;
screenshot tbd this is pretty specific&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pct =&amp;#039; ░▒▓█&amp;#039;;&lt;br /&gt;
startIndex = 1;//what layer do I start with ? or use thisComp.layer(&amp;#039;Null 1&amp;#039;).index;&lt;br /&gt;
lastCompIndex = thisComp.numLayers;&lt;br /&gt;
outputText = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=startIndex+1;i&amp;lt;=lastCompIndex;i++){&lt;br /&gt;
	L = thisComp.layer(i);&lt;br /&gt;
	inP = L.inPoint;&lt;br /&gt;
	outP = L.outPoint;&lt;br /&gt;
	if(time &amp;gt;= inP &amp;amp;&amp;amp; time &amp;lt; outP){&lt;br /&gt;
		curT = time - inP;&lt;br /&gt;
		maxT = outP - inP;&lt;br /&gt;
		per = curT/maxT;&lt;br /&gt;
		outputText += pct.substr(Math.floor(per*4),1) + &amp;#039; &amp;#039;;  //comment out if you dont want visual progress&lt;br /&gt;
		outputText += L.source.name + &amp;#039;\n&amp;#039;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
outputText;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Simple one liner if you put a text object above each layer (you have to do the in/out yourself)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
thisComp.layer(thisLayer.index+1).source.name&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spread items along a path ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ideOrrs.gif&lt;br /&gt;
&lt;br /&gt;
Change path to fit your need, apply expresson to the position of your item layer, duplicate and you&amp;#039;re set&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// pointonpath uses a range from 0..1 to grab a position so let&amp;#039;s figure our where we are&lt;br /&gt;
// by counting the number of layers in the comp (and omitting a &amp;#039;paths&amp;#039; layer at the bottom of the comp)&lt;br /&gt;
&lt;br /&gt;
// [0,1] [0,.5,1] [0,.333...,.666...,1] [0,.25,.5,.75,1] etc&lt;br /&gt;
var percentpos = ( thisLayer.index - 1 ) / (thisComp.numLayers - 2)&lt;br /&gt;
&lt;br /&gt;
// prevents a divide by 0 error if there is only one object halfway through the path&lt;br /&gt;
percentpos = isNaN(percentpos)? 0.5 : percentpos&lt;br /&gt;
&lt;br /&gt;
//grab path layer and path ugh, &amp;#039;path&amp;#039;&lt;br /&gt;
var pathLayer = thisComp.layer(&amp;quot;path&amp;quot;)&lt;br /&gt;
var path = pathLayer.content(&amp;quot;Shape 2&amp;quot;).content(&amp;quot;Path 1&amp;quot;).path&lt;br /&gt;
&lt;br /&gt;
//grab position on path and use toComp to place it in the right spot&lt;br /&gt;
var pos = path.pointOnPath( percentpos )&lt;br /&gt;
pathLayer.toComp(pos)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Retime (varispeed) any value ===&lt;br /&gt;
&lt;br /&gt;
Allows time remapping of animation without having to time remap (and precomp). This way you can link different animations. It&amp;#039;s using valueAtTime and a slider. You&amp;#039;ll have to edit it for n-dimensional values (like position).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;video width=&amp;quot;640&amp;quot; controls&amp;gt;&lt;br /&gt;
&amp;lt;source src=&amp;quot;https://i.imgur.com/KQhIIJi.mp4&amp;quot; type=&amp;quot;video/mp4&amp;quot; &amp;gt;&lt;br /&gt;
Your browser does not support the video tag.&lt;br /&gt;
&amp;lt;/video&amp;gt; &lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add a slider control that you call &amp;#039;speed&amp;#039;, and then add this expression to remap it according to the animation as seen in the video above&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
spd = effect(&amp;quot;speed&amp;quot;)(&amp;quot;Slider&amp;quot;);      //slider varies between 0 and 1, allows precise slow ups and downs in a single slider&lt;br /&gt;
p = thisProperty;                 &lt;br /&gt;
firstT = p.key(1).time;&lt;br /&gt;
lastT = p.key(p.numKeys).time;&lt;br /&gt;
p = p.valueAtTime(spd*(lastT-firstT)+firstT); &lt;br /&gt;
//might have to add [p[0],p[1]];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Padded timer text===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/pcRzMkB.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t = effect(&amp;quot;Slider Control&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
sign = (t&amp;lt;0)?&amp;#039;-&amp;#039;:&amp;#039;&amp;#039;;&lt;br /&gt;
t = Math.abs(t);&lt;br /&gt;
&lt;br /&gt;
s = t%60;&lt;br /&gt;
m = Math.floor(t/60)%60;&lt;br /&gt;
h =  Math.floor(t/ (60*60));&lt;br /&gt;
s =  (s&amp;lt;10)?&amp;#039;0&amp;#039;+s:s;  		// pads&lt;br /&gt;
m = (m&amp;lt;10)?&amp;#039;0&amp;#039;+m:m;&lt;br /&gt;
sign+h+&amp;#039;:&amp;#039;+m+&amp;#039;:&amp;#039;+s;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Rand characters===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/7hll8l1.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//seedRandom(1,true); //remove line to have it randomize each frame&lt;br /&gt;
chars = &amp;quot;GATC&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
characters = 6;&lt;br /&gt;
blocks = 4;&lt;br /&gt;
lines = 10;&lt;br /&gt;
&lt;br /&gt;
textValue = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=0;i&amp;lt;lines;i++){&lt;br /&gt;
	for(j=0;j&amp;lt;blocks;j++){&lt;br /&gt;
		for(k=0;k&amp;lt;characters;k++){&lt;br /&gt;
			randVar = random() * chars.length;&lt;br /&gt;
			characterVar = chars.charAt(randVar);&lt;br /&gt;
			textValue = textValue + characterVar;&lt;br /&gt;
		}&lt;br /&gt;
		textValue += &amp;quot; &amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	textValue += &amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
textValue;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Current Keyframe Index===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//gobelins&lt;br /&gt;
a = timeRemap;&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
curframe = 0;&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	curframe = nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	curframe = nk.index;&lt;br /&gt;
}&lt;br /&gt;
curframe*thisComp.frameDuration;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
https://gyazo.com/fa2262be189547b5381aa50041cdc32b.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = position;  //or whatever animated property&lt;br /&gt;
nk = a.nearestKey(time);&lt;br /&gt;
&lt;br /&gt;
if(nk.time &amp;gt; time){&lt;br /&gt;
	nk.index-1;&lt;br /&gt;
}else{&lt;br /&gt;
	nk.index;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== sample image pr les gobz ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a = sampleImage([thisComp.width/2,thisComp.height/2], [thisComp.width/2,thisComp.height/2], postEffect = true, t = time);&lt;br /&gt;
a[0]+a[1]+a[2]+a[3];&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
////bake&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;)-effect(&amp;quot;s&amp;quot;)(&amp;quot;Curseur&amp;quot;).valueAtTime(time-1/25)==0?0:1&lt;br /&gt;
&lt;br /&gt;
//////bake?&lt;br /&gt;
&lt;br /&gt;
v = effect(&amp;quot;hold&amp;quot;)(&amp;quot;Curseur&amp;quot;);&lt;br /&gt;
a = 0;&lt;br /&gt;
for(i=0;i&amp;lt;=time*25;i++){&lt;br /&gt;
	if(v.valueAtTime(i/25)==1){&lt;br /&gt;
		a = i/25;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
a;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== spread thingy ===&lt;br /&gt;
http://i.imgur.com/dlJJcXt.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
p = thisComp.layer(&amp;quot;Null 1&amp;quot;).transform.position;&lt;br /&gt;
delta = transform.position - p;&lt;br /&gt;
amplitude = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;amplitude&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
reach = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;reach&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
exponent = thisComp.layer(&amp;quot;Null 1&amp;quot;).effect(&amp;quot;exponent&amp;quot;)(&amp;quot;Slider&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
length = (length(delta));&lt;br /&gt;
if(length&amp;gt;0){&lt;br /&gt;
	transform.position += normalize( delta ) *  ( reach / Math.pow(length, exponent)  ) * amplitude;&lt;br /&gt;
}else{&lt;br /&gt;
	transform.position = [-2000,0]&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Sprites ===&lt;br /&gt;
Duplicate layers by hand, add CtrlNul, offsetLeftRight, offsetDepth, spread sliders&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//POSITION&lt;br /&gt;
seedRandom(index,true)&lt;br /&gt;
ctrl = thisComp.layer(&amp;quot;CtrlNul&amp;quot;);&lt;br /&gt;
dist = ctrl.effect(&amp;quot;dist&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
offset = [ctrl.effect(&amp;quot;offsetLeftRight&amp;quot;)(&amp;quot;Slider&amp;quot;),0,ctrl.effect(&amp;quot;offsetDepth&amp;quot;)(&amp;quot;Slider&amp;quot;)];&lt;br /&gt;
&lt;br /&gt;
depthjitter = ctrl.effect(&amp;quot;depthjitter&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
depthmult = ctrl.effect(&amp;quot;depthmult&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
spread = ctrl.effect(&amp;quot;spread&amp;quot;)(&amp;quot;Slider&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
dist = dist+random()*spread;&lt;br /&gt;
&lt;br /&gt;
transform.position+[(index%2)?-dist:dist,0,depthmult*index+depthjitter*random()]+offset&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//ORIENTATION&lt;br /&gt;
lookAt(transform.position,thisComp.layer(&amp;quot;Camera 1&amp;quot;).transform.position)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Write on effect ===&lt;br /&gt;
https://i.gyazo.com/091522309c14c61f0cc7d63672e78900.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
cursorSpeed = 3;&lt;br /&gt;
text.sourceText.slice(0,time*fps)+((Math.floor(time*cursorSpeed)%2)?&amp;quot;|&amp;quot;:&amp;quot; &amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Unscramble wip ===&lt;br /&gt;
http://i.imgur.com/k8Gf1wC.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wordspeed = time/1; //or a slider, or whatever&lt;br /&gt;
letterspeed = time*2;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
txtVar = &amp;quot;&amp;quot;;&lt;br /&gt;
chars = &amp;quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;quot;;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
maxLines = Math.min(splitxt.length-1, Math.floor(wordspeed));&lt;br /&gt;
for(i=0;i&amp;lt;=maxLines;i++){&lt;br /&gt;
	txtVar += (i&amp;gt;0)?splitxt[i-1]+&amp;quot;\r&amp;quot;:&amp;quot;&amp;quot;;&lt;br /&gt;
	len = splitxt[i].length;&lt;br /&gt;
	if(i == maxLines){&lt;br /&gt;
		for(j=0;j&amp;lt;=len-1;j++){&lt;br /&gt;
			txtVar += (j/len&amp;gt;=wordspeed-Math.floor(wordspeed))?chars.charAt(random()*chars.length):splitxt[i].charAt(j);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
txtVar&lt;br /&gt;
;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Poor man&amp;#039;s padding ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//for lack of a cleaner version, up to 4 digits&lt;br /&gt;
a = time*25;&lt;br /&gt;
a=(a&amp;lt;10)?&amp;quot;000&amp;quot;+a:((a&amp;lt;100)?&amp;quot;00&amp;quot;+a:((a&amp;lt;1000)?&amp;quot;0&amp;quot;+a:a));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== 2d lookat ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
LookAt = &amp;quot;ball&amp;quot;&lt;br /&gt;
offset = 0&lt;br /&gt;
diffx = position[0] - this_comp.layer(LookAt).position[0];&lt;br /&gt;
diffy = position[1] - this_comp.layer(LookAt).position[1];&lt;br /&gt;
if (diffx == 0) {&lt;br /&gt;
diffx = 1 }&lt;br /&gt;
sign = 1 + (-1 * (diffx / Math.abs(diffx))) * 90;&lt;br /&gt;
radians_to_degrees(Math.atan(diffy/diffx)) + sign + offset&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//with parenting&lt;br /&gt;
lookAt = &amp;quot;a&amp;quot;;&lt;br /&gt;
L = thisComp.layer(lookAt);&lt;br /&gt;
p = L.toComp(L.anchorPoint);&lt;br /&gt;
d = thisLayer.toComp(anchorPoint) - p&lt;br /&gt;
d[0] = (d[0]==0)?1:d[0];&lt;br /&gt;
rotation + radians_to_degrees(Math.atan(d[1]/d[0])) - 90*d[0]/ Math.abs(d[0])&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Pixilation (slow footage) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
numImages = 15;&lt;br /&gt;
fps = 1/thisComp.frameDuration;&lt;br /&gt;
framesToWait = 3;Math.floor(time*fps/framesToWait%numImages)/fps;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Text scroller (DOS!)===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/JlLNh1Z.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//add your text, three slider control effects and rename them &amp;#039;line width&amp;#039;, &amp;#039;line&amp;#039;, &amp;#039;line animation&amp;#039;&lt;br /&gt;
//then this expression on the source text&lt;br /&gt;
charlen = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line width&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //20&lt;br /&gt;
numlines = Math.ceil(Math.abs(Math. round(effect(&amp;quot;lines&amp;quot;)(&amp;quot;Slider&amp;quot;),0))); //5&lt;br /&gt;
step = Math.ceil(Math.abs(Math. round(effect(&amp;quot;line animation&amp;quot;)(&amp;quot;Slider&amp;quot;),0)));&lt;br /&gt;
&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;numlines+step;i++){&lt;br /&gt;
	output += (i &amp;gt; splitxt.length - numlines)?&amp;quot;.\r&amp;quot;:splitxt[i].substring(0,charlen)+&amp;quot;\r&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//single line&lt;br /&gt;
src = text.sourceText;&lt;br /&gt;
len = src.length;&lt;br /&gt;
scroll = time*25;&lt;br /&gt;
src.slice(scroll%len)+src.slice(0,scroll%len)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/////////////&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
charlen =200;&lt;br /&gt;
numlines = 15;&lt;br /&gt;
step = time/thisComp.frameDuration;&lt;br /&gt;
txt = text.sourceText;&lt;br /&gt;
splitxt = txt.split(&amp;quot;\r&amp;quot;);&lt;br /&gt;
output = &amp;quot;&amp;quot;;&lt;br /&gt;
for(i=step;i&amp;lt;(step+numlines);i++){&lt;br /&gt;
	output += (splitxt.length&amp;lt;=i)?&amp;quot;\r&amp;quot;:splitxt[i-1].substring(0,charlen)+&amp;quot;\r&amp;quot;;          &lt;br /&gt;
}&lt;br /&gt;
output;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Open two simultaneous After Effects===&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\AfterFX.exe&amp;quot; -m&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Command-Line===&lt;br /&gt;
&amp;lt;pre&amp;gt;@echo off&lt;br /&gt;
&amp;quot;C:\Program Files\Adobe\Adobe After Effects CS5\Support Files\aerender.exe&amp;quot; -mp -project &amp;quot;W:\&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=867</id>
		<title>Houdini 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=867"/>
		<updated>2025-09-23T15:53:57Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page was created when I learned Houdini, it might be old and wrong ! (looking at you, HOM expressions). I&amp;#039;ll try to add more info with the hindsight.&lt;br /&gt;
&lt;br /&gt;
== Where to Start ==&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s a ton more videos nowadays then when I learned houdini, and instead of pointing to a thousand websites (well, I do just afterwards in the [links]), I&amp;#039;ll simply point out that SideFX have a [https://www.sidefx.com/tutorials/ curated list of tutorials].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Links to stuff I&amp;#039;ve saved (could have been a webring. remember those?). This was started long ago, some sites might be down, let&amp;#039;s try to clean this up (2023 edit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Manuel and Moritz&amp;#039; &amp;#039;&amp;#039;&amp;#039;[http://www.entagma.com/ Entagma]&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re seeing this page, you probably know their soothing voice already !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Richard C Thomas&amp;#039;s [https://richardcthomas.com/tab-tools website]&amp;#039;&amp;#039;&amp;#039;, loads of cool stuff! &lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Junichiro Horikawa&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.youtube.com/c/JunichiroHorikawa/videos experiments], he&amp;#039;s also in the Houdini subreddit.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Webb Hinton&amp;#039;&amp;#039;&amp;#039;&amp;#039;s wicked-ass [https://github.com/wyhinton/AwesomeHoudini AwesomeHoudini github] page with &amp;#039;&amp;#039;loads&amp;#039;&amp;#039; of ressources about/for Houdini&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Juraj Tomori&amp;#039;&amp;#039;&amp;#039;&amp;#039;s gitgub [https://jtomori.github.io/vex_tutorial/ vex tutorial/code] and [https://www.youtube.com/@seals77/videos youtube] channel&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Howiem&amp;#039;s&amp;#039;&amp;#039;&amp;#039; [http://howiem.com/wordpress/ blog] with code, CHOPs and various hardware hacking.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Houdini Gubbins&amp;#039;&amp;#039;&amp;#039; [https://houdinigubbins.wordpress.com/ technical blog]. Doesn&amp;#039;t share HIPs, but goes through his ideas, thought processes and implements research papers. Super inspiring stuff !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Matt Estela&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.tokeru.com/cgwiki/Main_Page cgwiki], aka Our Houdini Jesus :° -- if you landed on my wiki you&amp;#039;ve probably seen him. Super nice ressource and eve nicer dude, gives away his files too.&lt;br /&gt;
&lt;br /&gt;
* Henry Foster, known as &amp;#039;&amp;#039;&amp;#039;Toadstorm&amp;#039;&amp;#039;&amp;#039; has a [http://www.toadstorm.com/blog/ blog], and also created [https://www.motionoperators.com/ MOPs], which tries to make Motion Design life more like Cinema4D (ie friendlier)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;John Kunz&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://wiki.johnkunz.com/index.php?title=Main_Page wiki], and associated [https://www.youtube.com/@JohnKunz/videos youtube] account with top Notch recorded streams.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Jake Rice&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://jakerice.design/blog/ blog] (not updated recently)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Probiner&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [http://probiner.xyz/logs/ website] (not updated much anymore)&lt;br /&gt;
&lt;br /&gt;
* Eetu Martola&amp;#039;s [https://dailyhip.wordpress.com/ blog] (last update 2021)&lt;br /&gt;
&lt;br /&gt;
* Sam Hancock&amp;#039;s [https://ihoudini.blogspot.fr/ blogspot] (last update 2020 but interesting articles nonetheless)&lt;br /&gt;
&lt;br /&gt;
* Technische Universität Berlin&amp;#039;s [http://dgd.service.tu-berlin.de/wordpress/houdini/ wordpress] &amp;#039;for Mathematicians&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rich Lord&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.richlord.com/ website], full of downloadable goodies (creatures made with constraints!) It had disappeared from the web glad to see it&amp;#039;s back up.&lt;br /&gt;
&lt;br /&gt;
* https://hakeemadam.info/procedural-tools&lt;br /&gt;
&lt;br /&gt;
* -------- TODO sort the rest of the list --------------&lt;br /&gt;
&lt;br /&gt;
* https://www.nicholas-taylor.com/&lt;br /&gt;
&lt;br /&gt;
* https://sites.google.com/site/fujitarium/Houdini/useful-expressions-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
* https://houdinigubbins.wordpress.com&lt;br /&gt;
&lt;br /&gt;
* http://pepefx.blogspot.fr/&lt;br /&gt;
&lt;br /&gt;
* http://www.deborahrfowler.com/HoudiniResources/HoudiniTipsAndTricks.html&lt;br /&gt;
&lt;br /&gt;
* http://www.preset.de/2007/0711/lorenz/&lt;br /&gt;
&lt;br /&gt;
* http://www.3daet.com/cat/27/houdini/&lt;br /&gt;
&lt;br /&gt;
* Python: http://wiki.dreamsteep.com/Pythonhoudini&lt;br /&gt;
&lt;br /&gt;
* https://www.andynicholas.com&lt;br /&gt;
&lt;br /&gt;
* http://dansportfolio.com/wordpress/&lt;br /&gt;
&lt;br /&gt;
* https://houdininote.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* http://lab.ikoon.cz/&lt;br /&gt;
&lt;br /&gt;
* https://lewisinthelandofmachines.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* https://www.keatonwilliamson.com/houdini&lt;br /&gt;
&lt;br /&gt;
* Attributes cheatsheet: http://mrkunz.com/blog/08_22_2018_VEX_Wrangle_Cheat_Sheet.html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Nifty links ===&lt;br /&gt;
&lt;br /&gt;
* Discord servers: [https://discord.gg/723NGrShdm Think Procedural]  and Houdini&amp;amp;Chill (it&amp;#039;s invite based, but worth it if you&amp;#039;re worth it); plus it has system to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;[http://fx-td.com/houdiniandchill/ publish the best (most liked) posts]&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; on the discord&lt;br /&gt;
&lt;br /&gt;
* https://www.reddit.com/r/Houdini/ I&amp;#039;m there daily and I like the fact that answers there will be crawlable by search engines (contrary to discord servers).&lt;br /&gt;
&lt;br /&gt;
* [https://mbaadsgaard.com/portfolio/laplacian-eigenvector-plugin-for-houdini/ HDK tutorial] on eigenvectors&lt;br /&gt;
&lt;br /&gt;
* https://inria.hal.science/hal-02541299/file/DeformerElastic-rigid_paper.pdf paper to implement&lt;br /&gt;
&lt;br /&gt;
=== Plug-ins/tools ===&lt;br /&gt;
&lt;br /&gt;
* To link: MOPs qLib OD &lt;br /&gt;
&lt;br /&gt;
* https://momme.gumroad.com&lt;br /&gt;
&lt;br /&gt;
* https://github.com/pedohorse (including RBF hda)&lt;br /&gt;
&lt;br /&gt;
== Flips ==&lt;br /&gt;
Basic setup:&lt;br /&gt;
&lt;br /&gt;
 http://i.imgur.com/cJpBIsk.png&lt;br /&gt;
&lt;br /&gt;
* better quality: more points (smaller point separatation) or decrease grid scale&lt;br /&gt;
* reseed particle in flipsolver: nice for droplets but /!\ particle IDs change&lt;br /&gt;
&lt;br /&gt;
==Expressions Vex &amp;amp; General Syntax==&lt;br /&gt;
[[Houdini_VEX]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$NPT number of points&lt;br /&gt;
$CEX $CEY $CEZ centroid&lt;br /&gt;
$FF frame number &lt;br /&gt;
$T time&lt;br /&gt;
$PT point&lt;br /&gt;
$CR $CB $CG color&lt;br /&gt;
&lt;br /&gt;
$OS use node name (in groups for ex)&lt;br /&gt;
&lt;br /&gt;
$CY copy number in copy node, use for placement w/o having to stamp&lt;br /&gt;
&lt;br /&gt;
`$OS`_`$OBJID` give objects nynamic names &lt;br /&gt;
opdigits($NAME)&lt;br /&gt;
&lt;br /&gt;
opinputpath(&amp;quot;sopnode&amp;quot;,inputN) get the incoming connection (0, 1...)&lt;br /&gt;
op:`opinputpath(&amp;quot;../&amp;quot;,1)`      ---&amp;gt; get full path of input 2 of parent&lt;br /&gt;
nprims(&amp;quot;../sort1&amp;quot;)       ----&amp;gt; number of primitives&lt;br /&gt;
$TEMP/houdiniCache/simdata.`padzero(4,if($F&amp;gt;70,70,$F))`.simdata   -----&amp;gt; read from cache, hold at a certain frame&lt;br /&gt;
&lt;br /&gt;
$OS.`substr(chs(&amp;quot;camera&amp;quot;),rindex(chs(&amp;quot;camera&amp;quot;), &amp;quot;/&amp;quot;)+1,200)`      ------&amp;gt; get the name of the camera if it&amp;#039;s a complicated name with lots of / / / &lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
in vex no uppercase:&lt;br /&gt;
v@Cd&lt;br /&gt;
i@id&lt;br /&gt;
set()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Print to console with precision===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
printf(&amp;quot;Point %*.*g\n&amp;quot;, 10, 10, @value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Expressions dump ===&lt;br /&gt;
On a switch; switches between inputs if first input is an empty vdb (fix for Maxwell&amp;#039;s shitty VDB implementation) - the second input being an &amp;#039;empty&amp;#039; but existing vdb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(prim(opinputpath(&amp;quot;.&amp;quot;,0),0,&amp;quot;file_voxel_count&amp;quot;,0)!=0,1,0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Shortcuts==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
general / viewport&lt;br /&gt;
---------------------&lt;br /&gt;
ctrl-b: full window&lt;br /&gt;
h: center&lt;br /&gt;
d: display options&lt;br /&gt;
p: show params&lt;br /&gt;
space-shift-h: center on object&lt;br /&gt;
h: center on grid/frame current selection&lt;br /&gt;
w: wireframe&lt;br /&gt;
left/right arrows =prev/next frame&lt;br /&gt;
ctrl-left arrow: go to first frame&lt;br /&gt;
&lt;br /&gt;
node editor&lt;br /&gt;
---------------------&lt;br /&gt;
u/i: up down hierachy&lt;br /&gt;
c: set color&lt;br /&gt;
ctrl-click on node blue bit: becomes purple = final output&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
animation&lt;br /&gt;
---------------------&lt;br /&gt;
alt on param boxes = keyframe&lt;br /&gt;
alt on param name = key x y z&lt;br /&gt;
&lt;br /&gt;
chops:&lt;br /&gt;
---------------------&lt;br /&gt;
d show points on curve&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
graph editor:&lt;br /&gt;
---------------------&lt;br /&gt;
shift-lmb: access graph editor&lt;br /&gt;
up down arrows: play forward&lt;br /&gt;
left right: prev next frame&lt;br /&gt;
v-h: zoom vertically/horizontally&lt;br /&gt;
j: show whole timeline&lt;br /&gt;
k: key all&lt;br /&gt;
g: group keys &lt;br /&gt;
&lt;br /&gt;
* drag &amp;amp; drop parameters&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
vopsop&lt;br /&gt;
----------------------&lt;br /&gt;
r: reverseinputs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POPNET==&lt;br /&gt;
* birthgroup: group of particles that were just borne, lasts 1 frame&lt;br /&gt;
&lt;br /&gt;
==DOPNET==&lt;br /&gt;
* can inherit params&lt;br /&gt;
* 3 solvers, bullet = speed&lt;br /&gt;
* &amp;#039;activate&amp;#039; node to do bullet time&lt;br /&gt;
* shelf tool always update latest created dopnet (&amp;#039;set always&amp;#039;)&lt;br /&gt;
* clones for active object = active creation with modulo&lt;br /&gt;
&lt;br /&gt;
== General == &lt;br /&gt;
* drag and drop nodes to get path names&lt;br /&gt;
&lt;br /&gt;
=== Color Schemes ===&lt;br /&gt;
* Red: out geo&lt;br /&gt;
* Purple: VOP_name&lt;br /&gt;
* Yellow: creation/merge&lt;br /&gt;
* Blue: POP DOP&lt;br /&gt;
* Light Green: Pre split&lt;br /&gt;
&lt;br /&gt;
==Volumes==&lt;br /&gt;
&lt;br /&gt;
* Volume from inside as well as surface:&lt;br /&gt;
&lt;br /&gt;
* add a &amp;#039;volume from points&amp;#039; &amp;gt; &amp;#039; stamp points &amp;#039; before volume object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* with volume dops: use curl noise --&amp;gt; 4d  with vector4 to do a time noise&lt;br /&gt;
&lt;br /&gt;
* displace noise: use shader, dig into &amp;#039;fireball&amp;#039; shader&lt;br /&gt;
&lt;br /&gt;
== Wtf ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
${OS}_new_pieces&lt;br /&gt;
pillar_?   (group)&lt;br /&gt;
@Cd&lt;br /&gt;
$VALUE&lt;br /&gt;
vector vexcode&lt;br /&gt;
`interpreted text`&lt;br /&gt;
op:opinputpath&lt;br /&gt;
chramp(values)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== rendering ==&lt;br /&gt;
* get motion blur on objects : geometry velocity motion blur&lt;br /&gt;
* plug stuff in a premade mantra surface (displace along normal, diffuse etc...)&lt;br /&gt;
&lt;br /&gt;
== Chops ==&lt;br /&gt;
* right click anim channels to add motion effects&lt;br /&gt;
* expressions: $I = ptnum, for curves&lt;br /&gt;
* object merge to grab channels&lt;br /&gt;
* multiply: order is important&lt;br /&gt;
* lookup: timeshift&lt;br /&gt;
* at Geo level, to get chop value: chop(&amp;quot;../CHOPNET/out/ty0&amp;quot;) or chopf&lt;br /&gt;
* recording mouse keyboard: keyboard - mouse -&amp;gt; record, hit &amp;#039;&amp;#039;&amp;#039;scroll lock&amp;#039;&amp;#039;&amp;#039; to disable shortcuts&lt;br /&gt;
&lt;br /&gt;
==l-systems==&lt;br /&gt;
http://algorithmicbotany.org/papers/&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proba: &amp;#039;:&amp;#039;&lt;br /&gt;
rules&lt;br /&gt;
function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==nodes==&lt;br /&gt;
&lt;br /&gt;
point replicate : multiply points&lt;br /&gt;
&lt;br /&gt;
attribute promote : transfer from point &amp;lt;&amp;gt; primitives&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Render Stuff and Cache Geo and Alembic ==&lt;br /&gt;
&lt;br /&gt;
* wedge: change node values an filenames with $WEDGENUM&lt;br /&gt;
&lt;br /&gt;
* fetch: renders specific ROPs, can be daisy-chained with a merge and (node to node)&lt;br /&gt;
&lt;br /&gt;
* multiple shapes in maya with alembic: in Houdini, need a primitive with strings called &amp;#039;name&amp;#039;, the names will be shape names, in the alembic exporter, make sur to choose Partition Mode: Use Combination Of Transform/Shape Node and choose previously created &amp;#039;name&amp;#039; as Attribute (also Ogawa format - ?)&lt;br /&gt;
&lt;br /&gt;
== Fur/hair ==&lt;br /&gt;
* put RBD solver before Wire solver in merge otherwise = bug&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
&lt;br /&gt;
[[Houdini_Python]]&lt;br /&gt;
&lt;br /&gt;
= Howto&amp;#039;s / Tricks =&lt;br /&gt;
* Wedges and HQUEUE:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/OjEpvpv.png&lt;br /&gt;
&lt;br /&gt;
* stop stepping: disable integer in frame options, add timeblend&lt;br /&gt;
&lt;br /&gt;
* using &amp;#039;rest&amp;#039; to get more interesting fractures&lt;br /&gt;
&lt;br /&gt;
* restpos: can be used in SHOPS to get object space&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/pZLGdrn.jpg&lt;br /&gt;
&lt;br /&gt;
* instances: instancepoint() w/ full point instancing --&amp;gt; packed disk primitives = refs = light&lt;br /&gt;
&lt;br /&gt;
* instances materials: &amp;quot;declare materials&amp;gt;declare all shops&amp;quot; to export shaders to instances (add using paramet interface window)&lt;br /&gt;
&lt;br /&gt;
* triangulate a whole object cleanly using foreach primitive &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/0KZYipI.jpg&lt;br /&gt;
&lt;br /&gt;
* On crashes: load with &amp;#039;Manual&amp;#039; and hit escape so nodes aren&amp;#039;t read (if file is corrupt for ever)&lt;br /&gt;
&lt;br /&gt;
* Blend between slowed down continuous mesh:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/eduhgBs.png&lt;br /&gt;
&lt;br /&gt;
* RBD Point Object from sequence from [http://forums.odforce.net/topic/16560-rbd-point-object-problem/?do=findComment&amp;amp;comment=101371 odforce]:&lt;br /&gt;
** add int attr on points (call it &amp;#039;shape&amp;#039;)&lt;br /&gt;
** write RBDs to disk&lt;br /&gt;
** in RBD point object override point value geometry path: /path/to/geo.*.bgeo&lt;br /&gt;
** &amp;#039;allow editing of contents&amp;#039; in RBD point object, dive inside fin &amp;#039;sopgeo2&amp;#039;, add a stamp OBJID, $OBJID, disable &amp;#039;Use External SOP&amp;#039;&lt;br /&gt;
** dive inside sopgeo, add a read file, and replace * from RBD point object to proper file path: `strreplace(chs(&amp;quot;../../geopath&amp;quot;), &amp;quot;*&amp;quot;, point(&amp;quot;/obj/gridscatter/OUT&amp;quot;, stamp(&amp;quot;..&amp;quot;, &amp;quot;OBJID&amp;quot;, -1), &amp;quot;shape&amp;quot;, 0))`&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Parenting: at object level or using &amp;#039;Rivet&amp;#039; for deforming geo&lt;br /&gt;
&lt;br /&gt;
* Group according to mesh connectivity: use &amp;#039;Island 1&amp;#039; &amp;#039;Island 2&amp;#039; etc... &lt;br /&gt;
&lt;br /&gt;
* If you get &amp;#039;unable to initialize UV rendering module with camera&amp;#039; when baking textures, add a new cam to your scene (cam1)&lt;br /&gt;
&lt;br /&gt;
* If you are using maxwell and alembic (abc) files to render particles (realflow plug-in)  you NEED a vector v and int id attributes otherwise it won&amp;#039;t render&lt;br /&gt;
&lt;br /&gt;
* Fluid simulation: collision reversed ? Use a vdb with a static solver, and reverse the vdb with a volume wrangle @surface *= -1&lt;br /&gt;
&lt;br /&gt;
[[Category:Houdini]]&lt;br /&gt;
&lt;br /&gt;
* Why is my motion blur not working ? You need &amp;#039;v&amp;#039; and to check &amp;#039;geometry velocity blur&amp;#039; on your object&lt;br /&gt;
&lt;br /&gt;
* I still have a hard time with vex code rotations/matrices even with matt estela&amp;#039;s wiki so VOP quicktip to rotate normals around an edge:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/RcrtESH.png&lt;br /&gt;
&lt;br /&gt;
=Specific Forum/Blog/Wiki articles=&lt;br /&gt;
===Modeling===&lt;br /&gt;
* [https://www.sidefx.com/forum/topic/44816/| Remeshing with external software] (quads, instant mesh) - lkruel&lt;br /&gt;
===Vex===&lt;br /&gt;
* [http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#Example:_Rotation| Understanding rotations] ([http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#More_on_rotation:_Orient.2C_Quaternions.2C_Matricies.2C_Offsets.2C_stuff| 2]) in houdini.... (vex, quaternions) - mestela&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=866</id>
		<title>Houdini 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=866"/>
		<updated>2025-09-23T15:30:21Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Plug-ins/tools */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page was created when I learned Houdini, it might be old and wrong ! (looking at you, HOM expressions). I&amp;#039;ll try to add more info with the hindsight.&lt;br /&gt;
&lt;br /&gt;
== Where to Start ==&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s a ton more videos nowadays then when I learned houdini, and instead of pointing to a thousand websites (well, I do just afterwards in the [links]), I&amp;#039;ll simply point out that SideFX have a [https://www.sidefx.com/tutorials/ curated list of tutorials].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Links to stuff I&amp;#039;ve saved (could have been a webring. remember those?). This was started long ago, some sites might be down, let&amp;#039;s try to clean this up (2023 edit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Manuel and Moritz&amp;#039; &amp;#039;&amp;#039;&amp;#039;[http://www.entagma.com/ Entagma]&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re seeing this page, you probably know their soothing voice already !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Richard C Thomas&amp;#039;s [https://richardcthomas.com/tab-tools website]&amp;#039;&amp;#039;&amp;#039;, loads of cool stuff! &lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Junichiro Horikawa&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.youtube.com/c/JunichiroHorikawa/videos experiments], he&amp;#039;s also in the Houdini subreddit.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Webb Hinton&amp;#039;&amp;#039;&amp;#039;&amp;#039;s wicked-ass [https://github.com/wyhinton/AwesomeHoudini AwesomeHoudini github] page with &amp;#039;&amp;#039;loads&amp;#039;&amp;#039; of ressources about/for Houdini&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Juraj Tomori&amp;#039;&amp;#039;&amp;#039;&amp;#039;s gitgub [https://jtomori.github.io/vex_tutorial/ vex tutorial/code] and [https://www.youtube.com/@seals77/videos youtube] channel&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Howiem&amp;#039;s&amp;#039;&amp;#039;&amp;#039; [http://howiem.com/wordpress/ blog] with code, CHOPs and various hardware hacking.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Houdini Gubbins&amp;#039;&amp;#039;&amp;#039; [https://houdinigubbins.wordpress.com/ technical blog]. Doesn&amp;#039;t share HIPs, but goes through his ideas, thought processes and implements research papers. Super inspiring stuff !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Matt Estela&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.tokeru.com/cgwiki/Main_Page cgwiki], aka Our Houdini Jesus :° -- if you landed on my wiki you&amp;#039;ve probably seen him. Super nice ressource and eve nicer dude, gives away his files too.&lt;br /&gt;
&lt;br /&gt;
* Henry Foster, known as &amp;#039;&amp;#039;&amp;#039;Toadstorm&amp;#039;&amp;#039;&amp;#039; has a [http://www.toadstorm.com/blog/ blog], and also created [https://www.motionoperators.com/ MOPs], which tries to make Motion Design life more like Cinema4D (ie friendlier)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;John Kunz&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://wiki.johnkunz.com/index.php?title=Main_Page wiki], and associated [https://www.youtube.com/@JohnKunz/videos youtube] account with top Notch recorded streams.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Jake Rice&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://jakerice.design/blog/ blog] (not updated recently)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Probiner&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [http://probiner.xyz/logs/ website] (not updated much anymore)&lt;br /&gt;
&lt;br /&gt;
* Eetu Martola&amp;#039;s [https://dailyhip.wordpress.com/ blog] (last update 2021)&lt;br /&gt;
&lt;br /&gt;
* Sam Hancock&amp;#039;s [https://ihoudini.blogspot.fr/ blogspot] (last update 2020 but interesting articles nonetheless)&lt;br /&gt;
&lt;br /&gt;
* Technische Universität Berlin&amp;#039;s [http://dgd.service.tu-berlin.de/wordpress/houdini/ wordpress] &amp;#039;for Mathematicians&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rich Lord&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.richlord.com/ website], full of downloadable goodies (creatures made with constraints!) It had disappeared from the web glad to see it&amp;#039;s back up.&lt;br /&gt;
&lt;br /&gt;
* https://hakeemadam.info/procedural-tools&lt;br /&gt;
&lt;br /&gt;
* -------- TODO sort the rest of the list --------------&lt;br /&gt;
&lt;br /&gt;
* https://sites.google.com/site/fujitarium/Houdini/useful-expressions-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
* https://houdinigubbins.wordpress.com&lt;br /&gt;
&lt;br /&gt;
* http://pepefx.blogspot.fr/&lt;br /&gt;
&lt;br /&gt;
* http://www.deborahrfowler.com/HoudiniResources/HoudiniTipsAndTricks.html&lt;br /&gt;
&lt;br /&gt;
* http://www.preset.de/2007/0711/lorenz/&lt;br /&gt;
&lt;br /&gt;
* http://www.3daet.com/cat/27/houdini/&lt;br /&gt;
&lt;br /&gt;
* Python: http://wiki.dreamsteep.com/Pythonhoudini&lt;br /&gt;
&lt;br /&gt;
* https://www.andynicholas.com&lt;br /&gt;
&lt;br /&gt;
* http://dansportfolio.com/wordpress/&lt;br /&gt;
&lt;br /&gt;
* https://houdininote.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* http://lab.ikoon.cz/&lt;br /&gt;
&lt;br /&gt;
* https://lewisinthelandofmachines.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* https://www.keatonwilliamson.com/houdini&lt;br /&gt;
&lt;br /&gt;
* Attributes cheatsheet: http://mrkunz.com/blog/08_22_2018_VEX_Wrangle_Cheat_Sheet.html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Nifty links ===&lt;br /&gt;
&lt;br /&gt;
* Discord servers: [https://discord.gg/723NGrShdm Think Procedural]  and Houdini&amp;amp;Chill (it&amp;#039;s invite based, but worth it if you&amp;#039;re worth it); plus it has system to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;[http://fx-td.com/houdiniandchill/ publish the best (most liked) posts]&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; on the discord&lt;br /&gt;
&lt;br /&gt;
* https://www.reddit.com/r/Houdini/ I&amp;#039;m there daily and I like the fact that answers there will be crawlable by search engines (contrary to discord servers).&lt;br /&gt;
&lt;br /&gt;
* [https://mbaadsgaard.com/portfolio/laplacian-eigenvector-plugin-for-houdini/ HDK tutorial] on eigenvectors&lt;br /&gt;
&lt;br /&gt;
* https://inria.hal.science/hal-02541299/file/DeformerElastic-rigid_paper.pdf paper to implement&lt;br /&gt;
&lt;br /&gt;
=== Plug-ins/tools ===&lt;br /&gt;
&lt;br /&gt;
* To link: MOPs qLib OD &lt;br /&gt;
&lt;br /&gt;
* https://momme.gumroad.com&lt;br /&gt;
&lt;br /&gt;
* https://github.com/pedohorse (including RBF hda)&lt;br /&gt;
&lt;br /&gt;
== Flips ==&lt;br /&gt;
Basic setup:&lt;br /&gt;
&lt;br /&gt;
 http://i.imgur.com/cJpBIsk.png&lt;br /&gt;
&lt;br /&gt;
* better quality: more points (smaller point separatation) or decrease grid scale&lt;br /&gt;
* reseed particle in flipsolver: nice for droplets but /!\ particle IDs change&lt;br /&gt;
&lt;br /&gt;
==Expressions Vex &amp;amp; General Syntax==&lt;br /&gt;
[[Houdini_VEX]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$NPT number of points&lt;br /&gt;
$CEX $CEY $CEZ centroid&lt;br /&gt;
$FF frame number &lt;br /&gt;
$T time&lt;br /&gt;
$PT point&lt;br /&gt;
$CR $CB $CG color&lt;br /&gt;
&lt;br /&gt;
$OS use node name (in groups for ex)&lt;br /&gt;
&lt;br /&gt;
$CY copy number in copy node, use for placement w/o having to stamp&lt;br /&gt;
&lt;br /&gt;
`$OS`_`$OBJID` give objects nynamic names &lt;br /&gt;
opdigits($NAME)&lt;br /&gt;
&lt;br /&gt;
opinputpath(&amp;quot;sopnode&amp;quot;,inputN) get the incoming connection (0, 1...)&lt;br /&gt;
op:`opinputpath(&amp;quot;../&amp;quot;,1)`      ---&amp;gt; get full path of input 2 of parent&lt;br /&gt;
nprims(&amp;quot;../sort1&amp;quot;)       ----&amp;gt; number of primitives&lt;br /&gt;
$TEMP/houdiniCache/simdata.`padzero(4,if($F&amp;gt;70,70,$F))`.simdata   -----&amp;gt; read from cache, hold at a certain frame&lt;br /&gt;
&lt;br /&gt;
$OS.`substr(chs(&amp;quot;camera&amp;quot;),rindex(chs(&amp;quot;camera&amp;quot;), &amp;quot;/&amp;quot;)+1,200)`      ------&amp;gt; get the name of the camera if it&amp;#039;s a complicated name with lots of / / / &lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
in vex no uppercase:&lt;br /&gt;
v@Cd&lt;br /&gt;
i@id&lt;br /&gt;
set()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Print to console with precision===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
printf(&amp;quot;Point %*.*g\n&amp;quot;, 10, 10, @value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Expressions dump ===&lt;br /&gt;
On a switch; switches between inputs if first input is an empty vdb (fix for Maxwell&amp;#039;s shitty VDB implementation) - the second input being an &amp;#039;empty&amp;#039; but existing vdb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(prim(opinputpath(&amp;quot;.&amp;quot;,0),0,&amp;quot;file_voxel_count&amp;quot;,0)!=0,1,0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Shortcuts==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
general / viewport&lt;br /&gt;
---------------------&lt;br /&gt;
ctrl-b: full window&lt;br /&gt;
h: center&lt;br /&gt;
d: display options&lt;br /&gt;
p: show params&lt;br /&gt;
space-shift-h: center on object&lt;br /&gt;
h: center on grid/frame current selection&lt;br /&gt;
w: wireframe&lt;br /&gt;
left/right arrows =prev/next frame&lt;br /&gt;
ctrl-left arrow: go to first frame&lt;br /&gt;
&lt;br /&gt;
node editor&lt;br /&gt;
---------------------&lt;br /&gt;
u/i: up down hierachy&lt;br /&gt;
c: set color&lt;br /&gt;
ctrl-click on node blue bit: becomes purple = final output&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
animation&lt;br /&gt;
---------------------&lt;br /&gt;
alt on param boxes = keyframe&lt;br /&gt;
alt on param name = key x y z&lt;br /&gt;
&lt;br /&gt;
chops:&lt;br /&gt;
---------------------&lt;br /&gt;
d show points on curve&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
graph editor:&lt;br /&gt;
---------------------&lt;br /&gt;
shift-lmb: access graph editor&lt;br /&gt;
up down arrows: play forward&lt;br /&gt;
left right: prev next frame&lt;br /&gt;
v-h: zoom vertically/horizontally&lt;br /&gt;
j: show whole timeline&lt;br /&gt;
k: key all&lt;br /&gt;
g: group keys &lt;br /&gt;
&lt;br /&gt;
* drag &amp;amp; drop parameters&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
vopsop&lt;br /&gt;
----------------------&lt;br /&gt;
r: reverseinputs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POPNET==&lt;br /&gt;
* birthgroup: group of particles that were just borne, lasts 1 frame&lt;br /&gt;
&lt;br /&gt;
==DOPNET==&lt;br /&gt;
* can inherit params&lt;br /&gt;
* 3 solvers, bullet = speed&lt;br /&gt;
* &amp;#039;activate&amp;#039; node to do bullet time&lt;br /&gt;
* shelf tool always update latest created dopnet (&amp;#039;set always&amp;#039;)&lt;br /&gt;
* clones for active object = active creation with modulo&lt;br /&gt;
&lt;br /&gt;
== General == &lt;br /&gt;
* drag and drop nodes to get path names&lt;br /&gt;
&lt;br /&gt;
=== Color Schemes ===&lt;br /&gt;
* Red: out geo&lt;br /&gt;
* Purple: VOP_name&lt;br /&gt;
* Yellow: creation/merge&lt;br /&gt;
* Blue: POP DOP&lt;br /&gt;
* Light Green: Pre split&lt;br /&gt;
&lt;br /&gt;
==Volumes==&lt;br /&gt;
&lt;br /&gt;
* Volume from inside as well as surface:&lt;br /&gt;
&lt;br /&gt;
* add a &amp;#039;volume from points&amp;#039; &amp;gt; &amp;#039; stamp points &amp;#039; before volume object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* with volume dops: use curl noise --&amp;gt; 4d  with vector4 to do a time noise&lt;br /&gt;
&lt;br /&gt;
* displace noise: use shader, dig into &amp;#039;fireball&amp;#039; shader&lt;br /&gt;
&lt;br /&gt;
== Wtf ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
${OS}_new_pieces&lt;br /&gt;
pillar_?   (group)&lt;br /&gt;
@Cd&lt;br /&gt;
$VALUE&lt;br /&gt;
vector vexcode&lt;br /&gt;
`interpreted text`&lt;br /&gt;
op:opinputpath&lt;br /&gt;
chramp(values)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== rendering ==&lt;br /&gt;
* get motion blur on objects : geometry velocity motion blur&lt;br /&gt;
* plug stuff in a premade mantra surface (displace along normal, diffuse etc...)&lt;br /&gt;
&lt;br /&gt;
== Chops ==&lt;br /&gt;
* right click anim channels to add motion effects&lt;br /&gt;
* expressions: $I = ptnum, for curves&lt;br /&gt;
* object merge to grab channels&lt;br /&gt;
* multiply: order is important&lt;br /&gt;
* lookup: timeshift&lt;br /&gt;
* at Geo level, to get chop value: chop(&amp;quot;../CHOPNET/out/ty0&amp;quot;) or chopf&lt;br /&gt;
* recording mouse keyboard: keyboard - mouse -&amp;gt; record, hit &amp;#039;&amp;#039;&amp;#039;scroll lock&amp;#039;&amp;#039;&amp;#039; to disable shortcuts&lt;br /&gt;
&lt;br /&gt;
==l-systems==&lt;br /&gt;
http://algorithmicbotany.org/papers/&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proba: &amp;#039;:&amp;#039;&lt;br /&gt;
rules&lt;br /&gt;
function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==nodes==&lt;br /&gt;
&lt;br /&gt;
point replicate : multiply points&lt;br /&gt;
&lt;br /&gt;
attribute promote : transfer from point &amp;lt;&amp;gt; primitives&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Render Stuff and Cache Geo and Alembic ==&lt;br /&gt;
&lt;br /&gt;
* wedge: change node values an filenames with $WEDGENUM&lt;br /&gt;
&lt;br /&gt;
* fetch: renders specific ROPs, can be daisy-chained with a merge and (node to node)&lt;br /&gt;
&lt;br /&gt;
* multiple shapes in maya with alembic: in Houdini, need a primitive with strings called &amp;#039;name&amp;#039;, the names will be shape names, in the alembic exporter, make sur to choose Partition Mode: Use Combination Of Transform/Shape Node and choose previously created &amp;#039;name&amp;#039; as Attribute (also Ogawa format - ?)&lt;br /&gt;
&lt;br /&gt;
== Fur/hair ==&lt;br /&gt;
* put RBD solver before Wire solver in merge otherwise = bug&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
&lt;br /&gt;
[[Houdini_Python]]&lt;br /&gt;
&lt;br /&gt;
= Howto&amp;#039;s / Tricks =&lt;br /&gt;
* Wedges and HQUEUE:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/OjEpvpv.png&lt;br /&gt;
&lt;br /&gt;
* stop stepping: disable integer in frame options, add timeblend&lt;br /&gt;
&lt;br /&gt;
* using &amp;#039;rest&amp;#039; to get more interesting fractures&lt;br /&gt;
&lt;br /&gt;
* restpos: can be used in SHOPS to get object space&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/pZLGdrn.jpg&lt;br /&gt;
&lt;br /&gt;
* instances: instancepoint() w/ full point instancing --&amp;gt; packed disk primitives = refs = light&lt;br /&gt;
&lt;br /&gt;
* instances materials: &amp;quot;declare materials&amp;gt;declare all shops&amp;quot; to export shaders to instances (add using paramet interface window)&lt;br /&gt;
&lt;br /&gt;
* triangulate a whole object cleanly using foreach primitive &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/0KZYipI.jpg&lt;br /&gt;
&lt;br /&gt;
* On crashes: load with &amp;#039;Manual&amp;#039; and hit escape so nodes aren&amp;#039;t read (if file is corrupt for ever)&lt;br /&gt;
&lt;br /&gt;
* Blend between slowed down continuous mesh:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/eduhgBs.png&lt;br /&gt;
&lt;br /&gt;
* RBD Point Object from sequence from [http://forums.odforce.net/topic/16560-rbd-point-object-problem/?do=findComment&amp;amp;comment=101371 odforce]:&lt;br /&gt;
** add int attr on points (call it &amp;#039;shape&amp;#039;)&lt;br /&gt;
** write RBDs to disk&lt;br /&gt;
** in RBD point object override point value geometry path: /path/to/geo.*.bgeo&lt;br /&gt;
** &amp;#039;allow editing of contents&amp;#039; in RBD point object, dive inside fin &amp;#039;sopgeo2&amp;#039;, add a stamp OBJID, $OBJID, disable &amp;#039;Use External SOP&amp;#039;&lt;br /&gt;
** dive inside sopgeo, add a read file, and replace * from RBD point object to proper file path: `strreplace(chs(&amp;quot;../../geopath&amp;quot;), &amp;quot;*&amp;quot;, point(&amp;quot;/obj/gridscatter/OUT&amp;quot;, stamp(&amp;quot;..&amp;quot;, &amp;quot;OBJID&amp;quot;, -1), &amp;quot;shape&amp;quot;, 0))`&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Parenting: at object level or using &amp;#039;Rivet&amp;#039; for deforming geo&lt;br /&gt;
&lt;br /&gt;
* Group according to mesh connectivity: use &amp;#039;Island 1&amp;#039; &amp;#039;Island 2&amp;#039; etc... &lt;br /&gt;
&lt;br /&gt;
* If you get &amp;#039;unable to initialize UV rendering module with camera&amp;#039; when baking textures, add a new cam to your scene (cam1)&lt;br /&gt;
&lt;br /&gt;
* If you are using maxwell and alembic (abc) files to render particles (realflow plug-in)  you NEED a vector v and int id attributes otherwise it won&amp;#039;t render&lt;br /&gt;
&lt;br /&gt;
* Fluid simulation: collision reversed ? Use a vdb with a static solver, and reverse the vdb with a volume wrangle @surface *= -1&lt;br /&gt;
&lt;br /&gt;
[[Category:Houdini]]&lt;br /&gt;
&lt;br /&gt;
* Why is my motion blur not working ? You need &amp;#039;v&amp;#039; and to check &amp;#039;geometry velocity blur&amp;#039; on your object&lt;br /&gt;
&lt;br /&gt;
* I still have a hard time with vex code rotations/matrices even with matt estela&amp;#039;s wiki so VOP quicktip to rotate normals around an edge:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/RcrtESH.png&lt;br /&gt;
&lt;br /&gt;
=Specific Forum/Blog/Wiki articles=&lt;br /&gt;
===Modeling===&lt;br /&gt;
* [https://www.sidefx.com/forum/topic/44816/| Remeshing with external software] (quads, instant mesh) - lkruel&lt;br /&gt;
===Vex===&lt;br /&gt;
* [http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#Example:_Rotation| Understanding rotations] ([http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#More_on_rotation:_Orient.2C_Quaternions.2C_Matricies.2C_Offsets.2C_stuff| 2]) in houdini.... (vex, quaternions) - mestela&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=865</id>
		<title>Houdini 101</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_101&amp;diff=865"/>
		<updated>2025-09-23T15:24:47Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Other Nifty links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page was created when I learned Houdini, it might be old and wrong ! (looking at you, HOM expressions). I&amp;#039;ll try to add more info with the hindsight.&lt;br /&gt;
&lt;br /&gt;
== Where to Start ==&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s a ton more videos nowadays then when I learned houdini, and instead of pointing to a thousand websites (well, I do just afterwards in the [links]), I&amp;#039;ll simply point out that SideFX have a [https://www.sidefx.com/tutorials/ curated list of tutorials].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Links to stuff I&amp;#039;ve saved (could have been a webring. remember those?). This was started long ago, some sites might be down, let&amp;#039;s try to clean this up (2023 edit)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;table&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Manuel and Moritz&amp;#039; &amp;#039;&amp;#039;&amp;#039;[http://www.entagma.com/ Entagma]&amp;#039;&amp;#039;&amp;#039;. If you&amp;#039;re seeing this page, you probably know their soothing voice already !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Richard C Thomas&amp;#039;s [https://richardcthomas.com/tab-tools website]&amp;#039;&amp;#039;&amp;#039;, loads of cool stuff! &lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Junichiro Horikawa&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.youtube.com/c/JunichiroHorikawa/videos experiments], he&amp;#039;s also in the Houdini subreddit.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Webb Hinton&amp;#039;&amp;#039;&amp;#039;&amp;#039;s wicked-ass [https://github.com/wyhinton/AwesomeHoudini AwesomeHoudini github] page with &amp;#039;&amp;#039;loads&amp;#039;&amp;#039; of ressources about/for Houdini&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Juraj Tomori&amp;#039;&amp;#039;&amp;#039;&amp;#039;s gitgub [https://jtomori.github.io/vex_tutorial/ vex tutorial/code] and [https://www.youtube.com/@seals77/videos youtube] channel&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Howiem&amp;#039;s&amp;#039;&amp;#039;&amp;#039; [http://howiem.com/wordpress/ blog] with code, CHOPs and various hardware hacking.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Houdini Gubbins&amp;#039;&amp;#039;&amp;#039; [https://houdinigubbins.wordpress.com/ technical blog]. Doesn&amp;#039;t share HIPs, but goes through his ideas, thought processes and implements research papers. Super inspiring stuff !&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Matt Estela&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.tokeru.com/cgwiki/Main_Page cgwiki], aka Our Houdini Jesus :° -- if you landed on my wiki you&amp;#039;ve probably seen him. Super nice ressource and eve nicer dude, gives away his files too.&lt;br /&gt;
&lt;br /&gt;
* Henry Foster, known as &amp;#039;&amp;#039;&amp;#039;Toadstorm&amp;#039;&amp;#039;&amp;#039; has a [http://www.toadstorm.com/blog/ blog], and also created [https://www.motionoperators.com/ MOPs], which tries to make Motion Design life more like Cinema4D (ie friendlier)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;John Kunz&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://wiki.johnkunz.com/index.php?title=Main_Page wiki], and associated [https://www.youtube.com/@JohnKunz/videos youtube] account with top Notch recorded streams.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Jake Rice&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://jakerice.design/blog/ blog] (not updated recently)&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Probiner&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [http://probiner.xyz/logs/ website] (not updated much anymore)&lt;br /&gt;
&lt;br /&gt;
* Eetu Martola&amp;#039;s [https://dailyhip.wordpress.com/ blog] (last update 2021)&lt;br /&gt;
&lt;br /&gt;
* Sam Hancock&amp;#039;s [https://ihoudini.blogspot.fr/ blogspot] (last update 2020 but interesting articles nonetheless)&lt;br /&gt;
&lt;br /&gt;
* Technische Universität Berlin&amp;#039;s [http://dgd.service.tu-berlin.de/wordpress/houdini/ wordpress] &amp;#039;for Mathematicians&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Rich Lord&amp;#039;&amp;#039;&amp;#039;&amp;#039;s [https://www.richlord.com/ website], full of downloadable goodies (creatures made with constraints!) It had disappeared from the web glad to see it&amp;#039;s back up.&lt;br /&gt;
&lt;br /&gt;
* https://hakeemadam.info/procedural-tools&lt;br /&gt;
&lt;br /&gt;
* -------- TODO sort the rest of the list --------------&lt;br /&gt;
&lt;br /&gt;
* https://sites.google.com/site/fujitarium/Houdini/useful-expressions-houdini&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
* https://houdinigubbins.wordpress.com&lt;br /&gt;
&lt;br /&gt;
* http://pepefx.blogspot.fr/&lt;br /&gt;
&lt;br /&gt;
* http://www.deborahrfowler.com/HoudiniResources/HoudiniTipsAndTricks.html&lt;br /&gt;
&lt;br /&gt;
* http://www.preset.de/2007/0711/lorenz/&lt;br /&gt;
&lt;br /&gt;
* http://www.3daet.com/cat/27/houdini/&lt;br /&gt;
&lt;br /&gt;
* Python: http://wiki.dreamsteep.com/Pythonhoudini&lt;br /&gt;
&lt;br /&gt;
* https://www.andynicholas.com&lt;br /&gt;
&lt;br /&gt;
* http://dansportfolio.com/wordpress/&lt;br /&gt;
&lt;br /&gt;
* https://houdininote.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* http://lab.ikoon.cz/&lt;br /&gt;
&lt;br /&gt;
* https://lewisinthelandofmachines.tumblr.com/&lt;br /&gt;
&lt;br /&gt;
* https://www.keatonwilliamson.com/houdini&lt;br /&gt;
&lt;br /&gt;
* Attributes cheatsheet: http://mrkunz.com/blog/08_22_2018_VEX_Wrangle_Cheat_Sheet.html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Nifty links ===&lt;br /&gt;
&lt;br /&gt;
* Discord servers: [https://discord.gg/723NGrShdm Think Procedural]  and Houdini&amp;amp;Chill (it&amp;#039;s invite based, but worth it if you&amp;#039;re worth it); plus it has system to &amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;[http://fx-td.com/houdiniandchill/ publish the best (most liked) posts]&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039; on the discord&lt;br /&gt;
&lt;br /&gt;
* https://www.reddit.com/r/Houdini/ I&amp;#039;m there daily and I like the fact that answers there will be crawlable by search engines (contrary to discord servers).&lt;br /&gt;
&lt;br /&gt;
* [https://mbaadsgaard.com/portfolio/laplacian-eigenvector-plugin-for-houdini/ HDK tutorial] on eigenvectors&lt;br /&gt;
&lt;br /&gt;
* https://inria.hal.science/hal-02541299/file/DeformerElastic-rigid_paper.pdf paper to implement&lt;br /&gt;
&lt;br /&gt;
=== Plug-ins/tools ===&lt;br /&gt;
&lt;br /&gt;
* To link: MOPs qLib OD &lt;br /&gt;
&lt;br /&gt;
* https://momme.gumroad.com&lt;br /&gt;
&lt;br /&gt;
== Flips ==&lt;br /&gt;
Basic setup:&lt;br /&gt;
&lt;br /&gt;
 http://i.imgur.com/cJpBIsk.png&lt;br /&gt;
&lt;br /&gt;
* better quality: more points (smaller point separatation) or decrease grid scale&lt;br /&gt;
* reseed particle in flipsolver: nice for droplets but /!\ particle IDs change&lt;br /&gt;
&lt;br /&gt;
==Expressions Vex &amp;amp; General Syntax==&lt;br /&gt;
[[Houdini_VEX]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$NPT number of points&lt;br /&gt;
$CEX $CEY $CEZ centroid&lt;br /&gt;
$FF frame number &lt;br /&gt;
$T time&lt;br /&gt;
$PT point&lt;br /&gt;
$CR $CB $CG color&lt;br /&gt;
&lt;br /&gt;
$OS use node name (in groups for ex)&lt;br /&gt;
&lt;br /&gt;
$CY copy number in copy node, use for placement w/o having to stamp&lt;br /&gt;
&lt;br /&gt;
`$OS`_`$OBJID` give objects nynamic names &lt;br /&gt;
opdigits($NAME)&lt;br /&gt;
&lt;br /&gt;
opinputpath(&amp;quot;sopnode&amp;quot;,inputN) get the incoming connection (0, 1...)&lt;br /&gt;
op:`opinputpath(&amp;quot;../&amp;quot;,1)`      ---&amp;gt; get full path of input 2 of parent&lt;br /&gt;
nprims(&amp;quot;../sort1&amp;quot;)       ----&amp;gt; number of primitives&lt;br /&gt;
$TEMP/houdiniCache/simdata.`padzero(4,if($F&amp;gt;70,70,$F))`.simdata   -----&amp;gt; read from cache, hold at a certain frame&lt;br /&gt;
&lt;br /&gt;
$OS.`substr(chs(&amp;quot;camera&amp;quot;),rindex(chs(&amp;quot;camera&amp;quot;), &amp;quot;/&amp;quot;)+1,200)`      ------&amp;gt; get the name of the camera if it&amp;#039;s a complicated name with lots of / / / &lt;br /&gt;
------------------------------------------------------------&lt;br /&gt;
in vex no uppercase:&lt;br /&gt;
v@Cd&lt;br /&gt;
i@id&lt;br /&gt;
set()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Print to console with precision===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
printf(&amp;quot;Point %*.*g\n&amp;quot;, 10, 10, @value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Expressions dump ===&lt;br /&gt;
On a switch; switches between inputs if first input is an empty vdb (fix for Maxwell&amp;#039;s shitty VDB implementation) - the second input being an &amp;#039;empty&amp;#039; but existing vdb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if(prim(opinputpath(&amp;quot;.&amp;quot;,0),0,&amp;quot;file_voxel_count&amp;quot;,0)!=0,1,0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Shortcuts==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
general / viewport&lt;br /&gt;
---------------------&lt;br /&gt;
ctrl-b: full window&lt;br /&gt;
h: center&lt;br /&gt;
d: display options&lt;br /&gt;
p: show params&lt;br /&gt;
space-shift-h: center on object&lt;br /&gt;
h: center on grid/frame current selection&lt;br /&gt;
w: wireframe&lt;br /&gt;
left/right arrows =prev/next frame&lt;br /&gt;
ctrl-left arrow: go to first frame&lt;br /&gt;
&lt;br /&gt;
node editor&lt;br /&gt;
---------------------&lt;br /&gt;
u/i: up down hierachy&lt;br /&gt;
c: set color&lt;br /&gt;
ctrl-click on node blue bit: becomes purple = final output&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
animation&lt;br /&gt;
---------------------&lt;br /&gt;
alt on param boxes = keyframe&lt;br /&gt;
alt on param name = key x y z&lt;br /&gt;
&lt;br /&gt;
chops:&lt;br /&gt;
---------------------&lt;br /&gt;
d show points on curve&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
graph editor:&lt;br /&gt;
---------------------&lt;br /&gt;
shift-lmb: access graph editor&lt;br /&gt;
up down arrows: play forward&lt;br /&gt;
left right: prev next frame&lt;br /&gt;
v-h: zoom vertically/horizontally&lt;br /&gt;
j: show whole timeline&lt;br /&gt;
k: key all&lt;br /&gt;
g: group keys &lt;br /&gt;
&lt;br /&gt;
* drag &amp;amp; drop parameters&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
vopsop&lt;br /&gt;
----------------------&lt;br /&gt;
r: reverseinputs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==POPNET==&lt;br /&gt;
* birthgroup: group of particles that were just borne, lasts 1 frame&lt;br /&gt;
&lt;br /&gt;
==DOPNET==&lt;br /&gt;
* can inherit params&lt;br /&gt;
* 3 solvers, bullet = speed&lt;br /&gt;
* &amp;#039;activate&amp;#039; node to do bullet time&lt;br /&gt;
* shelf tool always update latest created dopnet (&amp;#039;set always&amp;#039;)&lt;br /&gt;
* clones for active object = active creation with modulo&lt;br /&gt;
&lt;br /&gt;
== General == &lt;br /&gt;
* drag and drop nodes to get path names&lt;br /&gt;
&lt;br /&gt;
=== Color Schemes ===&lt;br /&gt;
* Red: out geo&lt;br /&gt;
* Purple: VOP_name&lt;br /&gt;
* Yellow: creation/merge&lt;br /&gt;
* Blue: POP DOP&lt;br /&gt;
* Light Green: Pre split&lt;br /&gt;
&lt;br /&gt;
==Volumes==&lt;br /&gt;
&lt;br /&gt;
* Volume from inside as well as surface:&lt;br /&gt;
&lt;br /&gt;
* add a &amp;#039;volume from points&amp;#039; &amp;gt; &amp;#039; stamp points &amp;#039; before volume object&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* with volume dops: use curl noise --&amp;gt; 4d  with vector4 to do a time noise&lt;br /&gt;
&lt;br /&gt;
* displace noise: use shader, dig into &amp;#039;fireball&amp;#039; shader&lt;br /&gt;
&lt;br /&gt;
== Wtf ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
${OS}_new_pieces&lt;br /&gt;
pillar_?   (group)&lt;br /&gt;
@Cd&lt;br /&gt;
$VALUE&lt;br /&gt;
vector vexcode&lt;br /&gt;
`interpreted text`&lt;br /&gt;
op:opinputpath&lt;br /&gt;
chramp(values)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== rendering ==&lt;br /&gt;
* get motion blur on objects : geometry velocity motion blur&lt;br /&gt;
* plug stuff in a premade mantra surface (displace along normal, diffuse etc...)&lt;br /&gt;
&lt;br /&gt;
== Chops ==&lt;br /&gt;
* right click anim channels to add motion effects&lt;br /&gt;
* expressions: $I = ptnum, for curves&lt;br /&gt;
* object merge to grab channels&lt;br /&gt;
* multiply: order is important&lt;br /&gt;
* lookup: timeshift&lt;br /&gt;
* at Geo level, to get chop value: chop(&amp;quot;../CHOPNET/out/ty0&amp;quot;) or chopf&lt;br /&gt;
* recording mouse keyboard: keyboard - mouse -&amp;gt; record, hit &amp;#039;&amp;#039;&amp;#039;scroll lock&amp;#039;&amp;#039;&amp;#039; to disable shortcuts&lt;br /&gt;
&lt;br /&gt;
==l-systems==&lt;br /&gt;
http://algorithmicbotany.org/papers/&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proba: &amp;#039;:&amp;#039;&lt;br /&gt;
rules&lt;br /&gt;
function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==nodes==&lt;br /&gt;
&lt;br /&gt;
point replicate : multiply points&lt;br /&gt;
&lt;br /&gt;
attribute promote : transfer from point &amp;lt;&amp;gt; primitives&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Render Stuff and Cache Geo and Alembic ==&lt;br /&gt;
&lt;br /&gt;
* wedge: change node values an filenames with $WEDGENUM&lt;br /&gt;
&lt;br /&gt;
* fetch: renders specific ROPs, can be daisy-chained with a merge and (node to node)&lt;br /&gt;
&lt;br /&gt;
* multiple shapes in maya with alembic: in Houdini, need a primitive with strings called &amp;#039;name&amp;#039;, the names will be shape names, in the alembic exporter, make sur to choose Partition Mode: Use Combination Of Transform/Shape Node and choose previously created &amp;#039;name&amp;#039; as Attribute (also Ogawa format - ?)&lt;br /&gt;
&lt;br /&gt;
== Fur/hair ==&lt;br /&gt;
* put RBD solver before Wire solver in merge otherwise = bug&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Python ==&lt;br /&gt;
&lt;br /&gt;
[[Houdini_Python]]&lt;br /&gt;
&lt;br /&gt;
= Howto&amp;#039;s / Tricks =&lt;br /&gt;
* Wedges and HQUEUE:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/OjEpvpv.png&lt;br /&gt;
&lt;br /&gt;
* stop stepping: disable integer in frame options, add timeblend&lt;br /&gt;
&lt;br /&gt;
* using &amp;#039;rest&amp;#039; to get more interesting fractures&lt;br /&gt;
&lt;br /&gt;
* restpos: can be used in SHOPS to get object space&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/pZLGdrn.jpg&lt;br /&gt;
&lt;br /&gt;
* instances: instancepoint() w/ full point instancing --&amp;gt; packed disk primitives = refs = light&lt;br /&gt;
&lt;br /&gt;
* instances materials: &amp;quot;declare materials&amp;gt;declare all shops&amp;quot; to export shaders to instances (add using paramet interface window)&lt;br /&gt;
&lt;br /&gt;
* triangulate a whole object cleanly using foreach primitive &lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/0KZYipI.jpg&lt;br /&gt;
&lt;br /&gt;
* On crashes: load with &amp;#039;Manual&amp;#039; and hit escape so nodes aren&amp;#039;t read (if file is corrupt for ever)&lt;br /&gt;
&lt;br /&gt;
* Blend between slowed down continuous mesh:&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/eduhgBs.png&lt;br /&gt;
&lt;br /&gt;
* RBD Point Object from sequence from [http://forums.odforce.net/topic/16560-rbd-point-object-problem/?do=findComment&amp;amp;comment=101371 odforce]:&lt;br /&gt;
** add int attr on points (call it &amp;#039;shape&amp;#039;)&lt;br /&gt;
** write RBDs to disk&lt;br /&gt;
** in RBD point object override point value geometry path: /path/to/geo.*.bgeo&lt;br /&gt;
** &amp;#039;allow editing of contents&amp;#039; in RBD point object, dive inside fin &amp;#039;sopgeo2&amp;#039;, add a stamp OBJID, $OBJID, disable &amp;#039;Use External SOP&amp;#039;&lt;br /&gt;
** dive inside sopgeo, add a read file, and replace * from RBD point object to proper file path: `strreplace(chs(&amp;quot;../../geopath&amp;quot;), &amp;quot;*&amp;quot;, point(&amp;quot;/obj/gridscatter/OUT&amp;quot;, stamp(&amp;quot;..&amp;quot;, &amp;quot;OBJID&amp;quot;, -1), &amp;quot;shape&amp;quot;, 0))`&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Parenting: at object level or using &amp;#039;Rivet&amp;#039; for deforming geo&lt;br /&gt;
&lt;br /&gt;
* Group according to mesh connectivity: use &amp;#039;Island 1&amp;#039; &amp;#039;Island 2&amp;#039; etc... &lt;br /&gt;
&lt;br /&gt;
* If you get &amp;#039;unable to initialize UV rendering module with camera&amp;#039; when baking textures, add a new cam to your scene (cam1)&lt;br /&gt;
&lt;br /&gt;
* If you are using maxwell and alembic (abc) files to render particles (realflow plug-in)  you NEED a vector v and int id attributes otherwise it won&amp;#039;t render&lt;br /&gt;
&lt;br /&gt;
* Fluid simulation: collision reversed ? Use a vdb with a static solver, and reverse the vdb with a volume wrangle @surface *= -1&lt;br /&gt;
&lt;br /&gt;
[[Category:Houdini]]&lt;br /&gt;
&lt;br /&gt;
* Why is my motion blur not working ? You need &amp;#039;v&amp;#039; and to check &amp;#039;geometry velocity blur&amp;#039; on your object&lt;br /&gt;
&lt;br /&gt;
* I still have a hard time with vex code rotations/matrices even with matt estela&amp;#039;s wiki so VOP quicktip to rotate normals around an edge:&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/RcrtESH.png&lt;br /&gt;
&lt;br /&gt;
=Specific Forum/Blog/Wiki articles=&lt;br /&gt;
===Modeling===&lt;br /&gt;
* [https://www.sidefx.com/forum/topic/44816/| Remeshing with external software] (quads, instant mesh) - lkruel&lt;br /&gt;
===Vex===&lt;br /&gt;
* [http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#Example:_Rotation| Understanding rotations] ([http://www.tokeru.com/cgwiki/index.php?title=HoudiniVex#More_on_rotation:_Orient.2C_Quaternions.2C_Matricies.2C_Offsets.2C_stuff| 2]) in houdini.... (vex, quaternions) - mestela&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Color_(Aces,_LUTs,_Gamma_etc)&amp;diff=864</id>
		<title>Color (Aces, LUTs, Gamma etc)</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Color_(Aces,_LUTs,_Gamma_etc)&amp;diff=864"/>
		<updated>2025-08-22T10:34:17Z</updated>

		<summary type="html">&lt;p&gt;Bernie: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Octane Guides ===&lt;br /&gt;
&lt;br /&gt;
https://www.behance.net/scottbenson#&lt;br /&gt;
&lt;br /&gt;
For C4D but applies everywhere.&lt;br /&gt;
&lt;br /&gt;
=== Important Links ===&lt;br /&gt;
ACES, OpenColorio, Octane, theory https://skientia.co/cgi&lt;br /&gt;
&lt;br /&gt;
https://chrisbrejon.com/articles/ocio-display-transforms-and-misconceptions/&lt;br /&gt;
&lt;br /&gt;
The Hitchhiker&amp;#039;s Guide to Digital Color (Sobotka) https://hg2dc.com/&lt;br /&gt;
&lt;br /&gt;
=== TBR (to be read) ===&lt;br /&gt;
&lt;br /&gt;
https://www.reddit.com/r/colorists/comments/1202vvi/100_dcip3_laptop_screen_got_questions/&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Margatron&lt;br /&gt;
&lt;br /&gt;
sRGB has the same white point as rec709 (D65), but it is gamma 2.2, whereas rec709 is gamma 2.4. P3 DCI has a warmer white point (D63) and is gamma 2.6. This is because in a theatre, it&amp;#039;s a darker viewing environment and reflected light rather than screen light. So your eyes adjust according to the environment, and then it &amp;quot;looks&amp;quot; the same as a screen.&lt;br /&gt;
&lt;br /&gt;
If you&amp;#039;re used to sRGB and need to output that colour space, then I would keep your screen set that way. Unless you&amp;#039;re making DCPs for a theatre, I don&amp;#039;t really see the need for a DCI screen. All your outputs will look dark and cooler as soon as you see it on an sRGB screen.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DigitalFilmMonkey&lt;br /&gt;
&lt;br /&gt;
A 2.6 EOTF (the correct term for screen gamma) is darker than a 2.4 EOTF... 2.2 is brighter. The green white point is irrelevant, as compensation is made to make sure whites remain visually D65. Actually, DCI P3 D65 is now commonly used.&lt;br /&gt;
&lt;br /&gt;
Also, Rec709 is not 100nits. A grade-1 display is specified at 100 nits for grading, but Rec709 has no such specification.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=863</id>
		<title>Afx Javascript</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Afx_Javascript&amp;diff=863"/>
		<updated>2025-08-13T18:26:02Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* List selected layers effects and their properties matchNames */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Scripts==&lt;br /&gt;
=== Copy footage to local folder as proxy===&lt;br /&gt;
https://i.imgur.com/oZ24pac.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* &lt;br /&gt;
 * simpleLocalProxy.jsx v1.0&lt;br /&gt;
 *&lt;br /&gt;
 * Copies footage from its current location to one chosen by the user (defaults to /tmp/) and allows to&lt;br /&gt;
 * switch original-proxy with a button&lt;br /&gt;
 * 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&amp;#039; file cache on scratch disks (&amp;#039;conformed media&amp;#039;)&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 * https://github.com/berniebernie/after-effects-scripts&lt;br /&gt;
 *    &lt;br /&gt;
 * Copyright 2015, bernie@berniebernie.fr&lt;br /&gt;
 *    &lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT  &lt;br /&gt;
 *&lt;br /&gt;
 * This script embeds js-md5 from github: https://github.com/emn178/js-md5 for practical reasons&lt;br /&gt;
 *&lt;br /&gt;
 * Script that can be launched or put in the scriptui folder of After Effects to be used as a panel&lt;br /&gt;
 *  &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * js-md5 v0.1.2&lt;br /&gt;
 * https://github.com/emn178/js-md5&lt;br /&gt;
 *&lt;br /&gt;
 * Copyright 2014, emn178@gmail.com&lt;br /&gt;
 *&lt;br /&gt;
 * Licensed under the MIT license:&lt;br /&gt;
 * http://www.opensource.org/licenses/MIT&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      js-md5.js&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//(function(root, undefined){&lt;br /&gt;
  //&amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  var HEX_CHARS = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
  var HEX_TABLE = {&lt;br /&gt;
    &amp;#039;0&amp;#039;: 0, &amp;#039;1&amp;#039;: 1, &amp;#039;2&amp;#039;: 2, &amp;#039;3&amp;#039;: 3, &amp;#039;4&amp;#039;: 4, &amp;#039;5&amp;#039;: 5, &amp;#039;6&amp;#039;: 6, &amp;#039;7&amp;#039;: 7, &amp;#039;8&amp;#039;: 8, &amp;#039;9&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;a&amp;#039;: 10, &amp;#039;b&amp;#039;: 11, &amp;#039;c&amp;#039;: 12, &amp;#039;d&amp;#039;: 13, &amp;#039;e&amp;#039;: 14, &amp;#039;f&amp;#039;: 15,&lt;br /&gt;
    &amp;#039;A&amp;#039;: 10, &amp;#039;B&amp;#039;: 11, &amp;#039;C&amp;#039;: 12, &amp;#039;D&amp;#039;: 13, &amp;#039;E&amp;#039;: 14, &amp;#039;F&amp;#039;: 15&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var R = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,&lt;br /&gt;
           5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,&lt;br /&gt;
           4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,&lt;br /&gt;
           6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];&lt;br /&gt;
&lt;br /&gt;
  var K = [0XD76AA478, 0XE8C7B756, 0X242070DB, 0XC1BDCEEE,&lt;br /&gt;
           0XF57C0FAF, 0X4787C62A, 0XA8304613, 0XFD469501,&lt;br /&gt;
           0X698098D8, 0X8B44F7AF, 0XFFFF5BB1, 0X895CD7BE,&lt;br /&gt;
           0X6B901122, 0XFD987193, 0XA679438E, 0X49B40821,&lt;br /&gt;
           0XF61E2562, 0XC040B340, 0X265E5A51, 0XE9B6C7AA,&lt;br /&gt;
           0XD62F105D, 0X02441453, 0XD8A1E681, 0XE7D3FBC8,&lt;br /&gt;
           0X21E1CDE6, 0XC33707D6, 0XF4D50D87, 0X455A14ED,&lt;br /&gt;
           0XA9E3E905, 0XFCEFA3F8, 0X676F02D9, 0X8D2A4C8A,&lt;br /&gt;
           0XFFFA3942, 0X8771F681, 0X6D9D6122, 0XFDE5380C,&lt;br /&gt;
           0XA4BEEA44, 0X4BDECFA9, 0XF6BB4B60, 0XBEBFBC70,&lt;br /&gt;
           0X289B7EC6, 0XEAA127FA, 0XD4EF3085, 0X04881D05,&lt;br /&gt;
           0XD9D4D039, 0XE6DB99E5, 0X1FA27CF8, 0XC4AC5665,&lt;br /&gt;
           0XF4292244, 0X432AFF97, 0XAB9423A7, 0XFC93A039,&lt;br /&gt;
           0X655B59C3, 0X8F0CCC92, 0XFFEFF47D, 0X85845DD1,&lt;br /&gt;
           0X6FA87E4F, 0XFE2CE6E0, 0XA3014314, 0X4E0811A1,&lt;br /&gt;
           0XF7537E82, 0XBD3AF235, 0X2AD7D2BB, 0XEB86D391];&lt;br /&gt;
&lt;br /&gt;
  var jsmd5 = function(message) {&lt;br /&gt;
    var blocks = hasUTF8(message) ? UTF8toBlocks(message) : ASCIItoBlocks(message);&lt;br /&gt;
    var h0 = 0x67452301;&lt;br /&gt;
    var h1 = 0xEFCDAB89;&lt;br /&gt;
    var h2 = 0x98BADCFE;&lt;br /&gt;
    var h3 = 0x10325476;&lt;br /&gt;
&lt;br /&gt;
    for(var i = 0, length = blocks.length;i &amp;lt; length;i += 16)&lt;br /&gt;
    {&lt;br /&gt;
      var a = h0;&lt;br /&gt;
      var b = h1;&lt;br /&gt;
      var c = h2;&lt;br /&gt;
      var d = h3;&lt;br /&gt;
      var f, g, tmp, x, y;&lt;br /&gt;
&lt;br /&gt;
      for(var j = 0;j &amp;lt; 64;++j)&lt;br /&gt;
      {&lt;br /&gt;
        if(j &amp;lt; 16)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (b &amp;amp; c) | ((~b) &amp;amp; d);&lt;br /&gt;
          f = d ^ (b &amp;amp; (c ^ d));&lt;br /&gt;
          g = j;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 32)&lt;br /&gt;
        {&lt;br /&gt;
          // f = (d &amp;amp; b) | ((~d) &amp;amp; c);&lt;br /&gt;
          f = c ^ (d &amp;amp; (b ^ c));&lt;br /&gt;
          g = (5 * j + 1) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else if(j &amp;lt; 48)&lt;br /&gt;
        {&lt;br /&gt;
          f = b ^ c ^ d;&lt;br /&gt;
          g = (3 * j + 5) % 16;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          f = c ^ (b | (~d));&lt;br /&gt;
          g = (7 * j) % 16;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        tmp = d;&lt;br /&gt;
        d = c&lt;br /&gt;
        c = b&lt;br /&gt;
&lt;br /&gt;
        // leftrotate&lt;br /&gt;
        x = (a + f + K[j] + blocks[i + g]);&lt;br /&gt;
        y = R[j];&lt;br /&gt;
        b += (x &amp;lt;&amp;lt; y) | (x &amp;gt;&amp;gt;&amp;gt; (32 - y));&lt;br /&gt;
        a = tmp;&lt;br /&gt;
      }&lt;br /&gt;
      h0 = (h0 + a) | 0;&lt;br /&gt;
      h1 = (h1 + b) | 0;&lt;br /&gt;
      h2 = (h2 + c) | 0;&lt;br /&gt;
      h3 = (h3 + d) | 0;&lt;br /&gt;
    }&lt;br /&gt;
    return toHexString(h0) + toHexString(h1) + toHexString(h2) + toHexString(h3);&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var toHexString = function(num) {&lt;br /&gt;
    var hex = &amp;quot;&amp;quot;;&lt;br /&gt;
    for(var i = 0; i &amp;lt; 4; i++)&lt;br /&gt;
    {&lt;br /&gt;
      var offset = i &amp;lt;&amp;lt; 3;&lt;br /&gt;
      hex += HEX_CHARS.charAt((num &amp;gt;&amp;gt; (offset + 4)) &amp;amp; 0x0F) + HEX_CHARS.charAt((num &amp;gt;&amp;gt; offset) &amp;amp; 0x0F);&lt;br /&gt;
    }&lt;br /&gt;
    return hex;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var hasUTF8 = function(message) {&lt;br /&gt;
    var i = message.length;&lt;br /&gt;
    while(i--)&lt;br /&gt;
      if(message.charCodeAt(i) &amp;gt; 127)&lt;br /&gt;
        return true;&lt;br /&gt;
    return false;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var ASCIItoBlocks = function(message) {&lt;br /&gt;
    // a block is 32 bits(4 bytes), a chunk is 512 bits(64 bytes)&lt;br /&gt;
    var length = message.length;&lt;br /&gt;
    var chunkCount = ((length + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    var i;&lt;br /&gt;
    for(i = 0;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    for(i = 0;i &amp;lt; length;++i)&lt;br /&gt;
      blocks[i &amp;gt;&amp;gt; 2] |= message.charCodeAt(i) &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[i &amp;gt;&amp;gt; 2] |= 0x80 &amp;lt;&amp;lt; ((i % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    blocks[blockCount - 2] = length &amp;lt;&amp;lt; 3; // length * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  var UTF8toBlocks = function(message) {&lt;br /&gt;
    var uri = encodeURIComponent(message);&lt;br /&gt;
    var blocks = [];&lt;br /&gt;
    for(var i = 0, bytes = 0, length = uri.length;i &amp;lt; length;++i)&lt;br /&gt;
    {&lt;br /&gt;
      var c = uri.charCodeAt(i);&lt;br /&gt;
      if(c == 37) // %&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= ((HEX_TABLE[uri.charAt(++i)] &amp;lt;&amp;lt; 4) | HEX_TABLE[uri.charAt(++i)]) &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      else&lt;br /&gt;
        blocks[bytes &amp;gt;&amp;gt; 2] |= c &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
      ++bytes;&lt;br /&gt;
    }&lt;br /&gt;
    var chunkCount = ((bytes + 8) &amp;gt;&amp;gt; 6) + 1;&lt;br /&gt;
    var blockCount = chunkCount &amp;lt;&amp;lt; 4; // chunkCount * 16&lt;br /&gt;
    var index = bytes &amp;gt;&amp;gt; 2;&lt;br /&gt;
    blocks[index] |= 0x80 &amp;lt;&amp;lt; ((bytes % 4) &amp;lt;&amp;lt; 3);&lt;br /&gt;
    for(var i = index + 1;i &amp;lt; blockCount;++i)&lt;br /&gt;
      blocks[i] = 0;&lt;br /&gt;
    blocks[blockCount - 2] = bytes &amp;lt;&amp;lt; 3; // bytes * 8&lt;br /&gt;
    return blocks;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  /*if(typeof(module) != &amp;#039;undefined&amp;#039;)&lt;br /&gt;
    module.exports = jsmd5;&lt;br /&gt;
  else if(root)&lt;br /&gt;
    root.jsmd5 = jsmd5;*/&lt;br /&gt;
//}(this));&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/************************************************************************************************************&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *      simpleLocalProxy.jsx &lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 *&lt;br /&gt;
 ************************************************************************************************************/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(str){&lt;br /&gt;
    //uncomment to allow debugging&lt;br /&gt;
    //$.writeln(str);&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pathToLocalizedPath(path){&lt;br /&gt;
        f = new File(path);&lt;br /&gt;
        return f.fsName.toString();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sequenceFilesWildcard(path){ &lt;br /&gt;
    //returns false or an array with everything before a sequence&amp;#039;s image number, and the extension: /c/path/file.0555.exr &amp;gt; { /c/pathfile. ; .exr }&lt;br /&gt;
    var myRegexp = /(.*[\.\-_a-z])[\d]{1,}(\.[a-zA-Z]*)$/g; &lt;br /&gt;
    var match = myRegexp.exec(path);&lt;br /&gt;
    if(!match){&lt;br /&gt;
        return false;&lt;br /&gt;
    }else{&lt;br /&gt;
        return match;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabPaths(footage){&lt;br /&gt;
    //returns false or the path of the given footage, if it&amp;#039;s a file sequence, returns the path with a wildcard for the current frame number: /c/path/file.0555.exr &amp;gt; /c/path/file.*.exr&lt;br /&gt;
    var f = footage;&lt;br /&gt;
    returnpath = false;&lt;br /&gt;
    if(f instanceof FootageItem &amp;amp;&amp;amp; f.file != null){       &lt;br /&gt;
        var source = f.mainSource.file.toString();&lt;br /&gt;
        if(!f.mainSource.isStill){&lt;br /&gt;
            var pathFromRegex = sequenceFilesWildcard(source);&lt;br /&gt;
            if(pathFromRegex){&lt;br /&gt;
                returnpath = pathFromRegex[1]+&amp;quot;*&amp;quot;+pathFromRegex[2];&lt;br /&gt;
            }else{&lt;br /&gt;
                returnpath = source;&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            returnpath = source;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return returnpath;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function grabFootagePathsAndCopyToLocal(){&lt;br /&gt;
    //main worker function&lt;br /&gt;
    &lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1 || !(sel[0] instanceof FootageItem)){&lt;br /&gt;
        alert(&amp;quot;Select footage(s) and try again&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        &lt;br /&gt;
        var debugcheck = (getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var useforcecopy = (getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;)==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        &lt;br /&gt;
        var isMacintosh = ($.os.toLowerCase().indexOf(&amp;quot;windows&amp;quot;)==-1);&lt;br /&gt;
        var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        &lt;br /&gt;
        var batchFile = (isMacintosh)?&amp;quot;# bash file used to copy After Effects footage to local storage&amp;quot;:&amp;quot;@echo off\nREM batch file to copy After Effects footage to local storage&amp;quot;;&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            if(!sel[i].useProxy){&lt;br /&gt;
                //grab path from current selection item&lt;br /&gt;
                var curPath = grabPaths(sel[i]);&lt;br /&gt;
                var fileName = curPath.split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                &lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
            &lt;br /&gt;
                if(isMacintosh){&lt;br /&gt;
                    //macos uses rsync to copy files&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrsync -v -a &amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot;-I &amp;quot;:&amp;quot;&amp;quot;);&lt;br /&gt;
                    batchFile += dir+fileName;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;+outputDir;&lt;br /&gt;
                    &lt;br /&gt;
                }else{&lt;br /&gt;
                    &lt;br /&gt;
                    //windows uses robocopy&lt;br /&gt;
                    sourcePath = pathToLocalizedPath(dir);&lt;br /&gt;
                    destinationPath = pathToLocalizedPath(outputDir);&lt;br /&gt;
                    filename = fileName.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
                    &lt;br /&gt;
                    batchFile += &amp;quot;\nrobocopy &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += sourcePath;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    //robocopy filename requires a wildcard, even if it&amp;#039;s a single file&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += destinationPath ;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;                    &lt;br /&gt;
                    batchFile += &amp;quot; &amp;quot;;&lt;br /&gt;
                    batchFile += &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += filename ;&lt;br /&gt;
                    batchFile += &amp;quot;*\&amp;quot;&amp;quot;;&lt;br /&gt;
                    batchFile += ((useforcecopy)?&amp;quot; /XO&amp;quot;:&amp;quot;&amp;quot;)+&amp;quot; /FFT&amp;quot;+((debugcheck)?&amp;quot;&amp;quot;:&amp;quot; /NJH /NJS&amp;quot;);&lt;br /&gt;
                    e(batchFile);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        batchFile += ((debugcheck)?(isMacintosh?&amp;quot;\nread a&amp;quot;:&amp;quot;\npause&amp;quot;):&amp;quot;&amp;quot;); //nested tertiary operators, sue me&lt;br /&gt;
        &lt;br /&gt;
        var txtFile;&lt;br /&gt;
        if(isMacintosh){&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.command&amp;quot;);&lt;br /&gt;
        }else{&lt;br /&gt;
            txtFile = new File(localSaveDir+&amp;quot;/AFX_footage_copy.bat&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        if(txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;) == true){&lt;br /&gt;
&lt;br /&gt;
            txtFile.write(batchFile);&lt;br /&gt;
            txtFile.close();&lt;br /&gt;
	    if(isMacintosh){&lt;br /&gt;
&lt;br /&gt;
            		system.callSystem(&amp;quot;chmod +x &amp;quot; + txtFile.toString() +&amp;quot;&amp;quot;);&lt;br /&gt;
system.callSystem(txtFile.toString());&lt;br /&gt;
		}else{&lt;br /&gt;
            txtFile.execute();&lt;br /&gt;
}&lt;br /&gt;
        }else{&lt;br /&gt;
            alert(&amp;quot;Write permission denied on\n&amp;quot;+localSaveDir);&lt;br /&gt;
        }&lt;br /&gt;
        //txtFile.remove();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function grabFootagePathsAndSwitchProxy(){&lt;br /&gt;
    var sel = app.project.selection;&lt;br /&gt;
    if(sel.length &amp;lt; 1){&lt;br /&gt;
        alert(&amp;quot;Select footage first&amp;quot;);    &lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            item = sel[i];&lt;br /&gt;
            if(item.useProxy){&lt;br /&gt;
                item.useProxy = false;&lt;br /&gt;
            }else{&lt;br /&gt;
                var curPath = grabPaths(item);&lt;br /&gt;
                var fileName = item.mainSource.file.toString().split(&amp;#039;/&amp;#039;).pop();&lt;br /&gt;
                var dir = curPath.substring(0,curPath.lastIndexOf(&amp;#039;/&amp;#039;)+1);&lt;br /&gt;
                var localSaveDir = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
                var outputDir = &amp;quot;&amp;quot;;&lt;br /&gt;
                if(getPref(&amp;quot;usemd5&amp;quot;,true)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                    outputDir = localSaveDir + &amp;quot;/&amp;quot;+ jsmd5(dir);&lt;br /&gt;
                }else{&lt;br /&gt;
                    outputDir = localSaveDir + dir;&lt;br /&gt;
                }&lt;br /&gt;
                var proxyFilePath = outputDir+&amp;quot;/&amp;quot;+fileName;&lt;br /&gt;
                var proxyFile = new File(proxyFilePath);&lt;br /&gt;
                e(proxyFilePath);&lt;br /&gt;
                if(proxyFile.exists){&lt;br /&gt;
                    if(item.mainSource.isStill){&lt;br /&gt;
                        item.setProxy(proxyFile);&lt;br /&gt;
                    }else{&lt;br /&gt;
                        //dirty workaround if the file is a movie file (.mpg, qucktime etc...)&lt;br /&gt;
                        try {&lt;br /&gt;
                            item.setProxyWithSequence(proxyFile,false);&lt;br /&gt;
                        }&lt;br /&gt;
                        catch(err) {&lt;br /&gt;
                            item.setProxy(proxyFile);&lt;br /&gt;
                        }   &lt;br /&gt;
                    }&lt;br /&gt;
                    item.proxySource.alphaMode = item.mainSource.alphaMode;&lt;br /&gt;
                    item.proxySource.premulColor = item.mainSource.premulColor;&lt;br /&gt;
                    item.proxySource.invertAlpha= item.mainSource.invertAlpha;&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(getPref(&amp;quot;debug&amp;quot;,false)==&amp;quot;true&amp;quot;){&lt;br /&gt;
                        alert(&amp;quot;&amp;gt;&amp;gt;&amp;gt; NO PROXY FOUND\n&amp;quot;+proxyFilePath);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setPref(pref,value){&lt;br /&gt;
    app.settings.saveSetting(&amp;quot;simpleLocalProxy&amp;quot;, pref, value);&lt;br /&gt;
}&lt;br /&gt;
function getPref(pref,defaultValue){&lt;br /&gt;
    prefsVar = &amp;quot;simpleLocalProxy&amp;quot;;&lt;br /&gt;
    if(app.settings.haveSetting(prefsVar, pref)){&lt;br /&gt;
        return app.settings.getSetting(prefsVar, pref);&lt;br /&gt;
    }else{&lt;br /&gt;
        app.settings.saveSetting(prefsVar, pref, defaultValue);&lt;br /&gt;
        setPref(pref,defaultValue)&lt;br /&gt;
        return defaultValue;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function simpleLocalProxy(thisObj) {&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Local Proxy&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    &lt;br /&gt;
    var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
    if (securitySetting != 1) {&lt;br /&gt;
        pan.add(&amp;quot;statictext&amp;quot;,[15,15,300,45],&amp;quot;Set prefs and re-launch&amp;quot;);&lt;br /&gt;
        alert(&amp;quot;You need to check \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        var localFolder = getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString());&lt;br /&gt;
        localFolder = pathToLocalizedPath(localFolder).replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        // UI DESCRIPTION&lt;br /&gt;
        &lt;br /&gt;
        res = &amp;quot;group { alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                        cols: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            col1: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                saveDirText: StaticText {text: &amp;#039;Proxy folder setup&amp;#039;},\&lt;br /&gt;
                                saveDirOptions: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    usemd5rbox: RadioButton {text: &amp;#039; Simple&amp;#039;,helpTip:&amp;#039;Uses a unique folder name per footage; no subfolders (32 character md5 hashes of filenames)&amp;#039;,value:true},\&lt;br /&gt;
                                    usepathrbox: RadioButton {text: &amp;#039; Copy Folder Structure&amp;#039;,helpTip:&amp;#039;Copies the target folder structure inside the proxy folder; more folders&amp;#039;}}},\&lt;br /&gt;
                            col2: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                optionsText: StaticText {text: &amp;#039;Options: &amp;#039;},\&lt;br /&gt;
                                optionsGrp: Group {orientation:&amp;#039;column&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;center&amp;#039;],\&lt;br /&gt;
                                    forcecopyChkbox: Checkbox {text: &amp;#039; Force copy&amp;#039;,helpTip:&amp;#039;Overwrite files (otherwise skips existing files)&amp;#039;},\&lt;br /&gt;
                                    debugChkbox: Checkbox {text: &amp;#039; Debug&amp;#039;,helpTip:&amp;#039;Show full batch process and pause at end of copies&amp;#039;}}}},\&lt;br /&gt;
                        cols2: Group {orientation:&amp;#039;row&amp;#039;,align:&amp;#039;left&amp;#039;, alignChildren:[&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;],\&lt;br /&gt;
                            localSaveDirBut: Button {text: &amp;#039; Choose Proxy Folder &amp;#039;, helpTip:&amp;#039;choose folder to copy files to&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                            browseBut: Button {text: &amp;#039; Browse &amp;#039; , preferredSize:[-1,30]}} , \&lt;br /&gt;
                        curentDirTxt: EditText {text: &amp;#039;&amp;quot; + localFolder +&amp;quot;&amp;#039;,enabled:false},\&lt;br /&gt;
                        copyFootageBut: Button {text: &amp;#039; Copy Footage(s) to Proxy Folder &amp;#039; ,helpTip:&amp;#039;Launches a background batch copy of selected footage&amp;#039;,preferredSize:[-1,30]} , \&lt;br /&gt;
                        switchproxyBut: Button {text: &amp;#039; Switch Proxy/Original &amp;#039;,helpTip:&amp;#039;Switches from original to proxy path and back,  warning if no local copy has been found&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    }&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        //UI DRAW&lt;br /&gt;
        &lt;br /&gt;
        pan.grp = pan.add(res); &lt;br /&gt;
        pan.layout.layout(true);&lt;br /&gt;
        &lt;br /&gt;
        //radio buttons and checkboxes prefs&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
        var usemd5 = getPref(&amp;quot;usemd5&amp;quot;,true);&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usemd5rbox.value = (usemd5==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        pan.grp.cols.col1.saveDirOptions.usepathrbox.value = (usemd5==&amp;quot;true&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
        var useforcecopy = getPref(&amp;quot;forcecopy&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value = (useforcecopy==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
        var debugcheck = getPref(&amp;quot;debug&amp;quot;,&amp;quot;false&amp;quot;);&lt;br /&gt;
        pan.grp.cols.col2.optionsGrp.debugChkbox.value = (debugcheck==&amp;quot;true&amp;quot;)?true:false;&lt;br /&gt;
&lt;br /&gt;
        pan.layout.resize();&lt;br /&gt;
        pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
&lt;br /&gt;
        // UI ACTIONS&lt;br /&gt;
        &lt;br /&gt;
        //browse button&lt;br /&gt;
        pan.grp.cols2.browseBut.onClick = function(){&lt;br /&gt;
                localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
                localSaveDir.execute();&lt;br /&gt;
        }&lt;br /&gt;
        //choose local folder button&lt;br /&gt;
        pan.grp.cols2.localSaveDirBut.onClick = function(){&lt;br /&gt;
            localSaveDir = new Folder(getPref(&amp;quot;localSaveDir&amp;quot;,Folder.temp.toString()));&lt;br /&gt;
            o = localSaveDir.selectDlg(&amp;quot;Choose folder to copy footage to&amp;quot;);&lt;br /&gt;
            if(o!=null){&lt;br /&gt;
                    setPref(&amp;quot;localSaveDir&amp;quot;,o.toString());&lt;br /&gt;
                    pan.grp.curentDirTxt.text = pathToLocalizedPath(o.toString());//.replace(/\\/g,&amp;quot;\\\\&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
        pan.grp.copyFootageBut.onClick = function(){&lt;br /&gt;
            //copy footages button (launches the &amp;#039;guts&amp;#039; of this script)&lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
            &lt;br /&gt;
            grabFootagePathsAndCopyToLocal();&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        pan.grp.switchproxyBut.onClick = function(){&lt;br /&gt;
            //checks for a local copy of the footage, and switches to a proxy accordingly &lt;br /&gt;
            &lt;br /&gt;
            //save prefs&lt;br /&gt;
            var usemd5 = pan.grp.cols.col1.saveDirOptions.usemd5rbox.value;&lt;br /&gt;
            var useforcecopy = pan.grp.cols.col2.optionsGrp.forcecopyChkbox.value;&lt;br /&gt;
            var debugcheck = pan.grp.cols.col2.optionsGrp.debugChkbox.value;&lt;br /&gt;
            &lt;br /&gt;
            setPref(&amp;quot;usemd5&amp;quot;,usemd5);&lt;br /&gt;
            setPref(&amp;quot;forcecopy&amp;quot;,useforcecopy);&lt;br /&gt;
            setPref(&amp;quot;debug&amp;quot;,debugcheck);&lt;br /&gt;
&lt;br /&gt;
            grabFootagePathsAndSwitchProxy();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (pan instanceof Window) pan.show() ;&lt;br /&gt;
}&lt;br /&gt;
simpleLocalProxy(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Expose Animation ===&lt;br /&gt;
Adds &amp;#039;hold&amp;#039; keyframes to your animation if nothing is moving between frames (ie detects animation) -- better tutorial video TBD.&lt;br /&gt;
&lt;br /&gt;
MAKE SURE TO BE IN 8BITS when doing the detection, turn it back on when finished.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;youtube width=&amp;quot;1000&amp;quot; height=&amp;quot;800&amp;quot; &amp;gt;9-3XVlME2tY&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//save as AutoExpose.jsx in your program files/after effects/scripts/ScriptUI folder or install from the AE ui&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
var currentSlider = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 function watchFolderUI(thisObj){&lt;br /&gt;
    pan = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Auto Expose&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
    var res = &lt;br /&gt;
    &amp;quot;group { \&lt;br /&gt;
                alignment: [&amp;#039;fill&amp;#039;,&amp;#039;fill&amp;#039;], \&lt;br /&gt;
                alignChildren: [&amp;#039;fill&amp;#039;,&amp;#039;top&amp;#039;], \&lt;br /&gt;
                orientation: &amp;#039;column&amp;#039;, \&lt;br /&gt;
                    setupDetector: Button {text: &amp;#039;Step 1: setup detector on layer&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt1: StaticText {text: &amp;#039;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 &amp;#039;Detector&amp;#039; picks up a change (value &amp;gt; 0)&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    bakeKeys: Button {text: &amp;#039;Step 2: bake to keys&amp;#039; ,preferredSize:[-1,30],enabled:true} , \&lt;br /&gt;
                    txt2: StaticText {text: &amp;#039;Bakes detected animation as time remapped keys to animation, this can be long, watch your &amp;quot;Info&amp;quot; panel. If some animation is not detected, change resoltion and run again&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
                    applyKeys: Button {text: &amp;#039;Step 3: apply as time remapping on selected layers&amp;#039; ,preferredSize:[-1,30]} , \&lt;br /&gt;
                    txt3: StaticText {text: &amp;#039;Select layers on which to apply time remapping (\&amp;quot;exposed\&amp;quot; keys). If you want sequential keys instead, turn expression on the time remapping&amp;#039;,properties:{multiline:true}} , \&lt;br /&gt;
            }&amp;quot;;	&lt;br /&gt;
    pan.grp = pan.add(res);        &lt;br /&gt;
    pan.grp.setupDetector.onClick = function () {&lt;br /&gt;
        //pan.grp.bakeKeys.enabled = true;        &lt;br /&gt;
        setupDetector();&lt;br /&gt;
        &lt;br /&gt;
    }&lt;br /&gt;
    pan.grp.bakeKeys.onClick = function () {bakeKeys();}&lt;br /&gt;
    pan.grp.applyKeys.onClick = function () {applyKeys();}&lt;br /&gt;
&lt;br /&gt;
    pan.layout.layout(true);&lt;br /&gt;
    pan.layout.resize();&lt;br /&gt;
    pan.onResizing = pan.onResize = function () {this.layout.resize();}&lt;br /&gt;
    return pan;&lt;br /&gt;
    }&lt;br /&gt;
watchFolderUI(this) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setupDetector(){&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Auto expose setup Detector&amp;quot;);&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    curlayer = layers[0];&lt;br /&gt;
&lt;br /&gt;
    var duplicatelayer = curlayer.duplicate();&lt;br /&gt;
    curlayer.moveBefore(duplicatelayer);&lt;br /&gt;
    var futureprecompindex = duplicatelayer.index;&lt;br /&gt;
    var precomp = app.project.activeItem.layers.precompose([duplicatelayer.index],duplicatelayer.name+&amp;quot;_anim_detection&amp;quot;,true);&lt;br /&gt;
    var precomplayer =  app.project.activeItem.layer(futureprecompindex);&lt;br /&gt;
    precomplayer.guideLayer = true;&lt;br /&gt;
&lt;br /&gt;
    var allLayers = app.project.activeItem.layers;&lt;br /&gt;
    for(i=1;i&amp;lt;=allLayers.length;i++){&lt;br /&gt;
        if(allLayers[i].enabled){&lt;br /&gt;
            allLayers[i].solo = false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    precomplayer.enabled = true;    &lt;br /&gt;
    precomplayer.solo = true;&lt;br /&gt;
&lt;br /&gt;
    var toplayer = precomp.layers[1];&lt;br /&gt;
    var props = toplayer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
    while(props.numProperties&amp;gt;0){&lt;br /&gt;
        props.property(1).remove();&lt;br /&gt;
    }&lt;br /&gt;
    var newlayer = toplayer.duplicate(); &lt;br /&gt;
&lt;br /&gt;
    newlayer.blendingMode = BlendingMode.CLASSIC_DIFFERENCE;&lt;br /&gt;
    newlayer.startTime += app.project.activeItem.frameDuration;&lt;br /&gt;
    newlayer.timeRemapEnabled = true;&lt;br /&gt;
    newlayer.inPoint -= app.project.activeItem.frameDuration;&lt;br /&gt;
&lt;br /&gt;
    var explainer = new MarkerValue(&amp;quot;1 frame shift + difference blendmode = highlight pixel changes&amp;quot;);&lt;br /&gt;
    newlayer.property(&amp;quot;Marker&amp;quot;).setValueAtTime(.5, explainer);&lt;br /&gt;
&lt;br /&gt;
    var blackSolid = precomp.layers.addSolid([0,0,0], &amp;quot;Black&amp;quot;, precomp.width, precomp.height, 1);&lt;br /&gt;
    blackSolid.moveToEnd();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var sliderctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    sliderctrl.name = &amp;quot;Resolution&amp;quot;;&lt;br /&gt;
    var resolutionslider = sliderctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
   &lt;br /&gt;
    resolutionslider.setValue(3);&lt;br /&gt;
&lt;br /&gt;
    var detectorctrl = precomplayer.Effects.addProperty(&amp;quot;ADBE Slider Control&amp;quot;);&lt;br /&gt;
    detectorctrl.name = &amp;quot;Detector&amp;quot;;&lt;br /&gt;
    var detectorslider = detectorctrl.property(&amp;quot;ADBE Slider Control-0001&amp;quot;);&lt;br /&gt;
    detectorexpression = &amp;quot;\&lt;br /&gt;
    resolution = effect(\&amp;quot;Resolution\&amp;quot;)(\&amp;quot;ADBE Slider Control-0001\&amp;quot;);\&lt;br /&gt;
    resolution = (resolution&amp;lt;1)?1:resolution;\&lt;br /&gt;
    a = [0,0,0,0];\&lt;br /&gt;
    for(i=0;i&amp;lt;resolution;i++){\&lt;br /&gt;
        for(j=0;j&amp;lt;resolution;j++){\&lt;br /&gt;
            center = [thisComp.width/resolution/2+thisComp.width/resolution * j ,thisComp.height/resolution/2+thisComp.height/resolution * i ];\&lt;br /&gt;
            sampledistance = [thisComp.width/resolution/2,thisComp.height/resolution/2];\&lt;br /&gt;
            a+= sampleImage(center, sampledistance , postEffect = true, t = time);\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    (a[0]+a[1]+a[2]+a[3])/(resolution*resolution)*10000-10000;\&lt;br /&gt;
    &amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    detectorslider.expression = detectorexpression;&lt;br /&gt;
    currentSlider = detectorslider;&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
function bakeKeys(){&lt;br /&gt;
    &lt;br /&gt;
    // bake slider keys, apply expression to figure out which frames have movement, then bake again to &amp;#039;clean&amp;#039; expression&lt;br /&gt;
    bakeCommand = app.findMenuCommandId(&amp;quot;Convert Expression to Keyframes&amp;quot;);&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    detectorexpression = &amp;quot;f = effect(&amp;#039;Detector&amp;#039;)(&amp;#039;ADBE Slider Control-0001&amp;#039;);\nf&amp;gt;0?1:0;&amp;quot;;&lt;br /&gt;
    currentSlider.expression = detectorexpression;&lt;br /&gt;
    currentSlider.expressionEnabled = true;&lt;br /&gt;
    currentSlider.selected = true;&lt;br /&gt;
    app.executeCommand( bakeCommand );&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
    // travel backwards through keys and remove keys that are == 0&lt;br /&gt;
    // set the the value of kept frames to be that of the time they&amp;#039;re on&lt;br /&gt;
    &lt;br /&gt;
    for(i=currentSlider.numKeys;i&amp;gt;0;i--){&lt;br /&gt;
        if(currentSlider.keyValue(i) &amp;gt; 0){&lt;br /&gt;
            currentSlider.setValueAtKey(i, currentSlider.keyTime(i));&lt;br /&gt;
            currentSlider.setInterpolationTypeAtKey(i,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }else{&lt;br /&gt;
            currentSlider.removeKey(i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // add &amp;#039;0&amp;#039; key on first frame&lt;br /&gt;
    &lt;br /&gt;
    currentSlider.addKey(0);&lt;br /&gt;
    currentSlider.setValueAtKey(1, 0);&lt;br /&gt;
    currentSlider.setInterpolationTypeAtKey(1,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
function applyKeys(){&lt;br /&gt;
    var layers = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for(i = 0;i&amp;lt;layers.length;i++){&lt;br /&gt;
        layers[i].timeRemapEnabled = true;&lt;br /&gt;
        var remap = layers[i].property(&amp;quot;Time Remap&amp;quot;);&lt;br /&gt;
        for(j=1;j&amp;lt;=currentSlider.numKeys;j++){&lt;br /&gt;
            &lt;br /&gt;
            v = currentSlider.keyValue(j);&lt;br /&gt;
            t = currentSlider.keyTime(j);&lt;br /&gt;
            remap.addKey(t);&lt;br /&gt;
            remap.setValueAtKey(j, v);&lt;br /&gt;
            remap.setInterpolationTypeAtKey(j,KeyframeInterpolationType.HOLD,KeyframeInterpolationType.HOLD);&lt;br /&gt;
        }&lt;br /&gt;
        remap.expression = &amp;quot;\&lt;br /&gt;
        //toggle this on to set sequential time remap\&lt;br /&gt;
        a = timeRemap;\&lt;br /&gt;
        nk = a.nearestKey(time);\&lt;br /&gt;
        curframe = 0;\&lt;br /&gt;
        if(nk.time &amp;gt; time){\&lt;br /&gt;
            curframe = nk.index-1;\&lt;br /&gt;
        }else{\&lt;br /&gt;
            curframe = nk.index;\&lt;br /&gt;
        }\&lt;br /&gt;
        curframe*thisComp.frameDuration;&amp;quot;;&lt;br /&gt;
        remap.expressionEnabled = false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create Null controllers on Puppet pins ===&lt;br /&gt;
http://i.imgur.com/HdFjaYZ.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//nulls created from puppet pins can be parented like normal layers&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Create Null Controls on Puppet Pinsv&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    function getLayerFromProperty(prop){&lt;br /&gt;
        return prop.propertyGroup(prop.propertyDepth)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var c = app.project.activeItem;&lt;br /&gt;
    if( c != null &amp;amp;&amp;amp; c.selectedProperties != null){&lt;br /&gt;
        var props = c.selectedProperties;&lt;br /&gt;
        j = 0;&lt;br /&gt;
        for(i = 0;i&amp;lt;props.length;i++){&lt;br /&gt;
            if(props[i].matchName == &amp;quot;ADBE FreePin3 PosPin Atom&amp;quot;){&lt;br /&gt;
                j++;&lt;br /&gt;
                child = props[i].property(&amp;quot;ADBE FreePin3 PosPin Position&amp;quot;);&lt;br /&gt;
                pos = [child.value[0],child.value[1]];&lt;br /&gt;
                nullLayer = app.project.activeItem.layers.addNull();&lt;br /&gt;
                nullLayer.name = &amp;quot;puppetCtrl&amp;quot;+j;&lt;br /&gt;
                nullLayer.position.setValue(pos);&lt;br /&gt;
                var expr = &amp;quot;thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).toWorld(thisComp.layer(\&amp;quot;&amp;quot;+nullLayer.name+&amp;quot;\&amp;quot;).transform.anchorPoint)&amp;quot;;&lt;br /&gt;
                child.expression = expr;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Batch replace file locations with text file===&lt;br /&gt;
http://i.imgur.com/88QHvlQ.jpg&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//now works with sequences or still images, windows only&lt;br /&gt;
//edit nov2017 so we get nicer paths (windows-like c:/file path instead of \c\file%20path&lt;br /&gt;
function URIToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function WinPathtoURI(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/ /g, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    //str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
{&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Change File Locations&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/tempAE.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    txt = &amp;quot;&amp;quot;;&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    var isSequence = new Array();&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
       alert(&amp;quot;Select footage items.&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            isSequence[i] = !sel[i].mainSource.isStill;&lt;br /&gt;
            txt += URIToWinPath(sel[i].mainSource.file.toString())+&amp;quot;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        txtFile.write(&amp;quot;*** 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&amp;quot;);&lt;br /&gt;
        txtFile.write(txt);&lt;br /&gt;
        txtFile.close();&lt;br /&gt;
        txtFile.execute();&lt;br /&gt;
        isOk = confirm(&amp;quot;Change file paths ?\n\nReloading might take a while!&amp;quot;);&lt;br /&gt;
        if(isOk){&lt;br /&gt;
           txtFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
           contents = txtFile.read();&lt;br /&gt;
           arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
           for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
                var tmpFile =  new File(WinPathtoURI(arrayContents[i+2]));&lt;br /&gt;
                //alert(tmpFile);&lt;br /&gt;
                if(isSequence[i]){&lt;br /&gt;
                    sel[i].replaceWithSequence(tmpFile,0);&lt;br /&gt;
                }else{&lt;br /&gt;
                    sel[i].replace(tmpFile);&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                writeLn(Math.round((i+1)/app.project.selection.length*100)+&amp;quot;%&amp;quot;);&lt;br /&gt;
           }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Loop Selected Layers===&lt;br /&gt;
https://i.gyazo.com/82c30ae9dd47a979c727a3b4f30ad617.gif&lt;br /&gt;
&lt;br /&gt;
No hassle looping&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Set loops&amp;quot;);&lt;br /&gt;
var layersList = app.project.activeItem.selectedLayers;&lt;br /&gt;
var frameD = app.project.activeItem.frameDuration;&lt;br /&gt;
for (i=0;i&amp;lt;layersList.length;i++){&lt;br /&gt;
    if(!layersList[i].timeRemapEnabled){&lt;br /&gt;
        var outP = layersList[i].outPoint;&lt;br /&gt;
        layersList[i].timeRemapEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP-frameD,outP-frameD);&lt;br /&gt;
        layersList[i].timeRemap.setValueAtTime(outP,0);&lt;br /&gt;
        layersList[i].timeRemap.expressionEnabled = true;&lt;br /&gt;
        layersList[i].timeRemap.expression = &amp;quot;loopOut()&amp;quot;;&lt;br /&gt;
        layersList[i].outPoint = app.project.activeItem.workAreaStart+app.project.activeItem.workAreaDuration;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&lt;br /&gt;
===Find Next Text Layer===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
    a = true;&lt;br /&gt;
                if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
                }&lt;br /&gt;
    if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
        var comp = app.project.item(i);&lt;br /&gt;
        for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
            a = true;&lt;br /&gt;
            comp.layer(j).selected = false;&lt;br /&gt;
            if(comp.layer(j) instanceof TextLayer){&lt;br /&gt;
                comp.layer(j).selected = true;&lt;br /&gt;
                $.writeln(comp.name);&lt;br /&gt;
                a = confirm(&amp;quot;Continue selecting text layers&amp;quot;,true);&lt;br /&gt;
            }&lt;br /&gt;
            if(!a){&lt;br /&gt;
                break;&lt;br /&gt;
            }else{&lt;br /&gt;
                comp.layer(j).selected = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
if(a){&lt;br /&gt;
    alert(&amp;quot;No (more) text layers found&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Simple Watchfolder===&lt;br /&gt;
http://i.imgur.com/poTWO.png&lt;br /&gt;
https://i.imgur.com/wvtgDqE.png&lt;br /&gt;
&lt;br /&gt;
I know a lot of people use media encoder but it&amp;#039;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&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
WatchFolder.js v1.2&lt;br /&gt;
--------------------&lt;br /&gt;
By bernie @ berniebernie.fr&lt;br /&gt;
&lt;br /&gt;
This script is a simple palette to automatically send your After Effects file to be processed by the watchfolder&lt;br /&gt;
In essence it&amp;#039;s a simplified Collect Files &amp;gt; Project only.&lt;br /&gt;
&lt;br /&gt;
Windows only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Was added:&lt;br /&gt;
- A check for missing footage&lt;br /&gt;
- Some UIs to turn AFX into a render worker, launch a worker or launch a simple .bat file instead&lt;br /&gt;
&lt;br /&gt;
TBD&lt;br /&gt;
- Some way to parse logs to figure what has rendered and what hasn&amp;#039;t, cause Adobe&amp;#039;s .html file is crap TBH. This is the &amp;#039;watchfolder watcher&amp;#039; script that I tried to do long time ago.&lt;br /&gt;
- set some WF options (flags)&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
{&lt;br /&gt;
    watchfolderLocation = &amp;quot;none&amp;quot;;&lt;br /&gt;
    watchfolderLocation = (app.settings.haveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) ? (app.settings.getSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)) : watchfolderLocation;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function sendToWF(wf) {&lt;br /&gt;
	var m=0;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                m++;&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
	if(m!=0){&lt;br /&gt;
		alert(&amp;quot;There are &amp;quot;+m+&amp;quot; missing footages in the project, the watchfolder will fail.\n\nYou can however batch locally using the aerender button&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
        	var saved = app.project.file;&lt;br /&gt;
        	var curFile = app.project.file.name;&lt;br /&gt;
        	curFile = curFile.substring(0, curFile.length - 4);&lt;br /&gt;
        	var myFolder = new Folder(wf + &amp;quot;/&amp;quot; + curFile + &amp;quot;_wf/&amp;quot;);&lt;br /&gt;
        	myFolder.create();&lt;br /&gt;
        	writeLn(&amp;quot;Copying AEP to watchfolder.&amp;quot;);&lt;br /&gt;
        	var mySaveFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;.aep&amp;quot;);&lt;br /&gt;
        	saved.copy(mySaveFile);&lt;br /&gt;
        	var myTextFile = new File(myFolder.toString() + &amp;quot;/&amp;quot; + curFile + &amp;quot;_RCF.txt&amp;quot;);&lt;br /&gt;
        	myTextFile.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;);&lt;br /&gt;
        	var text = &amp;quot;After Effects 13.2v1 Render Control File\nmax_machines=10\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot;;&lt;br /&gt;
        	myTextFile.write(text);&lt;br /&gt;
	        myTextFile.close();&lt;br /&gt;
        	writeLn(&amp;quot;Sent to watchfolder...&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setWatchFolder() {&lt;br /&gt;
        var tmpfile = new File(String(Folder.desktop) + &amp;quot;/save this temp file with any name in the watchfolder&amp;quot;);&lt;br /&gt;
        var selectedFolder = tmpfile.saveDlg(&amp;#039;Select Watchfolder Location&amp;#039;);&lt;br /&gt;
        if (selectedFolder) {&lt;br /&gt;
&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;watchfolderPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;, selectedFolder.path);&lt;br /&gt;
            watchfolderLocation = selectedFolder.path;&lt;br /&gt;
            return true;&lt;br /&gt;
        } else {&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function startWatchingFolder() {&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\AfterFX.exe&amp;quot; -m -re -wf &amp;quot;&amp;#039; + Folder(watchfolderLocation).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.desktop.toString() + &amp;quot;/launch_WatchFolder.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the AfterEffects worker&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    function startAERender() {&lt;br /&gt;
	//to add: multiframe rendering&lt;br /&gt;
        aerenderExe = &amp;#039;&amp;quot;&amp;#039; + Folder(Folder.decode(Folder.appPackage.absoluteURI)).fsName + &amp;#039;\\aerender.exe&amp;quot; -continueOnMissingFootage -project &amp;quot;&amp;#039; + File(app.project.file).fsName + &amp;#039;&amp;quot;&amp;#039;;&lt;br /&gt;
        batch = new File(Folder.temp.toString() + &amp;quot;/launch_aerender.bat&amp;quot;);&lt;br /&gt;
        if (batch.open(&amp;quot;w&amp;quot;, &amp;quot;TEXT&amp;quot;, &amp;quot;????&amp;quot;) == true) {&lt;br /&gt;
            batch.write(&amp;quot;@echo off\n&amp;quot;);&lt;br /&gt;
            batch.write(aerenderExe);&lt;br /&gt;
	    batch.write(&amp;quot;\nPAUSE&amp;quot;);&lt;br /&gt;
            batch.close();&lt;br /&gt;
            batch.execute();&lt;br /&gt;
        } else {&lt;br /&gt;
            alert(&amp;quot;unable to launch the aerender.exe&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function updateUI(dialog) {&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function watchFolderUI(thisObj) {&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
            alert(&amp;quot;You need to check &amp;#039;Allow Scripts to Write Files and Access Network&amp;#039; in your preferences for this script to work&amp;quot;);&lt;br /&gt;
        } else {&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var panelGlobal = thisObj;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            /*&lt;br /&gt;
            Code for Import https://scriptui.joonas.me&lt;br /&gt;
            */&lt;br /&gt;
&lt;br /&gt;
            // DIALOG&lt;br /&gt;
            // ======&lt;br /&gt;
            var dialog = (panelGlobal instanceof Panel) ? panelGlobal : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Simple Watchfolder&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
            if (!(panelGlobal instanceof Panel)) dialog.text = &amp;quot;Simple Watchfolder&amp;quot;;&lt;br /&gt;
            dialog.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            dialog.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            dialog.spacing = 10;&lt;br /&gt;
            dialog.margins = 16;&lt;br /&gt;
&lt;br /&gt;
            var grp = dialog.add(&amp;quot;group&amp;quot;, undefined, {&lt;br /&gt;
                name: &amp;quot;group0&amp;quot;&lt;br /&gt;
            });&lt;br /&gt;
            grp.alignement = [&amp;quot;fill&amp;quot;, &amp;quot;fill&amp;quot;];&lt;br /&gt;
            grp.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            grp.orientation = &amp;quot;column&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            grp.add(&amp;quot;statictext&amp;quot;, undefined, &amp;quot;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&amp;quot;, {&lt;br /&gt;
                multiline: true&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var group1 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group1&amp;quot;});&lt;br /&gt;
            group1.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group1.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group1.spacing = 10;&lt;br /&gt;
            group1.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var setWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;setWF&amp;quot;});&lt;br /&gt;
            setWF.text = &amp;quot;Set WatchFolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var openWF = group1.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;openWF&amp;quot;});&lt;br /&gt;
	    openWF.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true; &lt;br /&gt;
            openWF.text = &amp;quot;Open WF&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var curWF = grp.add(&amp;quot;statictext&amp;quot;, undefined, undefined, {name: &amp;quot;statictext2&amp;quot;});&lt;br /&gt;
            curWF.helpTip = &amp;quot;shows current watchfolder&amp;quot;;&lt;br /&gt;
            curWF.text = &amp;quot;(choose watchfolder)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var sendWF = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendWF&amp;quot;});&lt;br /&gt;
            sendWF.helpTip = &amp;quot;save file first!&amp;quot;;&lt;br /&gt;
            sendWF.text = &amp;quot;Send .aep to Watchfolder&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var sendAER = grp.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;sendAER&amp;quot;});&lt;br /&gt;
            sendAER.helpTip = &amp;quot;local cmdline render&amp;quot;;&lt;br /&gt;
            sendAER.text = &amp;quot;render local aerender.exe batch&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            var group2 = grp.add(&amp;quot;group&amp;quot;, undefined, {name: &amp;quot;group2&amp;quot;});&lt;br /&gt;
            group2.orientation = &amp;quot;row&amp;quot;;&lt;br /&gt;
            group2.alignChildren = [&amp;quot;fill&amp;quot;, &amp;quot;top&amp;quot;];&lt;br /&gt;
            group2.spacing = 10;&lt;br /&gt;
            group2.margins = 0;&lt;br /&gt;
&lt;br /&gt;
            var launchWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;launchWorker&amp;quot;});&lt;br /&gt;
            launchWorker.helpTip = &amp;quot;Saves a .bat file to your desktop that launches a worker&amp;quot;;&lt;br /&gt;
            launchWorker.text = &amp;quot;Launch a worker&amp;quot;;&lt;br /&gt;
	    launchWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            var switchToWorker = group2.add(&amp;quot;button&amp;quot;, undefined, undefined, {name: &amp;quot;switchToWorker&amp;quot;});&lt;br /&gt;
            switchToWorker.helpTip = &amp;quot;Turns the current After Effects into a watchfolder worker&amp;quot;;&lt;br /&gt;
            switchToWorker.text = &amp;quot;Set as worker&amp;quot;;&lt;br /&gt;
            switchToWorker.enabled = (watchfolderLocation==&amp;quot;none&amp;quot;)?false:true;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            dialog.layout.layout(true);&lt;br /&gt;
            dialog.layout.resize();&lt;br /&gt;
            dialog.onResizing = dialog.onResize = function() {&lt;br /&gt;
                this.layout.resize();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	     if (watchfolderLocation != &amp;quot;none&amp;quot;) {&lt;br /&gt;
                curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            setWF.onClick = function() {&lt;br /&gt;
                swf = setWatchFolder();&lt;br /&gt;
                if (swf) {&lt;br /&gt;
                    curWF.text = &amp;quot;Folder: &amp;quot; + Folder(watchfolderLocation).fsName;&lt;br /&gt;
		    openWF.enabled = launchWorker.enabled = launchWorker.enabled = true;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            launchWorker.onClick = function() {&lt;br /&gt;
                startWatchingFolder();&lt;br /&gt;
            }&lt;br /&gt;
	    switchToWorker.onClick = function() {&lt;br /&gt;
		app.watchFolder(watchfolderLocation);&lt;br /&gt;
	    }&lt;br /&gt;
            sendAER.onClick = function() {&lt;br /&gt;
                startAERender();&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            openWF.onClick = function() {&lt;br /&gt;
        	var myFolder = new Folder(watchfolderLocation);&lt;br /&gt;
        	myFolder.execute();&lt;br /&gt;
                //watchfolderLocation&lt;br /&gt;
            }&lt;br /&gt;
            sendWF.onClick = function() {&lt;br /&gt;
                if (watchfolderLocation == &amp;quot;none&amp;quot;) {&lt;br /&gt;
                    setWatchFolder();&lt;br /&gt;
                } else {&lt;br /&gt;
                    sendToWF(watchfolderLocation);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderUI(this);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Show original source location (WIN only)===&lt;br /&gt;
http://i.imgur.com/04O3r.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    function pathToWinPath(path){&lt;br /&gt;
        var str = path.toString().replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
        str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
        str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
        str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
        return str;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sel = app.project.selection;&lt;br /&gt;
    if(sel.length == 0){&lt;br /&gt;
        alert(&amp;quot;You need to select source(s) in the project panel&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        for(i=0;i&amp;lt;app.project.selection.length;i++){&lt;br /&gt;
            a = prompt(&amp;quot;&amp;#039;OK&amp;#039; continues, &amp;#039;cancel&amp;#039; stops displaying original sources\n\n[ &amp;quot;+sel[i].name+&amp;quot; ]&amp;quot;,pathToWinPath(sel[i].mainSource.file.path.toString()));    &lt;br /&gt;
            if(!a){break}&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===List selected layers effects and their properties matchNames ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//windows only&lt;br /&gt;
if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
    var txtFile = new File(&amp;quot;~/Desktop/effectList.txt&amp;quot;);&lt;br /&gt;
    txtFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    var col = app.project.activeItem.selectedLayers;&lt;br /&gt;
    for (i=0;i&amp;lt;col.length;i++){&lt;br /&gt;
        effs = col[i].property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
            for(j=1;j&amp;lt;=effs.numProperties;j++){&lt;br /&gt;
                txtFile.write(&amp;quot;\n( &amp;quot;+effs.property(j).matchName+&amp;quot; ) &amp;quot;+effs.property(j).name+&amp;quot;\n------------------------------------------\n&amp;quot;);&lt;br /&gt;
                for(k=1;k&amp;lt;=effs.property(j).numProperties;k++){&lt;br /&gt;
                    txtFile.write(effs.property(j).property(k).matchName+&amp;quot; --&amp;gt; &amp;quot;+effs.property(j).property(k).name);&lt;br /&gt;
                    if(effs.property(j).property(k).propertyValueType != PropertyValueType.NO_VALUE &amp;amp;&amp;amp; effs.property(j).property(k).value != undefined){&lt;br /&gt;
                        txtFile.write(&amp;quot; [ &amp;quot;+effs.property(j).property(k).value.toString()+&amp;quot; ] &amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                    txtFile.write(&amp;quot;\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    txtFile.write(&amp;quot;\n\n\n//Reminder (add effect and set property):\n\ns = app.project.activeItem.selectedLayers[0];\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v = s.Effects.addProperty(\&amp;quot;CC RepeTile\&amp;quot;);\n&amp;quot;);&lt;br /&gt;
    txtFile.write(&amp;quot;v.property(\&amp;quot;CC RepeTile-0001\&amp;quot;).setValue(10);&amp;quot;);    &lt;br /&gt;
    txtFile.close();&lt;br /&gt;
    txtFile.execute();&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Set scripting Prefs to enable to write to disk&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/IEUwe.gif&lt;br /&gt;
&lt;br /&gt;
==== Disable effects ====&lt;br /&gt;
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!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
(function toggleEffectGlobally() {&lt;br /&gt;
    app.beginUndoGroup(&amp;quot;Toggle Effect Globally&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    var sel = app.project.activeItem &amp;amp;&amp;amp; app.project.activeItem.selectedProperties;&lt;br /&gt;
    if (!sel || sel.length === 0) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect first.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var selectedEffect = null;&lt;br /&gt;
&lt;br /&gt;
    // Find the selected effect property group&lt;br /&gt;
    for (var i = 0; i &amp;lt; sel.length; i++) {&lt;br /&gt;
        if (sel[i].matchName &amp;amp;&amp;amp; sel[i].parentProperty &amp;amp;&amp;amp; sel[i].parentProperty.matchName === &amp;quot;ADBE Effect Parade&amp;quot;) {&lt;br /&gt;
            selectedEffect = sel[i];&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (!selectedEffect) {&lt;br /&gt;
        alert(&amp;quot;Please select an effect in the timeline.&amp;quot;);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var effectName = selectedEffect.matchName;&lt;br /&gt;
    var effectEnabled = selectedEffect.enabled;&lt;br /&gt;
&lt;br /&gt;
    // Loop through all comps in the project&lt;br /&gt;
    for (var p = 1; p &amp;lt;= app.project.numItems; p++) {&lt;br /&gt;
        var item = app.project.item(p);&lt;br /&gt;
        if (item instanceof CompItem) {&lt;br /&gt;
            for (var l = 1; l &amp;lt;= item.numLayers; l++) {&lt;br /&gt;
                var layer = item.layer(l);&lt;br /&gt;
                if (layer.property(&amp;quot;ADBE Effect Parade&amp;quot;)) {&lt;br /&gt;
                    var effects = layer.property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
                    for (var e = 1; e &amp;lt;= effects.numProperties; e++) {&lt;br /&gt;
                        var eff = effects.property(e);&lt;br /&gt;
                        if (eff.matchName === effectName) {&lt;br /&gt;
                            eff.enabled = !effectEnabled;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    app.endUndoGroup();&lt;br /&gt;
})();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===List effects===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 function projEffects(){&lt;br /&gt;
 	var effects = new Array();&lt;br /&gt;
 	var effects2 = new Array();&lt;br /&gt;
 	for(var i = 1; i &amp;lt;= app.project.numItems; i++){&lt;br /&gt;
 		if(app.project.item(i) instanceof CompItem){&lt;br /&gt;
 			   var comp = app.project.item(i);&lt;br /&gt;
 				for(j = 1; j &amp;lt;=  comp.layers.length;j++){&lt;br /&gt;
 					effs = comp.layer(j).property(&amp;quot;ADBE Effect Parade&amp;quot;);&lt;br /&gt;
 					for(k=1;k&amp;lt;=effs.numProperties;k++){&lt;br /&gt;
 						keyName = effs.property(k).matchName;&lt;br /&gt;
 						effects[keyName]  = 1;&lt;br /&gt;
 						}&lt;br /&gt;
 					   &lt;br /&gt;
 					}&lt;br /&gt;
 			}&lt;br /&gt;
 	}&lt;br /&gt;
 	for(a in effects){&lt;br /&gt;
 		effects2[effects2.length] = a;&lt;br /&gt;
 	}&lt;br /&gt;
 	effects2.sort();&lt;br /&gt;
 	return effects2;&lt;br /&gt;
 }&lt;br /&gt;
 alert(projEffects().join(&amp;quot;\n&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/hFNiQ.jpg&lt;br /&gt;
&lt;br /&gt;
===Multiple sequences import (Windows)===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Multiple sequences import (windows)&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//  bernie@berniebernie.fr&lt;br /&gt;
//  This script takes an image as an input and loads the sequences it finds in the same folder using a batch (.bat) file&lt;br /&gt;
//  Access to files network required --- no error checking.&lt;br /&gt;
//   &amp;gt; only works on windows so far&lt;br /&gt;
//   &amp;gt; only finds exrs/pngs/jpgs&lt;br /&gt;
//   &amp;gt; probably fails if your sequences dont have the same start frame&lt;br /&gt;
//   &amp;gt; will work with /path/image.####.jpg or similar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function uniq_fast(a) {&lt;br /&gt;
    var seen = {};&lt;br /&gt;
    var out = [];&lt;br /&gt;
    var len = a.length;&lt;br /&gt;
    var j = 0;&lt;br /&gt;
    for(var i = 0; i &amp;lt; len; i++) {&lt;br /&gt;
         var item = a[i];&lt;br /&gt;
         if(seen[item] !== 1) {&lt;br /&gt;
               seen[item] = 1;&lt;br /&gt;
               out[j++] = item;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return out;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function e(s){&lt;br /&gt;
    $.writeln(s);&lt;br /&gt;
}&lt;br /&gt;
curScript = new File($.fileName);&lt;br /&gt;
&lt;br /&gt;
startT = Date.now();&lt;br /&gt;
&lt;br /&gt;
sourceFolder = app.project.selection[0].mainSource.file.parent;&lt;br /&gt;
tmpFolder = Folder.temp;&lt;br /&gt;
tmpBat = Folder.temp.toString()+&amp;quot;/ae_dirlist.bat&amp;quot;;&lt;br /&gt;
tmpFilesList = Folder.temp.toString()+&amp;quot;/ae_imageslist.txt&amp;quot;;&lt;br /&gt;
var listFile = new File(tmpFilesList);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var batFile = new File(tmpBat);&lt;br /&gt;
batFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;REM Auto generated by this after effects script file: &amp;quot;+curScript.fsName);&lt;br /&gt;
batFile.write(&amp;quot;\npushd \&amp;quot;&amp;quot;+sourceFolder.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.write(&amp;quot;\ndir /b /on *.exr *.png *.jpg *.jpeg &amp;gt; \&amp;quot;&amp;quot;+listFile.fsName+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
batFile.close();&lt;br /&gt;
system.callSystem(batFile.fsName);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
listFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;); &lt;br /&gt;
contents = listFile.read();&lt;br /&gt;
arrayContents = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
listFile.close();&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;via batch: &amp;quot;+arrayContents.length+&amp;quot; files found in &amp;quot;+timer+&amp;quot;s &amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var myRe = new RegExp(&amp;quot;[0-9]{2,}(\)){0,1}\.[a-z]+$&amp;quot;);&lt;br /&gt;
var myArray = myRe.exec(arrayContents[0]);&lt;br /&gt;
endlength = myArray[0].length;&lt;br /&gt;
&lt;br /&gt;
for(i = 0;i&amp;lt;arrayContents.length;i++){&lt;br /&gt;
    arrayContents[i] = arrayContents[i].slice(0, -endlength);&lt;br /&gt;
}&lt;br /&gt;
unique = uniq_fast(arrayContents);&lt;br /&gt;
&lt;br /&gt;
dialog = confirm(unique.length+&amp;quot; sequences found. Load them ? \nIt might take a long time !&amp;quot;,false,&amp;quot;Confirm Loading&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(dialog){      &lt;br /&gt;
    for(i = 0;i&amp;lt;unique.length;i++){&lt;br /&gt;
        if(unique[i].length&amp;gt;0){&lt;br /&gt;
            sequenceStartFile = new File(sourceFolder.toString()+&amp;quot;/&amp;quot;+unique[i]+myArray[0]);&lt;br /&gt;
            &lt;br /&gt;
            if (sequenceStartFile) {&lt;br /&gt;
            writeLn(&amp;quot;Loading &amp;quot;+(i+1)+&amp;quot;/&amp;quot;+unique.length);&lt;br /&gt;
                try {&lt;br /&gt;
                    // Create a variable containing ImportOptions.&lt;br /&gt;
                    var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
                    importOptions.sequence = true;&lt;br /&gt;
                    try { &lt;br /&gt;
&lt;br /&gt;
                        app.project.importFile(importOptions);&lt;br /&gt;
                    } catch (error) {&lt;br /&gt;
                       e(error.toString());&lt;br /&gt;
                    }&lt;br /&gt;
                } catch (error) {&lt;br /&gt;
                    e(error.toString());&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    writeLn(&amp;quot;Canceled&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
timer = (Date.now()-startT)/1000;&lt;br /&gt;
e(&amp;quot;after regext: &amp;quot;+timer);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Split to Renderqueue ===&lt;br /&gt;
https://gyazo.com/0f9dcb815b75fef03af6c16d340614b6.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Split Layers to Renderqueue v2.0&lt;br /&gt;
// ----------------------------------------------&lt;br /&gt;
//&lt;br /&gt;
//  This script takes the one item from the renderqueue, and creates a file for each layer in the associated comp&lt;br /&gt;
//  using the in and out points. I use this along with &amp;#039;Magnum&amp;#039; the edit detector to split &amp;amp; render sequences.&lt;br /&gt;
//&lt;br /&gt;
//  v2.0 added the index of the layer in the filename to prevent duplicates, alos ignore the &amp;#039;base&amp;#039; renderqueue item that we derive everything from&lt;br /&gt;
&lt;br /&gt;
function pad(n, width, z) {&lt;br /&gt;
  z = z || &amp;#039;0&amp;#039;;&lt;br /&gt;
  n = n + &amp;#039;&amp;#039;;&lt;br /&gt;
  return n.length &amp;gt;= width ? n : new Array(width - n.length + 1).join(z) + n;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Split Layers to Renderqueue&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
if(app.project.renderQueue.items.length == 1){&lt;br /&gt;
    FE = app.project.renderQueue.items[1];&lt;br /&gt;
    ai = FE.comp;&lt;br /&gt;
    path = FE.outputModule(1).file.path;&lt;br /&gt;
    for(i=1;i&amp;lt;=ai.layers.length;i++){&lt;br /&gt;
&lt;br /&gt;
        RI = app.project.renderQueue.items[1].duplicate();&lt;br /&gt;
        RI.timeSpanStart = ai.layers[i].inPoint;&lt;br /&gt;
        RI.timeSpanDuration = ai.layers[i].outPoint-ai.layers[i].inPoint;&lt;br /&gt;
        $.writeln(ai.workAreaDuration+&amp;quot; &amp;quot;+ai.layers[i].outPoint);&lt;br /&gt;
        RI.outputModule(1).file =  new File(path+&amp;quot;/&amp;quot;+pad(i,2,&amp;quot;0&amp;quot;)+&amp;quot;_&amp;quot;+ai.layers[i].name);&lt;br /&gt;
    }&lt;br /&gt;
    app.project.renderQueue.items[1].render= false;&lt;br /&gt;
}else{&lt;br /&gt;
    alert(&amp;quot;Only 1 element should be in renderqueue&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.endUndoGroup;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Docked Panel SNIP ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//script panel&lt;br /&gt;
{&lt;br /&gt;
	var nested_file = new File(&amp;quot;U:\Matthieu Bernadat\afxscripts\Dandy_script.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//called file&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
	var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,[100, 100, 300, 300]);&lt;br /&gt;
	impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
	//impButton.onClick = openfile;&lt;br /&gt;
	return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Watchfolder watcher (WIP)===&lt;br /&gt;
http://i.imgur.com/lhO3k.gif&lt;br /&gt;
&lt;br /&gt;
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)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  watchwatchfolder: a tool to look at what&amp;#039;s in the watchfolder&lt;br /&gt;
//  v1.0 by bernie - mbernadat@gmail.com&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// icons by Mark Jame - http://www.famfamfam.com/lab/icons/silk/ licensed under a Creative Commons Attribution 2.5 License. &lt;br /&gt;
//&lt;br /&gt;
//  known limitations (v1.0), to be fixed:&lt;br /&gt;
//  -windows only for now&lt;br /&gt;
//  -only looks at 1 renderqueue element per AEP file.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// I coded this like a dirty monkey. I feel sorry if you have to look at this.&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
todo: check if icons are here&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var wf = app.settings.haveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)?new Folder(app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;)):null;&lt;br /&gt;
var scriptFile = new File($.fileName);&lt;br /&gt;
var scriptFolder = scriptFile.parent; //png icons should be here&lt;br /&gt;
b=0; //global string that will store the &lt;br /&gt;
var cancelTaskID;&lt;br /&gt;
var pal;&lt;br /&gt;
var timer = 0;&lt;br /&gt;
var refreshRate = 5;  //seconds&lt;br /&gt;
var reloadEditTxt;&lt;br /&gt;
var firstTime = true;&lt;br /&gt;
var shotinfos = new Array();&lt;br /&gt;
&lt;br /&gt;
//global ui names&lt;br /&gt;
var refreshBtn;&lt;br /&gt;
var list;&lt;br /&gt;
var wfStText;&lt;br /&gt;
var wfPbar;&lt;br /&gt;
var chckBox0;&lt;br /&gt;
var chckBox1;&lt;br /&gt;
var chckBox2;&lt;br /&gt;
var chckBox3;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function getLogonly(file){&lt;br /&gt;
    if(file.name.indexOf(&amp;quot;Logs)&amp;quot;) != -1){&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
        return false;&lt;br /&gt;
 }&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
function localToRessource(path){&lt;br /&gt;
    //windows, for now, the only one available!&lt;br /&gt;
    str = &amp;quot;/&amp;quot;+path.replace(&amp;quot;:\\&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(/\\/g, &amp;quot;/&amp;quot;);&lt;br /&gt;
    str = str.replace(&amp;quot; &amp;quot;, &amp;quot;%20&amp;quot;);&lt;br /&gt;
    str = str.substring(0,str.lastIndexOf(&amp;quot;/&amp;quot;));&lt;br /&gt;
    return str;&lt;br /&gt;
}&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
    fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
function logInfo(logFolderLocation){&lt;br /&gt;
    &lt;br /&gt;
    curFold = new Folder(logFolderLocation.toString());&lt;br /&gt;
    folds = returnFolderArray(logFolderLocation);&lt;br /&gt;
    txtfiles = fold.getFiles(&amp;quot;*RCF.txt&amp;quot;); //there should only be one&lt;br /&gt;
    htmlfiles =  fold.getFiles(&amp;quot;*.htm&amp;quot;);&lt;br /&gt;
    var htmlfile;&lt;br /&gt;
    if(htmlfiles[0]){&lt;br /&gt;
        htmlfile = htmlfiles[0].toString();&lt;br /&gt;
    }&lt;br /&gt;
  // $.writeln(&amp;quot;file &amp;gt;&amp;gt;&amp;gt; &amp;quot;+txtfiles[0].toString());&lt;br /&gt;
  var info = [logFolderLocation.name.toString(),1,logFolderLocation.toString(),logFolderLocation.name.toString()+&amp;quot;rrr&amp;quot;];&lt;br /&gt;
    var fcontents = &amp;quot;&amp;quot;;&lt;br /&gt;
    if(txtfiles[0]){&lt;br /&gt;
        f = txtfiles[0].toString();&lt;br /&gt;
        f = new File(f);&lt;br /&gt;
        f.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
        fcontents = f.read();&lt;br /&gt;
        f.close();&lt;br /&gt;
        var findItems = new RegExp(&amp;quot;^(item)[0-9]{1,}(=\()(.*)(\))$&amp;quot;,&amp;quot;mi&amp;quot;);&lt;br /&gt;
        v=findItems.exec(fcontents);&lt;br /&gt;
        if(v){&lt;br /&gt;
            if(v[4].indexOf(&amp;quot;Stopped&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,1,v[4].substring(1,v[4].indexOf(&amp;quot;,&amp;quot;))];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;In Progress&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,2,&amp;quot;Rendering&amp;quot;];  &lt;br /&gt;
            }else if(v[4].indexOf(&amp;quot;Finished&amp;quot;) != -1){&lt;br /&gt;
                info=[&amp;quot;name&amp;quot;,0,&amp;quot;&amp;quot;];  &lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
        }else{&lt;br /&gt;
           // $.writeln(&amp;quot;Buggy file&amp;quot;);&lt;br /&gt;
            info=[logFolderLocation.name.toString(),1,&amp;quot;--Bug--&amp;quot;,logFolderLocation.toString()+&amp;quot;eeee&amp;quot;];  &lt;br /&gt;
        }&lt;br /&gt;
        fold = new Folder(folds[0].toString());&lt;br /&gt;
        files = fold.getFiles(&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
        firstFile = files[files.length-1]; //used to be first file, works better with last text file&lt;br /&gt;
        if(firstFile == undefined || firstFile == null){&lt;br /&gt;
              info=[logFolderLocation.name.toString(),1,&amp;quot;Error&amp;quot;,htmlfile]; &lt;br /&gt;
        }else{&lt;br /&gt;
                //info = [&amp;quot;debug&amp;quot;,logFolderLocation.toString(),logFolderLocation.name.toString()];&lt;br /&gt;
                //info = [logFolderLocation.name.toString(),(info[1]==4)?1:info[1],,logFolderLocation.toString(),0];&lt;br /&gt;
               &lt;br /&gt;
                firstFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
                contents = firstFile.read();&lt;br /&gt;
                firstFile.close();&lt;br /&gt;
                lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
              //  $.writeln(logFolderLocation.toString()+&amp;quot; &amp;lt;&amp;lt;&amp;lt; &amp;quot;)&lt;br /&gt;
               info[3] = logFolderLocation.toString();&lt;br /&gt;
               info[0] = logFolderLocation.name.toString();&lt;br /&gt;
                for(i=0;i&amp;lt;lines.length;i++){&lt;br /&gt;
                    //$.writeln(&amp;quot;Doing shit&amp;quot;);&lt;br /&gt;
                    phrase =&amp;quot;Rendering started on&amp;quot;;&lt;br /&gt;
                    info[4] = &amp;quot;n/a&amp;quot;;&lt;br /&gt;
                    if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                        info[4] = lines[i].substring(s+phrase.length);        &lt;br /&gt;
                    }&lt;br /&gt;
                    phrase =&amp;quot;Output To: &amp;quot;;&lt;br /&gt;
                   &lt;br /&gt;
                   // info[2] = &amp;quot;n/a+&amp;quot;;&lt;br /&gt;
//                    info[1] = 1;&lt;br /&gt;
                    &lt;br /&gt;
                    if(info[1]==0 ||info[1]==2){&lt;br /&gt;
                       // $.writeln( lines[i].indexOf(phrase));&lt;br /&gt;
                            if(s = lines[i].indexOf(phrase) != -1){&lt;br /&gt;
                                s = lines[i].indexOf(phrase);&lt;br /&gt;
                                v = lines[i].substring(s+phrase.length);&lt;br /&gt;
                                wfold = v.substring(0,v.lastIndexOf(&amp;quot;\\&amp;quot;));&lt;br /&gt;
                               //$.writeln(wfold);&lt;br /&gt;
                                f =  wfold.substring(wfold.lastIndexOf(&amp;quot;\\&amp;quot;)+1,wfold.length);&lt;br /&gt;
                                ///info[3] = pathToWinPath(wfold);&lt;br /&gt;
                                 info[3] =localToRessource(wfold);&lt;br /&gt;
                                 $.writeln(info[3])&lt;br /&gt;
                                info[0] = f;&lt;br /&gt;
                               // info[2] = &amp;quot; &amp;quot;;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                   // info[1] = ;&lt;br /&gt;
                &lt;br /&gt;
                }&lt;br /&gt;
              //  $.writeln(info);&lt;br /&gt;
               // $.writeln(contents);&lt;br /&gt;
        }&lt;br /&gt;
   &lt;br /&gt;
    }&lt;br /&gt;
  $.writeln(info);&lt;br /&gt;
    return info;&lt;br /&gt;
            &lt;br /&gt;
}&lt;br /&gt;
function hasStarted(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    //$.writeln(files.length);&lt;br /&gt;
    if(files.length&amp;gt;0){&lt;br /&gt;
        return true;&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getFilePath(watchedFolderLocation){&lt;br /&gt;
    fold = new Folder(watchedFolderLocation.toString());&lt;br /&gt;
    files = fold.getFiles(getLogonly);&lt;br /&gt;
    alert(files[0].name);&lt;br /&gt;
    return files[0].name;&lt;br /&gt;
}&lt;br /&gt;
/*&lt;br /&gt;
function getPercentage(folder){&lt;br /&gt;
    myFolder = new Folder(folder);&lt;br /&gt;
    files = myFolder.getFiles(&amp;quot;*DandyWatch.txt&amp;quot;);&lt;br /&gt;
    if(files.length != 1){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    myTextFile = files[0];    &lt;br /&gt;
    myTextFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    contents = myTextFile.read();&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    lines = contents.split(&amp;quot;\n&amp;quot;);&lt;br /&gt;
    outPutFolder = new Folder(lines[0]);&lt;br /&gt;
    files = outPutFolder.getFiles();&lt;br /&gt;
    f = files.length;&lt;br /&gt;
    writeLn(f+&amp;quot;/&amp;quot;+(lines[1]+1));&lt;br /&gt;
    return parseFloat(files.length/(lines[1]+1));&lt;br /&gt;
    //var percentage = new Array();&lt;br /&gt;
}&lt;br /&gt;
//getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot);&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
function retrieveArray(){&lt;br /&gt;
    var state = 4; //queued, default state&lt;br /&gt;
     var vArray = new Array();&lt;br /&gt;
    l =0;&lt;br /&gt;
    counter=0;&lt;br /&gt;
    counter2=0;&lt;br /&gt;
    folders = returnFolderArray(wf);&lt;br /&gt;
    for(folder in folders){&lt;br /&gt;
    l++;&lt;br /&gt;
        var out = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(folders[folder].name != &amp;quot;anonymous&amp;quot;){ //??&lt;br /&gt;
            counter2++;&lt;br /&gt;
            if(hasStarted(folders[folder])){&lt;br /&gt;
                state = 0;&lt;br /&gt;
                out = logInfo(folders[folder]);&lt;br /&gt;
                //$.writeln(out);&lt;br /&gt;
                //out[3] = localToRessource(out[3]);&lt;br /&gt;
                                   &lt;br /&gt;
            }else{&lt;br /&gt;
                state = 4;&lt;br /&gt;
                out = [folders[folder].name,4,&amp;quot;&amp;quot;,folders[folder].toString()];&lt;br /&gt;
            }&lt;br /&gt;
            //$.writeln(&amp;quot;&amp;gt;&amp;gt;&amp;gt; &amp;quot;+state+&amp;quot; - &amp;quot;+out+&amp;quot; - &amp;quot;+folders[folder].name);&lt;br /&gt;
            &lt;br /&gt;
           // $.writeln(out);&lt;br /&gt;
            vArray[counter2] =out;&lt;br /&gt;
        }&lt;br /&gt;
      // $.writeln(vArray[counter2]);&lt;br /&gt;
        wfPbar.value = l/folders.length*100;&lt;br /&gt;
&lt;br /&gt;
        if (pal instanceof Window){&lt;br /&gt;
            pal.update();&lt;br /&gt;
        }else{&lt;br /&gt;
            if(counter &amp;lt; Math.round(wfPbar.value/5,0)*5){&lt;br /&gt;
                counter +=5;&lt;br /&gt;
                clearOutput();&lt;br /&gt;
                writeLn(counter+&amp;quot;%&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                write(&amp;quot;.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return vArray;&lt;br /&gt;
}&lt;br /&gt;
function gather(){&lt;br /&gt;
                refreshBtn.text = &amp;quot;refreshing...&amp;quot;;&lt;br /&gt;
                refreshBtn.enabled = false;&lt;br /&gt;
                wfStText.visible = false;&lt;br /&gt;
                wfPbar.visible = true;&lt;br /&gt;
                shotinfos = retrieveArray();&lt;br /&gt;
                list.removeAll();&lt;br /&gt;
                for(i=1;i&amp;lt;shotinfos.length;i++){&lt;br /&gt;
                    var p = shotinfos[i];&lt;br /&gt;
                                      //  $.writeln(&amp;quot;Debug&amp;quot;);$.writeln(p);&lt;br /&gt;
                    addItem(p[0],p[1],p[2]);&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
                writeLn(&amp;quot;Done&amp;quot;);&lt;br /&gt;
                wfStText.visible = true;&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
                &lt;br /&gt;
                //set (or not) the timer&lt;br /&gt;
                refreshBtn.enabled = true;&lt;br /&gt;
                if(parseInt(reloadEditTxt.text) &amp;gt; 0){&lt;br /&gt;
                    timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                    cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);                &lt;br /&gt;
                }else{&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
}&lt;br /&gt;
function watchWatchFolder(thisObj){&lt;br /&gt;
&lt;br /&gt;
        //UI Design&lt;br /&gt;
        {&lt;br /&gt;
            pal = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;WatchFolder&amp;quot;, undefined, {resizeable:true});&lt;br /&gt;
            var winGfx = pal.graphics;&lt;br /&gt;
            var darkColorBrush = winGfx.newPen(winGfx.BrushType.SOLID_COLOR, [0,0,0], 1);&lt;br /&gt;
            pal.bounds = [300,200,600,600];        &lt;br /&gt;
            var wfLocBtn = pal.add(&amp;quot;button&amp;quot;, [10,10,105,35],&amp;quot;Set Watchfolder&amp;quot;);&lt;br /&gt;
            wfStTextContents = ( wf != null)?app.settings.getSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;):&amp;quot;(not set...)&amp;quot;;&lt;br /&gt;
            wfStText = pal.add(&amp;quot;edittext&amp;quot;,[115,12,285,33], wfStTextContents);&lt;br /&gt;
                wfStText.active = false;&lt;br /&gt;
                wfStText.enabled = false;&lt;br /&gt;
            wfPbar = pal.add(&amp;quot;progressbar&amp;quot;,[115,12,285,33],0,100);&lt;br /&gt;
                wfPbar.visible = false;&lt;br /&gt;
            var sortStText = pal.add(&amp;quot;statictext&amp;quot;,[17,42,80,60], &amp;quot;Sort by: &amp;quot;);&lt;br /&gt;
            var sortByName = pal.add(&amp;#039;radiobutton&amp;#039;,[62,40,120,57], &amp;#039;name&amp;#039;);&lt;br /&gt;
            var sortByStatus = pal.add(&amp;#039;radiobutton&amp;#039;,[115,40,170,57], &amp;#039;status&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
                sortByName.value = true;&lt;br /&gt;
            var bottomElements = pal.add(&amp;quot;panel&amp;quot;,[10,310,260,360],undefined,{borderStyle:&amp;quot;none&amp;quot;});&lt;br /&gt;
            &lt;br /&gt;
            bl = 0;&lt;br /&gt;
            var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);      &lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[5,bl,20,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[0]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox0 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[22,bl,39,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox0.value = true;&lt;br /&gt;
            chckBox0.helpTip = &amp;quot;Show finished renders&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[45,bl,60,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[1]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox1 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[62,bl,79,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox1.value = true;&lt;br /&gt;
            chckBox1.helpTip = &amp;quot;Show errors&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[85,bl,100,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[2]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox2 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[103,bl,120,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox2.value = true;&lt;br /&gt;
            chckBox2.helpTip = &amp;quot;Show rendering&amp;quot;;&lt;br /&gt;
           &lt;br /&gt;
            bottomElements.add(&amp;#039;image&amp;#039;,[125,bl,140,bl+14],scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[4]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
            chckBox3 = bottomElements.add(&amp;#039;checkbox&amp;#039;,[143,bl,160,bl+16], &amp;#039;&amp;#039;);&lt;br /&gt;
            chckBox3.value = true;&lt;br /&gt;
            chckBox3.helpTip = &amp;quot;Show queued&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            bl = 25;&lt;br /&gt;
            var reloadStTxt =  bottomElements.add(&amp;quot;statictext&amp;quot;,[5,bl+2,50,bl+21], &amp;quot;Update: &amp;quot;);&lt;br /&gt;
            reloadEditTxt =  bottomElements.add(&amp;quot;edittext&amp;quot;,[55,bl,90,bl+20], 600);&lt;br /&gt;
            reloadEditTxt.enabled=false;&lt;br /&gt;
            refreshBtn =  bottomElements.add(&amp;quot;button&amp;quot;,[100,bl,260,bl+20], &amp;quot;Start&amp;quot;);&lt;br /&gt;
            reloadEditTxt.helpTip = &amp;quot;in seconds, 0 for manual refresh only.&amp;quot;;&lt;br /&gt;
            list = pal.add (&amp;quot;ListBox&amp;quot;, [10, 65, 260,320], &amp;quot;desc&amp;quot;,{numberOfColumns: 2,showHeaders: true});        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            //list.columnTitles = Array(&amp;quot;First Name&amp;quot;, &amp;quot;Last&amp;quot;); doesn&amp;#039;t work in CS5 apparently&lt;br /&gt;
            &lt;br /&gt;
         }&lt;br /&gt;
     &lt;br /&gt;
     &lt;br /&gt;
        //UI Callbacks&lt;br /&gt;
        {&lt;br /&gt;
            wfLocBtn.onClick = function(){&lt;br /&gt;
                wfLoc = Folder.selectDialog(&amp;quot;Select watchfolder&amp;quot;);&lt;br /&gt;
                if(wfLoc != null){&lt;br /&gt;
                    app.settings.saveSetting(&amp;quot;watchwatchfolder&amp;quot;, &amp;quot;wfloc&amp;quot;,wfLoc.toString());&lt;br /&gt;
                    wf = new Folder(wfLoc.toString());&lt;br /&gt;
                    if(wf){&lt;br /&gt;
                        wfStText.text = wfLoc.toString();&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }    &lt;br /&gt;
            pal.onResize = function(){&lt;br /&gt;
                //because using layouts is too confusing for me&lt;br /&gt;
                list.bounds = [list.bounds[0],list.bounds[1],pal.bounds[2]-pal.bounds[0]-10,pal.bounds[3]-pal.bounds[1]-100];&lt;br /&gt;
                wfStText.bounds = [wfStText.bounds[0],wfStText.bounds[1],pal.bounds[2]-pal.bounds[0]-10,wfStText.bounds[3]];&lt;br /&gt;
                wfPbar.bounds = wfStText.bounds;&lt;br /&gt;
                bottomElements.bounds = [list.bounds[0],list.bounds[3]+10,list.bounds[2],list.bounds[3]+60];&lt;br /&gt;
            }&lt;br /&gt;
            list.onDoubleClick = function(){//onChange&lt;br /&gt;
                sel = list.selection;&lt;br /&gt;
               &lt;br /&gt;
                if(list.selection != null){&lt;br /&gt;
                    // $.writeln(shotinfos.join(&amp;quot;\n&amp;quot;));&lt;br /&gt;
                    sL=shotinfos.length;&lt;br /&gt;
                    //$.writeln(sL);&lt;br /&gt;
                    for(i=1;i&amp;lt;sL;i++){&lt;br /&gt;
                       a=shotinfos[i];&lt;br /&gt;
                      //$.writeln(a);&lt;br /&gt;
                        //$.writeln(shotinfos[i][1]+&amp;quot; &amp;quot;+list.selection.toString());&lt;br /&gt;
                        if(a[0] == list.selection.toString()){&lt;br /&gt;
                           // $.writeln(&amp;quot;ww&amp;quot;+);&lt;br /&gt;
                       //  $.writeln(a);&lt;br /&gt;
                            //explore(a[3]+((a[1]==2)?&amp;quot;/&amp;quot;+a[0]:&amp;quot;&amp;quot;));&lt;br /&gt;
                            explore(a[3]);&lt;br /&gt;
                            break;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    //$.writeln(sel[0].subItems[0].text);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            }        &lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
            refreshBtn.onClick = function(){&lt;br /&gt;
                if((timer&amp;gt;0 &amp;amp;&amp;amp; !firstTime) || !pal.visible){&lt;br /&gt;
                        timer = 0;&lt;br /&gt;
                       // $.writeln(&amp;quot;stopped&amp;quot;);&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start&amp;quot;;&lt;br /&gt;
                        firstTime = true;&lt;br /&gt;
                        cancelTask(cancelTaskID);&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(reloadEditTxt.text == 0 || firstTime){&lt;br /&gt;
                            gather();&lt;br /&gt;
                            firstTime = false;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                &lt;br /&gt;
                ///timer = parseInt(reloadEditTxt.text)/refreshRate;&lt;br /&gt;
                //refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+(timer*5)+&amp;quot;s)&amp;quot;;&lt;br /&gt;
                //cancelTaskID = app.scheduleTask(&amp;quot;loop()&amp;quot;,refreshRate*1000,1);&lt;br /&gt;
            }&lt;br /&gt;
            reloadEditTxt.onClick = function(){reloadEditTxt.enabled=true};&lt;br /&gt;
            reloadEditTxt.onChange = function(){&lt;br /&gt;
                if(reloadEditTxt.text &amp;lt; 10 || (parseInt(reloadEditTxt.text) != reloadEditTxt.text)){&lt;br /&gt;
                    timer = 0;&lt;br /&gt;
                    reloadEditTxt.text = 0;&lt;br /&gt;
                    refreshBtn.text = &amp;quot;Refresh&amp;quot;;&lt;br /&gt;
                //}else if(reloadEditTxt.text &amp;gt;=10){&lt;br /&gt;
                }else{&lt;br /&gt;
                    if(timer &amp;lt;= 0){&lt;br /&gt;
                        refreshBtn.text = &amp;quot;Start (~ &amp;quot;+eggTimer(reloadEditTxt.text)+&amp;quot;)&amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            reloadEditTxt.enabled=false;                &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
        //Initialize the whole shebang&lt;br /&gt;
            if (pal instanceof Window){&lt;br /&gt;
                pal.center();&lt;br /&gt;
                pal.show();&lt;br /&gt;
            }&lt;br /&gt;
	   &lt;br /&gt;
        &lt;br /&gt;
      return pal;&lt;br /&gt;
     }&lt;br /&gt;
//reloadEditTxt.notify(&amp;quot;onChange&amp;quot;);&lt;br /&gt;
ui = watchWatchFolder(this);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function eggTimer(time){&lt;br /&gt;
    if(time&amp;lt;=59){&lt;br /&gt;
        time = time+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;=60*4){&lt;br /&gt;
        time = Math.floor(time/60)+&amp;quot;m &amp;quot;+(time%60)+&amp;quot;s&amp;quot;;&lt;br /&gt;
    }else if(time&amp;lt;60*60){&lt;br /&gt;
        time = Math.round(time/60,0)+&amp;quot;m&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        time = Math.round(time/(60*60),0)+&amp;quot;h&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    return time;&lt;br /&gt;
    }&lt;br /&gt;
function loop(){&lt;br /&gt;
    timer--;&lt;br /&gt;
  //  $.writeln(timer+&amp;quot; pal.visible:&amp;quot;+pal.visible);&lt;br /&gt;
    refreshBtn.text = &amp;quot;Click to stop (&amp;quot;+eggTimer(timer*5)+&amp;quot;)&amp;quot;;&lt;br /&gt;
    if(timer&amp;lt;=0 || !pal.visible){&lt;br /&gt;
&lt;br /&gt;
        cancelTask(cancelTaskID);&lt;br /&gt;
        if(reloadEditTxt.value == 0 || !pal.visible){&lt;br /&gt;
          &lt;br /&gt;
        }else{&lt;br /&gt;
            gather();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function cancelTask(id){&lt;br /&gt;
        app.cancelTask(id);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function grow(palette,value){&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
function addItem(name,state,msg){&lt;br /&gt;
    //    alert(listItem.selection);&lt;br /&gt;
    var arrayV = new Array(&amp;quot;green&amp;quot;,&amp;quot;red&amp;quot;,&amp;quot;orange&amp;quot;,&amp;quot;graygreen&amp;quot;,&amp;quot;gray&amp;quot;);&lt;br /&gt;
    //var texted = new Array(&amp;quot;...&amp;quot;,&amp;quot;error:&amp;quot;,&amp;quot;rendering&amp;quot;,&amp;quot;...&amp;quot;,&amp;quot;queued&amp;quot;);&lt;br /&gt;
    var item = list.add (&amp;#039;item&amp;#039;,name);&lt;br /&gt;
    //$.writeln(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+array[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    item.image = File(scriptFolder.toString()+&amp;quot;/flag_&amp;quot;+arrayV[state]+&amp;quot;.png&amp;quot;);&lt;br /&gt;
    //item.subItems[0].helpTip = texted(state);&lt;br /&gt;
    item.subItems[0].text =msg;&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
///////////////////////////////////////////&lt;br /&gt;
           /* if((logFolder =  getFilePath(watchfolderLocation+&amp;quot;/&amp;quot;+shot)) != undefined){&lt;br /&gt;
                $.writeln(logInfo(logFolder));&lt;br /&gt;
            }else{&lt;br /&gt;
                $.writeln(&amp;quot;no log folder&amp;quot;);&lt;br /&gt;
                }*/&lt;br /&gt;
           // $.writeln();&lt;br /&gt;
           // addItem(list,&amp;quot;G12_SC213_T1&amp;quot;,2);&lt;br /&gt;
           &lt;br /&gt;
           &lt;br /&gt;
           /*&lt;br /&gt;
var item1 = list.add (&amp;#039;item&amp;#039;, &amp;#039;GB15_SC138_T1&amp;#039;);&lt;br /&gt;
item1.image = File(&amp;quot;~/Desktop/flag_gray.png&amp;quot;);&lt;br /&gt;
item1.subItems[0].text = &amp;#039;Queued...&amp;#039;;&lt;br /&gt;
item1.enabled = false;&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
            //alert(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot));&lt;br /&gt;
          //  pBar.value = Math.round(getPercentage(watchfolderLocation+&amp;quot;/&amp;quot;+shot)*100);&lt;br /&gt;
          &lt;br /&gt;
                       // alert(&amp;quot;test&amp;quot;);&lt;br /&gt;
              //$.writeln(files[file].path+&amp;quot;/&amp;quot;+files[file].name);&lt;br /&gt;
                            //  a+=  getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name).name+&amp;quot;\n&amp;quot;;&lt;br /&gt;
                            &lt;br /&gt;
/*                            &lt;br /&gt;
                            &lt;br /&gt;
                            {&lt;br /&gt;
                                &lt;br /&gt;
        wfLocBtn.onClick = function(){&lt;br /&gt;
            app.scheduleTask(&amp;quot;loop()&amp;quot;,2000,1);&lt;br /&gt;
&lt;br /&gt;
           &lt;br /&gt;
            fold = new Folder( watchfolderLocation.toString());&lt;br /&gt;
            files = fold.getFiles();&lt;br /&gt;
                a= &amp;quot;&amp;quot;;&lt;br /&gt;
            for(file in files){&lt;br /&gt;
&lt;br /&gt;
              $.writeln(getFilePath(files[file].path+&amp;quot;/&amp;quot;+files[file].name));&lt;br /&gt;
&lt;br /&gt;
            }&lt;br /&gt;
           alert(a);&lt;br /&gt;
&lt;br /&gt;
            writeLn(pBar.value);&lt;br /&gt;
        }&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Import pos from maya===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track&amp;quot;,&lt;br /&gt;
[100, 100, 300, 300]);&lt;br /&gt;
impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 10, 100, 30], &amp;quot;Import&amp;quot;);&lt;br /&gt;
impButton.onClick = openfile;&lt;br /&gt;
return myPanel;&lt;br /&gt;
}&lt;br /&gt;
var myToolsPanel = createUI(this);&lt;br /&gt;
//myToolsPanel.show();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function openfile(){&lt;br /&gt;
	            var myFile = File.openDialog (&amp;quot;Select track file&amp;quot;,&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
            var fileOK = myFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
	//var fileD = OpenDlg (&amp;quot;Tracking Point File&amp;quot;,&amp;quot;*.txt&amp;quot;,true);&lt;br /&gt;
	//txt = fileD.readln ()&lt;br /&gt;
	alert(&amp;quot;txt&amp;quot;+readTxt(myFile));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function readTxt(myFile){&lt;br /&gt;
var myText = myFile.read();	&lt;br /&gt;
return myText;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Dandelion/Amazing World Of Gumball Shotbuilder===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
http://imgur.com/xKJWB.png&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For education purposes only, the script was run in production to helb build shots, it:&lt;br /&gt;
* created a list of shots &amp;amp; shows possible given a folder structure (&amp;quot;GB##_SHOWNAME_SC##_T##&amp;quot;)&lt;br /&gt;
* opened the last .AEP file found in said shot folder&lt;br /&gt;
* if no .AEP found, built a comp according to a given pre-cut animatic movie file found in previous folder&lt;br /&gt;
* imported footage from the appropriate sources folder (and made sure not to import twice when you re-clicked the &amp;#039;grab sources&amp;#039; button&lt;br /&gt;
* automatically sent files to the watchfolder to render on a small-ish farm. &lt;br /&gt;
Other buttons allowed to check for missing footage, open the comp&amp;#039;s folder, open the current shows&amp;#039; latest animatic, etc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//  &lt;br /&gt;
//  Dandelion Shot Builder for &amp;quot;the Amazing World Of Gumball&amp;quot; v0.7 by Bernie&lt;br /&gt;
//   last update 21/11/10&lt;br /&gt;
//&lt;br /&gt;
//  Known Bugs:&lt;br /&gt;
//      &lt;br /&gt;
//  -you can&amp;#039;t have several sequences in a single folder and expect the script to pick up all the sequences&lt;br /&gt;
//  -this will not set color profiles&lt;br /&gt;
// //TODO&lt;br /&gt;
//  &amp;gt;&amp;gt;&amp;gt; CHECK IF WF FOLDER ALREADY EXISTS&lt;br /&gt;
// &amp;gt;&amp;gt;&amp;gt;&amp;gt; GET TAKE FROM N DRIVE, NOT OUT FOLDER&lt;br /&gt;
//  -v0.7 fixes&lt;br /&gt;
//        -new scene after WF works properly&lt;br /&gt;
//        -changed output folder location to N:&lt;br /&gt;
//  -v0.6 fixes&lt;br /&gt;
//       -cancel watchfolder cancels watchfolder&lt;br /&gt;
//        -fixed GB##_SC_###_T1&lt;br /&gt;
//       -added options&lt;br /&gt;
//       -can work on a new location&lt;br /&gt;
//  -v0.5 fixes&lt;br /&gt;
//      -shows allow for a letter in the comp now (ie GB##_SC###a_T#)&lt;br /&gt;
//      -will warn if there is missing footage before sending to WF&lt;br /&gt;
//      -removed set take, added &amp;#039;missing&amp;#039; dialog.&lt;br /&gt;
//  -v0.4 fixes&lt;br /&gt;
//      -there shouldn&amp;#039;t be a refresh problem on show change anymore&lt;br /&gt;
//  -v0.3 fixes&lt;br /&gt;
//      -changed watchfolder location to anthony&amp;#039;s mac&lt;br /&gt;
//      -changed save as dialog&lt;br /&gt;
//      -removed unused buttons&lt;br /&gt;
//      -added WF (watchfolder) FE (for edit) XLS (comp chart) ANI (animatic)&lt;br /&gt;
//  -v0.2 fixes&lt;br /&gt;
//      -sequences weren&amp;#039;t getting imported.&lt;br /&gt;
//      -fixed UI/little problems&lt;br /&gt;
//      -added folders, refresh button, save before watchfoldering&lt;br /&gt;
var version = &amp;quot;0.7&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//default locations&lt;br /&gt;
var watchfolderLocation = &amp;quot;/c/__WATCHFOLDER__&amp;quot;;&lt;br /&gt;
var rootFolder = &amp;quot;/c/&amp;quot;;&lt;br /&gt;
var forEditFolderLoaction = &amp;quot;//MAC0023DFDF5429/for%20edit&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
rootFolder = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;)):rootFolder;&lt;br /&gt;
watchfolderLocation = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;)):watchfolderLocation;&lt;br /&gt;
forEditFolderLoaction = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;forEditFolderLoaction&amp;quot;)):forEditFolderLoaction;&lt;br /&gt;
&lt;br /&gt;
          //dirty hack&lt;br /&gt;
            a = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true;&lt;br /&gt;
            b = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;)):false;&lt;br /&gt;
            c = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;)):true;&lt;br /&gt;
            if(a==&amp;quot;false&amp;quot;) a = false;&lt;br /&gt;
            if(a==&amp;quot;true&amp;quot;) a = true;&lt;br /&gt;
            if(b==&amp;quot;false&amp;quot;) b = false;&lt;br /&gt;
            if(b==&amp;quot;true&amp;quot;) b = true;&lt;br /&gt;
            if(c==&amp;quot;false&amp;quot;) c = false;&lt;br /&gt;
            if(c==&amp;quot;true&amp;quot;) c = true;&lt;br /&gt;
            &lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//var watchfolderLocation = &amp;quot;/w/09_Dandelions_COMP-SHOWS/_DUMP_Watchfolder_&amp;quot;;&lt;br /&gt;
var loadFile = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;)):&amp;quot;&amp;quot;;&lt;br /&gt;
checkOutputSettings();&lt;br /&gt;
&lt;br /&gt;
Array.prototype.has = function(value) {&lt;br /&gt;
    var i;&lt;br /&gt;
    for (i=0;i&amp;lt;this.length;i++) {&lt;br /&gt;
        if (this[i] == value) {&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function addShotDialog(sel){&lt;br /&gt;
    alert(sel);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function pathToWinPath(path){&lt;br /&gt;
    path = path.toString();&lt;br /&gt;
	str = path.replace(/\//, &amp;quot;&amp;quot;);&lt;br /&gt;
	str = str.replace(/\//, &amp;quot;:/&amp;quot;);&lt;br /&gt;
	str = str.replace(/%20/g, &amp;quot; &amp;quot;);&lt;br /&gt;
	str = str.replace(/\//g, &amp;quot;\\&amp;quot;);&lt;br /&gt;
	return str;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnFolderArray(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
    foldArray = fold.getFiles()&lt;br /&gt;
    folderOnly = new Array();&lt;br /&gt;
    for(i=0;i&amp;lt;foldArray.length;i++){&lt;br /&gt;
        if(foldArray[i] instanceof Folder){&lt;br /&gt;
            folderOnly[folderOnly.length] = foldArray[i];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    return folderOnly;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function sendToWatchFolder(watchfolderlocation,compname){&lt;br /&gt;
    ocn = compname;&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
        c = confirm(&amp;quot;Save &amp;quot;+app.project.file.name+&amp;quot; ?&amp;quot;,false,&amp;quot;Save Project&amp;quot;);&lt;br /&gt;
        if(c){&lt;br /&gt;
            app.project.save();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    curFile = app.project.file;&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
    compname += &amp;quot;_WF&amp;quot;;&lt;br /&gt;
    myFolder.create();&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;.aep&amp;quot;);        &lt;br /&gt;
    app.project.save(mySaveFile);&lt;br /&gt;
    myTextFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname.substring(0,22)+&amp;quot;_RCF.txt&amp;quot;);    &lt;br /&gt;
    myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    text = &amp;quot;After Effects 10.0v1 Render Control File\nmax_machines=5\nnum_machines=0\ninit=0\nhtml_init=0\nhtml_name=\&amp;quot;\&amp;quot;\n&amp;quot; ;&lt;br /&gt;
    myTextFile.write(text);&lt;br /&gt;
    myTextFile.close();&lt;br /&gt;
    writeInfo(watchfolderlocation,ocn);&lt;br /&gt;
    if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;) == &amp;quot;false&amp;quot;){&lt;br /&gt;
        opFile = new File(curFile);&lt;br /&gt;
        if(opFile){&lt;br /&gt;
            app.open(opFile);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
        app.project.new();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    //explore(watchfolderlocation);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function writeInfo(watchfolderlocation,compname){&lt;br /&gt;
    myFolder = new Folder(watchfolderlocation.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_watch/&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    mySaveFile = new File(myFolder.toString()+&amp;quot;/&amp;quot;+compname+&amp;quot;_DandyWatch.txt&amp;quot;);    &lt;br /&gt;
    mySaveFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    rq = app.project.renderQueue;&lt;br /&gt;
    text =&amp;quot;&amp;quot;;    &lt;br /&gt;
    for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
        if(rq.items[i].render){&lt;br /&gt;
            text += rq.items[i].outputModules[1].file.path+&amp;quot;\n&amp;quot;;&lt;br /&gt;
            text += (Math.round(rq.items[i].timeSpanDuration*25)+&amp;quot;\n&amp;quot;);&lt;br /&gt;
            text += system.callSystem(&amp;quot;hostname&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    mySaveFile.write(text);&lt;br /&gt;
    mySaveFile.close();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function explore(location){&lt;br /&gt;
     fold = new Folder(location.toString());&lt;br /&gt;
     return fold.execute();     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkForMissingFootage(dialogFlag){&lt;br /&gt;
        var dialog = dialogFlag;&lt;br /&gt;
        var missing = false;&lt;br /&gt;
        for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
            app.project.items[i].selected = false;&lt;br /&gt;
            if(app.project.items[i] instanceof FootageItem &amp;amp;&amp;amp; app.project.items[i].file != null){&lt;br /&gt;
                            if(app.project.items[i].footageMissing){&lt;br /&gt;
                                app.project.items[i].selected = true;&lt;br /&gt;
                                missing = true;&lt;br /&gt;
                                if(dialog){&lt;br /&gt;
                                    dialog = prompt(&amp;quot;Original folder of \&amp;quot;&amp;quot;+(app.project.items[i].name)+&amp;quot;\&amp;quot; :&amp;quot;,pathToWinPath(app.project.items[i].file),&amp;quot;Missing footage! (hit cancel to suppress further dialogs)&amp;quot;);&lt;br /&gt;
                                }&lt;br /&gt;
                            }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return missing;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutputSettings(){&lt;br /&gt;
    renderSettingsSet = ((app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;)))?true:false;&lt;br /&gt;
   // renderSettingsSet = 1;&lt;br /&gt;
    if(!renderSettingsSet){&lt;br /&gt;
                alert(&amp;quot;Tiff output being setup, this should happen only once. It needs to close the current comp.&amp;quot;);&lt;br /&gt;
                opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
                if(opFile.exists){&lt;br /&gt;
                     app.open(opFile);&lt;br /&gt;
                     app.project.renderQueue.items[1].outputModules[1].saveAsTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                     app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;tiffsettings&amp;quot;,&amp;quot;is there&amp;quot;);&lt;br /&gt;
                     app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);&lt;br /&gt;
                     app.newProject();&lt;br /&gt;
                 }else{&lt;br /&gt;
                     alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function checkOutFolder(tpath){&lt;br /&gt;
    foldFile = new Folder(tpath);&lt;br /&gt;
    //$.writeln(tpath);&lt;br /&gt;
    if(foldFile.exists){&lt;br /&gt;
        files = foldFile.getFiles();  &lt;br /&gt;
        if(files.length &amp;gt; 1){&lt;br /&gt;
            return false;&lt;br /&gt;
        }else{&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
      newF = foldFile.create();&lt;br /&gt;
        return newF;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function getSetTake(force){&lt;br /&gt;
    show = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;);// showDDL.selection.toString();&lt;br /&gt;
    shot = app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;); //shotDDL.selection.toString();&lt;br /&gt;
    projectTake = getTake(findMainShot().name);&lt;br /&gt;
//    paths  = rootFolder+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/OUT/&amp;quot;;&lt;br /&gt;
    paths  = &amp;quot;/n/01_OUT/&amp;quot;+show+&amp;quot;/&amp;quot;+shot+&amp;quot;/&amp;quot;;&lt;br /&gt;
    lastFolder = getLastModified(paths);&lt;br /&gt;
    //alert(lastFolder.name+&amp;quot; --&amp;gt;&amp;quot;+paths);&lt;br /&gt;
    if(lastFolder){&lt;br /&gt;
        folderTake = getTake(lastFolder.name);&lt;br /&gt;
    }else{&lt;br /&gt;
        folderTake = false;&lt;br /&gt;
    }&lt;br /&gt;
    msgPt1 = (folderTake)? &amp;quot;The last take in OUT folder is take &amp;quot;+(folderTake)+&amp;quot;.\n&amp;quot;:&amp;quot;No take was found in the OUT folder.\n&amp;quot;;&lt;br /&gt;
    msgPt2 = &amp;quot;The current project take is &amp;quot;+projectTake+&amp;quot;. \n\nChoose the current take:&amp;quot;;&lt;br /&gt;
    if(force || (folderTake != (projectTake-1))){&lt;br /&gt;
        recommended = (folderTake)?((folderTake)+1):projectTake;&lt;br /&gt;
        answer = prompt(msgPt1+msgPt2,recommended);&lt;br /&gt;
        //$.writeln(&amp;quot;blabal &amp;quot;+answer);&lt;br /&gt;
        if(answer != null){&lt;br /&gt;
            return answer;&lt;br /&gt;
        }else{&lt;br /&gt;
             return false;&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
    return projectTake;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function setTake(value){&lt;br /&gt;
    comp = findMainShot();&lt;br /&gt;
    comp.name = comp.name.substring(0,(comp.name.lastIndexOf(&amp;quot;_T&amp;quot;)+2))+value;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getTake(normalizedName){&lt;br /&gt;
    tmp = normalizedName.split(&amp;quot;_T&amp;quot;);&lt;br /&gt;
    return  parseFloat(tmp[tmp.length-1]);&lt;br /&gt;
}&lt;br /&gt;
	// Smart Import.jsx&lt;br /&gt;
	//adobe-shipped smart import with tweaks to not import files that are already here, and not screw up in some cases&lt;br /&gt;
    //the only problem is that you can still not have 2 sequences in a single folder&lt;br /&gt;
   &lt;br /&gt;
function SmartImport(targetFolder){&lt;br /&gt;
         writeLn(&amp;quot;Importing files...&amp;quot;);&lt;br /&gt;
         var importedFiles = 0;&lt;br /&gt;
		var scriptName = &amp;quot;Smart Import&amp;quot;;&lt;br /&gt;
		var sourcePaths = getSourcePathsArray();&lt;br /&gt;
		// Ask the user for a folder whose contents are to be imported.&lt;br /&gt;
		//var targetFolder = Folder.selectDialog(&amp;quot;Import items from folder...&amp;quot;);&lt;br /&gt;
		if (targetFolder != null) {&lt;br /&gt;
			// If no project open, create a new project to import the files into.&lt;br /&gt;
			function processFile(theFile)&lt;br /&gt;
			{&lt;br /&gt;
				try {&lt;br /&gt;
					// Create a variable containing ImportOptions.&lt;br /&gt;
					var importOptions = new ImportOptions(theFile);&lt;br /&gt;
                    //alert();&lt;br /&gt;
                     if(theFile.name.toString().toLowerCase().lastIndexOf(&amp;quot;.psd&amp;quot;) != -1){&lt;br /&gt;
                        importOptions.importAs = ImportAsType.COMP;&lt;br /&gt;
                     }&lt;br /&gt;
                       importSafeWithError(importOptions);&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					// Ignore errors.&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			function testForSequences(files)&lt;br /&gt;
			{&lt;br /&gt;
				var searcher = new RegExp(&amp;quot;[0-9]{3,}$&amp;quot;);&lt;br /&gt;
				var movieFileSearcher = new RegExp(&amp;quot;(mov|avi|mpg)$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
				var parseResults = new Array;&lt;br /&gt;
                  var isFolder = new Array;&lt;br /&gt;
				&lt;br /&gt;
				// Test that we have a sequence. Stop parsing after 10 files.&lt;br /&gt;
				for (x = 0; (x &amp;lt; files.length) &amp;amp; x &amp;lt; 10; x++) {&lt;br /&gt;
					var movieFileResult = movieFileSearcher.exec(files[x].name);&lt;br /&gt;
					if (!movieFileResult) {&lt;br /&gt;
//******                           splitName = files[x].name.split(&amp;#039;.&amp;#039;)[0];&lt;br /&gt;
                            splitName=files[x].name.substring(0,files[x].name.length-4);&lt;br /&gt;
&lt;br /&gt;
                           //splitName[splitNameA.length] = &lt;br /&gt;
                           //alert(splitName);	&lt;br /&gt;
                         //  alert(files[x].name+&amp;quot; &amp;gt;&amp;gt;&amp;gt; &amp;quot;+files[x].name.split(&amp;#039;.&amp;#039;)[0]);&lt;br /&gt;
						var currentResult = searcher.exec(splitName);&lt;br /&gt;
						// Regular expressions return null if no match was found.&lt;br /&gt;
						// Otherwise, they return an array with the following information:&lt;br /&gt;
						// array[0] = the matched string.&lt;br /&gt;
						// array[1..n] = the matched capturing parentheses.&lt;br /&gt;
                   				&lt;br /&gt;
                    if (currentResult) { // We have a match -- the string contains numbers.&lt;br /&gt;
                           &lt;br /&gt;
                            // The match of those numbers is stored in the array[1]&lt;br /&gt;
							// Take that number and save it into parseResults.&lt;br /&gt;
							parseResults[parseResults.length] = currentResult[0];&lt;br /&gt;
						} else {&lt;br /&gt;
							parseResults[parseResults.length] = null;&lt;br /&gt;
						}&lt;br /&gt;
                           if(files[x].name == splitName){&lt;br /&gt;
                                isFolder[isFolder.length] = true;&lt;br /&gt;
                               // alert(&amp;quot;Folder &amp;quot;+files[x].name);&lt;br /&gt;
                           }else{&lt;br /&gt;
                                isFolder[isFolder.length] = false;&lt;br /&gt;
                           }&lt;br /&gt;
					} else {&lt;br /&gt;
						parseResults[parseResults.length] = null;&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// If all the files we just went through have a number in their file names, &lt;br /&gt;
				// assume they are part of a sequence and return the first file.&lt;br /&gt;
				&lt;br /&gt;
				var result = null;&lt;br /&gt;
				for (i = 0; i &amp;lt; parseResults.length; ++i) {&lt;br /&gt;
                   				&lt;br /&gt;
                   if(!isFolder[i]){&lt;br /&gt;
                    if (parseResults[i]) {&lt;br /&gt;
						if (!result) {&lt;br /&gt;
                            &lt;br /&gt;
							result = files[i];		&lt;br /&gt;
						}&lt;br /&gt;
					} else {&lt;br /&gt;
                        &lt;br /&gt;
						// In this case, a file name did not contain a number.&lt;br /&gt;
						result = null;&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
                   }&lt;br /&gt;
                }&lt;br /&gt;
				&lt;br /&gt;
				return result;&lt;br /&gt;
			}&lt;br /&gt;
        			&lt;br /&gt;
			&lt;br /&gt;
			function importSafeWithError(importOptions)&lt;br /&gt;
			{&lt;br /&gt;
              if(!(sourcePaths.has(importOptions.file.toString()))){&lt;br /&gt;
 				try { &lt;br /&gt;
                  b = app.project.importFile(importOptions);&lt;br /&gt;
                    writeLn(importOptions.file.toString());&lt;br /&gt;
                 sfi = sourcesFolderItem();&lt;br /&gt;
                   b.parentFolder = sfi;&lt;br /&gt;
                    if(importOptions.importAs == ImportAsType.COMP){&lt;br /&gt;
                        for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                            if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name.lastIndexOf(&amp;quot; Layers&amp;quot;) != -1){&lt;br /&gt;
                         app.project.items[i].parentFolder = sfi;&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                        writeLn(&amp;quot;Importing file (&amp;quot;+importedFiles+&amp;quot;)&amp;quot;);&lt;br /&gt;
                        importedFiles++;&lt;br /&gt;
				} catch (error) {&lt;br /&gt;
					alert(error.toString() + importOptions.file.fsName, scriptName);&lt;br /&gt;
				}&lt;br /&gt;
                // }else{&lt;br /&gt;
                //        alert(&amp;quot;file already here&amp;quot;);&lt;br /&gt;
                 }&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			&lt;br /&gt;
			function processFolder(theFolder)&lt;br /&gt;
			{&lt;br /&gt;
				// Get an array of files in the target folder.&lt;br /&gt;
				var files = theFolder.getFiles();&lt;br /&gt;
				//alert(files);&lt;br /&gt;
				// Test whether theFolder contains a sequence.&lt;br /&gt;
				var sequenceStartFile = testForSequences(files);&lt;br /&gt;
				&lt;br /&gt;
				// If it does contain a sequence, import the sequence,&lt;br /&gt;
				if (sequenceStartFile) {&lt;br /&gt;
					try {&lt;br /&gt;
						// Create a variable containing ImportOptions.&lt;br /&gt;
						var importOptions = new ImportOptions(sequenceStartFile);&lt;br /&gt;
						importOptions.sequence = true;&lt;br /&gt;
						// importOptions.forceAlphabetical = true;		// Un-comment this if you want to force alpha order by default.&lt;br /&gt;
						importSafeWithError(importOptions);&lt;br /&gt;
					} catch (error) {&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				&lt;br /&gt;
				// Otherwise, import the files and recurse.&lt;br /&gt;
				&lt;br /&gt;
				for (index in files) { // Go through the array and set each element to singleFile, then run the following.&lt;br /&gt;
					if (files[index] instanceof File) {&lt;br /&gt;
						if (!sequenceStartFile) { // If file is already part of a sequence, don&amp;#039;t import it individually.&lt;br /&gt;
							processFile(files[index]); // Calls the processFile function above.&lt;br /&gt;
						}&lt;br /&gt;
					}&lt;br /&gt;
					if (files[index] instanceof Folder) {&lt;br /&gt;
						processFolder(files[index]); // recursion&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			&lt;br /&gt;
			// Recursively examine that folder.&lt;br /&gt;
			processFolder(targetFolder);&lt;br /&gt;
		}&lt;br /&gt;
    clearOutput();	&lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
   &lt;br /&gt;
function sourcesFolderItem(){&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
       // alert(app.project.items[i].name);&lt;br /&gt;
     if(app.project.items[i].typeName == &amp;quot;Folder&amp;quot; &amp;amp;&amp;amp; app.project.items[i].name == &amp;quot;Sources&amp;quot;){&lt;br /&gt;
           return app.project.items[i];&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
    return app.project.items.addFolder(&amp;quot;Sources&amp;quot;);&lt;br /&gt;
    //else&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getSourcePathsArray(){&lt;br /&gt;
    var footageLocations = new Array();&lt;br /&gt;
    for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
        if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
            footageLocations[footageLocations.length] = app.project.item(i).file;&lt;br /&gt;
        }    &lt;br /&gt;
    }&lt;br /&gt;
    return footageLocations;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function getLastModified(location,extension){&lt;br /&gt;
      myFolder = new Folder(location.toString());&lt;br /&gt;
      if (!myFolder instanceof Folder){&lt;br /&gt;
          return false;&lt;br /&gt;
     }&lt;br /&gt;
     listFiles = myFolder.getFiles(extension);&lt;br /&gt;
     listDates = new Array();&lt;br /&gt;
     maxId = 0;&lt;br /&gt;
     maxValue = 0;&lt;br /&gt;
     for(i=0;i&amp;lt;listFiles.length;i++){&lt;br /&gt;
         if (!(extension == &amp;quot;&amp;quot; &amp;amp;&amp;amp; (listFiles[i] instanceof File))){&lt;br /&gt;
             d = listFiles[i].modified;&lt;br /&gt;
             formattedDate = d.getFullYear()+&amp;quot;&amp;quot;+pad(d.getMonth()+1,2,0)+&amp;quot;&amp;quot;+pad(d.getDate(),2,0)+&amp;quot;&amp;quot;+pad(d.getHours(),2,0)+&amp;quot;&amp;quot;+pad(d.getMinutes(),2,0)+&amp;quot;&amp;quot;+pad(d.getSeconds(),2,0);&lt;br /&gt;
             listDates[i] = formattedDate;&lt;br /&gt;
             if(Math.max(maxValue,formattedDate) == formattedDate){&lt;br /&gt;
                 maxValue = formattedDate;&lt;br /&gt;
                 maxId = i;&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
     &lt;br /&gt;
     }&lt;br /&gt;
    if(listFiles[maxId] == &amp;quot;&amp;quot; ||listFiles[maxId] == &amp;quot;undefined&amp;quot;||listFiles[maxId] == null){&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return listFiles[maxId];     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function pad(number, length, character) {&lt;br /&gt;
    if (character == null) {&lt;br /&gt;
        character = &amp;quot;0&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    var numberStr = &amp;quot;&amp;quot; + number;&lt;br /&gt;
    while (numberStr.length &amp;lt; length) {&lt;br /&gt;
        numberStr = character + numberStr;&lt;br /&gt;
    }&lt;br /&gt;
    return numberStr;&lt;br /&gt;
}&lt;br /&gt;
function grabAnimatic(folderLocation){&lt;br /&gt;
    newLocation = new Folder(folderLocation);&lt;br /&gt;
    animatic = newLocation.getFiles(&amp;quot;*.mov&amp;quot;);&lt;br /&gt;
    sfi = sourcesFolderItem();&lt;br /&gt;
    if(animatic[0]){&lt;br /&gt;
        try {&lt;br /&gt;
            var importOptions = new ImportOptions(animatic[0]);&lt;br /&gt;
            animatic = app.project.importFile(importOptions);&lt;br /&gt;
            for (i = 1; i &amp;lt;= app.project.numItems ; i++) {&lt;br /&gt;
                    if(app.project.item(i).typeName == &amp;quot;Footage&amp;quot;){&lt;br /&gt;
                   app.project.item(i).parentFolder = sfi;&lt;br /&gt;
                    }    &lt;br /&gt;
            }&lt;br /&gt;
            return animatic;&lt;br /&gt;
        }catch (error){&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
function figureOutShotName(filepath){&lt;br /&gt;
    folders = filepath.split(&amp;quot;/&amp;quot;);&lt;br /&gt;
    showFolder = folders[folders.length-2];&lt;br /&gt;
    showFolder = showFolder.split(&amp;quot;_&amp;quot;)[0];&lt;br /&gt;
    shotFolder = folders[folders.length-1];&lt;br /&gt;
    var searcher = new RegExp(&amp;quot;[0-9]{2,}$&amp;quot;);&lt;br /&gt;
    var currentResult = searcher.exec(shotFolder);&lt;br /&gt;
    if (currentResult){&lt;br /&gt;
        shotFolder = currentResult[0];&lt;br /&gt;
    }&lt;br /&gt;
    return showFolder+&amp;quot;_SC&amp;quot;+shotFolder;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function createMainComp(path){&lt;br /&gt;
    grabAnimatic(path);&lt;br /&gt;
    shotName = figureOutShotName(path);&lt;br /&gt;
    workComp = app.project.items.addComp(shotName+&amp;quot;_T1&amp;quot;, 1920, 1080,1.0 ,animatic.duration,25.0);&lt;br /&gt;
    animaticLayer = workComp.layers.add(animatic);&lt;br /&gt;
    animaticLayer.guideLayer = true;&lt;br /&gt;
    return workComp;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function returnList2(type){&lt;br /&gt;
     if(type == &amp;quot;show&amp;quot;){&lt;br /&gt;
        rArray = returnFolderArray(rootFolder);&lt;br /&gt;
        for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
            }&lt;br /&gt;
    }else if(type == &amp;quot;scene&amp;quot;){&lt;br /&gt;
        rArray = [&amp;quot;a&amp;quot;,&amp;quot;b&amp;quot;];&lt;br /&gt;
        alert(dPanel.shotLoad.showDDL.selection.toString());&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function returnList(folder){&lt;br /&gt;
    rArray = returnFolderArray(folder);&lt;br /&gt;
    for(i=0;i&amp;lt;rArray.length;i++){&lt;br /&gt;
            rArray[i] = rArray[i].name;&lt;br /&gt;
    }&lt;br /&gt;
    return rArray;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function findMainShot(){&lt;br /&gt;
    //fix GB##_SC_###_T1&lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_SC_)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               nuName = app.project.items[i].name;&lt;br /&gt;
               nuName = nuName.split(&amp;quot;SC_&amp;quot;);&lt;br /&gt;
               app.project.items[i].name = nuName[0]+&amp;quot;SC&amp;quot;+nuName[1];&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    var findShot = new RegExp(&amp;quot;^(GB)[0-9]{2}(_)(SC)[0-9]{1,3}[a-z]{0,}(_T)[0-9]{1,}$&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
    var shot = null;&lt;br /&gt;
    for(i=1;i&amp;lt;=app.project.numItems;i++){&lt;br /&gt;
        if(app.project.items[i] instanceof CompItem){&lt;br /&gt;
           var movieFileResult =  findShot.exec(app.project.items[i].name);&lt;br /&gt;
           if(movieFileResult){&lt;br /&gt;
               shot = app.project.items[i];&lt;br /&gt;
               break;&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if(shot == null){&lt;br /&gt;
        alert(&amp;quot;Could not find a comp named like \&amp;quot;GB##_SC###(a-z)_T#\&amp;quot;\n\nRename main comp accordingly, render again&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
    return shot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function dandyShotBuilderUI(thisObj){&lt;br /&gt;
    &lt;br /&gt;
    ////////////////////////////////////// UI ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
    &lt;br /&gt;
        dPanel = (thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;DandyShotBuilderFix&amp;quot;, [100, 100, 300, 300]);&lt;br /&gt;
        var securitySetting = app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;, &amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;);&lt;br /&gt;
        if (securitySetting != 1) {&lt;br /&gt;
           var msg = &amp;quot;This script requires the scripting security preference to be set. \nGo the \&amp;quot;General\&amp;quot; panel of your application preferences and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.\n\n Restart AFX&amp;quot;;&lt;br /&gt;
           var shotInfo = dPanel.add(&amp;quot;edittext&amp;quot;,[10,5,235,305],msg,{multiline:true}); &lt;br /&gt;
         }else{&lt;br /&gt;
      var shotLoad = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,175],&amp;quot;Load Shot&amp;quot;);&lt;br /&gt;
         var shotLoadTxt1 = shotLoad.add(&amp;quot;statictext&amp;quot;,[15,24,52,44],&amp;quot;Show:&amp;quot;);&lt;br /&gt;
         var showDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,19,210,44], returnList(rootFolder));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)){&lt;br /&gt;
                 showDDL.selection = showDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 showDDL.selection = 0;&lt;br /&gt;
                 app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
                }&lt;br /&gt;
         var shotLoadTxt2 = shotLoad.add(&amp;quot;statictext&amp;quot;,[12,59,52,79],&amp;quot;Scene:&amp;quot;);&lt;br /&gt;
         var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;show&amp;quot;)));&lt;br /&gt;
            if (app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;shot&amp;quot;)){&lt;br /&gt;
                 shotDDL.selection = shotDDL.find(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;));&lt;br /&gt;
                 //parseFloat(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;));&lt;br /&gt;
                }else{&lt;br /&gt;
                 shotDDL.selection = 0;&lt;br /&gt;
                }&lt;br /&gt;
         &lt;br /&gt;
          &lt;br /&gt;
         var shotStatusTxt = shotLoad.add(&amp;quot;statictext&amp;quot;,[25,95,52,115],&amp;quot;File:&amp;quot;);&lt;br /&gt;
         var shotStatusEditTxt = shotLoad.add(&amp;quot;edittext&amp;quot;,[56,92,210,115],&amp;quot;...&amp;quot;);&lt;br /&gt;
            shotStatusEditTxt.active = false;&lt;br /&gt;
            shotStatusEditTxt.enabled = false;&lt;br /&gt;
         var shotLoadBtn = shotLoad.add(&amp;quot;button&amp;quot;,[56,125,96,150],&amp;quot;Load&amp;quot;);&lt;br /&gt;
            shotLoadBtn.enabled = false;&lt;br /&gt;
         var shotBuildBtn = shotLoad.add(&amp;quot;button&amp;quot;,[101,125,141,150],&amp;quot;Build&amp;quot;);&lt;br /&gt;
            shotBuildBtn.enabled = false;&lt;br /&gt;
         var shotRefreshBtn = shotLoad.add(&amp;quot;button&amp;quot;,[146,125,168,150],&amp;quot;r&amp;quot;);&lt;br /&gt;
         //var shotOptns = shotLoad.add(&amp;quot;button&amp;quot;,[174,125,210,150],&amp;quot;optns&amp;quot;);&lt;br /&gt;
            //shotSetBtn.enabled = false;&lt;br /&gt;
        &lt;br /&gt;
       var shotTools = dPanel.add(&amp;quot;panel&amp;quot;,[10,185,235,300],&amp;quot;Misc&amp;quot;);&lt;br /&gt;
            shotToolsBtn1 =  shotTools.add(&amp;quot;button&amp;quot;,[12,12,79,35],&amp;quot;Open Folder&amp;quot;);&lt;br /&gt;
            shotToolsBtn1Xtra =  shotTools.add(&amp;quot;button&amp;quot;,[82,12,102,35],&amp;quot;N:&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2 =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,197,35],&amp;quot;Flick Last Take&amp;quot;);&lt;br /&gt;
            //shotToolsBtn2.enabled = false; &lt;br /&gt;
            shotToolsBtnWF =  shotTools.add(&amp;quot;button&amp;quot;,[107,12,135,35],&amp;quot;WF&amp;quot;);&lt;br /&gt;
            shotToolsBtnWF.onClick = function(){explore(watchfolderLocation)};&lt;br /&gt;
             shotToolsBtnFE =  shotTools.add(&amp;quot;button&amp;quot;,[139,12,167,35],&amp;quot;FE&amp;quot;);&lt;br /&gt;
            shotToolsBtnFE.onClick = function(){explore(forEditFolderLoaction)};&lt;br /&gt;
            shotToolsBtnXLS =  shotTools.add(&amp;quot;button&amp;quot;,[172,12,197,35],&amp;quot;xls&amp;quot;);&lt;br /&gt;
            //shotToolsBtnXLS.enabled = false;             &lt;br /&gt;
                &lt;br /&gt;
            shotToolsBtn3 =  shotTools.add(&amp;quot;button&amp;quot;,[12,40,102,63],&amp;quot;Grab Sources&amp;quot;);&lt;br /&gt;
            shotToolsBtn4 =  shotTools.add(&amp;quot;button&amp;quot;,[107,40,135,63],&amp;quot;Miss&amp;quot;);&lt;br /&gt;
            shotToolsBtnANI =  shotTools.add(&amp;quot;button&amp;quot;,[139,40,167,63],&amp;quot;ANI&amp;quot;);&lt;br /&gt;
            shotToolsBtnOPT =  shotTools.add(&amp;quot;button&amp;quot;,[172,40,197,63],&amp;quot;Opt&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
              //  shotToolsBtn4.enabled = false;            &lt;br /&gt;
            shotToolsBtn5 =  shotTools.add(&amp;quot;button&amp;quot;,[12,68,102,91],&amp;quot;Renderqueue&amp;quot;);&lt;br /&gt;
            shotToolsBtn6 =  shotTools.add(&amp;quot;button&amp;quot;,[107,68,197,91],&amp;quot;To Watchfolder&amp;quot;);&lt;br /&gt;
                //shotToolsBtn6.enabled = false; &lt;br /&gt;
            }&lt;br /&gt;
       var optnsGrp = dPanel.add(&amp;quot;panel&amp;quot;,[10,5,235,300],&amp;quot;Options&amp;quot;);&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
            mgL = 20;&lt;br /&gt;
            mgT=15;&lt;br /&gt;
            mgBet=3;&lt;br /&gt;
            i=0;&lt;br /&gt;
            w=210;&lt;br /&gt;
            h=23;&lt;br /&gt;
            version = optnsGrp.add(&amp;quot;statictext&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;version &amp;quot;+version);i++;&lt;br /&gt;
            optMissingFootage =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Check for missing footage\rlonger, but safer&amp;quot;);i++;&lt;br /&gt;
            //$.writeln(&amp;quot;load missing footage pref set to&amp;quot;+(app.settings.haveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;))?(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;)):true);&lt;br /&gt;
                optMissingFootage.value = a;&lt;br /&gt;
            optNuComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;New comp after sending to WF&amp;quot;);i++;&lt;br /&gt;
                optNuComp.value = b;&lt;br /&gt;
            optSaveComp =  optnsGrp.add(&amp;quot;checkbox&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Ask to save before sending to WF&amp;quot;);i+=2;&lt;br /&gt;
                optSaveComp.value = c;&lt;br /&gt;
            rootFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Root folder location&amp;quot;);i++;&lt;br /&gt;
            watchFolderLoc =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;Watchfolder location&amp;quot;);i+=2;&lt;br /&gt;
            okBtn =  optnsGrp.add(&amp;quot;button&amp;quot;,[mgL,mgT+(i*(h+mgBet)),w,mgT+((i+1)*(h+mgBet))],&amp;quot;OK&amp;quot;);&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
       ///////////////////////////////// UI FUNCTION ///////////////////////////////////////////////////////////&lt;br /&gt;
       &lt;br /&gt;
        showDDL.onChange = function(){&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;show&amp;quot;,showDDL.selection.toString());&lt;br /&gt;
           // shotLoad.remove(shotDDL);&lt;br /&gt;
          shotDDL.removeAll();&lt;br /&gt;
&lt;br /&gt;
          items = returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString());&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               shotDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
        }&lt;br /&gt;
        shotDDL.onChange = function(){&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;,&amp;quot;shot&amp;quot;,shotDDL.selection.toString());&lt;br /&gt;
            //$.writeln(&amp;quot;Changing&amp;quot;);&lt;br /&gt;
            var lastAEP = getLastModified(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/AEP&amp;quot;,&amp;quot;*.aep&amp;quot;);&lt;br /&gt;
            if(!lastAEP){&lt;br /&gt;
                shotStatusEditTxt.text = &amp;quot;no shot found, build one!&amp;quot;;&lt;br /&gt;
                   shotLoadBtn.enabled = false;&lt;br /&gt;
                   shotBuildBtn.enabled = true;&lt;br /&gt;
            &lt;br /&gt;
            }else{&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;loadscene&amp;quot;,lastAEP.toString());&lt;br /&gt;
                loadFile = lastAEP.toString();&lt;br /&gt;
                fullSplitPath = lastAEP.toString().split(&amp;quot;/&amp;quot;);&lt;br /&gt;
                fileName = fullSplitPath[fullSplitPath.length-1];&lt;br /&gt;
                if(fileName.length &amp;gt; 20){&lt;br /&gt;
                    shotStatusEditTxt.text = fileName.substr(0,12)+&amp;quot;[...]&amp;quot;+fileName.substr(fileName.length-8);&lt;br /&gt;
                }else{&lt;br /&gt;
                    shotStatusEditTxt.text = fileName;&lt;br /&gt;
                } &lt;br /&gt;
                    shotLoadBtn.enabled = true;&lt;br /&gt;
                    shotBuildBtn.enabled = false;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotLoadBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(loadFile);&lt;br /&gt;
            if(opFile){&lt;br /&gt;
             app.open(opFile);&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Can&amp;#039;t open file&amp;quot;);&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
        shotRefreshBtn.onClick = function(){&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
            writeLn(&amp;quot;If it doesn&amp;#039;t seem to refresh, close and launch again.&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        shotBuildBtn.onClick = function(){&lt;br /&gt;
            opFile = new File(rootFolder+&amp;quot;base.aep&amp;quot;);&lt;br /&gt;
            if(opFile.exists){&lt;br /&gt;
                app.open(opFile);&lt;br /&gt;
                for(i=1;i&amp;lt;=app.project.numItems;i++){                     &lt;br /&gt;
                    app.project.items[i].remove();&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;Error:\n\nThere should be a file called\n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;base.aep&amp;quot;+&amp;quot;\&amp;quot;&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            createMainComp(targetFolder);&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            app.project.items.addFolder(&amp;quot;Precomps&amp;quot;);&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
            saveConf = confirm(&amp;quot;Save &amp;quot;+shotDDL.selection.toString()+&amp;quot; in the right folder ?&amp;quot;,false,&amp;quot;Dandelion Shot Builder&amp;quot;);&lt;br /&gt;
            if(saveConf){&lt;br /&gt;
                saveFile = new File(targetFolder+&amp;quot;/AEP/&amp;quot;+showDDL.selection.toString()+&amp;quot;_&amp;quot;+shotDDL.selection.toString()+&amp;quot;_comp01.aep&amp;quot;);&lt;br /&gt;
                app.project.save(saveFile);&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1.onClick = function(){&lt;br /&gt;
            explore(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString());&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn1Xtra.onClick = function(){&lt;br /&gt;
            //$.writeln(&amp;quot;button&amp;quot;);&lt;br /&gt;
            p = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            checkOutFolder(p);&lt;br /&gt;
            explore(p);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtn3.onClick = function(){&lt;br /&gt;
            app.project.bitsPerChannel = 16;&lt;br /&gt;
            targetFolder = rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString();&lt;br /&gt;
            importFolder = new Folder(targetFolder+&amp;quot;/Source&amp;quot;);&lt;br /&gt;
            SmartImport(importFolder);&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnXLS.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.xls*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .xls shortcut found in in:&amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
       shotToolsBtnANI.onClick = function(){&lt;br /&gt;
            loc =  rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString();&lt;br /&gt;
            fold = new Folder(loc.toString());&lt;br /&gt;
            files = fold.getFiles(&amp;quot;*.mov*&amp;quot;);&lt;br /&gt;
            if(files.length &amp;gt; 0){&lt;br /&gt;
                files[0].execute();&lt;br /&gt;
            }else{&lt;br /&gt;
                writeLn(&amp;quot;No .mov shortcut found in in: &amp;quot;);&lt;br /&gt;
                writeLn(showDDL.selection.toString());&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        shotToolsBtnOPT.onClick = function(){&lt;br /&gt;
            shotTools.visible = false;&lt;br /&gt;
            shotLoad.visible = false;&lt;br /&gt;
            optnsGrp.visible = true;&lt;br /&gt;
        }&lt;br /&gt;
    okBtn.onClick = function(){&lt;br /&gt;
            shotTools.visible = true;&lt;br /&gt;
            shotLoad.visible = true;&lt;br /&gt;
            optnsGrp.visible = false;&lt;br /&gt;
           &lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;,optMissingFootage.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;,optNuComp.value);&lt;br /&gt;
            app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optSaveComp&amp;quot;,optSaveComp.value);&lt;br /&gt;
           // $.writeln(&amp;quot;new comp checked: &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optNuComp&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
        //send to renderqueue&lt;br /&gt;
        //uses the comp name to figure out folder&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
        shotToolsBtn5.onClick = function(){&lt;br /&gt;
        rflag = false;&lt;br /&gt;
        //$.writeln(&amp;quot;missing fottage &amp;quot;+app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;));&lt;br /&gt;
        if(app.settings.getSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;optMissingFootage&amp;quot;) == &amp;quot;true&amp;quot;){&lt;br /&gt;
            rflag = checkForMissingFootage(false);&lt;br /&gt;
        }&lt;br /&gt;
        if(!rflag){&lt;br /&gt;
            rcomp = findMainShot();&lt;br /&gt;
                if(rcomp){&lt;br /&gt;
                    shotNumber = getSetTake();&lt;br /&gt;
                    if(shotNumber != false){&lt;br /&gt;
                        //$.writeln(shotNumber);&lt;br /&gt;
                        setTake(shotNumber);&lt;br /&gt;
                        rq = app.project.renderQueue;&lt;br /&gt;
                        for(i=1;i&amp;lt;=rq.numItems;i++){    &lt;br /&gt;
                                rq.items[i].render = false;&lt;br /&gt;
                         }&lt;br /&gt;
                        rqitem = app.project.renderQueue.items.add(rcomp);&lt;br /&gt;
                        rqitem.outputModules[1].applyTemplate(&amp;quot;TiffOutput&amp;quot;);&lt;br /&gt;
                        rqitem.applyTemplate(&amp;quot;Multi-Machine Settings&amp;quot;);&lt;br /&gt;
                        //savePath = rootFolder+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/OUT/&amp;quot;+rcomp.name;&lt;br /&gt;
                        savePath = &amp;quot;/n/01_OUT/&amp;quot;+showDDL.selection.toString()+&amp;quot;/&amp;quot;+shotDDL.selection.toString()+&amp;quot;/&amp;quot;+rcomp.name;&lt;br /&gt;
                        &lt;br /&gt;
                        &lt;br /&gt;
                        if(checkOutFolder(savePath)){&lt;br /&gt;
                           rqitem.outputModules[1].file = new File(savePath+&amp;quot;/&amp;quot;+rcomp.name+&amp;quot;_[#####].tif&amp;quot;);&lt;br /&gt;
                        }else{&lt;br /&gt;
                           alert(&amp;quot;Couldn&amp;#039;t figure out an output folder for \&amp;quot;&amp;quot;+(rcomp.name)+&amp;quot;\&amp;quot;, select a folder manually.&amp;quot;);&lt;br /&gt;
                          rqitem.outputModules[1].file = new File();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                alert(&amp;quot;This project is missing source files and will not render. \nUse the &amp;#039;miss&amp;#039; button to find out where the footage should be&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn4.onClick = function(){&lt;br /&gt;
            //shotNumber = getSetTake(true);&lt;br /&gt;
            //setTake(shotNumber);&lt;br /&gt;
            checkForMissingFootage(true);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        shotToolsBtn6.onClick = function(){&lt;br /&gt;
           wf = new Folder(watchfolderLocation);&lt;br /&gt;
           if(wf.exists){&lt;br /&gt;
               sendToWatchFolder(wf,findMainShot().name);&lt;br /&gt;
               }else{&lt;br /&gt;
                   alert(&amp;quot;Watchfolder error, sorry!&amp;quot;);&lt;br /&gt;
               }&lt;br /&gt;
        }&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    rootFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Root folder is currently set to \n\n\&amp;quot;&amp;quot;+rootFolder+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New root folder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;rootfolder&amp;quot;,v);     &lt;br /&gt;
                rootFolder = v;&lt;br /&gt;
                          showDDL.removeAll();&lt;br /&gt;
                          items = returnList(rootFolder);&lt;br /&gt;
          //  alert(items);&lt;br /&gt;
            for(i in items){&lt;br /&gt;
               showDDL.add(&amp;quot;item&amp;quot;, items[i]);&lt;br /&gt;
            }&lt;br /&gt;
            //var shotDDL = shotLoad.add (&amp;quot;dropdownlist&amp;quot;, [56,54,210,79], returnList(rootFolder+&amp;quot;/&amp;quot;+showDDL.selection.toString()));&lt;br /&gt;
            shotDDL.selection = 0;&lt;br /&gt;
            shotDDL.notify();&lt;br /&gt;
                showDDL.notify();&lt;br /&gt;
                shotDDL.notify();&lt;br /&gt;
                //$.writeln(&amp;quot;notified&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    watchFolderLoc.onClick = function(){&lt;br /&gt;
        y = confirm(&amp;quot;Watchfolder is currently set to \n\n\&amp;quot;&amp;quot;+watchfolderLocation+&amp;quot;\&amp;quot;\n\nChange it ?&amp;quot;);&lt;br /&gt;
        if(y){&lt;br /&gt;
            //v = new Folder();&lt;br /&gt;
            v = Folder.selectDialog (&amp;quot;New watchfolder location&amp;quot;).toString();&lt;br /&gt;
            if(v!=null &amp;amp;&amp;amp; v!=&amp;quot;undefined&amp;quot;){&lt;br /&gt;
                app.settings.saveSetting(&amp;quot;dandybuildPrefs&amp;quot;, &amp;quot;watchfolderLocation&amp;quot;,v);     &lt;br /&gt;
                watchfolderLocation = v;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    shotDDL.notify();&lt;br /&gt;
    }&lt;br /&gt;
    dandyShotBuilderUI(this) ;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===layer Position to .txt===&lt;br /&gt;
https://i.imgur.com/G5DX1T0.gif&lt;br /&gt;
Old, might not work! (but should :)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// script created by mlk: mlkdesign@gmail.com Jan 2007 (my old internet handle !)&lt;br /&gt;
//&lt;br /&gt;
// The script will write to a text file the x &amp;amp; y values of the layer position for every frame comprised in&lt;br /&gt;
// a selection of keyframes, or for the whole comp duration if no keyframes are selected&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
function timeToFrameNum(myTime){&lt;br /&gt;
   return Math.floor(myTime) * app.project.activeItem.frameRate + (myTime - Math.floor(myTime)) / (1/app.project.activeItem.frameRate);&lt;br /&gt;
}&lt;br /&gt;
function framesToTime(myTime){&lt;br /&gt;
   return myTime/app.project.activeItem.frameRate;&lt;br /&gt;
}&lt;br /&gt;
function timeToTimeCode(myTime){&lt;br /&gt;
   var framesN = myTime * app.project.activeItem.frameRate;&lt;br /&gt;
   fr = addZero(Math.round((myTime - Math.floor(myTime))/(1/app.project.activeItem.frameRate)));&lt;br /&gt;
   ho = addZero(Math.floor(myTime/3600));&lt;br /&gt;
   mi = addZero(Math.floor(myTime/60)-ho*60);&lt;br /&gt;
   se = addZero(Math.floor(myTime)-mi*60-ho*3600);&lt;br /&gt;
   return ho+&amp;quot;:&amp;quot;+mi+&amp;quot;:&amp;quot;+se+&amp;quot;:&amp;quot;+fr;&lt;br /&gt;
}&lt;br /&gt;
function addZero(val){&lt;br /&gt;
   if(val&amp;lt;10){&lt;br /&gt;
      val = &amp;quot;0&amp;quot;+val;&lt;br /&gt;
   }&lt;br /&gt;
   return val;&lt;br /&gt;
}&lt;br /&gt;
var myDisp = app.project.timecodeDisplayType;&lt;br /&gt;
app.project.timecodeDisplayType = TimecodeDisplayType.TIMECODE;&lt;br /&gt;
var pText = &amp;quot;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 &amp;#039;16: 230;22&amp;#039;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if(app.project.activeItem != &amp;quot;null&amp;quot; &amp;amp;&amp;amp; app.project.activeItem != null &amp;amp;&amp;amp; app.project.activeItem != 0){&lt;br /&gt;
   if(app.project.activeItem.selectedLayers.length != 0){&lt;br /&gt;
      if(app.preferences.getPrefAsLong(&amp;quot;Main Pref Section&amp;quot;,&amp;quot;Pref_SCRIPTING_FILE_NETWORK_SECURITY&amp;quot;)){&lt;br /&gt;
         curLayer = app.project.activeItem.selectedLayers[0];&lt;br /&gt;
         var textName = &amp;quot;AEcoordinates.txt&amp;quot;;&lt;br /&gt;
         var myTextFile = filePutDialog(&amp;quot;Select a location to save your .txt file&amp;quot;, textName, &amp;quot;TEXT txt&amp;quot;);&lt;br /&gt;
         if(myTextFile == null){&lt;br /&gt;
            alert(&amp;quot;You must choose a place to save the file&amp;quot;);&lt;br /&gt;
         }else{&lt;br /&gt;
            var formatString = prompt(pText,&amp;quot;%i: %x;%y%l&amp;quot;);&lt;br /&gt;
            myTextFile.open(&amp;quot;w&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
            myKeys = curLayer.property(&amp;quot;position&amp;quot;).selectedKeys;&lt;br /&gt;
            if(myKeys.length != 0){&lt;br /&gt;
               strTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[0]);&lt;br /&gt;
               endTime = curLayer.property(&amp;quot;position&amp;quot;).keyTime(myKeys[myKeys.length-1]);&lt;br /&gt;
            }else{&lt;br /&gt;
               strTime = app.project.activeItem.workAreaStart;&lt;br /&gt;
               endTime = app.project.activeItem.workAreaStart + app.project.activeItem.workAreaDuration;&lt;br /&gt;
            }&lt;br /&gt;
            var startLoop = timeToFrameNum(strTime);&lt;br /&gt;
            var endLoop = timeToFrameNum(endTime);&lt;br /&gt;
            for(i=startLoop;i&amp;lt;=endLoop;i++){&lt;br /&gt;
               var curTime = framesToTime(i);&lt;br /&gt;
               out = formatString.replace(&amp;#039;%x&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[0]);&lt;br /&gt;
               out = out.replace(&amp;#039;%y&amp;#039;,curLayer.property(&amp;quot;position&amp;quot;).valueAtTime(curTime,true)[1]);&lt;br /&gt;
               out = out.replace(&amp;#039;%f&amp;#039;,i);timeToTimeCode&lt;br /&gt;
               out = out.replace(&amp;#039;%t&amp;#039;,timeToTimeCode(curTime));&lt;br /&gt;
               out = out.replace(&amp;#039;%i&amp;#039;,i-startLoop);&lt;br /&gt;
               out = out.replace(&amp;#039;%l&amp;#039;,&amp;#039;\n&amp;#039;);&lt;br /&gt;
               myTextFile.write(out);&lt;br /&gt;
               clearOutput();&lt;br /&gt;
               write(Math.round((i-startLoop)/(endLoop-startLoop)*100,0)+&amp;quot;% done...&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
         clearOutput();&lt;br /&gt;
         writeLn(endLoop-startLoop+&amp;quot; positions saved to file&amp;quot;);&lt;br /&gt;
         writeLn(&amp;quot;script written by mlk =)&amp;quot;);&lt;br /&gt;
         myTextFile.close();&lt;br /&gt;
         }&lt;br /&gt;
      } else {&lt;br /&gt;
         alert (&amp;quot;This script requires the scripting security preference to be set.\n&amp;quot; +&lt;br /&gt;
         &amp;quot;Go to the \&amp;quot;General\&amp;quot; panel of your application preferences,\n&amp;quot; +&lt;br /&gt;
         &amp;quot;and make sure that \&amp;quot;Allow Scripts to Write Files and Access Network\&amp;quot; is checked.&amp;quot;);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   }else{&lt;br /&gt;
      alert(&amp;quot;Select a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
}else{&lt;br /&gt;
   alert(&amp;quot;Select a composition and a layer with &amp;#039;position&amp;#039; keyframes&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
app.project.timecodeDisplayType = myDisp;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Other Scripts==&lt;br /&gt;
&lt;br /&gt;
Awesome git repo https://github.com/kyletmartinez/After-Effects-Scripts/blob/master/scripts/Center%20Composition.jsx&lt;br /&gt;
&lt;br /&gt;
===Shelf===&lt;br /&gt;
[[AFX Shelf]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//to hijack admin and load shelf from my documents/shelf.jsx, this needs to be in the scriptUI Panel folder&lt;br /&gt;
{&lt;br /&gt;
    var nested_file = new File(&amp;quot;~/Documents/shelfBernie.jsx&amp;quot;);&lt;br /&gt;
	nested_file.open(&amp;quot;r&amp;quot;);&lt;br /&gt;
	//alert(nested_file.read());&lt;br /&gt;
	eval(nested_file.read());&lt;br /&gt;
	nested_file.close();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:After Effects]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=862</id>
		<title>Houdini UI Customization</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_UI_Customization&amp;diff=862"/>
		<updated>2025-08-01T14:06:59Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Shelf Tools */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Various things I do to my Houdini&lt;br /&gt;
&lt;br /&gt;
== Shelf Tools ==&lt;br /&gt;
I find it odd that Sidefx makes you go through shelf items for various UI functions that have nothing to do with the shelf, but that&amp;#039;s the way the cookie crumbles. Also maybe I&amp;#039;m nostalgic but despite it&amp;#039;s flaws the maya shelves were kinda easier to understand.&lt;br /&gt;
As I understand it single shelf items in houdini can be stored in different files, which means if you fumble around you can easily have two items with the same name but saved in two different places (I think. I&amp;#039;m confused).&lt;br /&gt;
Just make sure you keep an eyeball on where your shelf tool is stored (often in Default.shelf or some random production folder if you use Rez like us)&lt;br /&gt;
&lt;br /&gt;
=== Selection to bounding box to delete ===&lt;br /&gt;
&lt;br /&gt;
Very often I manually select prims to delete a few local faces, then change something upstream, then my blast is all mangled. This will create a bounding box of the currently selected geo, group them with it and then blast the group. It&amp;#039;s never perfect because of how bounding boxes operate (points/prims/othogonal to world axes), but it&amp;#039;s very useful in my daily work.&lt;br /&gt;
&lt;br /&gt;
Also Gemini did 90% of the coding and actually does error catching. It&amp;#039;s a lot better than me :0&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/xMUPRr3.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou, toolutils&lt;br /&gt;
&lt;br /&gt;
def create_group_from_bbox():&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Creates a &amp;#039;group&amp;#039; node and sets its bounding region parameters&lt;br /&gt;
    based on the bounding box of the current geo selection&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    try:&lt;br /&gt;
        selected_nodes = hou.selectedNodes()&lt;br /&gt;
        geo = toolutils.sceneViewer().selectGeometry()&lt;br /&gt;
&lt;br /&gt;
        # Check if exactly one node is selected.&lt;br /&gt;
        if len(selected_nodes) != 1:&lt;br /&gt;
            print(&amp;quot;Please select a single geometry node to get the bounding box from.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        selected_node = selected_nodes[0]&lt;br /&gt;
        &lt;br /&gt;
        # Check if the selected node is a geometry node (SOP).&lt;br /&gt;
        if selected_node.type().category() != hou.sopNodeTypeCategory():&lt;br /&gt;
            print(&amp;quot;The selected node is not a geometry node (SOP). Please select a valid node.&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
&lt;br /&gt;
        hou.clearAllSelected()&lt;br /&gt;
&lt;br /&gt;
        bbox = geo.boundingBox()&lt;br /&gt;
        parent_node = selected_node.parent()&lt;br /&gt;
        selected_node.setSelected(0)&lt;br /&gt;
        group_node = parent_node.createNode(&amp;quot;groupcreate&amp;quot;, &amp;quot;bbox_group&amp;quot;)&lt;br /&gt;
        &lt;br /&gt;
        pos = [selected_node.position()[0],selected_node.position()[1]-1]&lt;br /&gt;
        group_node.setPosition(pos)&lt;br /&gt;
        group_node.setInput(0, selected_node)&lt;br /&gt;
        group_node.parm(&amp;quot;groupbounding&amp;quot;).set(1)&lt;br /&gt;
        group_node.parmTuple(&amp;quot;size&amp;quot;).set(bbox.sizevec())&lt;br /&gt;
        group_node.parmTuple(&amp;quot;t&amp;quot;).set(bbox.center())&lt;br /&gt;
        &lt;br /&gt;
        blast = parent_node.createNode(&amp;quot;blast&amp;quot;, &amp;quot;bbox_blast&amp;quot;)&lt;br /&gt;
        blast.setInput(0, group_node)&lt;br /&gt;
        pos[1] = pos[1] - 1&lt;br /&gt;
        blast.setPosition(pos)&lt;br /&gt;
        blast.parm(&amp;#039;group&amp;#039;).set(group_node.parm(&amp;quot;groupname&amp;quot;).eval())&lt;br /&gt;
&lt;br /&gt;
        group_node.setSelected(1)&lt;br /&gt;
        blast.setDisplayFlag(1)&lt;br /&gt;
        blast.setSelected(1)&lt;br /&gt;
        blast.setRenderFlag(1)&lt;br /&gt;
        group_node.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
        &lt;br /&gt;
    except hou.Error as e:&lt;br /&gt;
        print(f&amp;quot;An error occurred: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
create_group_from_bbox()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Toggle Viewport Background Color ===&lt;br /&gt;
Bound it to F6&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys&lt;br /&gt;
import toolutils&lt;br /&gt;
&lt;br /&gt;
bg = None&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    # cycle next bg&lt;br /&gt;
    if kwargs[&amp;#039;ctrlclick&amp;#039;]: raise&lt;br /&gt;
    bgs = hou.session.bg[:]&lt;br /&gt;
    bgs = bgs[1:]+bgs[:1]&lt;br /&gt;
    if kwargs[&amp;#039;shiftclick&amp;#039;]: bgs = [&amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;, &amp;#039;wb&amp;#039;]&lt;br /&gt;
    bg = bgs[0]&lt;br /&gt;
    hou.session.bg = bgs&lt;br /&gt;
except:&lt;br /&gt;
    # set up default bg vars&lt;br /&gt;
    hou.session.bg = [&amp;#039;wb&amp;#039;, &amp;#039;bw&amp;#039;, &amp;#039;light&amp;#039;]&lt;br /&gt;
    bg = hou.session.bg[0]&lt;br /&gt;
&lt;br /&gt;
bgs = { &amp;#039;wb&amp;#039;:&amp;#039;dark&amp;#039;, &amp;#039;bw&amp;#039;:&amp;#039;grey&amp;#039;, &amp;#039;light&amp;#039;:&amp;#039;light&amp;#039; }&lt;br /&gt;
&lt;br /&gt;
hou.hscript(&amp;quot;viewdisplay -B %s *&amp;quot; % bg)&lt;br /&gt;
hou.ui.setStatusMessage(&amp;quot;Cycled background to %s&amp;quot; % bgs[bg].upper() )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Toggle Auto / Manual scene update ===&lt;br /&gt;
Bound to F11, I use it every day.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
mode = hou.updateModeSetting().name()&lt;br /&gt;
if mode == &amp;#039;AutoUpdate&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.Manual)&lt;br /&gt;
if mode == &amp;#039;Manual&amp;#039;:&lt;br /&gt;
    hou.setUpdateMode(hou.updateMode.AutoUpdate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Auto create a wrangle node ===&lt;br /&gt;
I&amp;#039;m pretty sure this exists already but why not reinvent the wheel. Creates an attr wrangle and keeps inputs/outputs, with the code box already in focus. I only use the &amp;#039;P&amp;#039; parameter viewer (the one that pops up in your network view) so other desktops might not work.&lt;br /&gt;
&lt;br /&gt;
bound to ctrl-shift-w&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# TODO  fix some weird inputs/outputs configurations&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
sn = hou.selectedNodes()&lt;br /&gt;
if sn:&lt;br /&gt;
    w = sn[-1].parent().createNode(&amp;#039;attribwrangle&amp;#039;)&lt;br /&gt;
    outputs = sn[-1].outputs()&lt;br /&gt;
    &lt;br /&gt;
    w.setInput(0,sn[-1])&lt;br /&gt;
    w.setCurrent(True, clear_all_selected=True)&lt;br /&gt;
    w.setSelected(True,clear_all_selected=True, show_asset_if_selected=True)&lt;br /&gt;
    w.setDisplayFlag(True)&lt;br /&gt;
    sn[-1].setDisplayFlag(False)&lt;br /&gt;
    if outputs:&lt;br /&gt;
        for output in outputs:&lt;br /&gt;
            output.setInput(0,w)&lt;br /&gt;
else:&lt;br /&gt;
    #should not error out if it&amp;#039;s launched in a shelf above a network plane&lt;br /&gt;
    w = hou.ui.curDesktop().paneTabUnderCursor().pwd().createNode(&amp;#039;attribwrangle&amp;#039;)    &lt;br /&gt;
&lt;br /&gt;
w.moveToGoodPosition(relative_to_inputs=True, move_inputs=False, move_outputs=True, move_unconnected=False)    &lt;br /&gt;
&lt;br /&gt;
networkview = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkview.setCurrentNode(w,True)&lt;br /&gt;
networkview.parmMoveFocusTo(&amp;#039;snippet&amp;#039;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Copy auto Paste-Merge ===&lt;br /&gt;
Copy from https://berniebernie.fr/wiki/Houdini_Python#Paste_clipboard_nodes_to_object_merges&lt;br /&gt;
&lt;br /&gt;
Allow to copy nodes elsewhere with an object merge. Bound it to alt-v (so ctrl-c, alt-v in another place)&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/M7N0Stq.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# this snippet will paste nodes in clipboard to object merges&lt;br /&gt;
# use it with a shortcut (I overrode &amp;#039;alt-v&amp;#039; in network pane context)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
&lt;br /&gt;
network = hou.ui.curDesktop().paneTabUnderCursor()&lt;br /&gt;
networkpath = network.pwd().path()&lt;br /&gt;
pos = network.cursorPosition()&lt;br /&gt;
&lt;br /&gt;
clipboard = hou.ui.getTextFromClipboard()&lt;br /&gt;
&lt;br /&gt;
n = 0&lt;br /&gt;
&lt;br /&gt;
if clipboard:&lt;br /&gt;
    list = clipboard.split()&lt;br /&gt;
    for item in list:&lt;br /&gt;
        if hou.node(item) != None:&lt;br /&gt;
            merge = hou.node(networkpath).createNode(&amp;#039;object_merge&amp;#039;,&amp;#039;merge_&amp;#039;+item.split(&amp;#039;/&amp;#039;)[-1])&lt;br /&gt;
            merge.parm(&amp;#039;objpath1&amp;#039;).set(str(item))&lt;br /&gt;
            merge.setPosition(pos)&lt;br /&gt;
            merge.move([n*2,0])&lt;br /&gt;
            if n == 0:&lt;br /&gt;
                merge.setSelected(True,True)&lt;br /&gt;
            else:&lt;br /&gt;
                merge.setSelected(True,False)&lt;br /&gt;
            n = n + 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Screenshot to background image===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/kuc1PnO.gif&lt;br /&gt;
&lt;br /&gt;
Customize your capture command line, no error checking &amp;#039;cause I&amp;#039;m not a professional programmer, aka it works on my machine.&lt;br /&gt;
&lt;br /&gt;
Should work with windows (hardcoded path to [https://www.donationcoder.com/software/mouser/popular-apps/minicap minicap] ) and linux centos with xfce-screenshoter&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#python 3.5+&lt;br /&gt;
import hou&lt;br /&gt;
import os&lt;br /&gt;
import subprocess&lt;br /&gt;
import nodegraphutils as utils&lt;br /&gt;
from time import gmtime, strftime&lt;br /&gt;
from os.path import expanduser&lt;br /&gt;
home = expanduser(&amp;quot;~&amp;quot;)&lt;br /&gt;
from pathlib import Path&lt;br /&gt;
&lt;br /&gt;
exe = home+&amp;quot;\Downloads\exe\MiniCap.exe&amp;quot;&lt;br /&gt;
&lt;br /&gt;
widthRatio = 4                      # change to make screenshot bigger or smaller, this is ~x4 node width&lt;br /&gt;
&lt;br /&gt;
def takeScreenShot(savePath):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;change to your preferred capture app /!\ no error checking for now &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    subprocess.check_call([exe,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &lt;br /&gt;
    #following is older code for linux i&amp;#039;ll update it if I linux again&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    Path(os.path.dirname(Path(savePath))).mkdir(parents=True, exist_ok=True)&lt;br /&gt;
    if platform == &amp;quot;linux&amp;quot; or platform == &amp;quot;linux2&amp;quot;:&lt;br /&gt;
        savepath = os.path.dirname(Path(savePath)) #screenshot dir    &lt;br /&gt;
        screenshotPNG = subprocess.check_output(&amp;#039;mv &amp;quot;$(xfce4-screenshooter -ro ls)&amp;quot; -v &amp;#039;+savepath,shell=True)&lt;br /&gt;
        screenshotPNG = screenshotPNG.decode().strip().split(&amp;#039;\n&amp;#039;)[0].split(&amp;#039; -&amp;gt; &amp;#039;)[1].replace(&amp;quot;&amp;#039;&amp;quot;,&amp;quot;&amp;quot;) #helluva nasty oneliner&lt;br /&gt;
        os.chdir(savepath)&lt;br /&gt;
        os.rename(os.path.basename(screenshotPNG),os.path.basename(savePath))&lt;br /&gt;
        &lt;br /&gt;
        # linux&lt;br /&gt;
    elif platform == &amp;quot;darwin&amp;quot;:&lt;br /&gt;
        exit&lt;br /&gt;
    elif platform == &amp;quot;win32&amp;quot;:&lt;br /&gt;
        subprocess.check_call([r&amp;quot;C:\Users\bernie\Documents\houdini18.0\config\exe\MiniCap.exe&amp;quot;,&amp;quot;-captureregselect&amp;quot;,&amp;quot;-save&amp;quot;,savePath,&amp;quot;-exit&amp;quot;])&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
def removeBackgroundImage(**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; erases bg image from tuples of backgroundImages() if it can find it, updates bg &amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    deletingNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = deletingNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    backgroundImagesDic = tuple(x for x in backgroundImagesDic if x.path() != image)&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&lt;br /&gt;
def changeBackgroundImageBrightness(event_type,**kwargs):&lt;br /&gt;
    &amp;#039;&amp;#039;&amp;#039; changes brightness/visibility if template or bypass flags are checked -- its poorly written/thought but i was tired&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
    nullNode = [x[1] for x in  kwargs.items()][0]&lt;br /&gt;
    image = nullNode.parm(&amp;#039;houdinipath&amp;#039;).eval()&lt;br /&gt;
    brightness = 1.0&lt;br /&gt;
    if nullNode.isBypassed():&lt;br /&gt;
        brightness = 0.0&lt;br /&gt;
    elif nullNode.isTemplateFlagSet():&lt;br /&gt;
        brightness = 0.5&lt;br /&gt;
    editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
    backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
    i = 0&lt;br /&gt;
    for item in backgroundImagesDic:&lt;br /&gt;
        if item.path() == image:&lt;br /&gt;
            backgroundImagesDic[i].setBrightness(brightness)&lt;br /&gt;
            break&lt;br /&gt;
        i = i + 1&lt;br /&gt;
    editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
    utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
    &lt;br /&gt;
#generate unique(ish) path for screenshot&lt;br /&gt;
timestamp = strftime(&amp;#039;%Y%m%d_%H%M%S&amp;#039;, gmtime())&lt;br /&gt;
hipname = str(hou.getenv(&amp;#039;HIPNAME&amp;#039;))&lt;br /&gt;
hippath = str(hou.getenv(&amp;#039;HIP&amp;#039;)) + &amp;#039;/screenshots&amp;#039;&lt;br /&gt;
screenshotName = hipname + &amp;#039;.&amp;#039; + timestamp + &amp;#039;.png&amp;#039;&lt;br /&gt;
pythonpath = hippath+&amp;#039;/&amp;#039;+screenshotName&lt;br /&gt;
systempath = hippath + &amp;#039;\\&amp;#039; + screenshotName&lt;br /&gt;
systempath = Path(systempath)&lt;br /&gt;
&lt;br /&gt;
print(systempath)&lt;br /&gt;
&lt;br /&gt;
houdinipath = &amp;#039;$HIP/screenshots/&amp;#039;+screenshotName&lt;br /&gt;
&lt;br /&gt;
#take screenshot with capture region&lt;br /&gt;
takeScreenShot(systempath)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#set up background image plane&lt;br /&gt;
editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)&lt;br /&gt;
image = hou.NetworkImage()&lt;br /&gt;
image.setPath(houdinipath)&lt;br /&gt;
sel = hou.selectedNodes()&lt;br /&gt;
nullNode = &amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
if sel:&lt;br /&gt;
    lastSel = sel[-1]&lt;br /&gt;
    nullNode = lastSel.parent().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;)&lt;br /&gt;
    if lastSel.outputConnections():&lt;br /&gt;
        nullNode.setInput(0,lastSel)                   &lt;br /&gt;
&lt;br /&gt;
else:&lt;br /&gt;
    nullNode = editor.pwd().createNode(&amp;#039;null&amp;#039;,&amp;#039;screenshot&amp;#039;) &lt;br /&gt;
    nullNode.moveToGoodPosition()&lt;br /&gt;
    lastSel = nullNode&lt;br /&gt;
&lt;br /&gt;
#configure image plane placement&lt;br /&gt;
nullNode.setUserData(&amp;#039;nodeshape&amp;#039;,&amp;#039;task&amp;#039;)&lt;br /&gt;
nullNode.setPosition(lastSel.position())&lt;br /&gt;
nullNode.setColor(hou.Color(.3,.3,.3))&lt;br /&gt;
nullNode.move([lastSel.size()[0]*2,-lastSel.size()[1]*2])&lt;br /&gt;
&lt;br /&gt;
rez = hou.imageResolution(pythonpath)&lt;br /&gt;
ratio = 1.0*rez[1]/rez[0]&lt;br /&gt;
rect = hou.BoundingRect(0,-lastSel.size()[1]*1.1,widthRatio,-widthRatio*ratio-lastSel.size()[1]*1.1)&lt;br /&gt;
image.setRelativeToPath(nullNode.path())&lt;br /&gt;
image.setRect(rect)&lt;br /&gt;
&lt;br /&gt;
#following is adding a spare parm with image path to be able to know which node corresponds to which background image&lt;br /&gt;
#could have used a user attribute or relativeToPath() and smarter logic but it works and it helps me visualize filepath&lt;br /&gt;
&lt;br /&gt;
hou_parm_template_group = hou.ParmTemplateGroup()&lt;br /&gt;
hou_parm_template = hou.LabelParmTemplate(&amp;quot;houdinipath&amp;quot;, &amp;quot;Label&amp;quot;, column_labels=([&amp;#039;\\&amp;#039;+houdinipath]))&lt;br /&gt;
hou_parm_template.hideLabel(True)&lt;br /&gt;
hou_parm_template_group.append(hou_parm_template)&lt;br /&gt;
nullNode.setParmTemplateGroup(hou_parm_template_group)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#attach a function that deletes the background image plane if the corresponding node is deleted (faster than doing it by hand)&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.BeingDeleted,), removeBackgroundImage)&lt;br /&gt;
&lt;br /&gt;
#attach a function to change visibility or opacity if corresponding node flags are changed&lt;br /&gt;
nullNode.addEventCallback((hou.nodeEventType.FlagChanged,), changeBackgroundImageBrightness)&lt;br /&gt;
&lt;br /&gt;
#add image to network background&lt;br /&gt;
backgroundImagesDic = editor.backgroundImages()&lt;br /&gt;
backgroundImagesDic = backgroundImagesDic + (image,)&lt;br /&gt;
editor.setBackgroundImages(backgroundImagesDic)&lt;br /&gt;
utils.saveBackgroundImages(editor.pwd(), backgroundImagesDic)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other UI ==&lt;br /&gt;
=== Previews ===&lt;br /&gt;
==== Detach parameter window à la maya ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/3txLohO.gif&lt;br /&gt;
&lt;br /&gt;
==== Auto add frame offset parm ====&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZXNrwvC.gif&lt;br /&gt;
&lt;br /&gt;
=== Userdocs XMLs ===&lt;br /&gt;
This is for right click context menus. Use the hscript &amp;#039;menurefresh&amp;#039; when developping so as not to relaunch H each time (like a reload(module) in python).&lt;br /&gt;
==== PARMmenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
	&amp;lt;menu&amp;gt;&lt;br /&gt;
		&amp;lt;subMenu id=&amp;quot;bernie_rclick&amp;quot;&amp;gt;&lt;br /&gt;
		&amp;lt;label&amp;gt;Bernie&amp;#039;s&amp;lt;/label&amp;gt;&lt;br /&gt;
			&amp;lt;modifyItem&amp;gt;&amp;lt;insertAfter&amp;gt;motion_effects_menu&amp;lt;/insertAfter&amp;gt;&amp;lt;/modifyItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Add a frame offset parm to $F#&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						return bernie_tools.validate_sequence_parm(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.add_sequence_offset_spareparm(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_set_frameoffset_parm&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Browse to...&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;context&amp;gt;&lt;br /&gt;
					&amp;lt;expression&amp;gt;&lt;br /&gt;
						import bernie_tools&lt;br /&gt;
						reload(bernie_tools)&lt;br /&gt;
						return bernie_tools.validate_uri(kwargs)&lt;br /&gt;
					&amp;lt;/expression&amp;gt;&lt;br /&gt;
				&amp;lt;/context&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.browse_to(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
			&amp;lt;scriptItem id=&amp;quot;bernie_open_parm_spreadsheet&amp;quot;&amp;gt;&lt;br /&gt;
				&amp;lt;label&amp;gt;Open/Add in Parameter Spreadsheet&amp;lt;/label&amp;gt;&lt;br /&gt;
				&amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.open_parm_spreadsheet(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
			&amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
		&amp;lt;/subMenu&amp;gt;&lt;br /&gt;
	&amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ParmGearMenu.xml ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
 (long sidefx text)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;menuDocument&amp;gt;&lt;br /&gt;
    &amp;lt;!-- menuDocument can only contain 1 menu element, whose id is &lt;br /&gt;
         implicitly &amp;quot;root_menu&amp;quot;&lt;br /&gt;
      --&amp;gt;&lt;br /&gt;
    &amp;lt;menu&amp;gt;&lt;br /&gt;
        &amp;lt;scriptItem id=&amp;quot;detach_parameter_window&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;label&amp;gt;Detach Parameter Window...&amp;lt;/label&amp;gt;&lt;br /&gt;
            &amp;lt;scriptCode&amp;gt;&amp;lt;![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.detach_parameter_window(kwargs)]]&amp;gt;&amp;lt;/scriptCode&amp;gt;&lt;br /&gt;
        &amp;lt;/scriptItem&amp;gt;&lt;br /&gt;
    &amp;lt;/menu&amp;gt;&lt;br /&gt;
&amp;lt;/menuDocument&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== bernie_tools.py ===&lt;br /&gt;
==== py3 ====&lt;br /&gt;
Some updates&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
		name = existing_parm.name()&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(name+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(name+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(name+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+name+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+name+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	# todo: keep existing parm mask to append to it. Can&amp;#039;t retrieve current value AFAIK.&lt;br /&gt;
	&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	#selectedParms = return_first_parm(kwargs)&lt;br /&gt;
	&lt;br /&gt;
	parms = []&lt;br /&gt;
	for parm in kwargs[&amp;#039;parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	for parm in kwargs[&amp;#039;locked_parms&amp;#039;]:&lt;br /&gt;
		parms.append(parm)&lt;br /&gt;
	&lt;br /&gt;
	selectedParm = parms[0]&lt;br /&gt;
&lt;br /&gt;
	parms = [x.name() for x in parms]&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
&lt;br /&gt;
	parmsheetP = selectedParm.name()&lt;br /&gt;
	parmsheetPaths = nodepaths&lt;br /&gt;
&lt;br /&gt;
	if kwargs[&amp;#039;ctrlclick&amp;#039;]:&lt;br /&gt;
		rmi = hou.ui.readMultiInput(&amp;#039;Edit the node path and parms.&amp;#039;, [&amp;#039;Node(s)&amp;#039;,&amp;#039;Parm(s)&amp;#039;], buttons=(&amp;#039;OK&amp;#039;,&amp;#039;Cancel&amp;#039;), severity=hou.severityType.Message, default_choice=0, close_choice=1, help=&amp;#039;Node path can have wildcards&amp;#039;, title=&amp;#039;Parm Chooser&amp;#039;, initial_contents=(nodepaths.split(&amp;#039; &amp;#039;, 1)[0] ,selectedParm.name()))&lt;br /&gt;
		if rmi[0]==0:&lt;br /&gt;
			parmsheetPaths = rmi[1][0]&lt;br /&gt;
			parmsheetP = rmi[1][1]&lt;br /&gt;
&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 0 -p &amp;quot;&amp;#039;+parmsheetP+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+parmsheetPaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== py2 ====&lt;br /&gt;
Need to update to py3 for H19&lt;br /&gt;
&lt;br /&gt;
In my userdocs\houdini18.5\python2.7libs folder&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#march2022 update: fuck putin and improved the add offset to sequence so that you can still choose a new file and keep the offset without having to delete the spare parms (for imageplane frame offset for instance)&lt;br /&gt;
&lt;br /&gt;
import hou&lt;br /&gt;
import traceback&lt;br /&gt;
import re&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def return_first_parm(allKwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;given a right click context with kwargs, return what we want, the parameter -- which can be locked or not&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = lockedparm = normalparm = False&lt;br /&gt;
	try:&lt;br /&gt;
		lockedparm = allKwargs[&amp;#039;locked_parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	try:&lt;br /&gt;
		normalparm = allKwargs[&amp;#039;parms&amp;#039;][0]&lt;br /&gt;
	except:&lt;br /&gt;
		pass&lt;br /&gt;
	if lockedparm:&lt;br /&gt;
		parm = lockedparm&lt;br /&gt;
	elif normalparm:&lt;br /&gt;
		parm = normalparm&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;bug&amp;#039;)&lt;br /&gt;
	return parm&lt;br /&gt;
&lt;br /&gt;
def validate_sequence_parm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the first parm contains a value with $F&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	try:&lt;br /&gt;
		returnvalue = &amp;#039;$F&amp;#039; in parm.rawValue() &lt;br /&gt;
	except:&lt;br /&gt;
		print(&amp;quot;ERROR: %s&amp;quot; % traceback.format_exc())&lt;br /&gt;
	return returnvalue&lt;br /&gt;
	&lt;br /&gt;
def validate_uri(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Checks if the given argument, or its parent or grandparent is either a file or folder&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	parm = parm.eval()&lt;br /&gt;
	parm = str(parm)&lt;br /&gt;
	returnvalue = False&lt;br /&gt;
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)) or os.path.isdir(os.path.dirname(os.path.dirname(parm))):&lt;br /&gt;
		returnvalue = True&lt;br /&gt;
	return returnvalue&lt;br /&gt;
&lt;br /&gt;
def browse_to(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;opens a browser to this given path (if a file), or its parent if it doesn&amp;#039;t exist&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	dir = &amp;#039;&amp;#039;&lt;br /&gt;
	if os.path.isdir(path):&lt;br /&gt;
		dir = path&lt;br /&gt;
	elif os.path.isdir(os.path.dirname(path)):&lt;br /&gt;
		dir = os.path.dirname(path)&lt;br /&gt;
	else:&lt;br /&gt;
		print(&amp;#039;No directory found&amp;#039;)&lt;br /&gt;
	if dir != &amp;#039;&amp;#039;:&lt;br /&gt;
		os.startfile(os.path.realpath(dir))&lt;br /&gt;
&lt;br /&gt;
def add_sequence_offset_spareparm(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Adds an int spare parm + file parm below a parm that has a file sequence expression of type &amp;#039;$F#&amp;#039; using hscript&amp;#039;s padzero&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	parm = return_first_parm(kwargs)&lt;br /&gt;
	path = os.path.dirname(parm.eval())&lt;br /&gt;
	parmval = parm.rawValue()&lt;br /&gt;
	matchObj = re.match( r&amp;#039;^(.*)\$F(\d*)(.*)&amp;#039;, parmval, re.M|re.I)&lt;br /&gt;
	if matchObj:&lt;br /&gt;
		n = parm.node()&lt;br /&gt;
		parmGrp = n.parmTemplateGroup()&lt;br /&gt;
		existing_parm = parmGrp.find(parm.name())&lt;br /&gt;
		label = existing_parm.label()&lt;br /&gt;
&lt;br /&gt;
		#create an offset parm with a similar name. No error checking!&lt;br /&gt;
&lt;br /&gt;
		fileParmTemplate = hou.StringParmTemplate(label+&amp;#039;_file&amp;#039;,label+&amp;#039;_file&amp;#039;, 1, default_value=([parmval]),string_type=hou.stringParmType.FileReference)&lt;br /&gt;
		offsetParmTemplate = hou.IntParmTemplate(label+&amp;#039;_offset&amp;#039;,label+&amp;#039;_offset&amp;#039;, 1, default_expression=([&amp;quot;$F + 0&amp;quot;]))&lt;br /&gt;
		parmGrp.insertAfter(existing_parm, fileParmTemplate)&lt;br /&gt;
		parmGrp.insertAfter(fileParmTemplate, offsetParmTemplate)&lt;br /&gt;
&lt;br /&gt;
		n.setParmTemplateGroup(parmGrp)&lt;br /&gt;
		#display the expression as it&amp;#039;s most like we want to mmb through the offset&lt;br /&gt;
		n.parm(label+&amp;#039;_offset&amp;#039;).showExpression(1)&lt;br /&gt;
		&lt;br /&gt;
		expression = &amp;quot;path=parm(&amp;#039;&amp;quot;+label+&amp;quot;_file&amp;#039;).rawValue().split(&amp;#039;$F&amp;#039;)\nframe=parm(&amp;#039;&amp;quot;+label+&amp;quot;_offset&amp;#039;).eval()\npad = int(path[1][0]) if path[1][1] is &amp;#039;.&amp;#039; else 0\nreturn path[0]+str(frame).zfill(pad)+&amp;#039;.&amp;#039;+path[1].split(&amp;#039;.&amp;#039;)[-1];&amp;quot;&lt;br /&gt;
		hou_keyframe = hou.StringKeyframe()&lt;br /&gt;
		hou_keyframe.setTime(1) #arbitrary keyframe needed to enable expression i think&lt;br /&gt;
		hou_keyframe.setExpression(expression, hou.exprLanguage.Python)&lt;br /&gt;
		parm.setKeyframe(hou_keyframe)&lt;br /&gt;
&lt;br /&gt;
def detach_parameter_window(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Open a floating parameter pane for a particular node.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	node = kwargs[&amp;#039;node&amp;#039;]&lt;br /&gt;
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)&lt;br /&gt;
	pane_tab.setCurrentNode(node)&lt;br /&gt;
	pane_tab.setPin(True)&lt;br /&gt;
	return pane_tab&lt;br /&gt;
&lt;br /&gt;
def open_parm_spreadsheet(kwargs):&lt;br /&gt;
	&amp;#039;&amp;#039;&amp;#039;Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
	#import pprint&lt;br /&gt;
	#pprint.pprint(kwargs)&lt;br /&gt;
	selectedParm = return_first_parm(kwargs)&lt;br /&gt;
	nodepaths = &amp;quot; &amp;quot;.join([node.path() for node in hou.selectedNodes()])&lt;br /&gt;
	if not hou.selectedNodes():&lt;br /&gt;
		nodepaths = selectedParm.node().path()&lt;br /&gt;
	#print(nodepaths)&lt;br /&gt;
	ps = hou.ui.findPaneTab(&amp;#039;parmsheet&amp;#039;)&lt;br /&gt;
	if not ps:&lt;br /&gt;
		desktop = hou.ui.curDesktop()&lt;br /&gt;
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)&lt;br /&gt;
	cmd = &amp;#039;parmsheet -w 1 -p &amp;quot;&amp;#039;+selectedParm.name()+&amp;#039;&amp;quot; -o &amp;quot;&amp;#039;+nodepaths+&amp;#039;&amp;quot; &amp;#039;+ps.name()&lt;br /&gt;
	hou.hscript(cmd)&lt;br /&gt;
&lt;br /&gt;
def test(var):&lt;br /&gt;
	print(var)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Maya_Mel&amp;diff=861</id>
		<title>Maya Mel</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Maya_Mel&amp;diff=861"/>
		<updated>2025-07-29T14:02:34Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Scale/Rot/Move Manip to screenSpace (camera angle) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=My mels=&lt;br /&gt;
=== Hide each sub object in a group according to the current frame===&lt;br /&gt;
I add this expression to the group or any object in it. Here it shows all the children in the group &amp;#039;buildings&amp;#039; one by one. Make sure to bake visibility for it to work properly because setAttr in expressions = crap.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$f_str = `getAttr &amp;quot;defaultRenderGlobals.startFrame&amp;quot;`;&lt;br /&gt;
int $ct = `currentTime -q`;&lt;br /&gt;
&lt;br /&gt;
int $i = 0;&lt;br /&gt;
for($o in `listRelatives -children buildings`){&lt;br /&gt;
    setAttr($o+&amp;quot;.visibility&amp;quot;) 0;&lt;br /&gt;
    if($i==$ct-$f_str){&lt;br /&gt;
        setAttr($o+&amp;quot;.visibility&amp;quot;) 1;        &lt;br /&gt;
    }&lt;br /&gt;
    $i++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Easy install tip ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//stolen from advanced skeleton file and other scripts&lt;br /&gt;
//basically call &amp;#039;whatIs&amp;#039; on a procedure that you just sourced so you know when the current mel file is&lt;br /&gt;
//useful for dragging and dropping files in viewport for installation&lt;br /&gt;
&lt;br /&gt;
global proc installDir (){}&lt;br /&gt;
string $p = `whatIs installDir`;&lt;br /&gt;
&lt;br /&gt;
string $fullPath=`substring $p 25 999`;&lt;br /&gt;
string $buffer[];&lt;br /&gt;
string $slash=&amp;quot;/&amp;quot;;&lt;br /&gt;
if (`gmatch $p &amp;quot;*\\\\*&amp;quot;`)//sourced from ScriptEditor&lt;br /&gt;
    $slash=&amp;quot;\\&amp;quot;;&lt;br /&gt;
int $numTok=`tokenize $fullPath $slash $buffer`;&lt;br /&gt;
int $numLetters=size($fullPath);&lt;br /&gt;
int $numLettersLastFolder=size($buffer[$numTok-1]);&lt;br /&gt;
string $scriptLocation=`substring $fullPath 1 ($numLetters-$numLettersLastFolder)`;&lt;br /&gt;
print $scriptLocation;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Random replace with instances ===&lt;br /&gt;
https://gyazo.com/c3df2085a6997b84b76f765f71a5d7f1.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
global float $deletepercentage;&lt;br /&gt;
&lt;br /&gt;
if(`objExists(&amp;quot;instances_set&amp;quot;)`){delete &amp;quot;instances_set&amp;quot;;};&lt;br /&gt;
if(`objExists(&amp;quot;replacingobjs_set&amp;quot;)`){delete &amp;quot;replacingobjs_set&amp;quot;;};&lt;br /&gt;
&lt;br /&gt;
string $window = `window -menuBar true -title &amp;quot;Random replace&amp;quot;`;&lt;br /&gt;
columnLayout -adjustableColumn true;&lt;br /&gt;
$a = `button -label &amp;quot;Instance Objects&amp;quot;`;&lt;br /&gt;
button -e -command (&amp;quot;setSet(\&amp;quot;instances_set\&amp;quot;,\&amp;quot;&amp;quot;+$a+&amp;quot;\&amp;quot;,\&amp;quot;Instance Objects\&amp;quot;)&amp;quot;) $a;&lt;br /&gt;
$b = `button -label &amp;quot;Objects To Replace&amp;quot;`;&lt;br /&gt;
button -e -command (&amp;quot;setSet(\&amp;quot;replacingobjs_set\&amp;quot;,\&amp;quot;&amp;quot;+$b+&amp;quot;\&amp;quot;,\&amp;quot;Objects To Replace\&amp;quot;)&amp;quot;) $b;&lt;br /&gt;
$c = `button -label (&amp;quot;Random delete (&amp;quot;+$deletepercentage+&amp;quot;%)&amp;quot;)`;&lt;br /&gt;
button -e -command (&amp;quot;setDelete(\&amp;quot;&amp;quot;+$c+&amp;quot;\&amp;quot;)&amp;quot;) $c;&lt;br /&gt;
button -label &amp;quot;Go!&amp;quot; -command (&amp;quot;replaceWithInstances;deleteUI -window \&amp;quot;&amp;quot;+$window+&amp;quot;\&amp;quot;;&amp;quot;);&lt;br /&gt;
showWindow $window;&lt;br /&gt;
proc setSet(string $name,string $button,string $buttonString){&lt;br /&gt;
    string $selectedElmts[] = `ls -sl -tr`;&lt;br /&gt;
    if(`objExists $name`){&lt;br /&gt;
        delete $name;&lt;br /&gt;
    }&lt;br /&gt;
    sets -n $name $selectedElmts;&lt;br /&gt;
    button -e -label ($buttonString+&amp;quot; (&amp;quot;+size($selectedElmts)+&amp;quot;)&amp;quot;) $button;&lt;br /&gt;
    select -cl;&lt;br /&gt;
}&lt;br /&gt;
proc setDelete(string $btn){&lt;br /&gt;
    global float $deletepercentage;&lt;br /&gt;
    string $text;&lt;br /&gt;
    string $result = `promptDialog&lt;br /&gt;
    -title &amp;quot;Delete random %&amp;quot;&lt;br /&gt;
    -message &amp;quot;Set chance of being deleted (0-100):&amp;quot;&lt;br /&gt;
    -button &amp;quot;OK&amp;quot; -button &amp;quot;Cancel&amp;quot;&lt;br /&gt;
    -defaultButton &amp;quot;OK&amp;quot; -cancelButton &amp;quot;Cancel&amp;quot;&lt;br /&gt;
    -dismissString &amp;quot;Cancel&amp;quot;`;&lt;br /&gt;
    &lt;br /&gt;
    if ($result == &amp;quot;OK&amp;quot;) {&lt;br /&gt;
        $text = `promptDialog -query -text`;&lt;br /&gt;
        $deletepercentage = float($text);&lt;br /&gt;
        button -e -label (&amp;quot;Random delete(&amp;quot;+$deletepercentage+&amp;quot;%)&amp;quot;) $btn;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
global proc replaceWithInstances(){&lt;br /&gt;
    global float $deletepercentage;&lt;br /&gt;
    string $instances[] = `listConnections -s 1 -d 0 -p 0 -c 0 &amp;quot;instances_set&amp;quot;`;&lt;br /&gt;
    string $objs[] = `listConnections -s 1 -d 0 -p 0 -c 0 &amp;quot;replacingobjs_set&amp;quot;`;&lt;br /&gt;
    int $end = size($objs)-1;&lt;br /&gt;
    global string $gMainProgressBar;  // This is defined on maya startup&lt;br /&gt;
    progressBar -edit -beginProgress -isInterruptable true -status &amp;quot;Replacing with instances&amp;quot; -maxValue $end $gMainProgressBar;&lt;br /&gt;
    refresh -su 1;&lt;br /&gt;
    for($i = 0;$i&amp;lt;=$end;$i++){&lt;br /&gt;
        if(`progressBar -query -isCancelled $gMainProgressBar`)&lt;br /&gt;
        break;&lt;br /&gt;
        if(`rand 1` &amp;gt; ($deletepercentage/100)){&lt;br /&gt;
            select -r $objs[$i] $instances[int(floor(rand(size($instances))))] ;&lt;br /&gt;
            replaceObjects 1 1 1 1;             &lt;br /&gt;
        }else{&lt;br /&gt;
            delete $objs[$i];&lt;br /&gt;
        }&lt;br /&gt;
        progressBar -edit -step 1 $gMainProgressBar;&lt;br /&gt;
    }&lt;br /&gt;
    progressBar -edit -endProgress $gMainProgressBar;    &lt;br /&gt;
        refresh -su 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== add equidistant locators on an edge selection ===&lt;br /&gt;
via a path animation&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/E4ECLOB.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proc locatorsOnEdge(int $numberOfLocators){&lt;br /&gt;
    cycleCheck -e off;&lt;br /&gt;
    int $clones = $numberOfLocators;&lt;br /&gt;
    string $objs[] = {};&lt;br /&gt;
    string $edg[] = `ls -sl`;&lt;br /&gt;
    string $ob[] = `ls -sl -o`;&lt;br /&gt;
    string $ptc[] = `polyToCurve -form 2 -degree 3`;&lt;br /&gt;
    string $lo[] = `spaceLocator -n ($ob[0]+&amp;quot;_loc&amp;quot;)`;&lt;br /&gt;
    string $pa = `pathAnimation -fractionMode true -follow true -followAxis x -upAxis y -worldUpType &amp;quot;vector&amp;quot; -worldUpVector 0 1 0 -inverseUp false -inverseFront false -bank false $ptc[0] $lo[0]`;&lt;br /&gt;
    disconnectAttr ($pa+&amp;quot;_uValue.output&amp;quot;) ($pa+&amp;quot;.uValue&amp;quot;);&lt;br /&gt;
    for($i = 0;$i&amp;lt;$clones;$i++){&lt;br /&gt;
        setAttr ($pa+&amp;quot;.uValue&amp;quot;) (($i*1.0)/($clones-1));&lt;br /&gt;
&lt;br /&gt;
        refresh;&lt;br /&gt;
    string $copy[] = `duplicate $lo[0]`;&lt;br /&gt;
    $objs[size($objs)]=$copy[0];   &lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    delete $lo;&lt;br /&gt;
    delete $ptc;&lt;br /&gt;
    select -r $objs;&lt;br /&gt;
    cycleCheck -e on;&lt;br /&gt;
}&lt;br /&gt;
locatorsOnEdge(3);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
edit 2022 - WIP to make it work straight from a curve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proc locatorsOnEdge(int $numberOfLocators){&lt;br /&gt;
    cycleCheck -e off;&lt;br /&gt;
    int $clones = $numberOfLocators;&lt;br /&gt;
    string $objs[] = {};&lt;br /&gt;
    string $edg[] = `ls -sl`;&lt;br /&gt;
    string $ob[] = `ls -sl -l -o`;&lt;br /&gt;
    string $shapes[] = `listRelatives -s -f $ob`;&lt;br /&gt;
    string $ptc[] = {};&lt;br /&gt;
    int $isCurve = false;&lt;br /&gt;
    if(size($shapes)&amp;gt;0 &amp;amp;&amp;amp; `nodeType $edg[0]` == &amp;quot;nurbsCurve&amp;quot;){&lt;br /&gt;
        $ptc[0] = $edg[0];&lt;br /&gt;
        $isCurve = true;&lt;br /&gt;
    }else if(size($shapes)&amp;gt;0 &amp;amp;&amp;amp; `nodeType $shapes[0]` == &amp;quot;nurbsCurve&amp;quot;){&lt;br /&gt;
        $ptc[0] = $shapes[0];&lt;br /&gt;
        $isCurve = true;&lt;br /&gt;
    }else{&lt;br /&gt;
        string $poinToCurves[] = `polyToCurve -form 2 -degree 3`;&lt;br /&gt;
        $ptc[0] = $poinToCurves[0];&lt;br /&gt;
    }&lt;br /&gt;
    string $lo[] = `spaceLocator -n ($ob[0]+&amp;quot;_loc&amp;quot;)`;&lt;br /&gt;
    string $pa = `pathAnimation -fractionMode true -follow true -followAxis x -upAxis y -worldUpType &amp;quot;vector&amp;quot; -worldUpVector 0 1 0 -inverseUp false -inverseFront false -bank false $ptc[0] $lo[0]`;&lt;br /&gt;
    disconnectAttr ($pa+&amp;quot;_uValue.output&amp;quot;) ($pa+&amp;quot;.uValue&amp;quot;);&lt;br /&gt;
    for($i = 0;$i&amp;lt;$clones;$i++){&lt;br /&gt;
        setAttr ($pa+&amp;quot;.uValue&amp;quot;) (($i*1.0)/($clones-1));&lt;br /&gt;
        refresh;&lt;br /&gt;
        string $copy[] = `duplicate $lo[0]`;&lt;br /&gt;
        $objs[size($objs)]=$copy[0];   &lt;br /&gt;
    }&lt;br /&gt;
    delete $lo;&lt;br /&gt;
    if(!$isCurve){&lt;br /&gt;
        delete $ptc;   &lt;br /&gt;
    }&lt;br /&gt;
    select -r $objs;&lt;br /&gt;
    cycleCheck -e on;&lt;br /&gt;
}&lt;br /&gt;
locatorsOnEdge(3);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== vray add user ids on selection shapes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//no error checking or multiple ids cause i haven&amp;#039;t need it so far&lt;br /&gt;
string $text;&lt;br /&gt;
string $result = `promptDialog&lt;br /&gt;
    -title &amp;quot;Add vray ids&amp;quot;&lt;br /&gt;
    -message &amp;quot;id value:&amp;quot;&lt;br /&gt;
    -button &amp;quot;OK&amp;quot; -button &amp;quot;Cancel&amp;quot;&lt;br /&gt;
    -defaultButton &amp;quot;OK&amp;quot; -cancelButton &amp;quot;Cancel&amp;quot;&lt;br /&gt;
    -dismissString &amp;quot;Cancel&amp;quot;`;&lt;br /&gt;
&lt;br /&gt;
if ($result == &amp;quot;OK&amp;quot;) {&lt;br /&gt;
    int $text = `promptDialog -query -text`;&lt;br /&gt;
    for($o in `ls -sl -l`){&lt;br /&gt;
        string $r[] = `listRelatives -s -f $o`;&lt;br /&gt;
        for($sh in $r){&lt;br /&gt;
            print $sh;&lt;br /&gt;
            vray addAttributesFromGroup $sh vray_objectID 1;&lt;br /&gt;
            setAttr ($sh+&amp;quot;.vrayObjectID&amp;quot;) (int($text));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== vray add random user scalar attribute to selection===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//works on parent groups too, good for instances !&lt;br /&gt;
$usrAttr = &amp;quot;mixvar&amp;quot;;&lt;br /&gt;
for($o in `ls -sl`){&lt;br /&gt;
    vray addAttributesFromGroup $o &amp;quot;vray_user_attributes&amp;quot; 1;&lt;br /&gt;
    setAttr ($o+&amp;quot;.vrayUserAttributes&amp;quot;) -type &amp;quot;string&amp;quot; ($usrAttr+&amp;quot;=&amp;quot;+rand(1));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===vray add random colorful material id per shader===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for($o in `ls -sl`){&lt;br /&gt;
    if(!attributeExists(&amp;quot;vrayColorId&amp;quot;,$o)){ //prevents fucking up old ids&lt;br /&gt;
            vray addAttributesFromGroup $o &amp;quot;vray_material_id&amp;quot; 1;&lt;br /&gt;
            vector $v = `hsv_to_rgb &amp;lt;&amp;lt;rand(1),1,1&amp;gt;&amp;gt;`;    &lt;br /&gt;
            setAttr ($o+&amp;quot;.vrayColorId&amp;quot;) -type &amp;quot;double3&amp;quot; ($v.x) ($v.y) ($v.z);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Select all objects constraining an object ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $selection[] = {};&lt;br /&gt;
for($c in `listRelatives -type &amp;quot;constraint&amp;quot;`){&lt;br /&gt;
    $nt = `nodeType $c`;&lt;br /&gt;
    $tl = eval($nt+ &amp;quot; -q -tl &amp;quot;+$c);&lt;br /&gt;
    $selection = stringArrayCatenate($selection , $tl);&lt;br /&gt;
}&lt;br /&gt;
select $selection;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Average skin weights from selected vertices to last vertex===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proc averageWeights(){&lt;br /&gt;
    if(`selectPref -q -tso` == 0){&lt;br /&gt;
        select -cl;&lt;br /&gt;
        selectPref -tso 1;&lt;br /&gt;
        warning &amp;quot;\Track selection order\&amp;quot; has been checked in your prefs (Settings&amp;gt;Selection), start the procedure again!&amp;quot;;&lt;br /&gt;
    }else{&lt;br /&gt;
        string $vertices[] = `ls -os -fl`;&lt;br /&gt;
        string $selObj[] = `ls -o -sl`;&lt;br /&gt;
        if(size($vertices)&amp;gt;0){&lt;br /&gt;
            string $sck = `findRelatedSkinCluster($selObj[0])`;&lt;br /&gt;
            string $influences[] = {};&lt;br /&gt;
            float $influencesVals[] = {};&lt;br /&gt;
            int $influenceVerticesCount = size($vertices)-1;&lt;br /&gt;
            for($i=0;$i&amp;lt;$influenceVerticesCount;$i++){&lt;br /&gt;
    &lt;br /&gt;
                $vertex = $vertices[$i];&lt;br /&gt;
        &lt;br /&gt;
                string $skinInfluences[]=`skinPercent -ib 0.000001 -q -t $sck $vertex`;&lt;br /&gt;
                float $sknVals[] = `skinPercent -ib 0.000001 -q -v  $sck $vertex`;&lt;br /&gt;
                for($k=0;$k&amp;lt;size($skinInfluences);$k++){&lt;br /&gt;
                    //ok, mel&amp;#039;s handling of dictionaries or whatever it&amp;#039;s called is 100% retarded, but the python implementation of skinPercent is a piece of shit too.&lt;br /&gt;
                    int $curListItem = stringArrayFind($skinInfluences[$k],0,$influences);&lt;br /&gt;
                    if($curListItem==-1){&lt;br /&gt;
                        int $listSize = size($influences);&lt;br /&gt;
                        $influences[$listSize] = $skinInfluences[$k];&lt;br /&gt;
                        $influencesVals[$listSize] = $sknVals[$k];&lt;br /&gt;
                    }else{&lt;br /&gt;
                        $influencesVals[$curListItem] += $sknVals[$curListItem];&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
                //print(&amp;quot;\n-----------\n&amp;quot;+$vertex+&amp;quot;:\n&amp;quot;);print(&amp;quot;\n----------------\n&amp;quot;);print($influences);print($influencesVals);&lt;br /&gt;
        &lt;br /&gt;
            }&lt;br /&gt;
            $eval = &amp;quot;skinPercent -zri 1&amp;quot;;&lt;br /&gt;
            string $targetVertex = $vertices[size($vertices)-1];&lt;br /&gt;
            for($i=0;$i&amp;lt;size($influences);$i++){&lt;br /&gt;
                if($influencesVals[$i] &amp;gt; 0){&lt;br /&gt;
                    $eval += &amp;quot; -tv &amp;quot;+$influences[$i]+&amp;quot; &amp;quot;+$influencesVals[$i]/$influenceVerticesCount;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        &lt;br /&gt;
            $eval += &amp;quot; &amp;quot;+$sck+&amp;quot; &amp;quot;+$targetVertex+&amp;quot;;\r\n&amp;quot;;&lt;br /&gt;
            eval($eval);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
averageWeights();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Show all maya icons ===&lt;br /&gt;
https://i.imgur.com/0WSBD90.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (`window -exists AllIconsWin`) deleteUI AllIconsWin;&lt;br /&gt;
if (`windowPref -exists AllIconsWin`) windowPref -remove AllIconsWin;&lt;br /&gt;
string $window = `window -title &amp;quot;All icons&amp;quot; -rtf 1 -widthHeight 840 550 AllIconsWin`;&lt;br /&gt;
&lt;br /&gt;
columnLayout -adj 1;&lt;br /&gt;
$labels =  `textFieldGrp -label &amp;quot;Resources: &amp;quot; -text  &amp;quot;click icons to get icon names&amp;quot;`;&lt;br /&gt;
$scrollLayout = `scrollLayout -verticalScrollBarThickness 16 -h 500`;&lt;br /&gt;
rowColumnLayout -numberOfColumns 25;&lt;br /&gt;
&lt;br /&gt;
string $icons[] = `resourceManager -nameFilter &amp;quot;*&amp;quot;`;&lt;br /&gt;
for($icon in $icons){&lt;br /&gt;
    nodeIconButton -w 32 -h 32 -style &amp;quot;iconOnly&amp;quot; -command (&amp;quot;displayname $labels \&amp;quot;&amp;quot;+$icon+&amp;quot;\&amp;quot;&amp;quot;) -image1 $icon;&lt;br /&gt;
}&lt;br /&gt;
showWindow $window;&lt;br /&gt;
&lt;br /&gt;
proc displayname(string $textfield,string $string){&lt;br /&gt;
    textFieldGrp -edit -text $string $textfield;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== add edge loops from single selected rings edges===&lt;br /&gt;
https://i.imgur.com/PiVJ350.gif&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $singleEdges[] = {};&lt;br /&gt;
string $sel[] = `ls -sl -fl`;&lt;br /&gt;
&lt;br /&gt;
for($o in $sel){&lt;br /&gt;
    select -r $o;&lt;br /&gt;
    SelectEdgeRingSp;&lt;br /&gt;
    polySplitRing -ch 0;&lt;br /&gt;
    string $sel2[] = `ls -sl -fl -hd 1`;&lt;br /&gt;
    $singleEdges[size($singleEdges)] = $sel2[0];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
select -r $singleEdges;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hide things that are not in control balls ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
global string $objs;&lt;br /&gt;
global string $controlsObjs;&lt;br /&gt;
$objs = &amp;quot;&amp;quot;;&lt;br /&gt;
$controlsObjs = &amp;quot;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
string $window = `window -menuBar true -title &amp;quot;Hide stuff with balls&amp;quot; &amp;quot;johny&amp;quot;`;&lt;br /&gt;
columnLayout -adjustableColumn true;&lt;br /&gt;
button -label &amp;quot;Select objects to be hidden&amp;quot; -command &amp;quot;set(0)&amp;quot;;&lt;br /&gt;
button -label &amp;quot;Select balls to control&amp;quot; -command &amp;quot;set(1)&amp;quot;;&lt;br /&gt;
showWindow $window;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
proc set(int $n){&lt;br /&gt;
    global string $objs;&lt;br /&gt;
    global string $controlsObjs;&lt;br /&gt;
    string $names[] = {&amp;quot;objs&amp;quot;,&amp;quot;control&amp;quot;};&lt;br /&gt;
    $createSetResult = `sets -name $names[$n]`;&lt;br /&gt;
    if($n==0){&lt;br /&gt;
        $objs = $createSetResult;&lt;br /&gt;
    }&lt;br /&gt;
    if($n==1){&lt;br /&gt;
        $controlsObjs = $createSetResult;&lt;br /&gt;
    }&lt;br /&gt;
    print($objs+&amp;quot;------&amp;quot;+$controlsObjs);&lt;br /&gt;
    if($n){&lt;br /&gt;
        string $expr = &amp;quot;&amp;quot;;&lt;br /&gt;
        string $controls[] = `listConnections -s 1 -d 0 -p 0 -c 0 $controlsObjs`;&lt;br /&gt;
        string $updates = &amp;quot;&amp;quot;;&lt;br /&gt;
        for($o in $controls){&lt;br /&gt;
            $updates += $o+&amp;quot;.tx +&amp;quot;+$o+&amp;quot;.sx +&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $updates += &amp;quot;0&amp;quot;;&lt;br /&gt;
        for($o in $controls){&lt;br /&gt;
            $expr += $o+&amp;quot;.shearXY = 0*(&amp;quot;+$updates+&amp;quot;);\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        $expr += &amp;quot;string $objs[] = `listConnections -s 1 -d 0 -p 0 -c 0 \&amp;quot;&amp;quot;+$objs+&amp;quot;\&amp;quot;`;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;string $controls[] = `listConnections -s 1 -d 0 -p 0 -c 0 \&amp;quot;&amp;quot;+$controlsObjs+&amp;quot;\&amp;quot;`;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;float $controlCenters[] = {};\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;for($o in $controls){\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    float $center[] = `xform -ws -q -t $o`;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    $i = size($controlCenters);\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    $controlCenters[$i] = $center[0];\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    $controlCenters[$i+1] = $center[1];\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    $controlCenters[$i+2] = $center[2];\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    $controlCenters[$i+3] = getAttr($o+\&amp;quot;.sx\&amp;quot;);\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;}\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;for($o in $objs){\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    float $c[] = `xform -ws -q -bb $o`;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    $c = {($c[0]+$c[3])/2,($c[1]+$c[4])/2,($c[2]+$c[5])/2};\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    int $visib = false;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    for($i = 0;$i&amp;lt;size($controlCenters);$i+=4){\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;        vector $distVect = &amp;lt;&amp;lt;$controlCenters[$i]-$c[0],$controlCenters[$i+1]-$c[1],$controlCenters[$i+2]-$c[2]&amp;gt;&amp;gt;;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;        float $dist = `mag($distVect)`;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;        if($dist&amp;lt;abs($controlCenters[$i+3])){\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;            $visib = true;\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;        }\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    }\n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;    setAttr($o+\&amp;quot;.visibility\&amp;quot;) $visib;   \n&amp;quot;;&lt;br /&gt;
        $expr += &amp;quot;} \n&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
        expression -s $expr &amp;quot;objs&amp;quot;;&lt;br /&gt;
        deleteUI &amp;quot;johny&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add .455 gamma to fileTextures===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//todo: make it so you don&amp;#039;t have to load the filetexture&lt;br /&gt;
string $fileTextures[] = `ls -sl`;&lt;br /&gt;
for($o in $fileTextures){&lt;br /&gt;
    $c = `listConnections -p 1 -d 1 ($o+&amp;quot;.outColor&amp;quot;)`;&lt;br /&gt;
    string $gamma = `shadingNode -asUtility gammaCorrect -n ($o+&amp;quot;_gammaCorrect&amp;quot;)`;&lt;br /&gt;
    setAttr ($gamma + &amp;quot;.gammaX&amp;quot;) 0.455;&lt;br /&gt;
    setAttr ($gamma + &amp;quot;.gammaY&amp;quot;) 0.455;&lt;br /&gt;
    setAttr ($gamma + &amp;quot;.gammaZ&amp;quot;) 0.455;&lt;br /&gt;
    connectAttr -force ($o + &amp;quot;.outColor&amp;quot;) ($gamma + &amp;quot;.value&amp;quot;);&lt;br /&gt;
    connectAttr -force ($gamma + &amp;quot;.outValue&amp;quot;) ($c);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Add camera center of interest locator ===&lt;br /&gt;
&amp;#039;&amp;#039;better python version: [[Maya_Python#zDepth_control_tool|zDepth Control Tool]]&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/8XCU06h.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
source channelBoxCommand;&lt;br /&gt;
string $camz[] = `ls -sl`;&lt;br /&gt;
if(size($camz)==1){&lt;br /&gt;
    string $shapes[] = `listRelatives -f $camz[0]`;&lt;br /&gt;
    if(`nodeType $shapes[0]` == &amp;quot;camera&amp;quot;){&lt;br /&gt;
        string $sl[] = {};&lt;br /&gt;
        string $curve = &amp;quot;curve -d 1 &amp;quot;;&lt;br /&gt;
        $w = 16;&lt;br /&gt;
        $h = 9;&lt;br /&gt;
        for($i=0;$i&amp;lt;=$w;$i++){&lt;br /&gt;
            $curve += &amp;quot;-p &amp;quot;+($i-$w/2.0)+&amp;quot; &amp;quot;+(($i%2)*$h-$h/2.0)+&amp;quot; 0 &amp;quot;; &lt;br /&gt;
            $curve += &amp;quot;-p &amp;quot;+($i-$w/2.0)+&amp;quot; &amp;quot;+((1-$i%2)*$h-$h/2.0)+&amp;quot; 0 &amp;quot;; &lt;br /&gt;
        }&lt;br /&gt;
        for($i=0;$i&amp;lt;=$h;$i++){&lt;br /&gt;
            $curve += &amp;quot;-p &amp;quot;+(($i%2)*$w-$w/2.0)+&amp;quot; &amp;quot;+($i-$h/2.0)+&amp;quot; 0 &amp;quot;; &lt;br /&gt;
            $curve += &amp;quot;-p &amp;quot;+((1-$i%2)*$w-$w/2.0)+&amp;quot; &amp;quot;+($i-$h/2.0)+&amp;quot; 0 &amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $curve += &amp;quot;-n focusedPlane&amp;quot;;&lt;br /&gt;
        &lt;br /&gt;
        $sl[0] = eval($curve);&lt;br /&gt;
&lt;br /&gt;
        setAttr -lock true ($sl[0]+&amp;quot;.tx&amp;quot;);&lt;br /&gt;
        setAttr -lock true ($sl[0]+&amp;quot;.ty&amp;quot;);&lt;br /&gt;
        setAttr ($sl[0]+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
        setAttr ($sl[0]+&amp;quot;.overrideColor&amp;quot;) 13;  &lt;br /&gt;
        string $group = `group -w -n ($camz[0]+&amp;quot;_focusLocator&amp;quot;) $sl[0]`;&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
        string $l[] = `listRelatives -c $group`;&lt;br /&gt;
        $sl[0] = $group+&amp;quot;|&amp;quot;+$l[0];&lt;br /&gt;
&lt;br /&gt;
        string $pcs[] = `parentConstraint -weight 1 $camz[0] $group`;&lt;br /&gt;
        setAttr($pcs[0]+&amp;quot;.target[0].targetOffsetRotateY&amp;quot;) 180;&lt;br /&gt;
        float $distance  = `getAttr ($shapes[0]+&amp;quot;.centerOfInterest&amp;quot;)`;&lt;br /&gt;
        setAttr($sl[0]+&amp;quot;.tz&amp;quot;) ($distance);&lt;br /&gt;
       &lt;br /&gt;
        $lc = `listConnections ($shapes[0]+&amp;quot;.centerOfInterest&amp;quot;)`;&lt;br /&gt;
        if(size($lc)&amp;gt;0 &amp;amp;&amp;amp; `nodeType $lc` == &amp;quot;animCurveTL&amp;quot;){&lt;br /&gt;
            int $ck = `copyKey -at &amp;quot;centerOfInterest&amp;quot; -time &amp;quot;:&amp;quot; $shapes[0]`;&lt;br /&gt;
            CBdeleteConnection ($shapes[0]+&amp;quot;.coi&amp;quot;);&lt;br /&gt;
            if($ck &amp;lt; 1){&lt;br /&gt;
                warning(&amp;quot;Can&amp;#039;t copy animation, sorry&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                pasteKey -at &amp;quot;tz&amp;quot; $sl[0];&lt;br /&gt;
                print(&amp;quot;Pasted &amp;quot;+$ck+&amp;quot; center of interest keyframes to the focusPlane&amp;quot;);&lt;br /&gt;
                catchQuiet(&amp;quot;delete $lc&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        expression -s ($shapes[0]+&amp;quot;.centerOfInterest=abs(&amp;quot;+$sl[0]+&amp;quot;.tz)&amp;quot;) -o $camz[0] -ae 1 -uc all ;&lt;br /&gt;
        string $an = `annotate -tx &amp;quot;&amp;quot; -p 0 0 0 $sl[0]`;&lt;br /&gt;
        string $anPar[] = `listRelatives -p $an`;&lt;br /&gt;
        &lt;br /&gt;
        parent -r $anPar[0] $camz[0];&lt;br /&gt;
        setAttr ($an+&amp;quot;.overrideDisplayType&amp;quot;) 1;&lt;br /&gt;
        setAttr ($an+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
        setAttr ($an+&amp;quot;.overrideColor&amp;quot;) 13;  &lt;br /&gt;
        select -r $sl;&lt;br /&gt;
        &lt;br /&gt;
    }else{&lt;br /&gt;
        warning &amp;quot;select camera only&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    warning &amp;quot;select a camera&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Select faces by angle (slow-ish)===&lt;br /&gt;
https://i.imgur.com/nxFaG74.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//super hackish code eww eww eww&lt;br /&gt;
global string $selectedFace;&lt;br /&gt;
string $selected[] = `ls -sl`;&lt;br /&gt;
$selectedFace = $selected[0]; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string $window = `window -title &amp;quot;Select similar faces&amp;quot;`;&lt;br /&gt;
columnLayout;&lt;br /&gt;
floatSliderGrp -label &amp;quot;Angle threshold   &amp;quot; -field true&lt;br /&gt;
    -minValue 0 -maxValue 180&lt;br /&gt;
    -cc &amp;quot;growSel&amp;quot;&lt;br /&gt;
    -value 0 &amp;quot;slidAngle&amp;quot;;&lt;br /&gt;
showWindow $window;&lt;br /&gt;
&lt;br /&gt;
proc growSel(){&lt;br /&gt;
    global string $selectedFace;&lt;br /&gt;
    string $newSelection[] = `ls -sl`;&lt;br /&gt;
    if(size($newSelection)==1){&lt;br /&gt;
        $selectedFace = $newSelection[0];&lt;br /&gt;
    }&lt;br /&gt;
    float $threshold = `floatSliderGrp  -q -v &amp;quot;slidAngle&amp;quot;`;&lt;br /&gt;
    growByAngle($selectedFace,$threshold);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
proc growByAngle(string $face, float $threshold){&lt;br /&gt;
    vector $base = norm($face);&lt;br /&gt;
    string $newSele[] = {};&lt;br /&gt;
    float $olds = 0;&lt;br /&gt;
    float $news = 1;&lt;br /&gt;
    int $round = 0;&lt;br /&gt;
    while($olds != $news || $round &amp;gt; 999){&lt;br /&gt;
        $round++;&lt;br /&gt;
        $olds = size(`ls -sl`);&lt;br /&gt;
        if($olds == 0){&lt;br /&gt;
            select -r $face;&lt;br /&gt;
        }&lt;br /&gt;
        GrowPolygonSelectionRegion;&lt;br /&gt;
        string $grow[] = `ls -sl -fl`;&lt;br /&gt;
        &lt;br /&gt;
        for($face in $grow){&lt;br /&gt;
           vector $fAngle = norm($face);&lt;br /&gt;
           float $result[] = `angleBetween -euler -v1 ($base.x) ($base.y) ($base.z) -v2 ($fAngle.x) ($fAngle.y) ($fAngle.z)`;&lt;br /&gt;
           if((abs($result[0])+abs($result[1])+abs($result[2]))&amp;lt;$threshold){&lt;br /&gt;
               $newSele[size($newSele)] = $face;&lt;br /&gt;
           }&lt;br /&gt;
        }&lt;br /&gt;
        select -r $newSele;&lt;br /&gt;
        $news = size(`ls -sl`);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
proc vector norm( string $face )&lt;br /&gt;
{&lt;br /&gt;
    &lt;br /&gt;
  //from Joseph A. Hansen (Beyond Games).&lt;br /&gt;
  vector $normal;&lt;br /&gt;
  float $x;&lt;br /&gt;
  float $y;&lt;br /&gt;
  float $z;&lt;br /&gt;
&lt;br /&gt;
  string $pins[] = `polyInfo -fn $face`;&lt;br /&gt;
  string $pin = $pins[0];&lt;br /&gt;
  string $tokens[];&lt;br /&gt;
  int $numTokens = `tokenize $pin &amp;quot; &amp;quot; $tokens`;&lt;br /&gt;
&lt;br /&gt;
  if ( ( $numTokens &amp;gt; 3 ) &amp;amp;&amp;amp; ( $tokens[0] == &amp;quot;FACE_NORMAL&amp;quot; ) )&lt;br /&gt;
  {&lt;br /&gt;
    $x = ($tokens[$numTokens-3]);&lt;br /&gt;
    $y = ($tokens[$numTokens-2]);&lt;br /&gt;
    $z = ($tokens[$numTokens-1]);&lt;br /&gt;
&lt;br /&gt;
    $normal = &amp;lt;&amp;lt; $x, $y, $z &amp;gt;&amp;gt;;&lt;br /&gt;
    $normal = `unit $normal`;&lt;br /&gt;
  }&lt;br /&gt;
  return $normal;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Select similar shells ===&lt;br /&gt;
https://i.imgur.com/ma38hQc.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//not tested on really heavy meshes&lt;br /&gt;
//&lt;br /&gt;
//selects shells with same number of faces&lt;br /&gt;
//expects 1 shell to be selected in face mode&lt;br /&gt;
//change numberOfTests according to mesh density&lt;br /&gt;
&lt;br /&gt;
int $numberOfTests = 5000;  //change according to mesh density and distribution, this took approx 10s on a 25k poly object&lt;br /&gt;
int $useMoreThanFacesCount = 1; //change to 1 if you want to match using UVs/edge, can be nice if you have same poly count but different UVs/edges count&lt;br /&gt;
int $debug = 0;&lt;br /&gt;
&lt;br /&gt;
string $selectionList[] = {};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string $sele[] = `ls -sl -fl`;&lt;br /&gt;
string $obj = `match &amp;quot;^[^\.]*&amp;quot; $sele[0]`;&lt;br /&gt;
&lt;br /&gt;
float $matchCount = size($sele);&lt;br /&gt;
&lt;br /&gt;
string $extra[] = `polyListComponentConversion -fv -fe -ff -fvf -te`; //uvs, change -tuv to -te to compare with edges instead of UVs&lt;br /&gt;
select -r $extra;&lt;br /&gt;
float $matchCountExtra = size(`ls -fl -sl`);&lt;br /&gt;
print($debug?(&amp;quot;faces: &amp;quot;+$matchCount+&amp;quot; uvs: &amp;quot;+$matchCountExtra+&amp;quot;\n-------------------------------------------------\n&amp;quot;):&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
string $faces[] = `ls -fl ($obj+&amp;quot;.f[*]&amp;quot;)`;&lt;br /&gt;
$s = size($faces);&lt;br /&gt;
&lt;br /&gt;
for($i=0;$i&amp;lt;=$s;$i+=($s/$numberOfTests)){&lt;br /&gt;
    &lt;br /&gt;
    $shl = `polySelect -asSelectString -ets $i $obj`;&lt;br /&gt;
    string $shell[] = `ls -sl -fl`;&lt;br /&gt;
    float $shellSize = size($shell);&lt;br /&gt;
    &lt;br /&gt;
    float $shellUVs = 0;&lt;br /&gt;
    if($useMoreThanFacesCount){&lt;br /&gt;
        string $extraCount[] = `polyListComponentConversion -fv -fe -ff -fvf -te`; //uvs, change -tuv to -te to compare with edges instead of UVs&lt;br /&gt;
        select -r $extraCount;&lt;br /&gt;
        $shellUVs = size(`ls -sl -fl`);&lt;br /&gt;
        print($debug?(&amp;quot;faces: &amp;quot;+$shellSize+&amp;quot; uvs: &amp;quot;+$shellUVs+&amp;quot;\n&amp;quot;):&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if($shellSize ==$matchCount &amp;amp;&amp;amp; ($shellUVs== $matchCountExtra || $useMoreThanFacesCount==0 ) ){&lt;br /&gt;
        for($j = 0;$j&amp;lt;size($shl);$j++){&lt;br /&gt;
            $selectionList[size($selectionList)] = $shl[$j];&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
select -r $selectionList;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Select similar to last object===&lt;br /&gt;
https://i.imgur.com/rfskawS.jpg&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Selects the objects that are similar to the last of the current selection, in the current selection, based on face counts (useful when cleaning up geo)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $selection[] = `ls -sl`;&lt;br /&gt;
string $newSelection[];&lt;br /&gt;
int $faces[] = `polyEvaluate -f $selection[size($selection)-1]`;&lt;br /&gt;
for($obj in $selection){&lt;br /&gt;
    int $pe[] = `polyEvaluate -f $obj`;    &lt;br /&gt;
    if($pe[0] == $faces[0]){&lt;br /&gt;
        $newSelection[size($newSelection)] = $obj;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
select -r $newSelection;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select shapes according to wire color&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $os[] = `ls -dag -s -sl`;&lt;br /&gt;
$last = $os[size($os)-1];&lt;br /&gt;
int $color = `getAttr ($last+&amp;quot;.overrideColor&amp;quot;)`;  &lt;br /&gt;
string $lists[] = {};&lt;br /&gt;
for($obj in $os){&lt;br /&gt;
    if(`getAttr ($obj+&amp;quot;.overrideColor&amp;quot;)` == $color){&lt;br /&gt;
        $lists[size($lists)] = $obj;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
select -r $lists;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Stupid Pos/Rot/Scale Export &amp;amp; Stupid shader assignement export ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proc writeToTempTextFileAndOpen(string $textToWrite){&lt;br /&gt;
    //cross platform write to temp textfile&lt;br /&gt;
    string $file = `file -q -sn -shn`;&lt;br /&gt;
    int $r = `rand 1000 10000`;&lt;br /&gt;
    $file = ($file==&amp;quot;&amp;quot;)?&amp;quot;tmp_maya_file.&amp;quot;+$r+&amp;quot;.txt&amp;quot;:$file+&amp;quot;.&amp;quot;+$r+&amp;quot;.txt&amp;quot;;&lt;br /&gt;
    string $myScriptDir = `internalVar -utd`;&lt;br /&gt;
    string $tmpfile = $myScriptDir+$file;&lt;br /&gt;
    int $outFileId = fopen($tmpfile,&amp;quot;w&amp;quot;);&lt;br /&gt;
    if ($outFileId == 0 ) {            &lt;br /&gt;
        scriptEditorInfo -ch;&lt;br /&gt;
        print $textToWrite;&lt;br /&gt;
        warning (&amp;quot;\n\n//  Could not open output file &amp;quot; + $tmpfile + &amp;quot; wrote to the script editor window instead.&amp;quot;);&lt;br /&gt;
    }else{&lt;br /&gt;
        fprint $outFileId $textToWrite;&lt;br /&gt;
        fclose $outFileId;&lt;br /&gt;
        if(`about -win` == 1){&lt;br /&gt;
            system(&amp;quot;load &amp;quot; + `toNativePath($tmpfile)`);     //win &lt;br /&gt;
        }else if(`about -li` == 1){&lt;br /&gt;
            system(&amp;quot;gedit &amp;quot;+$tmpfile+&amp;quot;&amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;);  //linux &lt;br /&gt;
        }else if(`about -mac` == 1){&lt;br /&gt;
            system(&amp;quot;TextEdit &amp;quot;+$tmpfile+&amp;quot;&amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;);  //macOs, untested      &lt;br /&gt;
        }else{&lt;br /&gt;
            scriptEditorInfo -ch;&lt;br /&gt;
            print $textToWrite;&lt;br /&gt;
            warning (&amp;quot;\n\n//  Could not figure out system, wrote to the script editor window instead.&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
https://i.imgur.com/62XVT80.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//output translate, rotate, scale for the current frame&lt;br /&gt;
scriptEditorInfo -ch;&lt;br /&gt;
print(&amp;quot;\n\n//==========EDIT AND PASTE IN THE SCRIPT EDITOR ===========================================================\n\n\n&amp;quot;);&lt;br /&gt;
proc outputTransforms(){&lt;br /&gt;
    for($o in `ls -sl -l`){&lt;br /&gt;
    float $po[] = `getAttr ($o+&amp;quot;.translate&amp;quot;)`;&lt;br /&gt;
    print(&amp;quot;setAttr (\&amp;quot;&amp;quot;+$o+&amp;quot;.translate\&amp;quot;) &amp;quot;+$po[0]+&amp;quot; &amp;quot;+$po[1]+&amp;quot; &amp;quot;+$po[2]+&amp;quot;;\n&amp;quot;);&lt;br /&gt;
    float $ro[] = `getAttr ($o+&amp;quot;.rotate&amp;quot;)`;&lt;br /&gt;
    print(&amp;quot;setAttr (\&amp;quot;&amp;quot;+$o+&amp;quot;.rotate\&amp;quot;) &amp;quot;+$ro[0]+&amp;quot; &amp;quot;+$ro[1]+&amp;quot; &amp;quot;+$ro[2]+&amp;quot;;\n&amp;quot;);&lt;br /&gt;
    float $sc[] = `getAttr ($o+&amp;quot;.scale&amp;quot;)`;&lt;br /&gt;
    print(&amp;quot;setAttr (\&amp;quot;&amp;quot;+$o+&amp;quot;.scale\&amp;quot;) &amp;quot;+$sc[0]+&amp;quot; &amp;quot;+$sc[1]+&amp;quot; &amp;quot;+$sc[2]+&amp;quot;;\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
evalDeferred(&amp;quot;outputTranforms&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
shaders:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//for now face assignement doesn&amp;#039;t work&lt;br /&gt;
scriptEditorInfo -ch;&lt;br /&gt;
proc listShadersAssignement(){&lt;br /&gt;
    print(&amp;quot;\n\n//==========EDIT AND PASTE IN THE SCRIPT EDITOR ===========================================================\n\n\n&amp;quot;);&lt;br /&gt;
    for($o in `ls -sl -l`){&lt;br /&gt;
        string $shapes[] = `listRelatives -s  -f $o`;&lt;br /&gt;
        if(`nodeType $shapes[0]` == &amp;quot;mesh&amp;quot;){&lt;br /&gt;
            for($p in  `listConnections $shapes[0]`){&lt;br /&gt;
                $type = `nodeType $p`;&lt;br /&gt;
                if($type == &amp;quot;shadingEngine&amp;quot;){&lt;br /&gt;
                    $u = &amp;quot;&amp;quot;;&lt;br /&gt;
                    for($i = 1;$i&amp;lt;=40 - size($p);$i++){&lt;br /&gt;
                        $u += &amp;quot; &amp;quot;;&lt;br /&gt;
                    }&lt;br /&gt;
                    print(&amp;quot;\nsets -forceElement &amp;quot;+$p+&amp;quot;&amp;quot;+$u+&amp;quot;&amp;quot;+$o+&amp;quot;;&amp;quot;); &lt;br /&gt;
                    break; //stops at first shader found&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
     &lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
evalDeferred(&amp;quot;listShadersAssignement&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Save selection to temp text file ===&lt;br /&gt;
useful if you&amp;#039;re between mayas and are switching selections between two similar scenes&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $myScriptDir = `internalVar -utd`;&lt;br /&gt;
string $tmpfile = $myScriptDir+&amp;quot;tmp_sel.txt&amp;quot;;&lt;br /&gt;
int $outFileId = fopen($tmpfile,&amp;quot;w&amp;quot;);&lt;br /&gt;
if ($outFileId != 0) {&lt;br /&gt;
    string $sel[] = `ls -sl`;&lt;br /&gt;
    $selstring = &amp;quot;select -add &amp;quot;+stringArrayToString($sel,&amp;quot; &amp;quot;);&lt;br /&gt;
    fprint $outFileId $selstring;&lt;br /&gt;
    fclose $outFileId;&lt;br /&gt;
    exec(&amp;quot;notepad &amp;quot;+$tmpfile);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Save weights to text file===&lt;br /&gt;
https://i.imgur.com/iG1eQu3.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    scriptEditorInfo -ch; //run first if linux&lt;br /&gt;
    &lt;br /&gt;
    string $vertices[] = `ls -sl -fl -l`;&lt;br /&gt;
    string $selObj[] = `ls -o -sl`;&lt;br /&gt;
    if(size($vertices)&amp;gt;0){&lt;br /&gt;
        string $sck = `findRelatedSkinCluster($selObj[0])`;&lt;br /&gt;
    &lt;br /&gt;
        &lt;br /&gt;
    &lt;br /&gt;
        string $eval = &amp;quot;//------------COPY IN SCRIPT EDITOR WINDOW------------//\r\n\r\n&amp;quot;;&lt;br /&gt;
        for($vertex in $vertices){&lt;br /&gt;
            string $skinInfluences[]=`skinPercent -ib 0.000001 -q -t $sck $vertex`;&lt;br /&gt;
            float $sknVals[] = `skinPercent -ib 0.000001 -q -v  $sck $vertex`;&lt;br /&gt;
            $eval += &amp;quot;skinPercent -zri 1&amp;quot;;&lt;br /&gt;
            for($i=0;$i&amp;lt;size($skinInfluences);$i++){&lt;br /&gt;
                if($sknVals[$i] &amp;gt; 0){&lt;br /&gt;
                    $eval += &amp;quot; -tv &amp;quot;+$skinInfluences[$i]+&amp;quot; &amp;quot;+$sknVals[$i];&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            $eval += &amp;quot; &amp;quot;+$sck+&amp;quot; &amp;quot;+$vertex+&amp;quot;;\r\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    &lt;br /&gt;
        &lt;br /&gt;
        if(`about -win` == 1){&lt;br /&gt;
            string $file = `file -q -sn -shn`;&lt;br /&gt;
            int $r = `rand 1000 10000`;&lt;br /&gt;
            $file = ($file==&amp;quot;&amp;quot;)?&amp;quot;tmp_skin_weights_maya.&amp;quot;+$r+&amp;quot;.txt&amp;quot;:$file+&amp;quot;.&amp;quot;+$r+&amp;quot;.txt&amp;quot;;&lt;br /&gt;
            string $myScriptDir = `internalVar -utd`;&lt;br /&gt;
            string $tmpfile = $myScriptDir+$file;&lt;br /&gt;
            int $outFileId = fopen($tmpfile,&amp;quot;w&amp;quot;);&lt;br /&gt;
            if ($outFileId == 0 ) {            &lt;br /&gt;
                scriptEditorInfo -ch&lt;br /&gt;
                print $eval;&lt;br /&gt;
                warning (&amp;quot;\n\n//  Could not open output file &amp;quot; + $tmpfile + &amp;quot; wrote to the script editor window instead.&amp;quot;);&lt;br /&gt;
            }else{&lt;br /&gt;
                fprint $outFileId $eval;&lt;br /&gt;
                fclose $outFileId;&lt;br /&gt;
                system(&amp;quot;load &amp;quot; + `toNativePath($tmpfile)`); //win only&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            &lt;br /&gt;
           print($eval);&lt;br /&gt;
            print &amp;quot;\n\n//  Wrote to the script editor window instead.&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        warning &amp;quot;select vertices to extract and save weights from&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Simple incremental save w/ prompt ===&lt;br /&gt;
https://i.imgur.com/2GeQRpR.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proc incrementAndSave(){&lt;br /&gt;
    //takes the current scene, increments (or adds &amp;#039;01&amp;#039; suffix if none found). Padding set to 2&lt;br /&gt;
    //so many regexes because I suck at this&lt;br /&gt;
    string $path = `file -q -loc`;&lt;br /&gt;
    $path = `match &amp;quot;^.*/&amp;quot; $path`;&lt;br /&gt;
    string $file = `file -q -sn -shn`;&lt;br /&gt;
    $ext = fileExtension($file);&lt;br /&gt;
    $file = `match &amp;quot;^[^\.]*&amp;quot; $file`;&lt;br /&gt;
    string $suffix = `match &amp;quot;[0-9]+$&amp;quot; $file`;&lt;br /&gt;
    $file = `match &amp;quot;.*[^0-9]&amp;quot; $file`;&lt;br /&gt;
    int $suff = 1;&lt;br /&gt;
    if($suffix != &amp;quot;&amp;quot;){  &lt;br /&gt;
        $suff = $suffix;&lt;br /&gt;
        $suff++;&lt;br /&gt;
    }&lt;br /&gt;
    $suffix = ($suff&amp;lt;10)?&amp;quot;0&amp;quot;+$suff:$suff;&lt;br /&gt;
    $fileprompt = `promptDialog -text ($file+$suffix+&amp;quot;.&amp;quot;+$ext) -title &amp;quot;Save as&amp;quot; -message &amp;quot;New file name:&amp;quot; -button &amp;quot;OK&amp;quot; -button &amp;quot;Cancel&amp;quot; -defaultButton &amp;quot;OK&amp;quot; -cancelButton &amp;quot;Cancel&amp;quot;`;&lt;br /&gt;
    if($fileprompt == &amp;quot;OK&amp;quot;){&lt;br /&gt;
        $scene = `promptDialog -q -text`;&lt;br /&gt;
        $type = (`tolower $ext` == &amp;quot;ma&amp;quot;)?&amp;quot;mayaAscii&amp;quot;:&amp;quot;mayaBinary&amp;quot;;&lt;br /&gt;
        file -rename ($path+$scene); file -save -type $type;&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
incrementAndSave;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Simple renderview batch ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//if batch rendering only works in viewport. Uses rendersettings except for padding set to 4. Make sure you&amp;#039;re rendering 32bit in the viewport&lt;br /&gt;
proc string pad(float $n){return ($n&amp;lt;1000)? ( ($n&amp;lt;100)?( ($n&amp;lt;10)?&amp;quot;000&amp;quot;+$n:&amp;quot;00&amp;quot;+$n ):&amp;quot;0&amp;quot;+$n ) : $n+&amp;quot;&amp;quot;;}&lt;br /&gt;
proc batchrenderview(){&lt;br /&gt;
    $f_str = `getAttr &amp;quot;defaultRenderGlobals.startFrame&amp;quot;`;&lt;br /&gt;
    $f_end = `getAttr &amp;quot;defaultRenderGlobals.endFrame&amp;quot;`;&lt;br /&gt;
    $c = `lookThru -q`;&lt;br /&gt;
    $c = &amp;quot;Render &amp;quot;+($f_end-$f_str+1)+&amp;quot; frames with camera &amp;#039;&amp;quot;+$c+&amp;quot;&amp;#039; ?&amp;quot;;&lt;br /&gt;
    $button = `confirmDialog -title &amp;quot;Confirm&amp;quot; -message $c -button &amp;quot;OK&amp;quot; -button &amp;quot;Cancel&amp;quot; -defaultButton &amp;quot;OK&amp;quot;    -cancelButton &amp;quot;Cancel&amp;quot; -dismissString &amp;quot;nope&amp;quot;`;&lt;br /&gt;
    if($button == &amp;quot;OK&amp;quot;){&lt;br /&gt;
        setAttr &amp;quot;defaultRenderGlobals.outFormatControl&amp;quot; 0;&lt;br /&gt;
        setAttr &amp;quot;defaultRenderGlobals.animation&amp;quot; 1;&lt;br /&gt;
        setAttr &amp;quot;defaultRenderGlobals.putFrameBeforeExt&amp;quot; 1;&lt;br /&gt;
        setAttr &amp;quot;defaultRenderGlobals.extensionPadding&amp;quot; 4;&lt;br /&gt;
        $log = 0;&lt;br /&gt;
        $dir = &amp;quot;&amp;quot;;&lt;br /&gt;
        for($i = $f_str;$i&amp;lt;=$f_end;$i++){&lt;br /&gt;
            currentTime $i;&lt;br /&gt;
            renderIntoNewWindow render;&lt;br /&gt;
            string $f = pad($i);&lt;br /&gt;
            string $tempf[] = `renderSettings -gin $f -fpt`;&lt;br /&gt;
            string $tof[] = `renderSettings -gin $f -fp`;&lt;br /&gt;
            $dir = `match &amp;quot;^.*/&amp;quot; $tof[0]`;&lt;br /&gt;
            sysFile -md $dir;&lt;br /&gt;
            $log += `sysFile -ren $tof[0] $tempf[0]`;&lt;br /&gt;
        }&lt;br /&gt;
        warning($log+&amp;quot; files copied to     &amp;quot;+toNativePath($dir));&lt;br /&gt;
    &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
batchrenderview;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Particle mover ===&lt;br /&gt;
https://i.imgur.com/F1ciuNQ.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//select particles that you want to move&lt;br /&gt;
//launch script, move locator around, scale &amp;amp; rotate&lt;br /&gt;
//hit remove once you&amp;#039;re happy. Initial state is set.&lt;br /&gt;
//&lt;br /&gt;
// doesn&amp;#039;t yet work on particles systems that have been moved around&lt;br /&gt;
&lt;br /&gt;
global string $gPart;&lt;br /&gt;
global string $gLoc;&lt;br /&gt;
global float $gPos[];&lt;br /&gt;
global float $gIds[];&lt;br /&gt;
&lt;br /&gt;
string $particles[] = `ls -sl -fl`;&lt;br /&gt;
string $partName = `match &amp;quot;^[^\.]*&amp;quot; $particles[0]`;&lt;br /&gt;
string $partiName[] = `listRelatives -s $partName`;&lt;br /&gt;
&lt;br /&gt;
float $ids[] = `getParticleAttr -a 1 -at id $particles`;&lt;br /&gt;
float $positions[] = `getParticleAttr -a 1 -at position $particles`;&lt;br /&gt;
float $locpos[] = `getParticleAttr -at position $particles`;&lt;br /&gt;
/*setToolTo moveSuperContext; &lt;br /&gt;
float $centerPosi[] = `manipMoveContext -q -position Move`;*/&lt;br /&gt;
select -cl;&lt;br /&gt;
string $loc[] = `spaceLocator`;&lt;br /&gt;
/*parent $loc[0] $partName;&lt;br /&gt;
setToZero($loc[0]);&lt;br /&gt;
parent -w $loc[0];*/&lt;br /&gt;
&lt;br /&gt;
//move -r $centerPosi[0] $centerPosi[1] $centerPosi[2];&lt;br /&gt;
move -r $locpos[0] $locpos[1] $locpos[2];&lt;br /&gt;
makeIdentity -apply true -t 1 -r 0 -s 0 -n 0 $loc[0];&lt;br /&gt;
&lt;br /&gt;
$gPart = $partiName[0];&lt;br /&gt;
$gLoc = $loc[0];&lt;br /&gt;
$gPos = $positions;&lt;br /&gt;
$gIds = $ids;&lt;br /&gt;
&lt;br /&gt;
string $window = `window -menuBar true -title &amp;quot;Move Particles&amp;quot;`;&lt;br /&gt;
columnLayout -adjustableColumn true;&lt;br /&gt;
button -label &amp;quot;Apply&amp;quot; -command moveParticles;&lt;br /&gt;
button -label &amp;quot;Reset&amp;quot; -command (&amp;quot;setToZero(\&amp;quot;&amp;quot;+$loc[0]+&amp;quot;\&amp;quot;)&amp;quot;);&lt;br /&gt;
button -label &amp;quot;Finish&amp;quot; -command (&amp;quot;delete &amp;quot;+$loc[0]+&amp;quot;; deleteUI -window &amp;quot;+$window+&amp;quot;; select -r &amp;quot;+stringArrayToString($particles,&amp;quot; &amp;quot;));&lt;br /&gt;
showWindow $window;&lt;br /&gt;
&lt;br /&gt;
proc moveParticles(){&lt;br /&gt;
    global string $gPart;&lt;br /&gt;
    global string $gLoc;&lt;br /&gt;
    global float $gPos[];&lt;br /&gt;
    global float $gIds[];    &lt;br /&gt;
&lt;br /&gt;
    string $locator = $gLoc;&lt;br /&gt;
    string $part = $gPart;&lt;br /&gt;
    float $pids[] = $gIds;&lt;br /&gt;
    float $posis[] = $gPos;&lt;br /&gt;
    &lt;br /&gt;
    string $sel[] = `ls -sl`;&lt;br /&gt;
    float $m[] = `xform -query -matrix $locator`;&lt;br /&gt;
    for($i = 0;$i&amp;lt;size($pids);$i++){&lt;br /&gt;
        float $po[] = {$posis[$i*3],$posis[$i*3+1],$posis[$i*3+2]};&lt;br /&gt;
        $po =  pointMatrixMult($po,$m);&lt;br /&gt;
        $po = {$po[0]+$m[12],$po[1]+$m[13],$po[2]+$m[14]};&lt;br /&gt;
        select -r ($part+&amp;quot;.pt[&amp;quot;+$pids[$i]+&amp;quot;]&amp;quot;);&lt;br /&gt;
        setParticleAttr -vv $po[0] $po[1]$po[2] -at position;&lt;br /&gt;
    }&lt;br /&gt;
    select -r $gPart;&lt;br /&gt;
    catchQuiet(performSetNClothStartState(1));&lt;br /&gt;
    catchQuiet(saveInitialState($gPart));&lt;br /&gt;
    select -r $sel;&lt;br /&gt;
}&lt;br /&gt;
proc setToZero(string $obj){&lt;br /&gt;
    setAttr ($obj+&amp;quot;.translateX&amp;quot;) 0;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.translateY&amp;quot;) 0;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.translateZ&amp;quot;) 0;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.rotateX&amp;quot;) 0;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.rotateY&amp;quot;) 0;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.rotateZ&amp;quot;) 0;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.scaleX&amp;quot;) 1;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.scaleY&amp;quot;) 1;&lt;br /&gt;
    setAttr ($obj+&amp;quot;.scaleZ&amp;quot;) 1;&lt;br /&gt;
    moveParticles();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Turn all nucleuses on/off ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $nuc[] = `ls &amp;quot;*nucleus*&amp;quot;`; //assumes it hasn&amp;#039;t been renamed&lt;br /&gt;
if(size($nuc)&amp;gt;0)&lt;br /&gt;
{&lt;br /&gt;
    $mode = `getAttr ($nuc[0]+&amp;quot;.enable&amp;quot;)`;&lt;br /&gt;
    $mode = ($mode)?0:1;&lt;br /&gt;
    &lt;br /&gt;
    for($i=0;$i&amp;lt;size($nuc);$i++)&lt;br /&gt;
    {&lt;br /&gt;
        setAttr ($nuc[$i]+&amp;quot;.enable&amp;quot;) $mode;&lt;br /&gt;
        print(&amp;quot;All nucleuses &amp;quot;+(($mode)?&amp;quot;on&amp;quot;:&amp;quot;off&amp;quot;));&lt;br /&gt;
    }&lt;br /&gt;
}else{&lt;br /&gt;
    print(&amp;quot;No nucleus found&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Photometric camera &amp;amp; studio lights===&lt;br /&gt;
https://i.imgur.com/jWfi6XU.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//same as manually adding a simple lens exposure; select camera first&lt;br /&gt;
if(nodeType(listRelatives(`ls -sl`))==&amp;quot;camera&amp;quot;){&lt;br /&gt;
    $cam = listRelatives(`ls -sl`);&lt;br /&gt;
    $node = `createNode &amp;quot;mia_exposure_simple&amp;quot;`;&lt;br /&gt;
    connectAttr -f ($node+&amp;quot;.message&amp;quot;) ($cam[0]+&amp;quot;.miLensShader&amp;quot;);&lt;br /&gt;
}else{&lt;br /&gt;
    warning &amp;quot;Select a camera&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//creates an area light with photo studio shader &amp;amp; shadows&lt;br /&gt;
select -cl;&lt;br /&gt;
defaultAreaLight 1 1 1 1 0 0 0 0 0 1 0;&lt;br /&gt;
string $sel[] = `ls -sl`;&lt;br /&gt;
$ali = $sel[0];&lt;br /&gt;
setAttr ($ali+&amp;quot;.areaLight&amp;quot;) 1;&lt;br /&gt;
setAttr ($ali+&amp;quot;.areaVisible&amp;quot;) 1;&lt;br /&gt;
$pl = `createNode  mia_portal_light`;&lt;br /&gt;
connectAttr -force ($pl+&amp;quot;.message&amp;quot;) ($ali+&amp;quot;.mentalRayControls.miLightShader&amp;quot;);&lt;br /&gt;
setAttr ($pl+&amp;quot;.visible&amp;quot;) 1;&lt;br /&gt;
setAttr ($pl+&amp;quot;.use_custom_environment&amp;quot;) 1;&lt;br /&gt;
$bb= `createNode  mib_blackbody`;&lt;br /&gt;
connectAttr -force ($bb+&amp;quot;.message&amp;quot;) ($pl+&amp;quot;.custom_environment&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Save render view image to Desktop as png===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/A6FWR.png&lt;br /&gt;
&lt;br /&gt;
Save following proc as saveRenderViewToDesktop.mel in prefs/scripts:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
global proc saveRenderViewToDesktop(){&lt;br /&gt;
    string $folderName = &amp;quot;renderView&amp;quot;;&lt;br /&gt;
    string $fileName = `file -q -sceneName`;&lt;br /&gt;
    string $scene = `match &amp;quot;[^/\\]*$&amp;quot; $fileName`;&lt;br /&gt;
    $scene = `match &amp;quot;^[^\.]*&amp;quot; $scene`;&lt;br /&gt;
    string $desktop = getenv(&amp;quot;USERPROFILE&amp;quot;)+&amp;quot;/Desktop&amp;quot;;&lt;br /&gt;
    string $d = system(&amp;quot;echo %DATE%-%TIME%&amp;quot;);&lt;br /&gt;
    string $datetime = substring($d,9,10)+substring($d,4,5)+substring($d,1,2)+&amp;quot;_&amp;quot;+substring($d,12,13)+substring($d,15,16)+&amp;quot;_&amp;quot;+substring($d,18,18);&lt;br /&gt;
    sysFile -makeDir ($desktop+&amp;quot;/&amp;quot;+$folderName+&amp;quot;/&amp;quot;); // Windows&lt;br /&gt;
    string $path = $desktop+&amp;quot;/&amp;quot;+$folderName+&amp;quot;/&amp;quot;+$scene+&amp;quot;-&amp;quot;+$datetime;&lt;br /&gt;
    int $imf = `getAttr &amp;quot;defaultRenderGlobals.imageFormat&amp;quot;`;&lt;br /&gt;
    setAttr &amp;quot;defaultRenderGlobals.imageFormat&amp;quot; 32;&lt;br /&gt;
    catch(`renderWindowSaveImageCallback &amp;quot;renderView&amp;quot; $path &amp;quot;image&amp;quot;`);&lt;br /&gt;
    setAttr &amp;quot;defaultRenderGlobals.imageFormat&amp;quot; $imf;&lt;br /&gt;
    print(&amp;quot;Image saved to: &amp;quot;+$path+&amp;quot;.png&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add to \MayaPath\scripts\others\renderWindowPanel.mel (around line 3413, look for &amp;#039;&amp;#039;iconTextButton -i1 &amp;quot;rvRemoveIt.png&amp;quot;&amp;#039;&amp;#039;)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     iconTextButton -i1 &amp;quot;editRenderPass.png&amp;quot; -width $iconSize -height $iconSize&lt;br /&gt;
        -command (&amp;quot;saveRenderViewToDesktop&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== add miLabels ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
addmilabel;&lt;br /&gt;
global proc addmilabel(){&lt;br /&gt;
    string $objs[] = `ls -sl`;&lt;br /&gt;
    string $pd = `promptDialog -m &amp;quot;mi Label&amp;quot;`;&lt;br /&gt;
    int $mil = `promptDialog -q -text`;&lt;br /&gt;
    for($obj in $objs){&lt;br /&gt;
        $l = `listAttr -st &amp;quot;miLabel&amp;quot; $obj`;&lt;br /&gt;
        if($l[0] != &amp;quot;miLabel&amp;quot;){&lt;br /&gt;
    &lt;br /&gt;
            $cmd = `addAttr -at short -longName miLabel -defaultValue $mil $obj`;&lt;br /&gt;
            catchQuiet(`setAttr -k on ($obj+&amp;quot;.miLabel&amp;quot;)`);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Place circle at base of paths ===&lt;br /&gt;
https://i.imgur.com/KJnJn.png&lt;br /&gt;
https://i.imgur.com/JSKSRj6.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//places a circle at the base of a path&lt;br /&gt;
//select curve and run&lt;br /&gt;
//2021 edit: i don&amp;#039;t use maya or mel anymore but for the odd job added a line to give me a poly tube as in the screenshot above&lt;br /&gt;
&lt;br /&gt;
source generateChannelMenu.mel;&lt;br /&gt;
source channelBoxCommand.mel;&lt;br /&gt;
string $objs[] = `ls -sl`;&lt;br /&gt;
string $select[] = {};&lt;br /&gt;
for($obj in $objs){&lt;br /&gt;
&lt;br /&gt;
    string $circle[] = `circle -ch on -o on -nr 1 0 0 -r .5`;&lt;br /&gt;
    float $e = `playbackOptions -query -maxTime`;&lt;br /&gt;
    float $s = `playbackOptions -query -minTime`;&lt;br /&gt;
    string $pa = `pathAnimation -fractionMode true -follow true -followAxis x -upAxis y -worldUpType &amp;quot;vector&amp;quot; -worldUpVector 0 1 0 -inverseUp false -inverseFront false -bank false -eu 1 $circle[0] $obj`;&lt;br /&gt;
    CBdeleteConnection($pa+&amp;quot;.u&amp;quot;);&lt;br /&gt;
    //remove the following line if you don&amp;#039;t want tubes&lt;br /&gt;
    extrude -ch true -rn false -po 1 -et 2 -ucp 0 -fpt 0 -upn 1 -rotation 0 -scale 1 -rsp 1 $circle[0] $obj ;&lt;br /&gt;
    $select[size($select)]=$circle[0];&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
select -r $select;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===&amp;#039;Julienne cut&amp;#039;: multi polycut across shapes===&lt;br /&gt;
https://i.imgur.com/NhHYY.png&lt;br /&gt;
https://i.imgur.com/zBObLRr.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//adds as many polycuts as there are objects&lt;br /&gt;
//with one nurbs controller&lt;br /&gt;
//only works in worldspace&lt;br /&gt;
proc string cutObjs(){&lt;br /&gt;
    string $objs[] = `ls -sl -tr -o`;&lt;br /&gt;
    string $controlPlane[];&lt;br /&gt;
    if(size($objs) &amp;gt; 0){&lt;br /&gt;
        setToolTo moveSuperContext; &lt;br /&gt;
        vector $centerPos = `manipMoveContext -q -position Move`;&lt;br /&gt;
        float $bbox[] = `xform -q -ws -bb $objs[0]`;&lt;br /&gt;
        float $x = $bbox[3]-$bbox[0]; &lt;br /&gt;
        float $y = $bbox[4]-$bbox[1];&lt;br /&gt;
        float $z = $bbox[5]-$bbox[2];&lt;br /&gt;
        $controlPlane = `nurbsPlane -w ($x*1.1) -lr ($y/$x*1.1) -ax 0 0 1 -n &amp;quot;ctrlPlane&amp;quot;`;&lt;br /&gt;
        move -r -wd ($centerPos.x) ($centerPos.y) ($centerPos.z);&lt;br /&gt;
        addAttr -ln &amp;quot;Detach&amp;quot;  -at bool  $controlPlane[0];&lt;br /&gt;
        setAttr -e-keyable true ($controlPlane[0]+&amp;quot;.Detach&amp;quot;);   &lt;br /&gt;
        addAttr -ln &amp;quot;Delete&amp;quot;  -at bool  $controlPlane[0];&lt;br /&gt;
        setAttr -e-keyable true ($controlPlane[0]+&amp;quot;.Delete&amp;quot;);&lt;br /&gt;
        addAttr -ln &amp;quot;polyCutOffset&amp;quot;  -at double3  $controlPlane[0];&lt;br /&gt;
        addAttr -ln &amp;quot;polyCutOffsetX&amp;quot;  -at double -p polyCutOffset  $controlPlane[0];;&lt;br /&gt;
        addAttr -ln &amp;quot;polyCutOffsetY&amp;quot;  -at double -p polyCutOffset  $controlPlane[0];;&lt;br /&gt;
        addAttr -ln &amp;quot;polyCutOffsetZ&amp;quot;  -at double -p polyCutOffset  $controlPlane[0];;&lt;br /&gt;
        setAttr -type double3   ($controlPlane[0]+&amp;quot;.polyCutOffset&amp;quot;) 0 0 0;&lt;br /&gt;
        setAttr -e-keyable true ($controlPlane[0]+&amp;quot;.polyCutOffset&amp;quot;);&lt;br /&gt;
        setAttr -e-keyable true ($controlPlane[0]+&amp;quot;.polyCutOffsetX&amp;quot;);&lt;br /&gt;
        setAttr -e-keyable true ($controlPlane[0]+&amp;quot;.polyCutOffsetY&amp;quot;);&lt;br /&gt;
        setAttr -e-keyable true ($controlPlane[0]+&amp;quot;.polyCutOffsetZ&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
        string $polycuts[];&lt;br /&gt;
        for($t in $objs){&lt;br /&gt;
            string $polyCutTool[] = `polyCut  -ws 1  -cd &amp;quot;X&amp;quot; -ch 1 $t`;&lt;br /&gt;
            $polycuts[size($polycuts)] = $polyCutTool[0];&lt;br /&gt;
           connectAttr -f ($controlPlane[0]+&amp;quot;.translate&amp;quot;) ($polyCutTool[0]+&amp;quot;.cutPlaneCenter&amp;quot;);&lt;br /&gt;
           connectAttr -f ($controlPlane[0]+&amp;quot;.rotate&amp;quot;) ($polyCutTool[0]+&amp;quot;.cutPlaneRotate&amp;quot;);&lt;br /&gt;
           connectAttr -f ($controlPlane[0]+&amp;quot;.polyCutOffset&amp;quot;) ($polyCutTool[0]+&amp;quot;.extractOffset&amp;quot;);&lt;br /&gt;
           connectAttr -f ($controlPlane[0]+&amp;quot;.Detach&amp;quot;) ($polyCutTool[0]+&amp;quot;.extractFaces&amp;quot;);&lt;br /&gt;
           connectAttr -f ($controlPlane[0]+&amp;quot;.Delete&amp;quot;) ($polyCutTool[0]+&amp;quot;.deleteFaces&amp;quot;);&lt;br /&gt;
           &lt;br /&gt;
        }&lt;br /&gt;
    return $controlPlane[0];&lt;br /&gt;
    }else{&lt;br /&gt;
    return false;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/////////////////////////////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
proc multiCut(int $cutnumbers){&lt;br /&gt;
    //requires cutObjs proc above&lt;br /&gt;
    //expects a number of cuts (&amp;gt;2)&lt;br /&gt;
    string $ctrlplanes[];&lt;br /&gt;
    string $objs[] = `ls -sl -tr -o`;&lt;br /&gt;
    &lt;br /&gt;
    for($i = 0; $i &amp;lt; $cutnumbers; $i++){&lt;br /&gt;
        select -r $objs;&lt;br /&gt;
        $ctrlplanes[size($ctrlplanes)] = `cutObjs`;&lt;br /&gt;
    }&lt;br /&gt;
    if($cutnumbers &amp;gt; 2){&lt;br /&gt;
        $s = $ctrlplanes[0];&lt;br /&gt;
        $e = $ctrlplanes[$cutnumbers-1];&lt;br /&gt;
        $m = $cutnumbers-1;&lt;br /&gt;
        &lt;br /&gt;
        string $exp = &amp;quot;&amp;quot;;&lt;br /&gt;
        &lt;br /&gt;
        for($i = 1; $i &amp;lt; $cutnumbers-1; $i++){&lt;br /&gt;
            $c = $ctrlplanes[$i];&lt;br /&gt;
            $shp = `listRelatives -s $c`;&lt;br /&gt;
            setAttr ($shp[0]+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
            setAttr ($shp[0]+&amp;quot;.overrideShading&amp;quot;) 0;&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
            $exp += $c + &amp;quot;.tx = ( &amp;quot; + $s + &amp;quot;.tx * &amp;quot; + ( $m - $i ) + &amp;quot; + &amp;quot; + $e + &amp;quot;.tx * &amp;quot; + $i +&amp;quot; ) / &amp;quot; + $m + &amp;quot;;\n&amp;quot;;&lt;br /&gt;
            $exp += $c + &amp;quot;.ty = ( &amp;quot; + $s + &amp;quot;.ty * &amp;quot; + ( $m - $i ) + &amp;quot; + &amp;quot; + $e + &amp;quot;.ty * &amp;quot; + $i +&amp;quot; ) / &amp;quot; + $m + &amp;quot;;\n&amp;quot;;&lt;br /&gt;
            $exp += $c + &amp;quot;.tz = ( &amp;quot; + $s + &amp;quot;.tz * &amp;quot; + ( $m - $i ) + &amp;quot; + &amp;quot; + $e + &amp;quot;.tz * &amp;quot; + $i +&amp;quot; ) / &amp;quot; + $m + &amp;quot;;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            $exp += $c + &amp;quot;.rx = ( &amp;quot; + $s + &amp;quot;.rx * &amp;quot; + ( $m - $i ) + &amp;quot; + &amp;quot; + $e + &amp;quot;.rx * &amp;quot; + $i +&amp;quot; ) / &amp;quot; + $m + &amp;quot;;\n&amp;quot;;&lt;br /&gt;
            $exp += $c + &amp;quot;.ry = ( &amp;quot; + $s + &amp;quot;.ry * &amp;quot; + ( $m - $i ) + &amp;quot; + &amp;quot; + $e + &amp;quot;.ry * &amp;quot; + $i +&amp;quot; ) / &amp;quot; + $m + &amp;quot;;\n&amp;quot;;&lt;br /&gt;
            $exp += $c + &amp;quot;.rz = ( &amp;quot; + $s + &amp;quot;.rz * &amp;quot; + ( $m - $i ) + &amp;quot; + &amp;quot; + $e + &amp;quot;.rz * &amp;quot; + $i +&amp;quot; ) / &amp;quot; + $m + &amp;quot;;\n&amp;quot;;&lt;br /&gt;
            $exp += &amp;quot;\r&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        expression -s $exp -o $s -ae 1 -uc all ;&lt;br /&gt;
        for($i = 1; $i &amp;lt; $cutnumbers; $i++){        &lt;br /&gt;
            connectAttr -f ( $s + &amp;quot;.Detach&amp;quot; ) ( $ctrlplanes[$i] + &amp;quot;.Detach&amp;quot; );&lt;br /&gt;
            connectAttr -f ( $s + &amp;quot;.polyCutOffset&amp;quot;)  ( $ctrlplanes[$i] + &amp;quot;.polyCutOffset&amp;quot; );&lt;br /&gt;
        }&lt;br /&gt;
        $z = getAttr ($ctrlplanes[0]+&amp;quot;.translateZ&amp;quot;);&lt;br /&gt;
        setAttr ($ctrlplanes[$m]+&amp;quot;.translateZ&amp;quot;) ($z+2);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    select -r $ctrlplanes[0];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
multiCut 6;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Scale/Rot/Move Manip to screenSpace (camera angle)==&lt;br /&gt;
&lt;br /&gt;
Sometimes I just want the manipulator to be aligned to my camera.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/tuslp.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//edit april 2019 fix string variable&lt;br /&gt;
string $objs[] = `ls -sl`;&lt;br /&gt;
string $obj = `match &amp;quot;^[^\.]*&amp;quot; $objs[size($objs)-1]`;&lt;br /&gt;
//thnx NathanN @cgtalk vv&lt;br /&gt;
setToolTo moveSuperContext; &lt;br /&gt;
vector $centerPos = `manipMoveContext -q -position Move`;&lt;br /&gt;
string $tmp = `group -em`;&lt;br /&gt;
move -r ($centerPos.x) ($centerPos.y) ($centerPos.z ) $tmp;&lt;br /&gt;
$cam = `lookThru -q`;&lt;br /&gt;
$ac = `aimConstraint -offset 0 0 0 -weight 1 -aimVector 1 0 0 -upVector 0 1 0 -worldUpType &amp;quot;objectrotation&amp;quot; -worldUpVector 0 1 0 -worldUpObject $cam $cam $tmp`;&lt;br /&gt;
float $ro[] = `xform -q -ws -ro $tmp`;&lt;br /&gt;
delete $ac $tmp;&lt;br /&gt;
$ro[0] = deg_to_rad($ro[0]);&lt;br /&gt;
$ro[1] = deg_to_rad($ro[1]);&lt;br /&gt;
$ro[2] = deg_to_rad($ro[2]);&lt;br /&gt;
select -r $objs;&lt;br /&gt;
if(`nodeType $objs[0]`!=&amp;quot;transform&amp;quot;){&lt;br /&gt;
    hilite $obj;&lt;br /&gt;
    setSelectMode components Components;&lt;br /&gt;
}&lt;br /&gt;
setToolTo scaleSuperContext;&lt;br /&gt;
manipScaleContext -e -ah 3 -useManipPivot 0 -useObjectPivot 0 -oa $ro[0] $ro[1] $ro[2] -m 6 Scale;&lt;br /&gt;
manipMoveContext -e -oa $ro[0] $ro[1] $ro[2] -m 6 Move;&lt;br /&gt;
manipRotateContext -e -oa $ro[0] $ro[1] $ro[2] -m 6 Rotate;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Locators on normals ==&lt;br /&gt;
https://i.imgur.com/MongsQW.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $objs[] = `ls -sl -fl`;&lt;br /&gt;
setToolTo moveSuperContext; &lt;br /&gt;
$manipMode = `manipMoveContext -q -mode Move`;&lt;br /&gt;
manipMoveContext -e -mode 10 Move;&lt;br /&gt;
&lt;br /&gt;
for($o in $objs){&lt;br /&gt;
    select -r $o;&lt;br /&gt;
    vector $centerPos = `manipMoveContext -q -position Move`;&lt;br /&gt;
    vector $centerRot = `manipMoveContext -q -oa Move`;&lt;br /&gt;
    string $loca[] = `spaceLocator -n &amp;quot;normalLoc&amp;quot;`;&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.tx&amp;quot;) ($centerPos.x);&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.ty&amp;quot;) ($centerPos.y);&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.tz&amp;quot;) ($centerPos.z);&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.rx&amp;quot;) (rad_to_deg($centerRot.x));&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.ry&amp;quot;) (rad_to_deg($centerRot.y));&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.rz&amp;quot;) (rad_to_deg($centerRot.z));&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.localScaleX&amp;quot;) 2;&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.localScaleY&amp;quot;) 0.2;&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.localScaleZ&amp;quot;) 0.2;&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
    setAttr ($loca[0]+&amp;quot;.overrideColor&amp;quot;) 17;&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Uniform weight for shells==&lt;br /&gt;
https://i.imgur.com/SFuUd.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//some code from tiddlyspot&lt;br /&gt;
//if you have skin bound objects that need to have a uniform skinning&lt;br /&gt;
//ie all the vertices in the shell should get their weights from the selected vertex&lt;br /&gt;
string $vertices[] = `ls -sl -fl -l`;&lt;br /&gt;
for($vertex in $vertices){&lt;br /&gt;
    select -r $vertex;&lt;br /&gt;
    string $selObj[] = `ls -o -sl`;&lt;br /&gt;
    string $selPt[] = `ls -sl`;&lt;br /&gt;
    string $sck = `findRelatedSkinCluster($selObj[0])`;&lt;br /&gt;
    string $skinInfluences[]=`skinPercent -ib 0.000001 -q -t $sck $selPt[0]`;&lt;br /&gt;
    float $sknVals[]=`skinPercent -ib 0.000001 -q -v  $sck $selPt[0]`;&lt;br /&gt;
    polyConvertToShell;&lt;br /&gt;
    string $shell[] = `ls -sl`;&lt;br /&gt;
    string $eval = &amp;quot;skinPercent -zri 1&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    for($i=0;$i&amp;lt;size($skinInfluences);$i++){&lt;br /&gt;
        if($sknVals[$i] &amp;gt; 0){&lt;br /&gt;
            $eval += (&amp;quot; -tv &amp;quot;+$skinInfluences[$i]+&amp;quot; &amp;quot;+$sknVals[$i]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $eval += &amp;quot; &amp;quot;+$sck+&amp;quot; &amp;quot;+stringArrayToString($shell,&amp;quot; &amp;quot;);&lt;br /&gt;
    eval($eval);&lt;br /&gt;
}&lt;br /&gt;
select -r $vertices;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
quad face to single vertex selection&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//select quads first&lt;br /&gt;
string $empty[] = {};&lt;br /&gt;
for($o in `ls -sl -fl`){&lt;br /&gt;
select -r $o;&lt;br /&gt;
ConvertSelectionToVertices;&lt;br /&gt;
string $v[] = `ls -sl -fl`;&lt;br /&gt;
    print($v);&lt;br /&gt;
    $empty[size($empty)] = $v[0];&lt;br /&gt;
}&lt;br /&gt;
select -r $empty;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//for multiple objects, with vtx 1&lt;br /&gt;
for($o in `ls -sl -fl -l`){&lt;br /&gt;
select -r ($o+&amp;quot;.vtx[1]&amp;quot;);&lt;br /&gt;
string $vertices[] = `ls -sl -fl -l`;&lt;br /&gt;
for($vertex in $vertices){&lt;br /&gt;
    select -r $vertex;&lt;br /&gt;
    string $selObj[] = `ls -o -sl`;&lt;br /&gt;
    string $selPt[] = `ls -sl`;&lt;br /&gt;
    string $sck = `findRelatedSkinCluster($selObj[0])`;&lt;br /&gt;
    string $skinInfluences[]=`skinPercent -ib 0.000001 -q -t $sck $selPt[0]`;&lt;br /&gt;
    float $sknVals[]=`skinPercent -ib 0.000001 -q -v  $sck $selPt[0]`;&lt;br /&gt;
    polyConvertToShell;&lt;br /&gt;
    string $shell[] = `ls -sl`;&lt;br /&gt;
    string $eval = &amp;quot;skinPercent -zri 1&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    for($i=0;$i&amp;lt;size($skinInfluences);$i++){&lt;br /&gt;
        if($sknVals[$i] &amp;gt; 0){&lt;br /&gt;
            $eval += (&amp;quot; -tv &amp;quot;+$skinInfluences[$i]+&amp;quot; &amp;quot;+$sknVals[$i]);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    $eval += &amp;quot; &amp;quot;+$sck+&amp;quot; &amp;quot;+stringArrayToString($shell,&amp;quot; &amp;quot;);&lt;br /&gt;
    eval($eval);&lt;br /&gt;
}&lt;br /&gt;
//select -r $vertices;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Recursive Parent==&lt;br /&gt;
https://i.imgur.com/vJfH2.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//recursive parent&lt;br /&gt;
//useful if you select a bunch of joints in order and want them parented to each other, not to the last of the selection&lt;br /&gt;
string $objs[] = `ls -sl`;&lt;br /&gt;
for($i=size($objs)-1;$i&amp;gt;0;$i--){&lt;br /&gt;
    parent $objs[$i] $objs[($i-1)];&lt;br /&gt;
}&lt;br /&gt;
select -r $objs[0];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Reconnect stuff==&lt;br /&gt;
===Reconnect follicles===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//select SHAPE then follicles&lt;br /&gt;
string $sel[] = `ls -sl`;&lt;br /&gt;
for($i=1;$i&amp;lt;size($sel);$i++){&lt;br /&gt;
    string $ch[] = `listRelatives -s $sel[$i]`;&lt;br /&gt;
    catchQuiet(`connectAttr -f ($sel[0]+&amp;quot;.worldMatrix[0]&amp;quot;) ($ch[0]+&amp;quot;.inputWorldMatrix&amp;quot;)`);&lt;br /&gt;
    catchQuiet(`connectAttr -f ($sel[0]+&amp;quot;.outMesh&amp;quot;) ($ch[0]+&amp;quot;.inputMesh&amp;quot;)`);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===Reconnect motionpaths===&lt;br /&gt;
Check out the [[Maya_Python#Reconnect_motion_paths|Better python version]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//select the curve to attach all motion paths to and run script&lt;br /&gt;
string $objs[] = `ls -sl -l`;&lt;br /&gt;
string $shape[] = `listRelatives -s -pa`;&lt;br /&gt;
&lt;br /&gt;
for($o in `ls -l -type &amp;quot;motionPath&amp;quot;`){&lt;br /&gt;
    print($o+&amp;quot;\n&amp;quot;);&lt;br /&gt;
    catchQuiet(`connectAttr -f ($shape[0]+&amp;quot;.worldSpace[0]&amp;quot;) ($o+&amp;quot;.geometryPath&amp;quot;)`);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Zero Out==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
zero;&lt;br /&gt;
global proc zero(){&lt;br /&gt;
    string $sel[] = `ls -sl`;&lt;br /&gt;
    for($obj in $sel){&lt;br /&gt;
        select -cl;&lt;br /&gt;
        string $grandParents[] = `listRelatives -p`;&lt;br /&gt;
        string $zer = `group -em -w -n ($obj+&amp;quot;_ZERO&amp;quot;)`;&lt;br /&gt;
        parent $zer $obj;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.rotateZ&amp;quot;) 0;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.translateX&amp;quot;) 0;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.translateY&amp;quot;) 0;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.translateZ&amp;quot;) 0;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.rotateX&amp;quot;) 0;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.rotateY&amp;quot;) 0;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.scaleZ&amp;quot;) 1;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.scaleX&amp;quot;) 1;&lt;br /&gt;
        setAttr ($zer+&amp;quot;.scaleY&amp;quot;) 1;&lt;br /&gt;
        if(size($grandParents)==0){&lt;br /&gt;
            parent -w $zer; &lt;br /&gt;
        }else{&lt;br /&gt;
        parent $zer $grandParents[0];&lt;br /&gt;
        }&lt;br /&gt;
        parent $obj $zer;&lt;br /&gt;
    }&lt;br /&gt;
    select -r $sel;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Select influenced bones / get skinned meshes from bone==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//select a mesh or a bone&lt;br /&gt;
//selects all bones rigging the mesh or selects all meshes being rigged by this bone&lt;br /&gt;
&lt;br /&gt;
string $sele[] = `ls -sl`;&lt;br /&gt;
if(`nodeType $sele[0]` == &amp;quot;joint&amp;quot;){&lt;br /&gt;
    select(`listConnections ($sele[0]+&amp;quot;.worldMatrix&amp;quot;)`);&lt;br /&gt;
    select(`listConnections -t &amp;quot;mesh&amp;quot;`);&lt;br /&gt;
}else{&lt;br /&gt;
    select(`findRelatedSkinCluster($sele[0])`);&lt;br /&gt;
    select -r (`listConnections &amp;quot;.matrix&amp;quot;`);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quick Change wire color==&lt;br /&gt;
https://i.imgur.com/VAXw1.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//edit: works with joints&lt;br /&gt;
global string $cursel[];&lt;br /&gt;
proc string changeColor(){&lt;br /&gt;
    global string $cursel[];&lt;br /&gt;
    if(size(`ls -sl`)&amp;gt;0 || size($cursel)&amp;gt; 0){&lt;br /&gt;
        if(size(`ls -sl`)&amp;gt;0){&lt;br /&gt;
            $cursel = `ls -sl`;&lt;br /&gt;
        }&lt;br /&gt;
        select -cl;&lt;br /&gt;
        for($obj in $cursel){&lt;br /&gt;
            string $shap[] = `listRelatives -s -f $obj`;&lt;br /&gt;
            &lt;br /&gt;
            $v = `colorIndexSliderGrp -q -v &amp;quot;colorSlider&amp;quot;`;&lt;br /&gt;
            if($shap[0] == &amp;quot;&amp;quot;){&lt;br /&gt;
                setAttr ($obj+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
                setAttr ($obj+&amp;quot;.overrideColor&amp;quot;) $v;  &lt;br /&gt;
            }&lt;br /&gt;
            for($s in $shap){ &lt;br /&gt;
                setAttr ($s+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
                setAttr ($s+&amp;quot;.overrideColor&amp;quot;) $v;  &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
proc plop(){&lt;br /&gt;
    global string $cursel[];&lt;br /&gt;
    select -r $cursel;&lt;br /&gt;
    $cursel = {};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
string $window = `window -title &amp;quot;Change color&amp;quot;`;&lt;br /&gt;
columnLayout;&lt;br /&gt;
string $slid = `colorIndexSliderGrp -label &amp;quot;Select Color&amp;quot; -min 0 -max 31 -cc &amp;quot;plop()&amp;quot; -dc &amp;quot;changeColor()&amp;quot; &amp;quot;colorSlider&amp;quot;`;&lt;br /&gt;
showWindow $window;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Match shape==&lt;br /&gt;
Forces the shape of object A on object B through a blendshape. To match controllers.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//matches shape if same number of points.&lt;br /&gt;
pickWalk -d down;&lt;br /&gt;
string $sele[] = `ls -sl`;&lt;br /&gt;
string $bls[] = `blendShape`;&lt;br /&gt;
string $shortname = `match &amp;quot;[^|]*$&amp;quot; $sele[0]`;&lt;br /&gt;
setAttr ($bls[0]+&amp;quot;.weight[0]&amp;quot;) 1;&lt;br /&gt;
//setAttr ($bls[0]+&amp;quot;.&amp;quot;+$shortname) 1;&lt;br /&gt;
delete -ch $sele[1];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Delete groups w/o children==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$deleteArray = {};&lt;br /&gt;
for($obj in `ls -l -tr`){&lt;br /&gt;
    $chld1 = `listRelatives $obj`;&lt;br /&gt;
    $chld2 = `listRelatives -s $obj`;&lt;br /&gt;
    if((size($chld1)+size($chld2)) == 0){&lt;br /&gt;
        $deleteArray[size($deleteArray)] = $obj;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
for($obj in $deleteArray){&lt;br /&gt;
    print(  &amp;quot;Deleted \&amp;quot;&amp;quot;+$obj+&amp;quot;\&amp;quot; \n&amp;quot;);&lt;br /&gt;
    delete $obj;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Emit from facing ratio ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector $bob = particleShape1.position;&lt;br /&gt;
vector $velo = particleShape1.velocity;&lt;br /&gt;
vector $prout = getAttr(&amp;quot;camera1.translate&amp;quot;);&lt;br /&gt;
vector $part = $prout-$bob;&lt;br /&gt;
$angle = (rad_to_deg(angle($velo,$prout))-90)/90;&lt;br /&gt;
$facingratio = (1-abs($angle));&lt;br /&gt;
//particleShape1.radiusPP = (1-abs($angle))/2;&lt;br /&gt;
&lt;br /&gt;
if($facingratio &amp;lt;.8){&lt;br /&gt;
particleShape1.lifespanPP = 0;&lt;br /&gt;
}else{&lt;br /&gt;
particleShape1.lifespanPP = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Random Color Viewport (à la 3dsmax)==&lt;br /&gt;
https://i.imgur.com/XbS1i.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//creates differently colored objects using vertex color (doesn&amp;#039;t affect shaders)&lt;br /&gt;
string $sel[] = `ls -sl`;&lt;br /&gt;
for($obj in $sel){&lt;br /&gt;
    select -cl;&lt;br /&gt;
    select -r $obj;&lt;br /&gt;
    $r = rand(0,1);&lt;br /&gt;
    $g = rand(0,1);&lt;br /&gt;
    $b = rand(0,1);&lt;br /&gt;
    polyColorPerVertex -r $r -g $g -b $b -a 1 -nun -cdo;&lt;br /&gt;
}&lt;br /&gt;
select -cl;&lt;br /&gt;
select -r $sel;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Set viewport color on selected obj/vertices ==&lt;br /&gt;
https://i.imgur.com/MGrQnOO.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
//if you want more details in the viewport (more info for animators). Does not affect shading&lt;br /&gt;
{&lt;br /&gt;
    proc chgcl(int $ch){&lt;br /&gt;
        &lt;br /&gt;
        if($ch){&lt;br /&gt;
            polyColorPerVertex -rem -nun;&lt;br /&gt;
            polyOptions -colorShadedDisplay false `ls -sl -o`;&lt;br /&gt;
        }else{&lt;br /&gt;
            float $r[] = `colorSliderGrp -q -rgb &amp;quot;col_r&amp;quot;`;&lt;br /&gt;
            float $g[] = `colorSliderGrp -q -rgb &amp;quot;col_g&amp;quot;`;&lt;br /&gt;
            float $b[] = `colorSliderGrp -q -rgb &amp;quot;col_b&amp;quot;`;&lt;br /&gt;
            colorSliderGrp -e -rgb ($r[0]) 0 0 &amp;quot;col_r&amp;quot;;&lt;br /&gt;
            colorSliderGrp -e -rgb 0 ($g[1]) 0 &amp;quot;col_g&amp;quot;;&lt;br /&gt;
            colorSliderGrp -e -rgb 0 0 ($b[2]) &amp;quot;col_b&amp;quot;;&lt;br /&gt;
            colorSliderGrp -e -rgb ($r[0]) ($g[1]) ($b[2]) &amp;quot;col_result&amp;quot;;&lt;br /&gt;
            polyColorPerVertex -rgb ($r[0]) ($g[1]) ($b[2]) -a 1 -nun -cdo;&lt;br /&gt;
        }    &lt;br /&gt;
    }&lt;br /&gt;
    if (`window -exists vtxCol`) deleteUI vtxCol;&lt;br /&gt;
    if (`windowPref -exists vtxCol`) windowPref -remove vtxCol;&lt;br /&gt;
    string $window = `window -title &amp;quot;Set Vertex Color&amp;quot; vtxCol`;&lt;br /&gt;
    columnLayout -columnAttach &amp;quot;left&amp;quot; 5 -rowSpacing 10 -columnWidth 250;&lt;br /&gt;
    //columnLayout;&lt;br /&gt;
    colorSliderGrp -label &amp;quot;Red&amp;quot; -rgb 1 0 0 -cc &amp;quot;chgcl 0&amp;quot; &amp;quot;col_r&amp;quot;;&lt;br /&gt;
    colorSliderGrp -label &amp;quot;Green&amp;quot; -rgb 0 1 0 -cc &amp;quot;chgcl 0&amp;quot; &amp;quot;col_g&amp;quot;;&lt;br /&gt;
    colorSliderGrp -label &amp;quot;Blue&amp;quot; -rgb 0 0 1 -cc &amp;quot;chgcl 0&amp;quot; &amp;quot;col_b&amp;quot;;&lt;br /&gt;
    colorSliderGrp -label &amp;quot;Result&amp;quot; -en 0 -rgb 1 1 1 &amp;quot;col_result&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
    rowLayout -numberOfColumns 2;&lt;br /&gt;
    button -label &amp;quot;close&amp;quot; -command &amp;quot;deleteUI vtxCol&amp;quot;;&lt;br /&gt;
    button -label &amp;quot;remove&amp;quot; -command &amp;quot;chgcl 1&amp;quot;;&lt;br /&gt;
    showWindow;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== postFrame status ==&lt;br /&gt;
https://i.imgur.com/7qPJO.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int $m=(((`currentTime -q`)-(`playbackOptions -q -min`))/(`playbackOptions -q -max`)*100);&lt;br /&gt;
$b= &amp;quot;rendering: &amp;quot;;&lt;br /&gt;
for($i=0;$i&amp;lt;=25;$i++){&lt;br /&gt;
$b += ($m/4&amp;lt;$i)?&amp;quot;.&amp;quot;:&amp;quot;#&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
trace($b+&amp;quot; &amp;quot;+$m+&amp;quot;% (&amp;quot;+`file -q -sn`+&amp;quot;)&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/// ou mettre ça dans un userSetup:&lt;br /&gt;
/// &lt;br /&gt;
$t = &amp;quot;int $m=(((`currentTime -q`)-(`playbackOptions -q -min`))/(`playbackOptions -q -max`)*100);$b= \&amp;quot;rendering: \&amp;quot;;for($i=0;$i&amp;lt;=25;$i++){$b += ($m/4&amp;lt;$i)?\&amp;quot;.\&amp;quot;:\&amp;quot;#\&amp;quot;;}trace($b+\&amp;quot; \&amp;quot;+$m+\&amp;quot;% (\&amp;quot;+`file -q -sn`+\&amp;quot;)\&amp;quot;);&amp;quot;;&lt;br /&gt;
setAttr -type &amp;quot;string&amp;quot; defaultRenderGlobals.postRenderMel $t;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//oneliner&lt;br /&gt;
int $e=`playbackOptions -q -max`;int $s=`playbackOptions -q -min`;int $c=`currentTime -q`;int $m=($c-$s)/$e*100;$b= &amp;quot;rndr: &amp;quot;;for($i=0;$i&amp;lt;=25;$i++){$b += ($m/4&amp;lt;$i)?&amp;quot;.&amp;quot;:&amp;quot;#&amp;quot;;}trace($b+&amp;quot; &amp;quot;+$m+&amp;quot;% (&amp;quot;+$c+&amp;quot;/&amp;quot;+$e+&amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Tracking -&amp;gt; Maya to After ==&lt;br /&gt;
=== Maya Mel ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// transform un point 3d en un point 2d (pour eviter a retracker derriere)&lt;br /&gt;
&lt;br /&gt;
//////////////////////////////////////////////////////&lt;br /&gt;
// Rob Bredow                                       //&lt;br /&gt;
// rob (at) 185vfx.com                              //&lt;br /&gt;
// https://www.185vfx.com/                           //&lt;br /&gt;
// Copyright 3/2002 Rob Bredow, All Rights Reserved //&lt;br /&gt;
//////////////////////////////////////////////////////&lt;br /&gt;
&lt;br /&gt;
global string $camera = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
proc float round(float $val,float $dec){&lt;br /&gt;
    $sign = `sign $val`;&lt;br /&gt;
    float $dec = `pow 10 $dec`;&lt;br /&gt;
    $val = (int) (($val + $sign*5/($dec*10)) * $dec);&lt;br /&gt;
    $val = ($val / $dec);&lt;br /&gt;
    return $val;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Get a matrix&lt;br /&gt;
proc matrix screenSpaceGetMatrix(string $attr){&lt;br /&gt;
  float $v[]=`getAttr $attr`;&lt;br /&gt;
  matrix $mat[4][4]=&amp;lt;&amp;lt;$v[0], $v[1], $v[2], $v[3];&lt;br /&gt;
             $v[4], $v[5], $v[6], $v[7];&lt;br /&gt;
             $v[8], $v[9], $v[10], $v[11];&lt;br /&gt;
             $v[12], $v[13], $v[14], $v[15]&amp;gt;&amp;gt;;&lt;br /&gt;
 return $mat;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Multiply the vector v by the 4x4 matrix m, this is probably&lt;br /&gt;
// already in mel but I cant find it.&lt;br /&gt;
proc vector screenSpaceVecMult(vector $v, matrix $m){&lt;br /&gt;
  matrix $v1[1][4]=&amp;lt;&amp;lt;$v.x, $v.y, $v.z, 1&amp;gt;&amp;gt;;&lt;br /&gt;
  matrix $v2[1][4]=$v1*$m;&lt;br /&gt;
  return &amp;lt;&amp;lt;$v2[0][0], $v2[0][1],  $v2[0][2]&amp;gt;&amp;gt;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
global proc int screenSpace()&lt;br /&gt;
{&lt;br /&gt;
global string $camera;&lt;br /&gt;
global float $noOfDecimals = 4;&lt;br /&gt;
&lt;br /&gt;
$w = `getAttr &amp;quot;defaultResolution.width&amp;quot;`;&lt;br /&gt;
$h = `getAttr &amp;quot;defaultResolution.height&amp;quot;`;&lt;br /&gt;
float $fs = `playbackOptions -q -min`;&lt;br /&gt;
float $fe = `playbackOptions -q -max`;&lt;br /&gt;
float $ct = `currentTime -q`;&lt;br /&gt;
$fps = `currentTime -edit 1sec -u 0`;&lt;br /&gt;
currentTime -edit $ct;&lt;br /&gt;
string $scenepath = `file -q -sn`;  &lt;br /&gt;
string $filename = `match &amp;quot;[^/\\]*$&amp;quot; $scenepath`;           &lt;br /&gt;
$filename = `match &amp;quot;^[^\.]*&amp;quot; $filename`;    &lt;br /&gt;
$filename = ($filename ==&amp;quot;&amp;quot;)?&amp;quot;untitled.ma&amp;quot;:$filename;&lt;br /&gt;
$firstline = &amp;quot;//maya to after track file: Filename:&amp;quot;+$filename+&amp;quot; Start:&amp;quot;+$fs+&amp;quot; End:&amp;quot;+$fe+&amp;quot; Fps:&amp;quot;+$fps+&amp;quot; Width:&amp;quot;+$w+&amp;quot; Height:&amp;quot;+$h+&amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
if($camera == &amp;quot;&amp;quot;){&lt;br /&gt;
    string $panel = `getPanel -withFocus`;&lt;br /&gt;
    $camera = `modelPanel -q -cam $panel`;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
  string $dumpList[] = `ls -sl -fl`;&lt;br /&gt;
  print (&amp;quot;Dumping selection...(&amp;quot;+$camera+&amp;quot;)\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  string $pointWsFile = `fileDialog -m 1 -dfn &amp;quot;trackpoints.txt&amp;quot;`;&lt;br /&gt;
  int $outFileId = fopen($pointWsFile,&amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
  if ($outFileId == 0) {&lt;br /&gt;
    print (&amp;quot;Could not open output file &amp;quot; + $pointWsFile);&lt;br /&gt;
    return -1;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  float $tx[],$ty[],$tz[];&lt;br /&gt;
fprint $outFileId $firstline;&lt;br /&gt;
for($dumpPt in $dumpList){&lt;br /&gt;
fprint $outFileId &amp;quot;\n&amp;quot;;&lt;br /&gt;
  for ($f=$fs;$f&amp;lt;=$fe;$f++) &lt;br /&gt;
  {&lt;br /&gt;
    currentTime $f;&lt;br /&gt;
&lt;br /&gt;
    // get the world space position of the point into a vector&lt;br /&gt;
    float $ptPosWs[] = `xform -q -ws -t $dumpPt`;&lt;br /&gt;
    vector $ptVecWs = &amp;lt;&amp;lt;$ptPosWs[0],$ptPosWs[1],$ptPosWs[2]&amp;gt;&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
    // Grab the worldInverseMatrix from cam_main&lt;br /&gt;
    matrix $cam_mat[4][4] = screenSpaceGetMatrix($camera+&amp;quot;.worldInverseMatrix&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    // Multiply the point by that matrix&lt;br /&gt;
    vector $ptVecCs = screenSpaceVecMult($ptVecWs,$cam_mat);&lt;br /&gt;
&lt;br /&gt;
    // Adjust the point&amp;#039;s position for the camera perspective&lt;br /&gt;
    float $hfv = `camera -q -hfv $camera`;&lt;br /&gt;
    float $ptx = (($ptVecCs.x/(-$ptVecCs.z))/tand($hfv/2))/2.0+.5;&lt;br /&gt;
    float $vfv = `camera -q -vfv $camera`;&lt;br /&gt;
    float $pty = (($ptVecCs.y/(-$ptVecCs.z))/tand($vfv/2))/2.0+.5;&lt;br /&gt;
&lt;br /&gt;
    float $ptz = $ptVecCs.z;&lt;br /&gt;
&lt;br /&gt;
    float $ww = ($ptx*$w);&lt;br /&gt;
    float $hh = ($pty*$h);&lt;br /&gt;
&lt;br /&gt;
    $hh = $h - $hh;&lt;br /&gt;
    $hh =   100.0/85*($hh-.5*$h)+.5*$h;     //nasty hack because somehow the output is scaled @ 85% in height, dunno why.&lt;br /&gt;
&lt;br /&gt;
    $ww = round($ww,$noOfDecimals);&lt;br /&gt;
    $hh = round($hh,$noOfDecimals);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    $line = $f+&amp;quot;\t&amp;quot;+$ww+ &amp;quot;\t&amp;quot; +$hh + &amp;quot;\n&amp;quot;;&lt;br /&gt;
    fprint $outFileId $line;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
    fclose $outFileId;&lt;br /&gt;
    currentTime -edit $ct;&lt;br /&gt;
    return 1;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Script After (Panel) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function createUI(thisObj) {&lt;br /&gt;
    var myPanel = ( thisObj instanceof Panel) ? thisObj : new Window(&amp;quot;palette&amp;quot;, &amp;quot;Maya Track 1.0&amp;quot;,[100, 100, 300, 300]);&lt;br /&gt;
&lt;br /&gt;
    myPanel.createOptPnl = myPanel.add(&amp;#039;panel&amp;#039;, [10,10,180,57], &amp;#039;Create&amp;#039;);&lt;br /&gt;
    myPanel.createOptPnl.toCtrl = myPanel.createOptPnl.add(&amp;#039;radiobutton&amp;#039;, [16,9,85,33], &amp;#039;PntCtrls&amp;#039;);&lt;br /&gt;
    myPanel.createOptPnl.toNull  = myPanel.createOptPnl.add(&amp;#039;radiobutton&amp;#039;, [95,9,160,33], &amp;#039;Nulls&amp;#039;);&lt;br /&gt;
    myPanel.createOptPnl.toCtrl.value = 1;&lt;br /&gt;
    &lt;br /&gt;
    myPanel.optionsOptPnl = myPanel.add(&amp;#039;panel&amp;#039;, [10,62,180,105], &amp;#039;Option(s)&amp;#039;);&lt;br /&gt;
    myPanel.optionsOptPnl.shiftKf = myPanel.optionsOptPnl.add(&amp;#039;Checkbox&amp;#039;, [16,7,160,30], &amp;#039;Nudge 1 keyframe back&amp;#039;);&lt;br /&gt;
    myPanel.optionsOptPnl.shiftKf.value = 1;&lt;br /&gt;
&lt;br /&gt;
    impButton = myPanel.add(&amp;quot;button&amp;quot;, [10, 113, 105, 137], &amp;quot;Import From File&amp;quot;);&lt;br /&gt;
    impButton.onClick = main;&lt;br /&gt;
    &lt;br /&gt;
    return myPanel;&lt;br /&gt;
}&lt;br /&gt;
function main(){&lt;br /&gt;
app.beginUndoGroup(&amp;quot;Add Maya Track Points&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
check = false;&lt;br /&gt;
       if (app.project != null) {&lt;br /&gt;
      if (app.project.activeItem != null) {&lt;br /&gt;
         if (app.project.activeItem instanceof CompItem) {&lt;br /&gt;
        check = true;&lt;br /&gt;
        comp = app.project.activeItem;&lt;br /&gt;
             &lt;br /&gt;
        file = openfile();&lt;br /&gt;
    &lt;br /&gt;
        name = &amp;quot;&amp;quot;;&lt;br /&gt;
        width = 0;&lt;br /&gt;
        height = 0;&lt;br /&gt;
        fps = 0;&lt;br /&gt;
        start = 0;&lt;br /&gt;
        end =0;&lt;br /&gt;
        &lt;br /&gt;
        firstline = readLine(file);&lt;br /&gt;
        split = firstline.split(&amp;quot; &amp;quot;);&lt;br /&gt;
        for(string in split){&lt;br /&gt;
            newsplit = split[string].split(&amp;quot;:&amp;quot;);&lt;br /&gt;
            switch(newsplit[0]){&lt;br /&gt;
                case &amp;quot;Filename&amp;quot;:&lt;br /&gt;
                name = newsplit[1];&lt;br /&gt;
                break;&lt;br /&gt;
                case &amp;quot;Start&amp;quot;:&lt;br /&gt;
                start = newsplit[1];&lt;br /&gt;
                break;  &lt;br /&gt;
                case &amp;quot;End&amp;quot;:&lt;br /&gt;
                end = newsplit[1];&lt;br /&gt;
                break;  &lt;br /&gt;
                case &amp;quot;Fps&amp;quot;:&lt;br /&gt;
                fps = newsplit[1];&lt;br /&gt;
                break;&lt;br /&gt;
                case &amp;quot;Width&amp;quot;:&lt;br /&gt;
                width = newsplit[1];&lt;br /&gt;
                break;                  &lt;br /&gt;
                case &amp;quot;Height&amp;quot;:&lt;br /&gt;
                height = newsplit[1];&lt;br /&gt;
                break;  &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        check2 = true;&lt;br /&gt;
        if(fps != comp.frameRate || width != comp.width || height != comp.height){&lt;br /&gt;
            alertText = &amp;quot;The track file and comp don&amp;#039;t match, proceed?\n\n&amp;quot;;&lt;br /&gt;
            alertText += &amp;quot;Source\t\tRate\tWidth\tHeight\tStart\tEnd\t\n&amp;quot;;&lt;br /&gt;
            alertText += &amp;quot;-----------------------------------------------------------------------------------\n&amp;quot;;&lt;br /&gt;
            alertText += &amp;quot;Track File:\t&amp;quot;+fps+&amp;quot;\t&amp;quot;+width+&amp;quot;\t&amp;quot;+height+&amp;quot;\t&amp;quot;+start+&amp;quot;\t&amp;quot;+end+&amp;quot;\t\n&amp;quot;;&lt;br /&gt;
            alertText += &amp;quot;After Comp:\t&amp;quot;+comp.frameRate+&amp;quot;\t&amp;quot;+comp.width+&amp;quot;\t&amp;quot;+comp.height+&amp;quot;\t&amp;quot;+app.project.displayStartFrame+&amp;quot;\t&amp;quot;+comp.duration/comp.frameDuration+&amp;quot;\t\n&amp;quot;;&lt;br /&gt;
        &lt;br /&gt;
            check2 = confirm(alertText,0,&amp;quot;Proceed?&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if(check &amp;amp;&amp;amp; check2){&lt;br /&gt;
        curIt = &amp;quot;&amp;quot;;&lt;br /&gt;
        point = 0;&lt;br /&gt;
        processing = 0;&lt;br /&gt;
        //token = 0;&lt;br /&gt;
        while(c=readLine(file)){&lt;br /&gt;
            processing++;&lt;br /&gt;
            if(c == &amp;quot;&amp;gt;newpoint&amp;lt;&amp;quot;)&lt;br /&gt;
            {&lt;br /&gt;
                point++;&lt;br /&gt;
                if(mayaTrackPnl.createOptPnl.toCtrl.value == 1)&lt;br /&gt;
                {&lt;br /&gt;
                    curIt = addPointControl(&amp;quot;TrackPoint &amp;quot;+point);&lt;br /&gt;
                }else{&lt;br /&gt;
                    curIt = createNull(&amp;quot;TrackPoint &amp;quot;+point,comp.duration);&lt;br /&gt;
                }&lt;br /&gt;
            }else{&lt;br /&gt;
                    info = (c.split(&amp;quot;\t&amp;quot;));&lt;br /&gt;
                    nudge = mayaTrackPnl.optionsOptPnl.shiftKf.value;&lt;br /&gt;
                    curIt.setValueAtTime((info[0]-nudge)*comp.frameDuration,[info[1],info[2]]);&lt;br /&gt;
                    writeLn(&amp;quot;Track Line: &amp;quot;+processing+&amp;quot; (&amp;quot;+point+&amp;quot;)&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        if(!check){&lt;br /&gt;
        alert(&amp;quot;No comp opened... &amp;quot;);        &lt;br /&gt;
        }else{&lt;br /&gt;
            alert(&amp;quot;Error&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
clearOutput();&lt;br /&gt;
file.close();&lt;br /&gt;
app.endUndoGroup();     &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function openfile(){&lt;br /&gt;
    var myFile = File.openDialog (&amp;quot;Select track file&amp;quot;,&amp;quot;*.txt&amp;quot;); &lt;br /&gt;
    var fileOK = myFile.open(&amp;quot;r&amp;quot;,&amp;quot;TEXT&amp;quot;,&amp;quot;????&amp;quot;);&lt;br /&gt;
    if(fileOK){&lt;br /&gt;
        return myFile;&lt;br /&gt;
        }else{&lt;br /&gt;
            //alert(&amp;quot;Problem opening file&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function readLine(myFile){&lt;br /&gt;
    if(!myFile.eof){&lt;br /&gt;
        c = myFile.readln();&lt;br /&gt;
        if(!c){&lt;br /&gt;
            return &amp;quot;&amp;gt;newpoint&amp;lt;&amp;quot;;&lt;br /&gt;
        }else{&lt;br /&gt;
        return c;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function createNull(name,duration){&lt;br /&gt;
    theComp = app.project.activeItem;&lt;br /&gt;
    theLayers = theComp.layers;&lt;br /&gt;
    mynull = theLayers.addNull(duration);&lt;br /&gt;
    mynull.name = name;&lt;br /&gt;
    mynull.shy = 1;&lt;br /&gt;
return mynull.position;&lt;br /&gt;
}&lt;br /&gt;
function addPointControl(name){&lt;br /&gt;
    theComp = app.project.activeItem;&lt;br /&gt;
    theLayers = theComp.layers; &lt;br /&gt;
    trckL = theLayers.byName(&amp;quot;mayatrack&amp;quot;);&lt;br /&gt;
    if(trckL == null){&lt;br /&gt;
        trckL = theLayers.addNull();&lt;br /&gt;
        trckL.name = &amp;quot;mayatrack&amp;quot;;&lt;br /&gt;
        trckL.position.setValueAtTime(0,[0,0]);&lt;br /&gt;
    }&lt;br /&gt;
    nuPnt = trckL.Effects.addProperty(&amp;quot;ADBE Point Control&amp;quot;);&lt;br /&gt;
    nuPnt.name = name;&lt;br /&gt;
return nuPnt.property(1);&lt;br /&gt;
}&lt;br /&gt;
var mayaTrackPnl = createUI(this);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Get and keyframe object speed==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
global proc keySpeed(){&lt;br /&gt;
    $initialTime = `currentTime -q`;&lt;br /&gt;
    $target = `ls -sl`;&lt;br /&gt;
    if(!objExists ($target[0]+&amp;quot;.speed&amp;quot;)){&lt;br /&gt;
        addAttr -ln &amp;quot;speed&amp;quot;  -at double $target[0];&lt;br /&gt;
        setAttr -e -keyable true {$target[0]+&amp;quot;.speed&amp;quot;};&lt;br /&gt;
    }&lt;br /&gt;
    float $fs = `playbackOptions -q -min`;&lt;br /&gt;
    float $fe = `playbackOptions -q -max`;&lt;br /&gt;
    currentTime -e ($fs-1);&lt;br /&gt;
    $posA = getPos($target[0]);&lt;br /&gt;
    $posB = $posA;&lt;br /&gt;
    for($i = $fs;$i&amp;lt;=$fe;$i++){&lt;br /&gt;
        currentTime -e $i;&lt;br /&gt;
        $posA = getPos($target[0]);&lt;br /&gt;
        vector $magn = &amp;lt;&amp;lt;$posB[0]-$posA[0],$posB[1]-$posA[1],$posB[2]-$posA[2]&amp;gt;&amp;gt;;&lt;br /&gt;
        //print(&amp;quot;\n&amp;quot;+$i+&amp;quot; &amp;quot;+mag($magn));&lt;br /&gt;
        float $speed = `mag($magn)`;&lt;br /&gt;
        setAttr ($target[0]+&amp;quot;.speed&amp;quot;) $speed;&lt;br /&gt;
        setKeyframe ($target[0]+&amp;quot;.speed&amp;quot;);&lt;br /&gt;
        $posB = $posA;&lt;br /&gt;
    }&lt;br /&gt;
    currentTime -e $initialTime;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
global proc float[] getPos(string $obj){&lt;br /&gt;
    select -r $obj;&lt;br /&gt;
    setToolTo moveSuperContext; &lt;br /&gt;
    float $centerPosi[] = `manipMoveContext -q -position Move`;&lt;br /&gt;
    return $centerPosi;&lt;br /&gt;
}&lt;br /&gt;
keySpeed;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Duplicates current cam and tear off copy==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $panel = `getPanel -withFocus`;&lt;br /&gt;
string $cameraName = `modelPanel -q -cam $panel`;&lt;br /&gt;
$duplicate = `duplicate -n {$cameraName+&amp;quot;_duplicate&amp;quot;} $cameraName`;&lt;br /&gt;
&lt;br /&gt;
string $window = `window -title $duplicate -wh 300 200 -tlb 0`;&lt;br /&gt;
paneLayout;&lt;br /&gt;
$pan = `modelPanel -cam  $duplicate -mbv 1`;&lt;br /&gt;
showWindow $window;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
erase cam&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $panel = `getPanel -withFocus`;&lt;br /&gt;
string $cameraName = `modelPanel -q -cam $panel`;&lt;br /&gt;
catchQuiet(`delete $cameraName`);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Charge un dossier de XPM dans le renderview==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
global proc loadXPMs(string $path){&lt;br /&gt;
    string $filez[] = `getFileList -folder $path -filespec &amp;quot;*.xpm&amp;quot;`;&lt;br /&gt;
    for($file in $filez){&lt;br /&gt;
        renderWindowLoadImageCallback &amp;quot;renderView&amp;quot; ($path+$file) &amp;quot;image&amp;quot;;&lt;br /&gt;
        renderWindowMenuCommand keepImageInRenderView renderView;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
global proc openDir( string $filename, string $fileType )&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    loadXPMs($filename+&amp;quot;/&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
fileBrowserDialog -m 4 -fc &amp;quot;openDir&amp;quot; -ft &amp;quot;directory&amp;quot; -an &amp;quot;Open_Dir&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Reverse Selection ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// select: obj2 - obj3 --&amp;gt; $obj 3 - $obj2 - $obj1&lt;br /&gt;
//&lt;br /&gt;
string $sele = &amp;quot;&amp;quot;;&lt;br /&gt;
for($obj in `ls -sl`){&lt;br /&gt;
     $sele = $obj+&amp;quot; &amp;quot;+$sele;&lt;br /&gt;
}&lt;br /&gt;
eval(&amp;quot;select -r &amp;quot;+$sele);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bake To Clamp Keys==&lt;br /&gt;
&amp;lt;pre&amp;gt;//Bake toutes les courbes d&amp;#039;anim en clamp et les déplaces de -0.5 dans le temps&lt;br /&gt;
//Pour Lionel &lt;br /&gt;
$from = `playbackOptions -q -ast`;&lt;br /&gt;
$to = `playbackOptions -q -aet`;&lt;br /&gt;
$range = $from+&amp;quot;:&amp;quot;+$to;&lt;br /&gt;
string $sel[] = `ls`;&lt;br /&gt;
bakeResults -simulation true -t $range -sampleBy 1 -disableImplicitControl true -preserveOutsideKeys true -sparseAnimCurveBake false -removeBakedAttributeFromLayer false -bakeOnOverrideLayer false -controlPoints false -shape true $sel;&lt;br /&gt;
string $curves[] = `ls -type &amp;quot;animCurve&amp;quot;`;&lt;br /&gt;
selectKey -k $curves;&lt;br /&gt;
keyTangent -ott step;&lt;br /&gt;
keyframe -animation keys -option over -relative -timeChange (0 - .5) ;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Merge to shape ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
proc parentToShape(){&lt;br /&gt;
    &lt;br /&gt;
    //  bernie - mbernadat@gmail - feel free to take &amp;amp; modify ! v1&lt;br /&gt;
    //&lt;br /&gt;
    //  adds the current selected objects to the last object of the selection as shapes&lt;br /&gt;
    //  use it to add shapes to controlers. Renames the resulting shapes as &amp;quot;[Targetobject]Shape#&amp;quot;&lt;br /&gt;
    //&lt;br /&gt;
    //  ajoute les objets de la selection dans le dernier objet séléctionné (pour foutre des controlleurs&lt;br /&gt;
    //  sur des bones par exemple). Renomme les shapes resultantes en &amp;quot;[NomObjet]Shape#&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    string $sel[] = `ls -sl`;&lt;br /&gt;
    &lt;br /&gt;
    if(size($sel) &amp;gt; 1){&lt;br /&gt;
        $target = $sel[size($sel)-1];&lt;br /&gt;
        for($a = 0;$a&amp;lt;size($sel)-1;$a++){&lt;br /&gt;
            if(size(`listRelatives -p $sel[$a]`) &amp;gt; 0){&lt;br /&gt;
                catch(`parent -w $sel[$a]`);&lt;br /&gt;
            }&lt;br /&gt;
            parent $sel[$a] $target;&lt;br /&gt;
            makeIdentity -apply true -t 1 -r 1 -s 1 -n 0;&lt;br /&gt;
            $shp = `listRelatives -shapes`;&lt;br /&gt;
            select $shp;&lt;br /&gt;
            select -add $target;&lt;br /&gt;
            parent -s -r;&lt;br /&gt;
        }&lt;br /&gt;
        delete(`listRelatives -typ &amp;quot;transform&amp;quot; $target`);&lt;br /&gt;
        select -r `listRelatives -shapes $target`;&lt;br /&gt;
        renameSelectionList($target+&amp;quot;Shape&amp;quot;);&lt;br /&gt;
        select -r $target;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
parentToShape;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Linear Align Objects==&lt;br /&gt;
https://imgur.com/sitnm.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
arrange;&lt;br /&gt;
global proc arrange(){&lt;br /&gt;
    string $sel[] = `ls -sl`;&lt;br /&gt;
    $first = $sel[0];&lt;br /&gt;
    $size = size($sel);&lt;br /&gt;
    $last = $sel[$size-1];&lt;br /&gt;
    float $firstTr[] = `xform -q -r -t $first`;&lt;br /&gt;
    float $firstRt[] = `xform -q -r -ro $first`;&lt;br /&gt;
    float $firstSl[] = `xform -q -r -s $first`;&lt;br /&gt;
    float $lastTr[] = `xform -q -r -t $last`;&lt;br /&gt;
    float $lastRt[] = `xform -q -r -ro $last`;&lt;br /&gt;
    float $lastSl[] = `xform -q -r -s $last`;&lt;br /&gt;
    for($a = 0; $a&amp;lt;$size;$a++){&lt;br /&gt;
        $r = ($a*1.0)/($size-1);&lt;br /&gt;
        //super beau code  \o/&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.translateX&amp;quot;) ($firstTr[0]+$r*($lastTr[0]-$firstTr[0]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.translateY&amp;quot;) ($firstTr[1]+$r*($lastTr[1]-$firstTr[1]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.translateZ&amp;quot;) ($firstTr[2]+$r*($lastTr[2]-$firstTr[2]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.rotateX&amp;quot;) ($firstRt[0]+$r*($lastRt[0]-$firstRt[0]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.rotateY&amp;quot;) ($firstRt[1]+$r*($lastRt[1]-$firstRt[1]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.rotateZ&amp;quot;) ($firstRt[2]+$r*($lastRt[2]-$firstRt[2]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.scaleX&amp;quot;) ($firstSl[0]+$r*($lastSl[0]-$firstSl[0]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.scaleY&amp;quot;) ($firstSl[1]+$r*($lastSl[1]-$firstSl[1]));&lt;br /&gt;
        setAttr ($sel[$a]+&amp;quot;.scaleZ&amp;quot;) ($firstSl[2]+$r*($lastSl[2]-$firstSl[2]));&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Space attributes blender===&lt;br /&gt;
https://i.imgur.com/tE5eXKS.jpg&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    if (`window -exists blenderW`) deleteUI blenderW;&lt;br /&gt;
    if (`windowPref -exists blenderW`) windowPref -remove blenderW;&lt;br /&gt;
    string $window = `window -widthHeight 250 350 -title &amp;quot;Blender&amp;quot; blenderW `;&lt;br /&gt;
    columnLayout -columnAttach &amp;quot;both&amp;quot; 6 -rowSpacing 10 -columnWidth 250;&lt;br /&gt;
    text -ww 1 -l &amp;quot;Blends translates, rotates and scales of elements &amp;#039;A&amp;#039; with elements &amp;#039;B&amp;#039; to elements &amp;#039;C&amp;#039;. \n Order is important. The blend attribute will be created on first element of A&amp;quot;;  &lt;br /&gt;
    checkBoxGrp -numberOfCheckBoxes 3 -vr -label &amp;quot;Attrs to blend &amp;quot; -va3 1 1 1 -labelArray3 &amp;quot;Translate&amp;quot; &amp;quot;Rotate&amp;quot; &amp;quot;Scale&amp;quot; cbg;&lt;br /&gt;
    button -label &amp;quot;Set &amp;#039;A&amp;#039; Selection&amp;quot; -command &amp;quot;selA&amp;quot; selAButton;&lt;br /&gt;
    button -label &amp;quot;Set &amp;#039;B&amp;#039; Selection&amp;quot; -en 0 -command &amp;quot;selB&amp;quot; selBButton;&lt;br /&gt;
&lt;br /&gt;
    button -label &amp;quot;Set &amp;#039;C&amp;#039; (target) selection&amp;quot; -en 0 -command &amp;quot;selC&amp;quot; selCButton;&lt;br /&gt;
    button -label &amp;quot;Apply&amp;quot; -command &amp;quot;blendAttr&amp;quot; -en 0 blendButton;&lt;br /&gt;
    button -label &amp;quot;Reset&amp;quot; -command &amp;quot;resetAll&amp;quot; resetButton;&lt;br /&gt;
    button -label &amp;quot;Cancel/close&amp;quot; -command &amp;quot;cancelpr&amp;quot;;&lt;br /&gt;
    showWindow $window;&lt;br /&gt;
    global string $A[];&lt;br /&gt;
    global string $B[];&lt;br /&gt;
    global string $C[];&lt;br /&gt;
    proc selA(){&lt;br /&gt;
        $objs = `ls -sl`;&lt;br /&gt;
        if(size($objs)!=0){&lt;br /&gt;
            button -e -en 0 -label (&amp;quot;Set &amp;#039;A&amp;#039; Selection (&amp;quot;+size($objs[0])+&amp;quot;)&amp;quot;) &amp;quot;selAButton&amp;quot;;&lt;br /&gt;
            button -e -en 1 &amp;quot;selBButton&amp;quot;;&lt;br /&gt;
            $A = $objs;&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
    proc selB(){&lt;br /&gt;
        $objs = `ls -sl`;&lt;br /&gt;
        if(size($objs)!=0){&lt;br /&gt;
            button -e -en 0 -label (&amp;quot;Set &amp;#039;B&amp;#039; Selection (&amp;quot;+size($objs[0])+&amp;quot;)&amp;quot;) &amp;quot;selBButton&amp;quot;;&lt;br /&gt;
            button -e -en 1 &amp;quot;selCButton&amp;quot;;&lt;br /&gt;
            $B = $objs;&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
    proc selC(){&lt;br /&gt;
        $objs = `ls -sl`;&lt;br /&gt;
        if(size($objs)!=0){&lt;br /&gt;
            button -e -en 0 -label (&amp;quot;Set &amp;#039;C&amp;#039; (target) selection (&amp;quot;+size($objs[0])+&amp;quot;)&amp;quot;) &amp;quot;selCButton&amp;quot;;&lt;br /&gt;
            button -e -en 1 &amp;quot;blendButton&amp;quot;;&lt;br /&gt;
            $C = $objs;&lt;br /&gt;
            }&lt;br /&gt;
    }&lt;br /&gt;
    proc blendAttr(){&lt;br /&gt;
        $objs = $C;&lt;br /&gt;
        if(size($objs)!=0){&lt;br /&gt;
    &lt;br /&gt;
            int $trCB = `checkBoxGrp -q -v1 &amp;quot;cbg&amp;quot;`;&lt;br /&gt;
            int $roCB = `checkBoxGrp -q -v2 &amp;quot;cbg&amp;quot;`;&lt;br /&gt;
            int $scCB = `checkBoxGrp -q -v3 &amp;quot;cbg&amp;quot;`;&lt;br /&gt;
    &lt;br /&gt;
            string $at = `addAttr -ln &amp;quot;blender&amp;quot;  -at double  -min 0 -max 1 -dv (0.5) $A[0]`;&lt;br /&gt;
            setAttr -e-keyable true ($A[0]+&amp;quot;.blender&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
            for($i = 0; $i &amp;lt; size($B);$i++){&lt;br /&gt;
                string $ob_a = $A[$i];&lt;br /&gt;
                string $ob_b = $B[$i];&lt;br /&gt;
                string $ob_c = $objs[$i];&lt;br /&gt;
                if($trCB){&lt;br /&gt;
                    $md = `createNode blendColors`;&lt;br /&gt;
                    connectAttr -f ($ob_a+&amp;quot;.translate&amp;quot;) ($md+&amp;quot;.color1&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($ob_b+&amp;quot;.translate&amp;quot;) ($md+&amp;quot;.color2&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($A[0]+&amp;quot;.blender&amp;quot;) ($md+&amp;quot;.blender&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($md+&amp;quot;.output&amp;quot;) ($ob_c+&amp;quot;.translate&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                if($roCB){&lt;br /&gt;
                    $md = `createNode blendColors`;&lt;br /&gt;
                    connectAttr -f ($ob_a+&amp;quot;.rotate&amp;quot;) ($md+&amp;quot;.color1&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($ob_b+&amp;quot;.rotate&amp;quot;) ($md+&amp;quot;.color2&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($A[0]+&amp;quot;.blender&amp;quot;) ($md+&amp;quot;.blender&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($md+&amp;quot;.output&amp;quot;) ($ob_c+&amp;quot;.rotate&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
                if($scCB){&lt;br /&gt;
                    $md = `createNode blendColors`;&lt;br /&gt;
                    connectAttr -f ($ob_a+&amp;quot;.scale&amp;quot;) ($md+&amp;quot;.color1&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($ob_b+&amp;quot;.scale&amp;quot;) ($md+&amp;quot;.color2&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($A[0]+&amp;quot;.blender&amp;quot;) ($md+&amp;quot;.blender&amp;quot;);&lt;br /&gt;
                    connectAttr -f ($md+&amp;quot;.output&amp;quot;) ($ob_c+&amp;quot;.scale&amp;quot;);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            select -r $A[0];&lt;br /&gt;
            resetAll();&lt;br /&gt;
        }else{&lt;br /&gt;
            warning &amp;quot;No objects selected&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    proc resetAll(){&lt;br /&gt;
        $A = {&amp;quot;&amp;quot;};&lt;br /&gt;
        $B = {&amp;quot;&amp;quot;};&lt;br /&gt;
        $C = {&amp;quot;&amp;quot;}; &lt;br /&gt;
        button -e -en 1 -label (&amp;quot;Set &amp;#039;A&amp;#039; selection&amp;quot;) &amp;quot;selAButton&amp;quot;;&lt;br /&gt;
        button -e -en 0 -label (&amp;quot;Set &amp;#039;B&amp;#039; selection&amp;quot;) &amp;quot;selBButton&amp;quot;;&lt;br /&gt;
        button -e -en 0 -label (&amp;quot;Set &amp;#039;C&amp;#039; (target) selection&amp;quot;) &amp;quot;selCButton&amp;quot;; &lt;br /&gt;
        button -e -en 0 &amp;quot;blendButton&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    proc cancelpr(){&lt;br /&gt;
        $A = {&amp;quot;&amp;quot;};&lt;br /&gt;
        $B = {&amp;quot;&amp;quot;};&lt;br /&gt;
        $C = {&amp;quot;&amp;quot;};&lt;br /&gt;
        deleteUI blenderW;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==PNG snapshot of current cam==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//saves a png snapshot of the current cam in the scene folder&lt;br /&gt;
string $sel[] = `ls -sl`;&lt;br /&gt;
select -d;&lt;br /&gt;
string $scenepath = `file -q -sn`;                      &lt;br /&gt;
string $filename = `match &amp;quot;[^/\\]*$&amp;quot; $scenepath`;           &lt;br /&gt;
string $filename = `match &amp;quot;^[^\.]*&amp;quot; $filename`;         &lt;br /&gt;
string $workdir = `substitute &amp;quot;/[^/]*$&amp;quot; $scenepath &amp;quot;/&amp;quot;`;        &lt;br /&gt;
string $png = $workdir+$filename+&amp;quot;.png&amp;quot;;&lt;br /&gt;
$time = `currentTime -q`;&lt;br /&gt;
string $panel = `getPanel -withFocus`;&lt;br /&gt;
string $cameraName = `modelPanel -q -cam $panel`;&lt;br /&gt;
setAttr &amp;quot;defaultRenderGlobals.imageFormat&amp;quot; 32;&lt;br /&gt;
&lt;br /&gt;
//SAVE DISPLAY OPTIONS&lt;br /&gt;
string $editorName = `getPanel -withFocus`;&lt;br /&gt;
$curState = `modelEditor -q -sts $editorName`;&lt;br /&gt;
string $modelEditorSettings = `match &amp;quot;^[^\//\;]*&amp;quot; $curState`;&lt;br /&gt;
$polyHud = `optionVar -q polyCountVisibility`;&lt;br /&gt;
$axisHud = `optionVar -q viewAxisVisibility`;&lt;br /&gt;
$frameHud = `optionVar -q currentFrameVisibility`;&lt;br /&gt;
&lt;br /&gt;
//SET DISPLAY OPTIONS&lt;br /&gt;
setPolyCountVisibility 0;&lt;br /&gt;
setViewAxisVisibility 0;&lt;br /&gt;
setCurrentFrameVisibility 1;&lt;br /&gt;
string $optns = `modelEditor -e&lt;br /&gt;
                -useInteractiveMode 0&lt;br /&gt;
                -activeOnly 0&lt;br /&gt;
                -wireframeOnShaded 0&lt;br /&gt;
                -headsUpDisplay 1&lt;br /&gt;
                -selectionHiliteDisplay 1&lt;br /&gt;
                -xray 0&lt;br /&gt;
                -jointXray 0&lt;br /&gt;
                -activeComponentsXray 0&lt;br /&gt;
                -displayTextures 1&lt;br /&gt;
                -nurbsCurves 0&lt;br /&gt;
                -nurbsSurfaces 1&lt;br /&gt;
                -polymeshes 1&lt;br /&gt;
                -subdivSurfaces 1&lt;br /&gt;
                -planes 1&lt;br /&gt;
                -cameras 0&lt;br /&gt;
                -controlVertices 0&lt;br /&gt;
                -hulls 0&lt;br /&gt;
                -grid 0&lt;br /&gt;
                -joints 0&lt;br /&gt;
                -ikHandles 0&lt;br /&gt;
                -deformers 0&lt;br /&gt;
                -dynamics 0&lt;br /&gt;
                -fluids 0&lt;br /&gt;
                -hairSystems 0&lt;br /&gt;
                -follicles 0&lt;br /&gt;
                -nCloths 0&lt;br /&gt;
                -nRigids 0&lt;br /&gt;
                -dynamicConstraints 0&lt;br /&gt;
                -manipulators 0&lt;br /&gt;
                -handles 0&lt;br /&gt;
                -pivots 0 $editorName`;&lt;br /&gt;
setRendererInModelPanel hwRender_OpenGL_Renderer $editorName;&lt;br /&gt;
playblast -frame $time -format image -w 832 -h 468  -p 100 -cf $png;&lt;br /&gt;
setRendererInModelPanel base_OpenGL_Renderer $editorName;&lt;br /&gt;
setPolyCountVisibility $polyHud;&lt;br /&gt;
setViewAxisVisibility $axisHud;&lt;br /&gt;
setCurrentFrameVisibility $frameHud;&lt;br /&gt;
eval($modelEditorSettings);&lt;br /&gt;
select -r $sel;&lt;br /&gt;
print $png;&lt;br /&gt;
                &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Right click in shelf (&amp;lt;maya2010)==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//rajouter un flag -mi dans le mel de la shelf (ouvrir avec wordpad)&lt;br /&gt;
    shelfButton&lt;br /&gt;
        ......&lt;br /&gt;
        -style &amp;quot;iconOnly&amp;quot; &lt;br /&gt;
        -marginWidth 1&lt;br /&gt;
        -marginHeight 1&lt;br /&gt;
        -command &amp;quot;warning \&amp;quot;Clique droit!\&amp;quot;;&amp;quot; &lt;br /&gt;
        -sourceType &amp;quot;mel&amp;quot; &lt;br /&gt;
        -actionIsSubstitute 0&lt;br /&gt;
        -commandRepeatable 1&lt;br /&gt;
        -mi &amp;quot;Menu droit 1&amp;quot; (&amp;quot;Commande&amp;quot;)&lt;br /&gt;
        -mi &amp;quot;Menu droit 2&amp;quot; (&amp;quot;print(\&amp;quot;HI\&amp;quot;)&amp;quot;)&lt;br /&gt;
    ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Remove useless keys==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//removes useless keys for ppl who hit shift-s like monkeys&lt;br /&gt;
&lt;br /&gt;
source generateChannelMenu.mel;&lt;br /&gt;
source channelBoxCommand.mel;&lt;br /&gt;
for($obj in `ls -sl`){&lt;br /&gt;
    float $testVar;&lt;br /&gt;
    float $testVar2;&lt;br /&gt;
    $from = `playbackOptions -q -ast`;&lt;br /&gt;
    $to = `playbackOptions -q -aet`;&lt;br /&gt;
    $safe = false;&lt;br /&gt;
    string $Attrs[] = `listAnimatable $obj`;&lt;br /&gt;
    for($Attr in $Attrs){&lt;br /&gt;
        $safe = false;&lt;br /&gt;
        $numbK = `keyframe -query -keyframeCount $Attr`;&lt;br /&gt;
        if($numbK &amp;gt; 0){&lt;br /&gt;
            $testVar = `getAttr -t $from $Attr`;&lt;br /&gt;
            $testVar2 = 0;&lt;br /&gt;
            for($i = $from;$i&amp;lt;=$to;$i++){&lt;br /&gt;
                $testVar2 = `getAttr -t $i $Attr`;&lt;br /&gt;
                    if($testVar2 != $testVar){&lt;br /&gt;
                    //print(&amp;quot;Clés sur &amp;quot;+$Attr+&amp;quot;\n&amp;quot;);&lt;br /&gt;
                    $safe = true;&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
                $testVar = $testVar2;&lt;br /&gt;
            }&lt;br /&gt;
            //print(&amp;quot;\n&amp;quot;+($safe)?&amp;quot;\nAnim: &amp;quot;+$Attr:&amp;quot;\nNotAnim: &amp;quot;+$Attr);&lt;br /&gt;
        if(!$safe){&lt;br /&gt;
            CBdeleteConnection($Attr);&lt;br /&gt;
        }&lt;br /&gt;
        }       &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Create ID Shaders==&lt;br /&gt;
Crée des Surface Shaders pour les obj IDs&lt;br /&gt;
chopper les objets et lancer le script (attention, il efface les shaders inutilisés) &lt;br /&gt;
le script fout des surfaceShaders vachement colorés, en utilisant une liste specifique de couleur pour les objets les plus près de la caméra active on peut regler les min et max des Teinte Saturation Luminosité des autres &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
global proc createIDShaders(int $deleteUnusedShaders){&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    string $forcedColors[] = {&amp;quot;1 0 0&amp;quot;,&amp;quot;0 1 0&amp;quot;,&amp;quot;0 0 1&amp;quot;,&amp;quot;1 1 1&amp;quot;,&amp;quot;0 1 1&amp;quot;,&amp;quot;1 1 0&amp;quot;,&amp;quot;1 0 1&amp;quot;,&amp;quot;.5 .5 .5&amp;quot;};  //&amp;#039;base&amp;#039; colors &lt;br /&gt;
    float $maxHue = 1;  //color hue &lt;br /&gt;
    float $minHue = 0;  //&lt;br /&gt;
    float $maxSat = 1;  //color saturation&lt;br /&gt;
    float $minSat =.8;  //&lt;br /&gt;
    float $maxVal = 1;  //color darkness&lt;br /&gt;
    float $minVal =.6;     //&lt;br /&gt;
&lt;br /&gt;
    string $sel[] = `ls -sl`;&lt;br /&gt;
    $cam = getCurCam();&lt;br /&gt;
    $sortedobjects = sortFromClosestToFurthest($cam , $sel);&lt;br /&gt;
    float $oLen = `size($sortedobjects)`;&lt;br /&gt;
    float $cLen = `size($forcedColors)`;&lt;br /&gt;
    for($a = 0;$a&amp;lt;=$oLen-1;$a++){&lt;br /&gt;
        string $curColor;&lt;br /&gt;
        if($a&amp;lt;$cLen){&lt;br /&gt;
            $curColor = $forcedColors[$a];&lt;br /&gt;
        }else{&lt;br /&gt;
            $curColor = createRGBColorString($minHue,$minSat,$minVal,$maxHue,$maxSat,$maxVal);&lt;br /&gt;
        }&lt;br /&gt;
        //string $curColor = ($a&amp;lt;$cLen-1)?$forcedColors[$a]:createRGBColorString($minHue,$minSat,$minVal,$maxHue,$maxSat,$maxVal);&lt;br /&gt;
        $shader = createSurfShader($sortedobjects[$a],$curColor);&lt;br /&gt;
        connectShader($sortedobjects[$a],$shader);      &lt;br /&gt;
    }&lt;br /&gt;
    if($deleteUnusedShaders){&lt;br /&gt;
        $cmd = `MLdeleteUnused`;&lt;br /&gt;
    }&lt;br /&gt;
    $cmd = `select -r $sel`;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
proc string getCurCam(){&lt;br /&gt;
    string $currentPanel = `getPanel -withFocus`;&lt;br /&gt;
    string $cameraName = `modelPanel -q -cam $currentPanel`;&lt;br /&gt;
    return $cameraName;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
proc float distanceFrom(string $from,string $to){&lt;br /&gt;
    float $worldPosFrom[] = `xform -q -ws -t $from`;&lt;br /&gt;
    float $worldPosTo[] = `xform -q -ws -t $to`;&lt;br /&gt;
    vector $diff = &amp;lt;&amp;lt; $worldPosFrom[0]-$worldPosTo[0], $worldPosFrom[1]-$worldPosTo[1], $worldPosFrom[2]-$worldPosTo[2]&amp;gt;&amp;gt;;&lt;br /&gt;
    float $distance = `mag $diff`;&lt;br /&gt;
    return $distance;&lt;br /&gt;
}&lt;br /&gt;
proc string[] sortFromClosestToFurthest(string $fromObj, string $objectList[]){&lt;br /&gt;
    //needs &amp;quot;distanceFrom()&amp;quot; proc&lt;br /&gt;
    int $objssize = size($objectList);  &lt;br /&gt;
    string $distances[];&lt;br /&gt;
&lt;br /&gt;
    //build array with distances and object names to sort them easily later&lt;br /&gt;
    for($a= 0;$a&amp;lt;= $objssize-1;$a++){&lt;br /&gt;
        $distances[$a] = distanceFrom($fromObj,$objectList[$a])+&amp;quot;=&amp;quot;+$objectList[$a];&lt;br /&gt;
        //print($distances[$a]+&amp;quot;\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //sort&lt;br /&gt;
    $distances = `sort $distances`;&lt;br /&gt;
    &lt;br /&gt;
    //clean array to have sorted object names only&lt;br /&gt;
    for($a= 0;$a&amp;lt;= $objssize-1;$a++){&lt;br /&gt;
        string $component = `substitute &amp;quot;^[^=]*\\=&amp;quot; $distances[$a] &amp;quot;&amp;quot;`;&lt;br /&gt;
        $distances[$a] = $component;&lt;br /&gt;
    }&lt;br /&gt;
    return $distances;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
proc string createSurfShader(string $name,string $r_g_b){&lt;br /&gt;
    string $nuSurfShader = `shadingNode -asShader surfaceShader`;&lt;br /&gt;
    string $nuName = $name+&amp;quot;_IDShade&amp;quot;;&lt;br /&gt;
    $nuSurfShader = `rename $nuSurfShader $nuName`;&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$nuSurfShader+&amp;quot;.outColor -type double3 &amp;quot;+$r_g_b);&lt;br /&gt;
    eval(&amp;quot;sets -renderable true -noSurfaceShader true -empty -name &amp;quot;+$nuSurfShader+&amp;quot;SG&amp;quot;);&lt;br /&gt;
    eval(&amp;quot;connectAttr -f &amp;quot;+$nuSurfShader+&amp;quot;.outColor &amp;quot;+$nuSurfShader+&amp;quot;SG.surfaceShader&amp;quot;);&lt;br /&gt;
    return $nuSurfShader;&lt;br /&gt;
}&lt;br /&gt;
proc connectShader(string $obj, string $shader){&lt;br /&gt;
    string $shapes[] = `listRelatives -s -path $obj`;       //select shapes to prevent a parent selecting its children and giving them a shader&lt;br /&gt;
    $cmd = `select -r $shapes[0]`;                  //un-comment the next line  to keep this &amp;#039;feature&amp;#039;&lt;br /&gt;
    //$cmd = `select -r $obj`;&lt;br /&gt;
    $cmd = &amp;quot;sets -e -forceElement &amp;quot;+$shader+&amp;quot;SG&amp;quot;;&lt;br /&gt;
    print($cmd+&amp;quot;\n&amp;quot;);&lt;br /&gt;
    eval($cmd);&lt;br /&gt;
}&lt;br /&gt;
proc string createRGBColorString(float $mH,float $mS, float $mV,float $MH,float $MS, float $MV){&lt;br /&gt;
    vector $nucolor = `rand &amp;lt;&amp;lt;$mH,$mS,$mV&amp;gt;&amp;gt; &amp;lt;&amp;lt;$MH,$MS,$MV&amp;gt;&amp;gt;`;&lt;br /&gt;
    $nucolor = `hsv_to_rgb $nucolor`;&lt;br /&gt;
    return $nucolor.x+&amp;quot; &amp;quot;+$nucolor.y+&amp;quot; &amp;quot;+$nucolor.z;&lt;br /&gt;
}   &lt;br /&gt;
createIDShaders 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==ikSpline stretch==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $objs[] = `ls -sl`;&lt;br /&gt;
string $curve = $objs[`size($objs)`-1];&lt;br /&gt;
&lt;br /&gt;
string $arclen = `arclen -ch 1 $curve`;&lt;br /&gt;
string $multDiv = `createNode multiplyDivide -n &amp;quot;stretchMultiplier&amp;quot;`;&lt;br /&gt;
setAttr ($multDiv+&amp;quot;.operation&amp;quot;) 2;&lt;br /&gt;
connectAttr -f ($arclen+&amp;quot;.arcLength&amp;quot;) ($multDiv+&amp;quot;.input1X&amp;quot;);&lt;br /&gt;
float $val = `getAttr ($arclen+&amp;quot;.arcLength&amp;quot;)`;&lt;br /&gt;
setAttr ($multDiv+&amp;quot;.input2X&amp;quot;) $val;&lt;br /&gt;
int $l = `size($objs)`-1;&lt;br /&gt;
for($i=0;$i&amp;lt;$l;$i++){&lt;br /&gt;
    connectAttr -f ($multDiv+&amp;quot;.outputX&amp;quot;) ($objs[$i]+&amp;quot;.scaleX&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Curve Text Captions==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
float $corr[] = {-1.25, -0.5, 0};&lt;br /&gt;
float $corrScale = .1;&lt;br /&gt;
$objs = `ls -sl`;&lt;br /&gt;
string $textobjs = &amp;quot;&amp;quot;;&lt;br /&gt;
for($a=0;$a&amp;lt;=size($objs)-1;$a++){&lt;br /&gt;
    string $obj = $objs[$a];&lt;br /&gt;
    $text4 = `textCurves -ch 1 -f &amp;quot;Arial|h-11|w200|c0&amp;quot; -t $obj`;&lt;br /&gt;
    $t = $text4[0];&lt;br /&gt;
    $textobjs += &amp;quot; &amp;quot;+$t;&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$t+&amp;quot;.scaleX &amp;quot;+$corrScale);&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$t+&amp;quot;.scaleY &amp;quot;+$corrScale);&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$t+&amp;quot;.scaleZ &amp;quot;+$corrScale);&lt;br /&gt;
    makeIdentity -apply true -t 0 -r 0 -s 1 -n 0;   &lt;br /&gt;
    parent -relative $t $obj;&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$t+&amp;quot;.translateX &amp;quot;+$corr[0]);&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$t+&amp;quot;.translateY &amp;quot;+$corr[1]);&lt;br /&gt;
    eval(&amp;quot;setAttr &amp;quot;+$t+&amp;quot;.translateZ &amp;quot;+$corr[2]);&lt;br /&gt;
}&lt;br /&gt;
eval(&amp;quot;select -r &amp;quot;+$textobjs);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Rounding numbers in mel ==&lt;br /&gt;
Doesn&amp;#039;t exist, use this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  //round(30.3333,1) ==&amp;gt; 30.3&lt;br /&gt;
  proc float round(float $val,float $dec){&lt;br /&gt;
  &lt;br /&gt;
    $sign = `sign $val`;&lt;br /&gt;
    float $dec = `pow 10 $dec`;&lt;br /&gt;
    $val = (int) (($val + $sign*5/($dec*10)) * $dec);&lt;br /&gt;
    $val = ($val / $dec);&lt;br /&gt;
    return $val;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Super Snapshot / Duplicator / Anim Baker ==&lt;br /&gt;
https://i.imgur.com/U7WnBV1.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//like animation snapshot but works with other types of shapes&lt;br /&gt;
superSnapshot;&lt;br /&gt;
&lt;br /&gt;
global proc superSnapshot(){&lt;br /&gt;
    print &amp;quot;better animation snapshot tool by bernie@berniebernie.fr&amp;quot;;&lt;br /&gt;
    if (`window -exists superSnapshotWindow`) deleteUI superSnapshotWindow;&lt;br /&gt;
    if (`windowPref -exists superSnapshotWindow`) windowPref -remove superSnapshotWindow;&lt;br /&gt;
    &lt;br /&gt;
    int $e = `playbackOptions -query -maxTime`;&lt;br /&gt;
    int $s = `playbackOptions -query -minTime`;&lt;br /&gt;
        &lt;br /&gt;
    string $window = `window -t &amp;quot;Object Snapshot&amp;quot; superSnapshotWindow`;&lt;br /&gt;
    &lt;br /&gt;
    columnLayout -adjustableColumn true -columnAlign &amp;quot;center&amp;quot;;&lt;br /&gt;
    &lt;br /&gt;
        frameLayout -mh 10 -mw 10 -l &amp;quot;Options&amp;quot; ;&lt;br /&gt;
        &lt;br /&gt;
            checkBoxGrp -columnWidth2 100 165 -numberOfCheckBoxes 1 -label1 &amp;quot;Delete children (ie deformers handles)&amp;quot; -v1 true &amp;quot;sS_delChildren&amp;quot;;&lt;br /&gt;
            rowLayout -nc 2;&lt;br /&gt;
                text -label &amp;quot;Skip frames:&amp;quot; -align &amp;quot;center&amp;quot;;&lt;br /&gt;
                textField  -text 0 -w 50 &amp;quot;sS_skip&amp;quot;;&lt;br /&gt;
                setParent..;&lt;br /&gt;
            //checkBoxGrp -columnWidth2 100 165 -numberOfCheckBoxes 1 -label1 &amp;quot;Parent to group in world&amp;quot; -v1 true;&lt;br /&gt;
            setParent..;&lt;br /&gt;
    &lt;br /&gt;
        frameLayout -mh 10 -mw 10 -l &amp;quot;Quick Snapshot&amp;quot; ;&lt;br /&gt;
            &lt;br /&gt;
            rowLayout -nc 2 -adjustableColumn 2 -columnAlign  2 &amp;quot;right&amp;quot;;&lt;br /&gt;
                button -label &amp;quot;  &amp;lt;&amp;lt;          &amp;quot; -command (&amp;quot;superSnapshotProc(-1)&amp;quot;);&lt;br /&gt;
                button -label &amp;quot;          &amp;gt;&amp;gt;  &amp;quot; -command (&amp;quot;superSnapshotProc(1)&amp;quot;);&lt;br /&gt;
                setParent..;&lt;br /&gt;
            setParent..;&lt;br /&gt;
            &lt;br /&gt;
        frameLayout -mh 10 -mw 10 -l &amp;quot;Animation Snapshot&amp;quot; ; &lt;br /&gt;
            &lt;br /&gt;
            text -label &amp;quot;Start/End frames:&amp;quot; -align &amp;quot;center&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            rowLayout -nc 2;&lt;br /&gt;
                textField  -text $s &amp;quot;sS_start&amp;quot;;&lt;br /&gt;
                textField  -text $e &amp;quot;sS_end&amp;quot;;&lt;br /&gt;
            setParent..;&lt;br /&gt;
                &lt;br /&gt;
    &lt;br /&gt;
                &lt;br /&gt;
            checkBoxGrp -columnWidth2 100 165 -numberOfCheckBoxes 1 -label1 &amp;quot;Add visibility keyframes (animation)&amp;quot; -v1 false &amp;quot;sS_visib&amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            button -label &amp;quot;Create Animation Snapshot&amp;quot; -command (&amp;quot;superSnapshotProc(0)&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
    showWindow $window;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
global proc superSnapshotProc(float $quickSnapshot){&lt;br /&gt;
    &lt;br /&gt;
    string $objects[] = `ls -sl -l`;&lt;br /&gt;
    &lt;br /&gt;
    if(size($objects)&amp;gt;=1){        &lt;br /&gt;
        int $curFrame = `currentTime -q`;&lt;br /&gt;
        int $delChildren = `checkBoxGrp -q -v1 &amp;quot;sS_delChildren&amp;quot;`;&lt;br /&gt;
        int $sF = `textField -q -text &amp;quot;sS_start&amp;quot;`;&lt;br /&gt;
        int $eF = `textField -q -text &amp;quot;sS_end&amp;quot;`;&lt;br /&gt;
        int $skip = `textField -q -text &amp;quot;sS_skip&amp;quot;`;&lt;br /&gt;
        int $visibKey = `checkBoxGrp -q -v1 &amp;quot;sS_visib&amp;quot;`;&lt;br /&gt;
    &lt;br /&gt;
        string $parentGrp = &amp;quot;&amp;quot;;&lt;br /&gt;
        if(`objExists($objects[0]+&amp;quot;_snapshots&amp;quot;)`){&lt;br /&gt;
            $parentGrp = $objects[0]+&amp;quot;_snapshots&amp;quot;;&lt;br /&gt;
        }else{&lt;br /&gt;
            $parentGrp = `group -em -n ($objects[0]+&amp;quot;_snapshots&amp;quot;)`;&lt;br /&gt;
        }&lt;br /&gt;
       &lt;br /&gt;
        select -r $objects;&lt;br /&gt;
        &lt;br /&gt;
        if($quickSnapshot != 0){&lt;br /&gt;
            currentTime ($curFrame+$quickSnapshot+$quickSnapshot*$skip);&lt;br /&gt;
            string $newObjects[] = `duplicate -rc`;&lt;br /&gt;
            $newObjects = `ls -sl`;&lt;br /&gt;
            parent $newObjects $parentGrp;&lt;br /&gt;
            if($delChildren){&lt;br /&gt;
                catchQuiet(delete(`listRelatives -children -typ &amp;quot;transform&amp;quot; $newObjects`));&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            for($i = $sF;$i&amp;lt;=$eF;$i+=(1+$skip)){&lt;br /&gt;
                select -r $objects;&lt;br /&gt;
                currentTime $i;&lt;br /&gt;
                string $newObjects[] = `duplicate -rc`;&lt;br /&gt;
                $newObjects = `ls -sl`;&lt;br /&gt;
                parent $newObjects $parentGrp;&lt;br /&gt;
                if($delChildren){&lt;br /&gt;
                    delete(`listRelatives -children -typ &amp;quot;transform&amp;quot; $newObjects`);&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                if($visibKey){&lt;br /&gt;
                    for($o in $newObjects){&lt;br /&gt;
                        eval(&amp;quot;setKeyframe -time &amp;quot;+($i-1)+&amp;quot; -value 0 &amp;quot;+$o+&amp;quot;.visibility&amp;quot;);&lt;br /&gt;
                        eval(&amp;quot;setKeyframe -time &amp;quot;+$i+&amp;quot; -value 1 &amp;quot;+$o+&amp;quot;.visibility&amp;quot;);&lt;br /&gt;
                        eval(&amp;quot;setKeyframe -time &amp;quot;+($i+1)+&amp;quot; -value 0 &amp;quot;+$o+&amp;quot;.visibility&amp;quot;);&lt;br /&gt;
                    }&lt;br /&gt;
                }                &lt;br /&gt;
            }&lt;br /&gt;
            currentTime $curFrame;&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        warning &amp;quot;&amp;gt;&amp;gt;&amp;gt; No object(s) selected!&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    select -r $objects;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Particules or Objects to Curve==&lt;br /&gt;
https://imgur.com/5mgM9.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//cree une courbe passant par toutes les partoches dans l&amp;#039;ordre des IDs&lt;br /&gt;
string $objs[] = `ls -sl`;&lt;br /&gt;
$rel = listRelatives($objs[0]);&lt;br /&gt;
if(objectType($rel)==&amp;quot;particle&amp;quot;){&lt;br /&gt;
    $ptc = $rel[0];&lt;br /&gt;
    $cnt = `getAttr($ptc+&amp;quot;.count&amp;quot;)`;&lt;br /&gt;
    $curve = &amp;quot;curve&amp;quot;;&lt;br /&gt;
    float $ptAr[] = eval(&amp;quot;getParticleAttr -at position -array true &amp;quot;+$ptc+&amp;quot;.pt[0:&amp;quot;+($cnt-1)+&amp;quot;]&amp;quot;);&lt;br /&gt;
    for($a = 0;$a&amp;lt;($cnt-2);$a++){&lt;br /&gt;
        $curve += &amp;quot; -p &amp;quot;+$ptAr[$a*3]+&amp;quot; &amp;quot;+$ptAr[$a*3+1]+&amp;quot; &amp;quot;+$ptAr[$a*3+2];&lt;br /&gt;
    }&lt;br /&gt;
    eval($curve);&lt;br /&gt;
}else{&lt;br /&gt;
    warning &amp;quot;select particle object&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
https://i.imgur.com/DZ8b8ks.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$curve = &amp;quot;curve&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
for($o in `ls -sl`){&lt;br /&gt;
    $x = `xform -q -ws -t $o`;&lt;br /&gt;
    print($x);&lt;br /&gt;
    $curve += (&amp;quot; -p &amp;quot;+$x[0]+&amp;quot; &amp;quot;+$x[1]+&amp;quot; &amp;quot;+$x[2]);&lt;br /&gt;
}&lt;br /&gt;
eval($curve);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Selection to shelf 2==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// place that in a shelf button; or put it in a .mel file in your script directory and use a shelf button/shortcut that calls &amp;quot;selectionToShelf&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// takes your current controllers; shapes or whatever and saves the selection to the shelf, prompting you for a name&lt;br /&gt;
// if you are in an animation scene with lots of &amp;#039;cloned&amp;#039; references (that share controller names) and choose the option &amp;#039;All Characters&amp;#039;&lt;br /&gt;
// the created shelf button will select the controllers that are on the currently selected object using the namespace&lt;br /&gt;
//&lt;br /&gt;
// call &amp;quot;selectionToShelf 1&amp;quot; for a simpler interface&lt;br /&gt;
&lt;br /&gt;
global proc selectionToShelf(int $simple){&lt;br /&gt;
    $option = &amp;quot;&amp;quot;;    &lt;br /&gt;
    if(!$simple){&lt;br /&gt;
    string $option = `confirmDialog -title &amp;quot;Selection to shelf&amp;quot; -message &amp;quot;Create a button for this unique character or all similar characters ?&amp;quot;&lt;br /&gt;
        -button &amp;quot;This character&amp;quot;&lt;br /&gt;
        -button &amp;quot;All characters&amp;quot; -defaultButton &amp;quot;All characters&amp;quot;&lt;br /&gt;
        -button &amp;quot;Cancel&amp;quot;&lt;br /&gt;
        -cancelButton &amp;quot;Cancel&amp;quot; -dismissString &amp;quot;No&amp;quot;`;&lt;br /&gt;
    }else{&lt;br /&gt;
        $option=&amp;quot;This character&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    if($option!=&amp;quot;Cancel&amp;quot; &amp;amp;&amp;amp; size(`ls -sl`)&amp;gt;0){&lt;br /&gt;
        string $sel[] = `ls -sl`;&lt;br /&gt;
    &lt;br /&gt;
       &lt;br /&gt;
        string $text;&lt;br /&gt;
        string $result = `promptDialog&lt;br /&gt;
            -title &amp;quot;Selection Name&amp;quot;&lt;br /&gt;
            -message &amp;quot;Name your shelf button (3-6 letters):&amp;quot;&lt;br /&gt;
            -button &amp;quot;OK&amp;quot; -button &amp;quot;Cancel&amp;quot;&lt;br /&gt;
            -defaultButton &amp;quot;OK&amp;quot; -cancelButton &amp;quot;Cancel&amp;quot;&lt;br /&gt;
            -dismissString &amp;quot;Cancel&amp;quot;`;&lt;br /&gt;
        $text = `promptDialog -query -text`;&lt;br /&gt;
        if ($result == &amp;quot;OK&amp;quot;) {&lt;br /&gt;
            if($option==&amp;quot;This character&amp;quot;){&lt;br /&gt;
                $selstring = &amp;quot;select -add &amp;quot;+stringArrayToString($sel,&amp;quot; &amp;quot;);&lt;br /&gt;
                textToShelf ($text, $selstring);    &lt;br /&gt;
            }else{&lt;br /&gt;
                $namespace = `match &amp;quot;.*:&amp;quot; $sel[0]`;&lt;br /&gt;
                for($i=0;$i&amp;lt;size($sel);$i++){&lt;br /&gt;
                    $sel[$i] = `match &amp;quot;[^:.]*$&amp;quot; $sel[$i]`;&lt;br /&gt;
                }&lt;br /&gt;
                string $str= &amp;quot;__NS__&amp;quot;+stringArrayToString($sel,&amp;quot; __NS__&amp;quot;);&lt;br /&gt;
                $selstring = &amp;quot;string $sel[] = `ls -sl`;string $ns = `match \&amp;quot;.*:\&amp;quot; $sel[0]`;string $se[] = stringToStringArray(substituteAllString(\&amp;quot;&amp;quot;+$str+&amp;quot;\&amp;quot;,\&amp;quot;__NS__\&amp;quot;,$ns),\&amp;quot; \&amp;quot;);select -r $se;&amp;quot;;&lt;br /&gt;
                textToShelf ($text, $selstring);    &lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        warning &amp;quot;Select one or more object&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
selectionToShelf 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Snippets / Pastebins =&lt;br /&gt;
&lt;br /&gt;
Create follicle on points (mFollicles): https://pastebin.com/raw.php?i=52GGufMt&lt;br /&gt;
&lt;br /&gt;
3D point to 2d screenspace: https://pastebin.com/raw.php?i=C4nwpXLB &lt;br /&gt;
&lt;br /&gt;
==Sur le net==&lt;br /&gt;
*Redistribue les edges proprement sur une surface&lt;br /&gt;
** https://www.tgjay.com/htms/research/tgPolyRelax.htm&lt;br /&gt;
&lt;br /&gt;
== Annotate (arrow from obj to obj) ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $sl[] = `ls -sl`;&lt;br /&gt;
$from = $sl[1];&lt;br /&gt;
$to = $sl[0];&lt;br /&gt;
&lt;br /&gt;
$an = `annotate -tx &amp;quot;&amp;quot; -p 0 0 0 $from`;&lt;br /&gt;
catchQuiet(`parent -r $an $to`);&lt;br /&gt;
setAttr ($an+&amp;quot;.overrideDisplayType&amp;quot;) 1;&lt;br /&gt;
setAttr ($an+&amp;quot;.overrideEnabled&amp;quot;) 1;&lt;br /&gt;
setAttr ($an+&amp;quot;.overrideColor&amp;quot;) 13;  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Random rotates ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for($o in `ls -sl`){&lt;br /&gt;
    setAttr($o+&amp;quot;.rotate&amp;quot;) (rand(-180,180)) (rand(-180,180)) (rand(-180,180));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Random select 1/2 ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $sele[] = {};&lt;br /&gt;
for($o in `ls -sl`){&lt;br /&gt;
    if(rand(1)&amp;lt;.5){&lt;br /&gt;
        $sele[size($sele)] = $o;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
select -r $sele;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Random translates==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for($o in `ls -sl`){&lt;br /&gt;
    &lt;br /&gt;
    vector $v = `sphrand(1)`;&lt;br /&gt;
    float $f[] = `getAttr($o+&amp;quot;.translate&amp;quot;)`;&lt;br /&gt;
    setAttr($o+&amp;quot;.translate&amp;quot;) ($v.x+$f[0]) ($v.y+$f[1]) ($v.z+$f[2]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Array procs ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//is value in array&lt;br /&gt;
proc int inArray(float $v,float $thearray[]){&lt;br /&gt;
    int $flag = 0;&lt;br /&gt;
    for($item in $thearray){&lt;br /&gt;
        if($item == $v){&lt;br /&gt;
            $flag = 1;&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return $flag;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Edge to Loc==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $edges[] = `ls -sl`; //expects edges&lt;br /&gt;
for($edge in $edges){&lt;br /&gt;
    select -r $edge;&lt;br /&gt;
    string $vtx[] = `polyListComponentConversion -ff -fe -fuv -fvf -tv`;&lt;br /&gt;
    $vtx = `ls -fl $vtx`;&lt;br /&gt;
    float $p1[] = `xform -q -ws -t  $vtx[0]`;&lt;br /&gt;
    float $p2[] = `xform -q -ws -t  $vtx[1]`;&lt;br /&gt;
    float $diff[] = {$p2[0]-$p1[0],$p2[1]-$p1[1],$p2[2]-$p1[2]};&lt;br /&gt;
    float $angls[] = `angleBetween -euler -v1 1 0 0 -v2 $diff[0] $diff[1] $diff[2]`; //works in my case&lt;br /&gt;
    &lt;br /&gt;
    $loc = `spaceLocator`;&lt;br /&gt;
    move -r (($p1[0]+$p2[0])/2) (($p1[1]+$p2[1])/2) (($p1[2]+$p2[2])/2) $loc;&lt;br /&gt;
    rotate -r -os $angls[0] $angls[1] $angls[2]  $loc;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Find Centroid==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//NathanN @cgtalk&lt;br /&gt;
setToolTo moveSuperContext; &lt;br /&gt;
vector $centerPos = `manipMoveContext -q -position Move`;&lt;br /&gt;
$b = `spaceLocator`;&lt;br /&gt;
move -r ($centerPos.x) ($centerPos.y) ($centerPos.z) $b;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Faces to obj==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//selection of faces to objects&lt;br /&gt;
$selection = `ls -sl`;&lt;br /&gt;
select -cl;&lt;br /&gt;
for($obj in $selection){;&lt;br /&gt;
    string $no_component = `match &amp;quot;^[^\.]*&amp;quot; $obj`;&lt;br /&gt;
    select -add $no_component;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Remove useless edges==&lt;br /&gt;
https://i.imgur.com/FtcTh79.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
string $sel[] = `ls -sl`;&lt;br /&gt;
&lt;br /&gt;
for($o in $sel){&lt;br /&gt;
    //thnx to NathanN&lt;br /&gt;
    catchQuiet(removeUselessEdge($o));&lt;br /&gt;
}&lt;br /&gt;
select -r $sel;&lt;br /&gt;
proc removeUselessEdge(string $obj){&lt;br /&gt;
    select -r $obj;&lt;br /&gt;
    string $mySelection[] = `ls -sl`;&lt;br /&gt;
    $softEdge = `polySoftEdge -angle 1 -ch 1 $mySelection`;&lt;br /&gt;
    ConvertSelectionToEdges;&lt;br /&gt;
    polySelectConstraint -m 3 -type 32768 -sm 2;&lt;br /&gt;
    string $selectEdges[] = `ls -sl`;&lt;br /&gt;
    delete $softEdge;&lt;br /&gt;
    polyDelEdge -cv on;&lt;br /&gt;
    polySelectConstraint -m 0 -type 0x0000 -sm 0;   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Maya]]&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
	<entry>
		<id>https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_VEX&amp;diff=860</id>
		<title>Houdini VEX</title>
		<link rel="alternate" type="text/html" href="https://berniebernie.fr/mediawiki-1.37.1/index.php?title=Houdini_VEX&amp;diff=860"/>
		<updated>2025-07-29T12:31:46Z</updated>

		<summary type="html">&lt;p&gt;Bernie: /* Visualize parameter */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Remap curve ==&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/Kh9phrT.png&lt;br /&gt;
&lt;br /&gt;
Edit the point placement along the length of a (parametric) curve using a remap curve&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector uv;&lt;br /&gt;
int prim;&lt;br /&gt;
xyzdist(0,@P,prim,uv);&lt;br /&gt;
float umapped = chramp(&amp;quot;remap&amp;quot;,uv[0]);&lt;br /&gt;
uv[0] = umapped;&lt;br /&gt;
@P = primuv(0,&amp;quot;P&amp;quot;,prim,uv);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualize parameter ==&lt;br /&gt;
Why look at the animation editor when you can have a shittier version in the viewport. Don&amp;#039;t ask questions, it helped me.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/T5kLfEM.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//second input of detail wrangle should be frozen frame, &amp;#039;parm&amp;#039; channel is a path to the parm you want to see graphed, which can be an expression&lt;br /&gt;
&lt;br /&gt;
vector curPos;&lt;br /&gt;
//---graph--&lt;br /&gt;
&lt;br /&gt;
vector scalept(vector normalizedpt){&lt;br /&gt;
    vector bs = getbbox_size(1);&lt;br /&gt;
    float size = max(bs[0],max(bs[1],bs[2]))/3;&lt;br /&gt;
    vector bb = getbbox_center(1);&lt;br /&gt;
    return ((normalizedpt-{.5,-.1,0})*size+bb);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int start = `$FSTART`;&lt;br /&gt;
int end = `$FEND`;&lt;br /&gt;
int pts[] = {};&lt;br /&gt;
for(int i=0;i&amp;lt;end-start+1;i++){&lt;br /&gt;
    float val = ch(chs(&amp;#039;parm&amp;#039;),float(i+start)*@TimeInc);&lt;br /&gt;
    vector pos = set(float(i)/(end-start),val,0);&lt;br /&gt;
    int newpoint = addpoint(0,scalept(pos));&lt;br /&gt;
    append(pts,newpoint);&lt;br /&gt;
    if(i+start==@Frame){&lt;br /&gt;
        curPos = pos;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
addprim(0,&amp;quot;polyline&amp;quot;,pts);&lt;br /&gt;
&lt;br /&gt;
//-----------&lt;br /&gt;
&lt;br /&gt;
pts  = {};&lt;br /&gt;
float y = chf(&amp;#039;yMax&amp;#039;);&lt;br /&gt;
int ptA =  addpoint(0,scalept(set(0.0,y,0)));&lt;br /&gt;
int ptB =  addpoint(0,scalept(set(1.0,y,0)));&lt;br /&gt;
append(pts,ptA);&lt;br /&gt;
append(pts,ptB);&lt;br /&gt;
addprim(0,&amp;quot;polyline&amp;quot;,pts);&lt;br /&gt;
&lt;br /&gt;
//---x-------&lt;br /&gt;
&lt;br /&gt;
pts  = {};&lt;br /&gt;
ptA =  addpoint(0,scalept(curPos+set(-.01,-.01,0)));&lt;br /&gt;
ptB =  addpoint(0,scalept(curPos+set(.01,.01,0)));&lt;br /&gt;
append(pts,ptA);&lt;br /&gt;
append(pts,ptB);&lt;br /&gt;
int line = addprim(0,&amp;quot;polyline&amp;quot;,pts);&lt;br /&gt;
setprimattrib(0, &amp;#039;Cd&amp;#039;, line, set(1,0,0), &amp;#039;set&amp;#039;);&lt;br /&gt;
pts  = {};&lt;br /&gt;
ptA =  addpoint(0,scalept(curPos+set(.01,-.01,0)));&lt;br /&gt;
ptB =  addpoint(0,scalept(curPos+set(-.01,.01,0)));&lt;br /&gt;
append(pts,ptA);&lt;br /&gt;
append(pts,ptB);&lt;br /&gt;
line = addprim(0,&amp;quot;polyline&amp;quot;,pts);&lt;br /&gt;
setprimattrib(0, &amp;#039;Cd&amp;#039;, line, set(1,0,0), &amp;#039;set&amp;#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Connect random points by direction ==&lt;br /&gt;
&lt;br /&gt;
WIP-ish but was enough for what I needed might polish later (I did add some error checking though why the fuck...)&lt;br /&gt;
&lt;br /&gt;
This code takes two points or two group of points (of equal size) and attemps to connect points that are in the direction of a,b pairs given a maximum cone angle. This can be useful if you have randomly sorted points and the &amp;#039;&amp;#039;Connect Adjacent Pieces&amp;#039;&amp;#039; + &amp;#039;&amp;#039;Find Shortest Path&amp;#039;&amp;#039; SOPSs arent doing what you want. Which was my case, or I might have over-engineered something stupid.&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/uBQPOSL.gif&lt;br /&gt;
&lt;br /&gt;
TBD:&lt;br /&gt;
* Allow for adaptive cone width (if you get increasingly sharper &amp;#039;turns&amp;#039;)&lt;br /&gt;
* Alternatively read from point value (like injecting a sharp change of direction)&lt;br /&gt;
* Give preference to an axis (imagine a tightly wound spiral) so that it still works and doesn&amp;#039;t pick up weird points&lt;br /&gt;
* Give priorities to points that have confidence in the previous points, remove them from the available point list (would correct the example in my video)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    int a[],b[],bigAssPointsArray[];&lt;br /&gt;
    vector dirs[];&lt;br /&gt;
    vector posA,posB;&lt;br /&gt;
    &lt;br /&gt;
// channel values values hit the button to create --&amp;gt;&lt;br /&gt;
    string grp = chs(&amp;#039;group&amp;#039;);&lt;br /&gt;
    float dist = ch(&amp;#039;max_search_distance&amp;#039;);     // 1&lt;br /&gt;
    float cone = radians(ch(&amp;#039;cone_degrees&amp;#039;));   // 10       &lt;br /&gt;
    int iterations = chi(&amp;#039;iteration&amp;#039;);          // 4&lt;br /&gt;
    // V use direction if no second set of point(s) found: &lt;br /&gt;
    vector dir = normalize(chv(&amp;#039;start_dir&amp;#039;));   // 1 0 0&lt;br /&gt;
&lt;br /&gt;
// grp cleaning&lt;br /&gt;
    string error = &amp;#039;&amp;#039;;&lt;br /&gt;
    if(grp != &amp;#039;&amp;#039;){&lt;br /&gt;
        //if we found groups:&lt;br /&gt;
        if(find(grp,&amp;#039; &amp;#039;)&amp;gt;0){&lt;br /&gt;
            //if there is more than one it means we have two separate lists (or two points)&lt;br /&gt;
            string grps[] = split(grp,&amp;#039; &amp;#039;,1);&lt;br /&gt;
            if(len(grps)&amp;gt;2){&lt;br /&gt;
                error = &amp;#039;more than two groups found!&amp;#039;;&lt;br /&gt;
            }else{&lt;br /&gt;
                a = expandpointgroup(0,grps[0],&amp;quot;ordered&amp;quot;);&lt;br /&gt;
                b = expandpointgroup(0,grps[1],&amp;quot;ordered&amp;quot;);&lt;br /&gt;
                if(len(a)!=len(b)){&lt;br /&gt;
                    error = &amp;#039;the two groups have different point counts&amp;#039;;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }else{&lt;br /&gt;
            b = expandpointgroup(0,grp,&amp;quot;ordered&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        error = &amp;#039;at least group or point needed&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    &lt;br /&gt;
    if(error!=&amp;#039;&amp;#039;){&lt;br /&gt;
        error(error);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
// do the work&lt;br /&gt;
    &lt;br /&gt;
    if(len(a)==0){&lt;br /&gt;
        //no second group, fill directions array with initial start dir&lt;br /&gt;
        for(int i = 0 ; i &amp;lt; len(b) ; i ++){&lt;br /&gt;
            append(dirs,dir);&lt;br /&gt;
        }&lt;br /&gt;
    }else{&lt;br /&gt;
        //if there are two destinct groups, create a vector array with initial directions&lt;br /&gt;
        for(int i = 0 ; i &amp;lt; len(b) ; i ++){&lt;br /&gt;
            posA = point( 0 , &amp;#039;P&amp;#039; , a[i] );&lt;br /&gt;
            posB = point( 0 , &amp;#039;P&amp;#039; , b[i] );&lt;br /&gt;
            vector direction = normalize(posB-posA);&lt;br /&gt;
            append(dirs,direction);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    for(int j=0 ; j&amp;lt;len(b) ; j++){&lt;br /&gt;
        //for every point of the second group start finding out if there are points in the dir direction&lt;br /&gt;
        posB = point(0,&amp;#039;P&amp;#039;,b[j]);&lt;br /&gt;
        dir = dirs[j];&lt;br /&gt;
        if(len(a)&amp;gt;0){&lt;br /&gt;
            append(bigAssPointsArray,a[j]);&lt;br /&gt;
        }&lt;br /&gt;
        append(bigAssPointsArray,b[j]);&lt;br /&gt;
        for(int i=1; i&amp;lt;=iterations;i++){&lt;br /&gt;
            int points_found[] = pccone_radius(0,&amp;quot;P&amp;quot;,&amp;quot;none&amp;quot;,0.0,posB,dir,cone,dist,2);&lt;br /&gt;
            if(len(points_found)==1){&lt;br /&gt;
                //if nothing is found during the point cloud cone search, color the last point and break forloop&lt;br /&gt;
                setpointattrib(0,&amp;quot;Cd&amp;quot;,points_found[0],{1,0,0},&amp;quot;set&amp;quot;);&lt;br /&gt;
                break;&lt;br /&gt;
            }else{&lt;br /&gt;
                //we found a point ! use it for the next iteration&lt;br /&gt;
                int chosenpoint = points_found[1];&lt;br /&gt;
                append(bigAssPointsArray,chosenpoint);&lt;br /&gt;
                posA = posB;&lt;br /&gt;
                posB = point(0,&amp;quot;P&amp;quot;,chosenpoint);&lt;br /&gt;
                dir = normalize(posB - posA);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        /* we finished the max iterations, we could add the poly prim in the&lt;br /&gt;
        for loop but houdini doesn&amp;#039;t like it ? And no way to have multi&lt;br /&gt;
        dimensional array so let&amp;#039;s fill up a big ass one and split with&lt;br /&gt;
        a non-existing point number -1 */&lt;br /&gt;
            &lt;br /&gt;
        append(bigAssPointsArray,-1);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
//build the poly lines&lt;br /&gt;
    int pointsArray[] = {};&lt;br /&gt;
    int pr = addprim(0,&amp;#039;polyline&amp;#039;);&lt;br /&gt;
    for(int i=0;i&amp;lt;len(bigAssPointsArray)-1;i++){&lt;br /&gt;
        int pt = bigAssPointsArray[i];&lt;br /&gt;
        if(pt == -1){&lt;br /&gt;
            pr = addprim(0,&amp;#039;polyline&amp;#039;);&lt;br /&gt;
        }else{&lt;br /&gt;
            int newpt = addpoint(0,pt);&lt;br /&gt;
            addvertex(0,pr,pt);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Radial sort ==&lt;br /&gt;
=== sorting ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector bbcenter = getpointbbox_center(0);&lt;br /&gt;
float dx = bbcenter.x-@P.x;&lt;br /&gt;
float dz = bbcenter.z-@P.z;&lt;br /&gt;
@angle = atan2(dx,dz); //radians&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Then sort sop&lt;br /&gt;
=== Galaxy like animation with particles ===&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/ZyzlOia.gif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector bbcenter = set(0,0,0);&lt;br /&gt;
float dx = bbcenter.x-@P.x;&lt;br /&gt;
float dz = bbcenter.z-@P.z;&lt;br /&gt;
float mag = distance(@P,bbcenter);&lt;br /&gt;
@angle = atan2(dx,dz); //radians&lt;br /&gt;
&lt;br /&gt;
float ringmult = snoise(@P);&lt;br /&gt;
ringmult = fit(ringmult,-1,1,.8,1.6);&lt;br /&gt;
&lt;br /&gt;
@t=ringmult;&lt;br /&gt;
@angle += @Time * fit(rand(@ptnum),0,1,.5,.75) * 1 / pow(mag,ch(&amp;#039;exp&amp;#039;)) * ringmult;&lt;br /&gt;
&lt;br /&gt;
@P.x = cos(@angle) * mag;&lt;br /&gt;
@P.z = sin(@angle) * mag;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gradient Lattice ==&lt;br /&gt;
Simple XYZ&amp;lt;&amp;gt;splines deformer &lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/M1XaRdH.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector bb = relbbox(0,@P);&lt;br /&gt;
vector m = getbbox_min(0);&lt;br /&gt;
vector M = getbbox_max(0);&lt;br /&gt;
@P.x = lerp(m.x,M.x,chramp(&amp;quot;x&amp;quot;, bb.r));&lt;br /&gt;
@P.y = lerp(m.y,M.y,chramp(&amp;quot;y&amp;quot;, bb.g));&lt;br /&gt;
@P.z = lerp(m.z,M.z,chramp(&amp;quot;z&amp;quot;, bb.b));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Move points to sdf surface ==&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/cDlX1px.gif&lt;br /&gt;
&lt;br /&gt;
The second input is a number of iterations to refine the stickiness&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//move points towards the surface of the sdf using gradient&lt;br /&gt;
vector p = @P;&lt;br /&gt;
int max = chi(&amp;#039;max&amp;#039;);&lt;br /&gt;
for(int i=0;i&amp;lt;max;i++){&lt;br /&gt;
    vector noise = vector(noise(p)) - .5;&lt;br /&gt;
    noise *= ch(&amp;#039;noise&amp;#039;) ;&lt;br /&gt;
    float reach = volumesample(1,0,p+noise);&lt;br /&gt;
    p -= normalize(volumegradient(1,0,p+noise))*reach*(1.0*(i+1)/max)*ch(&amp;#039;mult&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
@P = p;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Remove geometry that isn&amp;#039;t in the other stream== &lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;better edit&amp;#039;&amp;#039;&amp;#039;&amp;#039;&amp;#039;: keeping the code below if I have an edge case but it&amp;#039;s faster (?) or at least clearer to use idtoprim() or idtopoint(), example here: [[Houdini_Stupid_Questions#How_do_I_work_on_two_objects_that_don&amp;#039;t_have_the_same_number_of_points/prims|How do I work on two objects that don&amp;#039;t have the same number of points/prims?]]&lt;br /&gt;
&lt;br /&gt;
For each prim check that it exists in the second input, if not, delete.&lt;br /&gt;
&lt;br /&gt;
I use this when I have proxy geometry which I have deleted and need the same deletion on the hirez packed geo (so I can use the transform pieces) as shown in the image below&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/zp4sYEe.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(int i=0;i&amp;lt;nprimitives(0);i++){&lt;br /&gt;
    string name = prim(0,&amp;quot;name&amp;quot;,i);&lt;br /&gt;
    if( findattribvalcount(1,&amp;quot;prim&amp;quot;,&amp;quot;name&amp;quot;,name) &amp;lt; 1 ){&lt;br /&gt;
        removeprim(0,i,1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In case of points, let&amp;#039;s say you split the stream, froze one bit at one frame, deleted a few particles, duplicated that setup a few times, here&amp;#039;s one way to take the original stream and only remove particles that don&amp;#039;t exist in the second input (i have special id called uid) &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for(int i=0;i&amp;lt;npoints(0);i++){&lt;br /&gt;
    int uid = point(0,&amp;quot;uid&amp;quot;,i);&lt;br /&gt;
    if( findattribvalcount(1,&amp;quot;point&amp;quot;,&amp;quot;uid&amp;quot;,uid) &amp;lt; 1 ){&lt;br /&gt;
        removepoint(0,i,1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Remove duplicate prims== &lt;br /&gt;
Only keep a single instance of a primitive that has specific attribute values on its corresponding points (think constraints):&lt;br /&gt;
&lt;br /&gt;
Walkthrough:&lt;br /&gt;
* We first retrieve the prim&amp;#039;s points&amp;#039; values and merge them&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//in a prim wrangle&lt;br /&gt;
int pt_ids[] = sort(primpoints(0,@primnum));&lt;br /&gt;
string point_A = point(0,&amp;quot;name&amp;quot;,pt_ids[0]);&lt;br /&gt;
string point_B = point(0,&amp;quot;name&amp;quot;,pt_ids[1]);&lt;br /&gt;
s@linkedpoints = join(sort(array(point_A,point_B)),&amp;quot;+&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* We sort prims by that concatenated attribute (like &amp;quot;nameA+nameB&amp;quot;)&lt;br /&gt;
&amp;#039;Sort&amp;#039; sop by prim attribute &amp;quot;linkedpoints&amp;quot;&lt;br /&gt;
* We finally go through the prims and look if the preceding prim already has that attribute value. If so, we deleted the prim&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//in a detail wrangle&lt;br /&gt;
string previous_linkedpoints = prim(0,&amp;quot;linkedpoints&amp;quot;,0);&lt;br /&gt;
for(int i=1;i&amp;lt;nprimitives(0);i++){&lt;br /&gt;
     string current_linkedpoints = prim(0,&amp;quot;linkedpoints&amp;quot;,i);&lt;br /&gt;
     if( current_linkedpoints == previous_linkedpoints ){&lt;br /&gt;
        removeprim(0,i,1);&lt;br /&gt;
     }&lt;br /&gt;
     previous_linkedpoints = current_linkedpoints;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hanging wire (parabola/catanery)==&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/GDzkzii.png https://i.imgur.com/1Wg0G53.gif&lt;br /&gt;
&lt;br /&gt;
https://en.wikipedia.org/wiki/Catenary&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// takes points and creates hanging &amp;#039;wire&amp;#039; points between said points, using the catenery formula.  &lt;br /&gt;
// Use in a detail wrangle, make sure to hit the &amp;#039;Create spare parameter on the right &amp;gt;&amp;#039; and add &lt;br /&gt;
// values like curve factor 3 and Rez 10 &lt;br /&gt;
&lt;br /&gt;
vector pts[];&lt;br /&gt;
int data[];&lt;br /&gt;
&lt;br /&gt;
float curve = ch(&amp;#039;curve_factor&amp;#039;);                        // &amp;gt; 0, gets close to a flat line when you go above 5  &lt;br /&gt;
int rez = chi(&amp;#039;rez&amp;#039;);                                    //number of points to create between given points (assumes the points are at uniform-ish distances)&lt;br /&gt;
rez+=1;&lt;br /&gt;
&lt;br /&gt;
vector firstpointpos = point(0,&amp;#039;P&amp;#039;,0);&lt;br /&gt;
append(pts,firstpointpos);&lt;br /&gt;
append(data,1);&lt;br /&gt;
removepoint(0,0); &lt;br /&gt;
&lt;br /&gt;
//go through initial points and append catenary coordinates to array with given resolution&lt;br /&gt;
for(int i=1;i&amp;lt;npoints(0);i++){&lt;br /&gt;
    vector a = point(0,&amp;#039;P&amp;#039;,i-1);&lt;br /&gt;
    vector b = point(0,&amp;#039;P&amp;#039;,i);&lt;br /&gt;
    for(int j=1;j&amp;lt;rez;j++){&lt;br /&gt;
        float lerpV = j*1.0/rez;                         // ( 0 &amp;lt; lerpV &amp;lt; 1 ) &lt;br /&gt;
        vector mix = lerp(a,b,lerpV);&lt;br /&gt;
        mix.y = mix.y + curve * cosh((lerpV-.5)/curve);&lt;br /&gt;
        mix.y -= curve * cosh(-.5/curve);&lt;br /&gt;
        append(pts,mix);&lt;br /&gt;
        append(data,0);&lt;br /&gt;
    }&lt;br /&gt;
    append(pts,b);&lt;br /&gt;
    append(data,1);&lt;br /&gt;
    removepoint(0,i);                                   //delete original point&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//create points by reading array, adding group attributes as we go&lt;br /&gt;
&lt;br /&gt;
int newpts[] = {}; &lt;br /&gt;
for (int k = 0; k&amp;lt;len(pts);k++){&lt;br /&gt;
    int newpoint = addpoint(0,pts[k]);&lt;br /&gt;
    append(newpts,newpoint);&lt;br /&gt;
    setpointgroup(0, &amp;quot;original&amp;quot;, newpoint, data[k], &amp;quot;set&amp;quot;);&lt;br /&gt;
    if(data[k]){&lt;br /&gt;
        setpointattrib(0,&amp;quot;Cd&amp;quot;,newpoint,{1,0,0});    &lt;br /&gt;
    }else{&lt;br /&gt;
        setpointattrib(0,&amp;quot;Cd&amp;quot;,newpoint,{0,0,1});    &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//join with polyline&lt;br /&gt;
addprim(0,&amp;quot;polyline&amp;quot;,newpts);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Torus / Helix (Toroidal Helical Coil)==&lt;br /&gt;
https://i.imgur.com/R0QNZx9.png&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
float t = 1.0*@elemnum/@numelem ;&lt;br /&gt;
&lt;br /&gt;
float completion = ch(&amp;#039;completion&amp;#039;) * 2 * $PI;&lt;br /&gt;
float coils = ch(&amp;#039;coils&amp;#039;);&lt;br /&gt;
float R = ch(&amp;#039;outerRadius&amp;#039;);&lt;br /&gt;
float r = ch(&amp;#039;innerRadius&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
float u = t * completion * coils ;&lt;br /&gt;
float v = t * completion ;&lt;br /&gt;
&lt;br /&gt;
float x = cos(v)*(R+r*cos(u));&lt;br /&gt;
float y = sin(v)*(R+r*cos(u));&lt;br /&gt;
float z = r * sin(u);&lt;br /&gt;
&lt;br /&gt;
@P = set(x,y,z);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== List of things to Read ==&lt;br /&gt;
 * Jake rice primUvs / split / greeble: https://github.com/jakericedesigns/Poly-Splitting-Blog&lt;br /&gt;
 * Un-catmullclarking https://dspace5.zcu.cz/bitstream/11025/6630/1/Laquentin.pdf&lt;br /&gt;
&lt;br /&gt;
== Get primitive angles==&lt;br /&gt;
&lt;br /&gt;
https://i.imgur.com/s3TEwHc.png&lt;br /&gt;
&lt;br /&gt;
Calculates the minimum angle (sharp) of each triangle&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//if each prim = triangle&lt;br /&gt;
&lt;br /&gt;
//get the 3 prim points&lt;br /&gt;
int pp[] = primpoints(0,@primnum);&lt;br /&gt;
&lt;br /&gt;
//grab their positions&lt;br /&gt;
vector a = point(0,&amp;quot;P&amp;quot;,pp[0]);&lt;br /&gt;
vector b = point(0,&amp;quot;P&amp;quot;,pp[1]);&lt;br /&gt;
vector c = point(0,&amp;quot;P&amp;quot;,pp[2]);&lt;br /&gt;
&lt;br /&gt;
//get the vectors to calculate angle&lt;br /&gt;
vector ab = b-a;&lt;br /&gt;
vector ac = c-a;&lt;br /&gt;
vector cb = b-c;&lt;br /&gt;
&lt;br /&gt;
//get angles from law of cosines: arccos(Ab.Ac) where Ab and Ac are normalized vectors&lt;br /&gt;
float angleA = degrees(acos(dot(normalize(ab),normalize(ac))));&lt;br /&gt;
float angleB = degrees(acos(dot(normalize(-ab),normalize(-cb))));&lt;br /&gt;
float angleC = 180-angleA-angleB;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
//@angleC = degrees(acos(dot(normalize(-ac),normalize(cb))));&lt;br /&gt;
//@angletotal = @angleA+@angleB+@angleC; // &amp;lt;-- should always be 180!&lt;br /&gt;
&lt;br /&gt;
@minAngle = min(angleA,min(angleB,angleC));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Oriented bounding box matrix transform ==&lt;br /&gt;
(2024 edit: I think this is broken I will look into it, in the meantime there is a labs sop that does the same thing, better)&lt;br /&gt;
Applying https://vimeo.com/214584753 with wrangle: rotates points with given perpendicular vectors&lt;br /&gt;
expects oriented BB from &amp;#039;box&amp;#039; as a second input&lt;br /&gt;
&lt;br /&gt;
http://i.imgur.com/29RqKNQ.png&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vector p0 = point(1,&amp;quot;P&amp;quot;,0);&lt;br /&gt;
vector p3 = point(1,&amp;quot;P&amp;quot;,3);&lt;br /&gt;
vector p4 = point(1,&amp;quot;P&amp;quot;,4);&lt;br /&gt;
&lt;br /&gt;
vector x = normalize(p3-p0);&lt;br /&gt;
vector z = normalize(p4-p0);&lt;br /&gt;
vector y = cross(x,z);&lt;br /&gt;
&lt;br /&gt;
matrix m = set(x[0],x[1],x[2],0,y[0],y[1],y[2],0,z[0],z[1],z[2],0,0,0,0,0);&lt;br /&gt;
@P = invert (m)*@P;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Plexus like effect==&lt;br /&gt;
http://imgur.com/o81IHkV.png&lt;br /&gt;
&lt;br /&gt;
Full copypastable code on http://pastebin.com/raw/nB9GLeiZ&lt;br /&gt;
&lt;br /&gt;
Trails can be done with the entagma tutorial: http://www.entagma.com/creating-geometry-with-vex/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//create an attribute on prims that will store the unique hash so we can remove duplicates later on&lt;br /&gt;
if(!hasprimattrib(0,&amp;#039;hash&amp;#039;))&lt;br /&gt;
{&lt;br /&gt;
    addprimattrib(0,&amp;#039;hash&amp;#039;, 0, &amp;#039;int&amp;#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//fetch the neighbours in an array&lt;br /&gt;
int neighbours[] = nearpoints(0, @P, ch(&amp;#039;radius&amp;#039;), (chi(&amp;#039;neighbours&amp;#039;)+1) )[1:];&lt;br /&gt;
int neighbourCount = len(neighbours);&lt;br /&gt;
&lt;br /&gt;
//only create triangles if there&amp;#039;s more than one neighbour&lt;br /&gt;
if (neighbourCount &amp;gt; 1)&lt;br /&gt;
{&lt;br /&gt;
    //for each neighbour, create all possible triangles with current point (ie for &amp;#039;a&amp;#039; and b,c,d =&amp;gt; abc abd acd)&lt;br /&gt;
    for(int i=0;i&amp;lt;neighbourCount;i++)&lt;br /&gt;
    {       &lt;br /&gt;
        for(int j=i+1;j&amp;lt;neighbourCount;j++)&lt;br /&gt;
        {&lt;br /&gt;
            //create an array with the 3 current points to create a triangle&lt;br /&gt;
            int sortPoints[];&lt;br /&gt;
            sortPoints[0] = @ptnum;&lt;br /&gt;
            sortPoints[1] = neighbours[i];&lt;br /&gt;
            sortPoints[2] = neighbours[j];&lt;br /&gt;
            sortPoints = sort(sortPoints);&lt;br /&gt;
            &lt;br /&gt;
            //create triangle&lt;br /&gt;
            int prim = addprim(0,&amp;#039;poly&amp;#039;);&lt;br /&gt;
            addvertex(0,prim,sortPoints[0]);&lt;br /&gt;
            addvertex(0,prim,sortPoints[1]);&lt;br /&gt;
            addvertex(0,prim,sortPoints[2]);&lt;br /&gt;
            &lt;br /&gt;
            //generate a &amp;#039;hash&amp;#039; of the triangle prim, so we can remove duplicates later on&lt;br /&gt;
            //for points 1,3,0 it will be hash(013) --&amp;gt; 67429030&lt;br /&gt;
            int rhash = random_ihash(atoi(itoa(sortPoints[0])+itoa(sortPoints[1])+itoa(sortPoints[2])));&lt;br /&gt;
            setprimattrib(0, &amp;#039;hash&amp;#039;, prim, rhash, &amp;#039;set&amp;#039;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Bernie</name></author>
	</entry>
</feed>