View difference between Paste ID: ry7Spm5x and Vs7FvV66
SHOW: | | - or go back to the newest paste.
1
'''
2
FCP XML to Nuke
3
v1.4 - 
4
	Fixed a bug handling XMLs with multiple sequences.
5
v1.3 - 
6
	Made support for Premiere FCPXML format more robust. Premiere FCP XML stores all sequences in the project, whereas
7
Final Cut Pro's XML format stores only a single sequence. I added a box that lets you choose what sequence 
8
of the Premiere XML file to process, and it should work more reliably now.
9
	Added support for transfer of linear TimeRemaps, Translates, Scales, and Rotates into Nuke.
10
	Also transfers framerate for each clip, and many other improvements.
11
v1.0 - Initial release.
12
13
This script takes a Final Cut XML file with a single flattened video track, and builds Nuke scripts for each clip in the timeline.
14
It is intended as a simple way to automate workflows between FCP/Premiere and Nuke.
15
It creates a Nuke script with global first and last frame set, a frameRange node with the proper framerange, and a Write node
16
set to the output path.
17
There is an option for creating subdirectories for every Nuke script created. Handles are also an option.
18
It can parse reel number and clip number from Red and Alexa footage, or can use the clip filename as the base naming for the output files.
19
20
This script was somewhat inspired by compflows.blogspot.com, but has been written from scratch and is a bit more flexible (although it only goes from XML->NukeScripts and not from renders back to an XML at the moment).
21
This has only been tested on OSX, but in theory should be cross-platform compatible. Comments and suggestions are welcome!
22
23
# The menu.py example entry below adds this script in a folder called "Scripts" in your toolbar.
24
import fcpxml_to_nuke
25
nuke.toolbar('Nodes').addMenu('Scripts').addCommand('FCP XML to Nuke', 'fcpxml_to_nuke.process_xml()')
26
'''
27
28
import nuke, os
29
from xml.dom.minidom import parse
30
31
def process_xml():
32
	'''
33
	Imports an FCP XML file, locates each clip in the timeline on Track 1, 
34
	and for each clip, builds a nuke script for that file and puts it in the output directory specified
35
36
	New Features that would be nice to have: 
37
	Customized naming patterns based on reel/clip number.
38
	Handle FCP XML from Premiere or FCP / FCPX ( Are there differences in the XML structure for these? )
39
	Choose additional output naming and directory formatting patterns
40
	'''
41
42
	# Build the Nuke Panel where locations and stuff is specified.
43
	p = nuke.Panel("FCP XML Import")
44
	xml_file = 'FCP XML To Import'
45
	output_dir = 'Directory to Output Nuke Scripts'
46
	subdirectories = 'Create subdirectories for each script?'
47
	render_dir = 'Render Directory for All Write Nodes'
48
	handle_length = "0 5 10 15 20 30 35 40"
49
	clip_name_format = "Bypass RED Alexa"
50
51
	p.addFilenameSearch("FCP XML File", xml_file)
52
	p.addBooleanCheckBox("Create subdirectories for each script", subdirectories)
53
	p.addFilenameSearch("Output Directory", output_dir)
54
	p.addFilenameSearch("Render Directory", render_dir)
55
	p.addEnumerationPulldown("Handle Length", handle_length)
56
	p.addEnumerationPulldown("Clip Name Format", clip_name_format)
57
	p.setWidth(600)
58
	if not p.show():
59
		return
60
61
	# Assign vars from Nuke Panel user-entered data 
62
	xml_file 	= p.value("FCP XML File")
63
	output_dir	= p.value("Output Directory")
64
	subdirectories = p.value("Create subdirectories for each script")
65
	render_dir 	= p.value("Render Directory")
66
	handle_length = int( p.value("Handle Length") )
67
	clip_name_format = p.value("Clip Name Format")
68
69
	# Create paths for render directory if it does not exist
70
	if not os.path.isdir(render_dir):
71
		os.mkdir(render_dir)
72
	if not os.path.isdir(output_dir):
73
		os.mkdir(output_dir)
74
75
76
	############################
77
	# Begin Parsing XML File
78
	############################
79
	dom = parse( xml_file )
80
81
	# Prompt user to choose which sequence to process, because Premiere's XML export includes all sequences.
82
	# Much of this complexity is to handle sequences with spaces in their names, because the nuke.panel
83
	# addEnumerationPulldown is space-demarcated. The logic below handles spaces in sequence names and allows the user 
