python

fcp 7 to nuke

Wed 9, May 2012

Scenario
A FCP 7 timeline with 20 shots in it (all in one video layer).
I need to comp them all separately in nuke.
I want to have each shot in a separate numbered directory, with subdirectories for “scripts” and “renders”

Here is a little python script i wrote this morning to export a basic FCP 7 timeline as a series of nuke scripts into numbered shot directories.
This script parses an XML file that I exported from FCP 7 (the sequence/edit).

I have only used this for the purposes i needed, but it works.
Perhaps it could be expanded to incorporate multiple video layers.

Each nuke script that is generated is just a read node, with the nuke.root frame range set to the duration of the clip in FCP 7.

Even if the script doesn’t help everyone’s fcp7-nuke needs I found that i learnt a lot about Python’s elementTree XML library. Nice clean simple.
And also this page was a big help to get things going:
http://drumcoder.co.uk/blog/2010/jun/17/using-elementtree-python/

The script needs to be run from a shell/terminal:
python /path/to/the/script/fcpNuke.py /path/to/you/xmlfile.xml

*file re-uploaded 11 May 2012

The script:
fcp7toNuke (107)

some Nuke Python snippets

Tue 11, May 2010

If anyone has some cool snippets to share please feel free to leave them in a comment below.

*last updated: 18 December 2013

take a RotoPaint and change each layer’s RGBA to a grey level based on it’s “depth” – useful for prep’ing for a stereo conversion i’d imagine

import random
a = nuke.selectedNode()
if a.Class() == "RotoPaint":
    knob = a['curves']
    i = 0.0
    for c in knob.rootLayer:
 
        pos = float ((len(knob.rootLayer) - i) / len(knob.rootLayer))
        i = i+1
 
        attrs = c.getAttributes()
        attrs.set(1, attrs.kRedOverlayAttribute, pos)
        attrs.set(1, attrs.kRedAttribute, pos)
        attrs.set(1, attrs.kGreenOverlayAttribute, pos)
        attrs.set(1, attrs.kGreenAttribute, pos)
        attrs.set(1, attrs.kBlueOverlayAttribute, pos)
        attrs.set(1, attrs.kBlueAttribute, pos)
        attrs.set(1, attrs.kAlphaOverlayAttribute, pos)
        attrs.set(1, attrs.kAlphaAttribute, pos)

for all selected Tracker nodes multiply their track values by a factor of 4

for a in nuke.selectedNodes():
    if a.Class()=="Tracker3":
 
	def scaleMe(track):
		if track.isAnimated():
			for j in range (0,2):
		        anim = track.animation(j)
		        keys = anim.keys()
		        scaleFactor = 4
		        while keys:
		            keys.pop().y *= scaleFactor
	#i am sure there is a much nicer way of iterating over all the knobs but this worked for what i quickly needed
	t1 = a['track1']
	t2 = a['track2']
	t3 = a['track3']
	t4 = a['track4']
	scaleMe(t1)
	scaleMe(t2)
	scaleMe(t3)
	scaleMe(t4)

set bbox to “B” for nodes inside all Group nodes

import nuke
 
def bboxGroup():
  for b in nuke.allNodes():
    if b.Class() == "Group":
      for a in nuke.toNode(b.name()).nodes():
        classTypes = ['Merge' , 'Keymix', 'Copy', ]
        print a.name()
        for n in classTypes:
          if n in a.Class():
            try:
              for p in a['bbox'].values():
                if 'B' in p:
                  a['bbox'].setValue(a['bbox'].values().index(p))
            except:
              pass

“Autowrite” node
Copy the following line into the python “beforeRender” field in a write node.
The write node’s “file” field will be filled based on the script’s name/path.
Obviously this all depends on your pipeline etc.

For my current situation each vfx shot has it’s own directory, which is then populated with “renders” & “scripts” subdirectories.
So for me I can do this:

nuke.thisNode()["file"].setValue(nuke.root().name().replace("scripts","renders").replace(".nk",".mov"))

