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