84
	# to choose which sequence to process, and stores the sequence to process as a variable to access
85
86
	sequences = []
87
	sequence_objects = []
88-
		seqname = seq.getElementsByTagName('name')[0].firstChild.data
88+
89-
		sequences.append( [seqname] )
89+
90-
		# If a sequence name has space characters, replace them with underscores for the Nuke enumeration pulldown panel
90+
91-
		if seqname.find(' ') != -1:
91+
92-
			sequences[i].append( seqname.replace(' ', '_') )
92+
			seq.getElementsByTagName('uuid')[0].firstChild.data #Catches all sequence objects with a uuid. These are edits.
93
			print "sequence name is:", seq.getElementsByTagName('name')[0].childNodes[0].data
94
			seqname = seq.getElementsByTagName('name')[0].firstChild.data
95
			sequences.append( [seqname] )
96
			sequence_objects.append( seq )
97
			# If a sequence name has space characters, replace them with underscores for the Nuke enumeration pulldown panel
98
			if seqname.find(' ') != -1:
99
				sequences[i].append( seqname.replace(' ', '_') )
100-
		i = 0
100+
101-
		for seq in sequences:
101+
			print i, " is not a sequence!"
102
			continue
103
		i += 1
104
105
	print "Sequences are: ", sequences
106-
			i += 1
106+
107
	# but only if there is more than one sequence in the XML file. If not, we'll just set the 'row' var to 0, and roll with that.
108
	if len(sequences) > 1:
109
		# Makes a " " demarcated string with all sequence names for the nuke enumeration pulldown panel
110
		seq_enum = ''
111
		for i, seq in enumerate( sequences ):
112
			if len(sequences[i]) == 1:
113
				seq_enum += sequences[i][0] + ' '
114
			else:
115
				seq_enum += sequences[i][1] + ' '
116
117
		# Create a Nuke panel for the user to choose which sequence to process
118
		seq_panel = nuke.Panel("Choose Sequence To Process")
119
		seq_panel.addEnumerationPulldown("Choose Sequence", seq_enum)
120
		seq_panel.setWidth(400)
121
		if not seq_panel.show():
122
			return
123
		chosen_sequence = seq_panel.value("Choose Sequence")
124
125-
	chosen_seqobj = dom.getElementsByTagName('sequence')[row]
125+
126-
	print "Chosen sequence is: ", chosen_seqobj.getElementsByTagName('name')[0].firstChild.data
126+
127
				try:
128
					column = i.index( chosen_sequence )
129
				except ValueError:
130
					continue
131
				break
132
	else:
133
		row = 0
134
	#chosen_seqobj = dom.getElementsByTagName('sequence')[row]
135
	chosen_seqobj = sequence_objects[row]
136
	print "Chosen sequence is number ", row, chosen_seqobj.getElementsByTagName('name')[0].firstChild.data
137
138
	seq_res_x = int( chosen_seqobj.getElementsByTagName('format')[0].getElementsByTagName('width')[0].firstChild.data )
139
	seq_res_y = int( chosen_seqobj.getElementsByTagName('format')[0].getElementsByTagName('height')[0].firstChild.data )
140
	print "Sequence resolution is ", seq_res_x,"x",seq_res_y
141
142
	# Set optional effect parameters to False
143
	timeremap_value = False
144
	scale_value = False
145
	x_move = False
146
	y_move = False
147
	rotation_value = False
148
	fps = 97
149
150
	seq_clip_number = 1
151
	track = chosen_seqobj.getElementsByTagName('track')[0]
152
	for clip in track.getElementsByTagName('clipitem'):
153
		# This loop performs the following for each clip on the first Track of the chosen Sequence.
154
		masterclipid 	= clip.getElementsByTagName('masterclipid')[0].firstChild.data
155
		clip_name 		= clip.getElementsByTagName("name")[0].firstChild.data
156
		in_point 		= int( clip.getElementsByTagName('in')[0].firstChild.data )
157
		out_point 		= int( clip.getElementsByTagName('out')[0].firstChild.data )
158
		clip_duration 	= int( clip.getElementsByTagName("duration")[0].firstChild.data )
159
		
160-
			print 'Failed to get pathurl in current clipitem. Searching other clipitems for matching name.'
160+
161
		# This is necessary because in Premiere XMLs, the pathurl for a clip is not always stored in the clipitem node, but rather in a seperate node in the master-clip
162
		
163
		#??? Instead: Check for pathurl node in current clip node. If it doesn't exist, cycle through all 