delete all nodes that are not selected

s = nuke.selectedNodes()
b = nuke.allNodes()
 
for n in b:
    if n not in s:
        nuke.delete(n)

selects all dependencies (input nodes & their parents) from a selected node

a = nuke.selectedNode()
nodesToSelect = []
 
nodesToSelect.append(a)
def climb(node):
    # print node.name()
    for n in node.dependencies():
        nodesToSelect.append(a)
        climb(n)
 
climb(a)
 
for x in nodesToSelect:
	print x.name()
    # x.setSelected(1)

set all Read nodes to cache locally

for a in nuke.allNodes():
    if a.Class()=='Read':
        a['cached'].setValue(1)
        a['cacheLocal'].setValue(0)

print last frame of script

print nuke.root()['last_frame'].value()

create a backdrop based on selected Nodes

margin = 100
xpMax = nuke.selectedNode().xpos()
xpMin = nuke.selectedNode().xpos()
ypMax = nuke.selectedNode().ypos()
ypMin = nuke.selectedNode().ypos()
 
for a in nuke.selectedNodes():
    if a.xpos() > xpMax:
        xpMax = a.xpos()
    if a.xpos() < xpMin:
        xpMin = a.xpos()
    if a.ypos() > ypMax:
        ypMax = a.ypos()
    if a.ypos() < ypMin:
        ypMin = a.ypos()
 
bd = nuke.nodes.BackdropNode(bdwidth=(xpMax-xpMin)+margin, bdheight=(ypMax-ypMin)+margin) 
bd.setXpos(xpMin-margin/2)
bd.setYpos(ypMin-margin/2)

disable “postage stamps” on only “Read” nodes

for a in nuke.allNodes():
   if a.Class()=='Read':
       a['postage_stamp'].setValue(0)

disable "postage stamps" on all nodes

for a in nuke.allNodes():
    try:
        a['postage_stamp'].setValue(0)
    except:
        pass

"unhide" all nodes' inputs - useful when receiving a sneaky comp/lighting script

for a in nuke.allNodes():
    try:
        a['hide_input'].setValue(0)
    except:
        pass

change the "first" frame of all selected nodes that are "Read" nodes:
(example changes the first frame to 1018)

for a in nuke.selectedNodes():
    if a.Class() == 'Read':
        a['first'].setValue(1018)

print a selected nodes' methods

import struct
node = nuke.selectedNode()
for a in node['lookup'].animations():
    print dir(a)

print inputs (dependencies) of a selected node:

for a in nuke.selectedNode().dependencies():
    print a.name()

print outputs (dependents) of a selected node:

for a in nuke.selectedNode().dependent():
    print a.name()

find all the TimeOffset nodes in a Group called "Group2", and change the value of each offset based on it's position in the array of found time offsets

tos = []
for a in nuke.toNode('Group2').nodes():
	if a.Class()=='TimeOffset':
		tos.append(a)
for b in tos:
	b['time_offset'].setValue(tos.index(b))

set the 'bbox' for any selected Merge, Keymix & Copy nodes to "B"

for a in nuke.selectedNodes():
	classTypes = ['Merge' , 'Keymix', 'Copy', ]
	for n in classTypes:
		if n in a.Class():
			for p in a['bbox'].values():
				if 'B' in p:
					a['bbox'].setValue(a['bbox'].values().index(p))

remove all animation from a selected nodes

for a in nuke.selectedNode().knobs():
	nuke.selectedNode()[a].clearAnimated()

add keyframes - animate a mix

for a in nuke.selectedNodes():
	a['mix'].setAnimated()
	a['mix'].setValueAt(1,nuke.frame())
	a['mix'].setValueAt(0,(nuke.frame() - 1))

half the colour value of all the Constant nodes in a script