164
		# clipitem nodes to find another that matches the name node with the current clip_name.
165
		# If it finds a matching named clipitem, search for a pathurl in that node.
166
		try:
167
			file_path = clip.getElementsByTagName('pathurl')[0].firstChild.data.split("file://localhost")[1].replace("%20", " ")
168
			fps = float(clip.getElementsByTagName('timebase')[0].firstChild.data)
169
		except:
170
			print 'Failed to get pathurl in clipitem', clip_name, '. Searching other clipitems for matching name.'
171
			for pathurl_clip in dom.getElementsByTagName('clipitem'):
172
				if pathurl_clip.getElementsByTagName('name')[0].firstChild.data == clip_name:
173
					try:
174
						file_path = pathurl_clip.getElementsByTagName('pathurl')[0].firstChild.data.split("file://localhost")[1].replace("%20", " ")
175
						fps = float(pathurl_clip.getElementsByTagName('timebase')[0].firstChild.data)
176
						break
177
					except:
178
						continue
179
		print clip_name, in_point, out_point, clip_duration, file_path, fps
180
181
		# Get resolution of this clip in the clipitem node, 
182
		# Else, look for a masterclip with the same name and try to get the resolution from there, Else: fail?
183
		try:
184
			clip_width = int( clip.getElementsByTagName('width')[0].firstChild.data )
185
			clip_height = int( clip.getElementsByTagName('height')[0].firstChild.data )
186
			print "Clip resolution is: ", clip_width, "x", clip_height
187
		except:	
188
			for masterclip in dom.getElementsByTagName('clipitem'):
189
				if masterclip.getElementsByTagName('name')[0].firstChild.data == clip_name:
190
					#!!! This is triggered if a clip is used more than once in the sequence.
191
					try:
192
						#print "found master clip: ", masterclip.getElementsByTagName('name')[0].firstChild.data, " and "
193
						clip_width = int( masterclip.getElementsByTagName('width')[0].firstChild.data )
194
						clip_height = int( masterclip.getElementsByTagName('height')[0].firstChild.data )
195
						break
196
					except:
197
						continue
198
		# Get all effects applied to this clip
199
		for effect in clip.getElementsByTagName('effect'):
200
			effect_name = effect.childNodes[1].firstChild.data
201
			if effect_name == 'Time Remap':
202
				# Loop through all parameters of the effect
203
				for param in effect.getElementsByTagName('parameter'):
204
					param_id = param.childNodes[1].firstChild.data
205
					if param_id == 'speed':
206
						timeremap_value = float( param.getElementsByTagName('value')[0].firstChild.data )
207
						print effect_name, param_id, timeremap_value
208
209
			if effect_name == 'Basic Motion':
210
				for param in effect.getElementsByTagName('parameter'):
211
					param_id = param.childNodes[1].firstChild.data
212
					if param_id == 'scale':
213
						scale_value = float( param.getElementsByTagName('value')[0].firstChild.data )
214
						print effect_name, param_id, scale_value
215
					if param_id == 'rotation':
216
						rotation_value = float( param.getElementsByTagName('value')[0].firstChild.data )
217
						print effect_name, param_id, rotation_value
218
					if param_id == 'center':
219
						x_move = float( param.getElementsByTagName('value')[0].childNodes[1].firstChild.data )
220
						y_move = float( param.getElementsByTagName('value')[0].childNodes[3].firstChild.data )
221
						print effect_name, param_id, x_move, y_move
222
						'''
223
						So.... Figuring out how Premiere handles position values:
224
						Prem 0-0 clip is centered upper left: value = .5,.5
225
						prem 1920-1080, clip is centered lower right: value = .5 .5??
226
						prem 1060-640, value: 0.052083 0.092593
227
						prem center bottom: 960-1080, value: 0.0 0.5
228
						prem center top: 960 0, value: 0.0 -0.5
229
						prem UR 1919 0, value: 0.499479 -0.5
230
						
231
						location 	x, y
232
						center 		0, 0
233
						UL			-0.5, -0.5
234
						UR 			0.5, -0.5
235
						LR 			0.5, 0.5
236
						LL 			0, 0.5
237
						y-up is negative
238
						x-right is positive
239
						The range from edge to edge of sequence space is 1. 
240
						-.5 is left, .5 is right. 
241
						-.5 is up, .5 is down.
242
						.125, 0 would be 1200x540 = (seq_res_x * x_move) = how many pixels to move from center) = 1920*.125 + 1920/2 = 1200
243
						for x: seq_res_x * x_move
244
						for y: seq_res_y * -y_move
245
						'''