for a in nuke.allNodes():
	if a.Class() == "Constant":
		a['color'].setValue(a['color'].value()[0] / 2 , 0)
		a['color'].setValue(a['color'].value()[1] / 2 , 1)
		a['color'].setValue(a['color'].value()[2] / 2 , 2)

find all the transform nodes in a script, and if their input is a Crop, set the 'scale' value to be twice it's current value (also checks if the scale is a list/array or a float)

for a in nuke.allNodes():
	if a.Class() == "Transform":
		if a.input(0).Class() == "Crop":
			x = a['scale'].value()
			if type(x).__name__ == 'list':
				a['scale'].setValue(x[0] * 2 , 0)
				a['scale'].setValue(x[1] * 2 , 1)
			if type(x).__name__ == 'float':
				a['scale'].setValue(x*2)

set all the gain values of all ColorCorrect nodes to be twice their current value

for a in nuke.allNodes():
	if a.Class() == "ColorCorrect":
		a['gain'].setValue(a['gain'].value() * 2)

print files with 'mov' in filename

for a in nuke.allNodes():
	if 'Read' in a['name'].value():
		if 'mov' in a['file'].value():
			print a['file'].value()

change font size of all "write" nodes in script

for a in nuke.selectedNodes():
	if "Write" in a['name'].value():
		a['note_font_size'].setValue(60)

create 20 constants with incrementing colour values

def makeConstants(amount):
	for i in range(amount):
		a= nuke.nodes.Constant()
		color= float( float(i) / float(amount) )
		a['color'].setValue(color)
makeConstants(20)

change the name of all Text nodes to contents the text "message"

for a in nuke.allNodes():
    if a.Class()=='Text':
        a.setName(a['message'].value())

##Expressions
(in an expression node) - alpha channel - if the y value of the pixel is even, make it 0, else make it 1 (for viewing fields?)
y%2==1?0:1

Learning Obj-C

Sat 30, May 2009

Yesterday I decided I have to give up on PyObjC for the moment and step into the world of Objective-C.

I have got to a point in developing SubIt that I can’t seem to get enough out of the PyObjC bridge to be able to get what SubIt needs.

So now I am in the process of rewriting SubIt in Obj-C.

Obj-C seems weird and complicated and I can’t see why it has to be this way when Python/Ruby seem just so much much easier to understand. Oh well, I have taken the plunge.

I spent most of yesterday reading a great and easy to follow PDF called “Become An Xcoder“, available for free download:
http://www.cocoalab.com/?q=becomeanxcoder

I have been looking at a bunch of podcasts/videos/books and to me this PDF was the quickest/easiest way to get a little up to speed.

Project: Subtitle App

Mon 23, March 2009

My new project – create a mac app that allows a user to view a video and create a “subtitle” text file on the fly.

Update: I created a dedicated page for SubIt – my attempt at creating a SubTitle file creator application with PyObjC

distributed rendering using Apple Qmaster 3 – success!!!

Wed 27, February 2008

qmaster3logo


Thanks to a fair amount of googling and a few days on and off of testing I seem to have a working setup for doing distributed processing using Apple Qmaster – for Shake, Maya and Compressor.

Here is my effort at explaining what I did to get it working – big apologies, it’s poorly written. I’ll revisit this post soon.

some notes on our workflow

  • Our current workflow for dealing with files/assets evolves around a directory structure that breaks each project up into shot numbers and the various departments we have etc..
  • Our xserve is the centralised area for our assets and the QMaster render manager.
  • Each artist’s local machine has a directory somewhere that mimics the xServe’s project directory structure for working locally (to keep network traffic down over gigabit ethernet).
  • I set up $JOBS_LOCAL and $JOBS_SERVER env variables on each machine and the xserve – these variables point to the relevant local project directory on the artist’s mac and the project directory on the server.
  • I created a python script that does a find and replace of the 2 variables and writes our a new shake script renamed “*_SERVER.shk”, or “*_LOCAL.shk”
  • (See further down for setting up the ENV variables.)

    Centralised $NR_INCLUDE_PATH
    I setup an env variable for $NR_INCLUDE_PATH for all Shake machines and the xserve – to look at a sharepoint (the nreal folder) on the xServe and automatically mount it – so all the Shake machines would be using the same macros/plugins and settings. I setup a new user on the xserve “shake” that can only mount the nreal directory.

    After some googling around I found a way to automount volumes:

    OS X 10.5 (fstab)
    /etc/fstab – to automount sharepoint on xServe
    LINK: http://blogs.sun.com/lowbit/entry/easy_afp_autmount_on_os

    # ————————————————————————————-
    # Mount AFP share from xServe via AFP
    # ————————————————————————————-
    ourserver.local:/nreal /Network/nreal url auto,url==afp://nreal:password@ourserver.local/nreal 0 0
    # ————————————————————————————-

    how to refresh automount(login as root/su):

    sudo automount -cv

    OS X 10.4 (netinfo manager)
    LINK: http://ask.metafilter.com/54223/Help-me-automount-my-smb-share-in-Apple-OS-X-reward-inside
    in terminal.app:

    nidump fstab . > /tmp/fstab
    echo “ourservernreal /Network/nreal url url==cifs://nreal:password@ourserver/nreal 0 0″ >> /tmp/fstab
    sudo niload fstab . < /tmp/fstab
    sudo killall -HUP automount

    how to refresh automount(login as root/su):

    sudo killall -HUP automount

    QMASTER
    I had tried for some time to get shake scripts to render over our network using Qmaster but it just wouldn’t work. The QMaster logs were where I found all my errors. ‘frame 0021 could not be found’, ‘UNIX error 3 file could not be found’

    things to check

  • shake / maya / qmaster 3 node is installed on all render machines
  • the location of all your media can be accessed by all machines
  • What seemed strange was that if I logged into the xServe and executed a script via terminal with shake (to render just on the xserve) the render would complete successfully. Then it clicked that maybe the environment variables I was using in my scripts ($xxx) might not be getting recognised by Qmaster or the way Qmaster launches Shake??

    The big tip-off
    I googled for the errors I kept seeing and luckily enough this forum post popped up:
    http://www.highend3d.com/boards/index.php?showtopic=204342

    “I have pinned this down to at least one reason – that the shake qmaster isn’t picking up the NR_INCLUDE_PATH environment variable. Does anyone know where you need to set this up on a cluster node (I can get the qmasterd to pick it up but that doesn’t solve the problem!) “

    If you are trying to use Qmaster, and need to set environment variables, then you need to create a wrapper script that sets the variables and then calls the appropriate version of shake.

    For example, (this was from Apple)

    NR_INCLUDE_PATH=/Network_Applications/Shake Plugins/nreal/include;export NR_INCLUDE_PATH
    NR_ICON_PATH=/Network_Applications/Shake Plugins/nreal/icons;export NR_ICON_PATH

    umask 000

    /Applications/Shake3.50/shake.app/Contents/MacOS/shake $*
    status=$?
    if [ $status -ne 0 ]
    then
    exit $status
    fi

    Then when using Qmaster, you run the application using this
    script (saved for example as /usr/bin/shakeWrapper) which must be
    installed on all nodes in the cluster.

    Regards
    Nell

    Cheers Nell!

    I took Nell’s script, added a few lines to it and stuck it in /usr/bin/

    /usr/bin/shakeWrapper – create a file to launch Shake respecting ENV variables – later alias ‘shake’ to this file

    echo
    echo “Shake 4 running through a wrapper script – /usr/bin/shakeWrapper”
    echo

    umask 000

    /Applications/Shake/shake.app/Contents/MacOS/shake $*
    status=$?
    if [ $status -ne 0 ]
    then
    exit $status
    fi

    I added the first few lines there so when I later made an alias to this script the Shake user would have some idea what is going on when launching Shake via the terminal.

    Setting up the ENV (environment) variables…

    /etc/profile – to declare system-wide Environment variables / aliases
    (alias shake to use a wrapper to make it launch respecting/recognising the env variables)

    # System-wide .profile for sh(1)

    if [ -x /usr/libexec/path_helper ]; then
    eval `/usr/libexec/path_helper -s`
    fi

    if [ "${BASH-no}" != "no" ]; then
    [ -r /etc/bashrc ] && . /etc/bashrc
    fi

    JOBS_LOCAL=”/Volumes/otherdrive/jobs”;
    export JOBS_LOCAL

    JOBS_SERVER=”/Volumes/ourserversharepoint/jobs”;
    export JOBS_SERVER

    NR_INCLUDE_PATH=”$HOME/nreal/include”:”/Network/nreal/include/”;
    export NR_INCLUDE_PATH

    alias shake=”/usr/bin/shakeWrapper”

    *remember to enter into terminal:
    source /etc/profile

    some Python & SSH remote machine coolness

    Fri 23, November 2007

    And now for a bit of self taught nerdy goodness…

    I just managed to get something kind of cool working.

    We have a cool tool in the pipeline, but in the meantime my hacked together python efforts will have to do! ;)

    A few months ago I created a python script for building ‘job’ directories – for new jobs that came into the building – a script that runs in a shell.

    I then learnt about wxpython and turned the script into a GUI app.

    This was cool to run on my machine, but I needed a way for it to be used by other artists / producers.

    We got an xServe and some disk storage so I put the script onto the xServe. I was able to log in via VNC and run the script to create the job directories that everyone could access. This gave us all a place to put files in a structured manner and not let things get too messed up (thanks to file permissions).

    But the problem was that for me to execute the script, I’d have to VNC or SSH into the server. I didn’t mind doing it, but it was not so useful when I wasn’t around.

    So back to this ‘new job’ script….

    I just worked out how to setup a producer’s mac to be able to “remotely execute the python ‘new job’ script on our xServe” using SSH and python.

    All a producer has to do is type into Terminal:

    newjob

    and it all starts up. The producer is asked the ‘name of the job’ and ‘the number of shots’ and the script goes ahead and builds all the directories.

    How I did it

    Getting into the xServe.
    I realised that I would not be able to do too much to the file structure of the xServe’s file system if I was to simply mount it on a Desktop – due to permissions etc… and I also didn’t want ‘the user’ to have to mount anything manually. It needed to brainless. SSH seemed the best way.

    I found out pretty quickly that there wasn’t an easy way to get out of entering the ‘admin password’ for the xServe to remotely execute the script (you have to enter a password when you ssh into another machine).

    I didn’t want to give away our admin password for the xserve. So I read up on SSH and discovered SSH-Key’s.
    A cool way to authenticate your machine to the remote machine without having to enter the ‘login password’ for the remote user. Instead you give the SSH connection a ‘passphrase’. Initially you have to login to the remote machine with the remote password, but after that it’s all ‘ about ‘enter your passphrase for key’.

    To set up the RSA key for your local machine I followed this easy to read tutorial from IBM.

    Basically you need to generate a public key and add that to the list of ‘known computers’ on the remote machine.

    To generate the public key (according to the IBM article) run this command in a shell:

    $ ssh-keygen -t rsa

    and follow the prompts.

    You can choose to not have a passphrase, but that seemed a little too much of a security risk for our setup.
    Next I got a bit confused, the command spat out 2 files into a ‘.ssh’ directory in your home folder:

    id_dsa
    id_dsa.pub

    These are the default names and location if u DON’T enter a name for the key.

    To get the public key (the .pub file) into the list of known machines on the xServe I mounted the xServe’s Admin’s home folder on the local machine.

    I got into the .ssh directory in the home folder of the xserve, and went looking for the file:

    .ssh/authorized_keys

    but couldn’t see it.

    So i created a file called ‘authorized_keys’ using the following command:

    touch authorized_keys

    Then to add the public key I ran a ‘cat’ command to append the public key file to the ‘authorized_keys’ file on the server.

    It worked! I was able to login the the xServe using the passphrase. Shweet!

    I found this post on an apple forum also very helpful.

    Executing the script

    On the remote machine / xServe is where the python script lives – in a folder called ‘scripts’ in the home folder.
    To run this script in a shell on the xServe I would type:

    python /Path/to/the/script/newjob.py

    So I figured from the local machine the command to execute using ssh would be:

    ssh admin@ourserver.local python /Path/to/the/script/newjob.py

    And that did work.

    But that was wayyyyyy too long for or our Producer to have to type up.

    So I made bash alias in the bash_profile:

    alias newjob ssh admin@ourserver.local python /Path/to/the/script/newjob.py

    i tried the alias, and it connected, but it didn’t seem the script was doing anything. But it was. I couldnt see the interactive text prompting me to enter a ‘jobname’ or ‘amount of shots’, but it WAS asking for it.

    Why couldn’t I see the text? I was stumped. Why wasn’t the shell showing me the feedback from the xServe?

    I read the man pages of SSH and found out a bunch of options you can pass the ssh command. Somehow I came across this:

    -t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.

    As soon as I put that into the command it all worked!

    ssh -t admin@ourserver.local python /Path/to/the/script/newjob.py

    The last thing I did to make this even easier was to create a ‘.command’ file in the OSX Finder, that can just be double clicked and it will open Terminal.app and run the above command.

    rsync

    Tue 25, September 2007

    life is good with rsync.

    I have been playing around with a python script using rsync to create an easy way for syncing new material from our server to local machines (it can be a pain in the ass to navigate folders looking for the new stuff) – keeping the folder structure etc… – and most importantly not destroying anything.

    I’d never really understood how to use it – thinking it was only for local to remote transfers – but it works on anything. Brilliant!

    RSYNC rocks!

    Python script to get all maximum values in an image sequence

    Fri 14, September 2007

    Yesterday i fudged together a python script to use Shake and create an image that is the maximum values of that sequence.

    The output of the python script is a Shake script that contains as many FileIns as there are images in the sequence – each one slipped one frame more than the previous. Does that make any sense?

    Each image is plugged into a Max layer node and then that is plugged into the next Max layer node etc….

    So depending on the length of your sequence the Shake script can be enormous!

    Python is awesome.



    shake_maxscript_image

    Here is the script:
    shake max python script (796)

    “snake eyes” or the story of how I have spent too much time with a computer Python.

    Tue 29, May 2007

    Catchy title? ok, just lame…

    I have finally made some steps with 1 progamming language. Years ago some good folks at Emerald City Design said I should try get into the programming language Python. I never got very far and just couldn’t grasp the concept. Since then I opened a book on Objective C and had a serious brain fade.

    Luckily I got the hang of some basic Shell scripting and started to feel a little better about the whole speaking computer thing.
    Then the other day I wanted to recreate some sort of tool I saw at a few post houses I’ve worked at. A “new job” creation tool – so when a new job for VISA card comes in and you need to setup 50 folders for it with all the sub directories etc… you can just punch in VISA and 50 folders and it will be made.

    Well I have a very basic version that seems to work fine from a shell.
    I have also got a basic version running in a GUI using the Tkinter modules (are they modules? i still have no idea).
    I have no user error things setup so if the user types in they’d like BANANA shots instead of 34 then the scripts will probably get all confused.
    Also I wanted to test out an if/then statement, so i used it to change the number padding of the shot folder names depending on how many shots you needed – very pointless and not cool – but it works

    NewJob_GUI.py – v1
    NewJob_script.py – v1

    (WordPress user note – if you want to upload files using the ‘inline upload’ when writing posts, certain files aren’t allowed – like “.zip” – so get THIS PLUGIN)

    I’m about to try compile the script so it can be run by itself. This is my lead so far:
    http://effbot.org/zone/python-compile.htm

    Anyway, it’s a start…