246
		
247
		# Gets the shot name, which is the formatted clip_name with clip# and reel#, with the sequence clipnumber.
248
		# uses camera type (Red, Alexa, etc), and the clip_name string (the filename of the clip used in FCP) 
249
		# Also takes the seq_clip_number for returning the correct shot_name (the name that will be used to name the nuke script)
250
		if clip_name_format == 'RED':
251
			# This works for Red footage of format: A###_C###_RANDOMDATA
252
			reel_number = clip_name.split('_')[0][1:]
253
			clip_number = clip_name.split('_')[1][1:]
254
255
		if clip_name_format == 'Alexa':
256
			# Alexa footage is A###C###_######_R####
257
			reel_number = int(clip_name.split('C')[0][1:])
258
			clip_number = int( clip_name.split('C')[1].split('_')[0] )
259
260
		if clip_name_format == 'Bypass':
261
			shot_name = "%02d0_%s" %(seq_clip_number, os.path.splitext(clip_name)[0])
262
		else:
263
			# shot_name is the string that defines the name that the nuke script is saved to. seq_clip_number+0_A{reelnumber}_C{clipnumber}
264
			shot_name = "%02d0_A%sC%s" %(seq_clip_number, reel_number, clip_number)
265
266
267
		############################
268
		# Build Nuke Script
269
		############################
270
		
271
		# if the subdirectories checkbox is checked, set the output_shotdir to be a subdirectory named with the shot_name
272
		if subdirectories:
273
			output_shotdir = output_dir
274
			output_shotdir = os.path.join(output_dir, shot_name)
275
		else:
276
			output_shotdir = output_dir
277
		# If the output_shotdir does not exist, create it (auto-creates subdirectories)
278
		if not os.path.isdir(output_shotdir):
279
				os.mkdir(output_shotdir)
280
281
		
282
		###########################
283
		# Compute values to plug into the Nuke Script
284
285
		# Compute Handles and set first_frame and last_frame
286
		first_frame = in_point - handle_length
287
		last_frame = out_point-1 + handle_length
288
289
		if timeremap_value:
290
			'''
291
			The XML gives us the duration of the original clip, and the in and out points of the retimed clip 
292
			originalIn = newIn * retime
293
			originalOut = newOut * retime
294
			new duration = lastFrame - first_frame
295
			'''
296
			timeremap_value 	= timeremap_value/100
297
			new_clip_duration 	= last_frame - first_frame
298
			clip_duration 		= clip_duration * timeremap_value
299
			first_frame 		= first_frame * timeremap_value
300
			last_frame 			= first_frame + new_clip_duration
301
			
302
			
303
		# Set Format 
304
		if seq_res_x == 1920 and seq_res_y == 1080:
305
			fcp_xml_resolution = 'HD'
306
		else:
307
			fcp_xml_resolution = 'from_xml'
308
309
310
		#!!! This creates a nuke script by appending text to the .nk file instead of using nuke.nodeCopy(), which is slow and messy, and so that root node settings can be added.
311
		# The strings that are written are triple-quoted. newlines are created with '\n'. {} chars in the string have to be doubled so as not to throw a KeyError
312
		nuke_file = os.path.join(output_shotdir, "%s_v001.nk"%(shot_name))
313
		nuke_script = open(nuke_file, 'a+')
314
		# Create Root node
315
		nuke_script.write('''Root {{\n inputs 0\n name \"{0}\"\n project_directory \"\[python \{{nuke.script_directory()\}}]\"\n first_frame {1}\n last_frame {2}\n fps {6}\n format \"{3} {4} 0 0 {3} {4} 1 {5}\"\n proxy_type scale\n}}\n'''.format(nuke_file, first_frame, last_frame, seq_res_x, seq_res_y, fcp_xml_resolution, fps))
316
		# Create Read node
317
		nuke_script.write('''Read {{\n inputs 0\n file \"{0}\"\n first 0\n last {1}\n frame_mode offset\n frame 1\n origlast {1}\n origset true\n name Read1\n selected true\n xpos -425\n ypos -40\n}}\n'''.format(file_path, clip_duration))
318
		# Create TimeRemap if there is retiming on the clip
319
		if timeremap_value:
320
			nuke_script.write('''
321
Text {
322
 message "\[frame]"
323
 font /Library/Fonts/Arial.ttf
324
 yjustify center
325
 box {480 270 1440 810}
326
 translate {1314 -498}
327
 center {960 540}
328
 name FrameNumber
329
 selected true
330
 xpos -425
331
 ypos 42
332
}
333
''')
334
			nuke_script.write('''Group {{
335
 name RetimeFromFrame
336
 selected true
337
 addUserKnob {{20 Retime t "Retime From Frame Parameters"}}
338
 addUserKnob {{41 StartFrame l "SourceStart Frame" t "The source frame from which retiming starts. For example, if you have a clip that you are using a range from frames 200-300 in, and you want to retime that clip to be 50\% speed, you would set this to be 200. \\n\\nThis gizmo references the root.first_frame value to determine the \\\"in-point\\\" of the clip." T RetimeControls.StartFrame}}
339
 addUserKnob {{41 PlaybackSpeed l "Playback Speed" t "Retime speed as a fraction of one. That is, 0.5 = 50\% speed, 2 = 200\% speed." T RetimeControls.PlaybackSpeed}}
340
}}
341
 Input {{
342
  inputs 0
343
  name Input1
344
  xpos 0
345
 }}
346
 TimeOffset {{
347
  time_offset {{{{-RetimeControls.StartFrame/RetimeScreen.timingSpeed}}}}
348
  name Retime_TimeOffset
349
  tile_color 0xff0000ff
350
  xpos 0
351
  ypos 132
352
 }}
353
 OFXuk.co.thefoundry.time.oflow_v100 {{
354
  method Motion
355
  timing Speed
356
  timingFrame 1
357
  timingSpeed {{{{RetimeControls.PlaybackSpeed}}}}
358
  filtering Normal
359
  warpMode Normal
360
  correctLuminance false
361
  automaticShutterTime false
362
  shutterTime 0
363
  shutterSamples 1
364
  vectorDetail 0.2
365
  smoothness 0.5
366
  blockSize 6
367
  Tolerances 0
368
  weightRed 0.3
369
  weightGreen 0.6
370
  weightBlue 0.1
371
  showVectors false
372
  cacheBreaker false
373
  name RetimeScreen
374
  tile_color 0xff0000ff
375
  selected true
376
  xpos 0
377
  ypos 156
378
 }}
379
 TimeOffset {{
380
  time_offset {{{{root.first_frame}}}}
381
  name GlobalStart_Offset
382
  tile_color 0xff0000ff
383
  xpos 0
384
  ypos 180
385
 }}
386
 Output {{
387
  name Output1
388
  xpos 0
389
  ypos 393
390
 }}
391
 NoOp {{
392
  inputs 0
393
  name RetimeControls
394
  xpos -174
395
  ypos 126
396
  addUserKnob {{20 User}}
397
  addUserKnob {{7 PlaybackSpeed l "Playback Speed" R 0 100}}
398
  PlaybackSpeed {0}
399
  addUserKnob {{3 StartFrame l "Start Frame" t "Offset video start frame"}}
400
  StartFrame {1}
401
 }}
402
end_group\n'''.format( timeremap_value, first_frame ))
403
		
404
		# Create FrameRange node 
405
		nuke_script.write('''FrameRange {{\n first_frame {0}\n last_frame {1}\n name FrameRange1\n label "\\[knob first_frame]-\\[knob last_frame]"\n selected true\n}}\n'''.format(first_frame, last_frame))
406
		
407
		# Create a Transform node with pans and scales, if they exist
408
		if 	scale_value or x_move or y_move or rotation_value:
409
			if not scale_value:
410
				scale_value = 100
411
			if not x_move:
412
				x_move = 0
413
			if not y_move:
414
				y_move = 0
415
			if not rotation_value:
416
				rotation_value = 0
417
			# Create a reformat node if there are pans or scales
418
			nuke_script.write('''Reformat {{
419
 resize none
420
 black_outside true
421
 name Reformat1
422
 selected true
423
}}
424
'''.format())
425
			nuke_script.write( '''Transform {{
426
 translate {{{0} {1}}}
427
 rotate {2}
428
 scale {3}
429
 center {{{4} {5}}}
430
 name Transform1
431
 selected true
432
 }}
433
'''.format( (seq_res_x * x_move), (seq_res_y * -y_move), -rotation_value, scale_value/100, seq_res_x/2, seq_res_y/2 ) )
434
435
		# Create Write node
436
		nuke_script.write('''Write {{\n file \"{0}\"\n file_type mov\n codec apch\n fps 23.976\n checkHashOnRead false\n name Write1\n selected true\n}}\n'''.format('{0}_v001.mov'.format(os.path.join(render_dir, shot_name)) ) )
437
		# Create Viewer node
438
		nuke_script.write('''Viewer {\n name Viewer1\n selected true\n}\n''')
439
		# Create Backdrop node
440
		nuke_script.write('''BackdropNode {{\n inputs 0\n name BackdropNode1\n tile_color 0x26434dff\n label \"<img src=\\\"Read.png\\\"> Read Plate <br/><font size=1> {0} <br/> {1}-{2}<br/>{3} frame handles\"\n note_font_size 30\n selected true\n xpos -500\n ypos -150\n bdwidth 234\n bdheight 254\n}}\n'''.format(shot_name, in_point, out_point, handle_length))
441
442
		seq_clip_number += 1
443
		# Reset option effect parameters to false for next clip iteration
444
		timeremap_value = False
445
		scale_value = False
446
		x_move = False
447
		y_move = False
448
		rotation_value = False
449
		### End of for loop which processes each clip in timeline.
450
451
452
	nuke.message('All clips processed successfully!')
453
	return
454
455
456
457
'''
458
		### ??? ALTERNATIVE METHOD FOR CREATING THE SCRIPT FILES
459
		### This approach uses the script that import_xml is executed from as a base for creating nodes, and then using nuke.nodeCopy() to 'paste' the data into each script file.
460
		### The approach I ended up using instead just uses python to format .nk scripts as text, inputting the variables where relevant. It is much faster and more efficient.
461
		for node in nuke.allNodes():
462
			node.setSelected(True)
463
		nuke.nodeDelete()
464
465
		# Create Read node
466
		read = nuke.createNode("Read")
467
		read.knob("file").setValue(file_path)
468
		read.knob("frame_mode").setValue('offset')
469
		read.knob("frame").setValue('1')
470
		read.knob("first").setValue(0)
471
		read.knob("last").setValue(clip_duration)
472
473
		# Create a NoOp node to hold shot info
474
		#!!! This is not needed
475
		#shotInfo = nuke.createNode('NoOp')
476
		#shotInfo.knob('name').setValue(shot_name+"_info")
477
		#shotInfo.addKnob( nuke.String_Knob("The original name of the clip", "clip_name", clip_name) )
478
		#shotInfo.addKnob( nuke.String_Knob("Base name of the nuke script", "shot_name", shot_name) )
479
480
		# Create FrameRange node
481
		frame_range = nuke.createNode("FrameRange")
482
		frame_range.knob('label').setValue('[knob first_frame]-[knob last_frame]')
483
		frame_range.knob('first_frame').setValue( in_point - handle_length ) 
484
		frame_range.knob('last_frame').setValue( out_point-1 + handle_length )
485
		
486
		# Create Write node
487
		write = nuke.createNode("Write")
488
		write.knob('file').setValue('{0}{1}_v001.mov'.format(render_dir, shot_name))
489
		#write.knob("file_type").setValue("mov")
490
		#write.knob("mov.codec").setValue("apch")
491
		#write.knob("mov.fps").setValue("23.976")
492
493
		# Create Viewer Node
494
		nuke.createNode('Viewer')
495
496
		# Informational Backdrop Node
497
		bd_node = nuke.createNode("BackdropNode")
498
		bd_node.knob("tile_color").setValue(0x26434dff)
499
		bd_node.knob("note_font_size").setValue(30)
500
		bd_node.knob("bdwidth").setValue(234)
501
		bd_node.knob("bdheight").setValue(254)
502
		bd_node.knob("label").setValue('<img src=\"Read.png\"> Read Plate <br/><font size=1> %s <br/> %s-%s'%(shot_name,int(in_point),int(out_point)))
503
504
		# Set root script values
505
		#nuke.toNode('root').knob('project_directory').setValue('[python {nuke.script_directory()}]')
506
		#nuke.toNode('root').knob('first_frame').setValue( in_point-handle_length)
507
		#nuke.toNode('root').knob('first_frame').setValue( (out_point-1) + handle_length)
508
		#nuke.toNode('root').knob('format').setValue('HD')
509
510
		# Select all created nodes and copy them into a new script
511
		for node in nuke.allNodes():
512
			node.setSelected(True)
513
		nuke.nodeCopy(nuke_file)
514
		'''