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' | |
| 41 | output_dir = 'Directory to Output Nuke Scripts' #"set dir for outputting nuke scripts" | |
| 42 | subdirectories = 'Create subdirectories for each script?' | |
| 43 | render_dir = 'Render Directory for All Write Nodes' #"set render dir for 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 = pathurl_clip.getElementsByTagName('pathurl')[0].firstChild.data.split("file://localhost")[1].replace("%20", " ")
| |
| 154 | except: | |
| 155 | for pathurl_clip in dom.getElementsByTagName('clipitem'):
| |
| 156 | if pathurl_clip.getElementsByTagName('name')[0].firstChild.data == clip_name:
| |
| 157 | try: | |
| 158 | file_path = pathurl_clip.getElementsByTagName('pathurl')[0].firstChild.data.split("file://localhost")[1].replace("%20", " ")
| |
| 159 | break | |
| 160 | except: | |
| 161 | continue | |
| 162 | ||
| 163 | print "These 2 names should be equal: ", file_name, " : ", clip_name | |
| 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 | clip_width = int( clip.getElementsByTagName('width')[0].firstChild.data )
| |
| 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 | # !!! This is calculating the move from the sequence resolution center, not the clip resolution center, as it should. | |
| 205 | # Should this be done with a reformat node instead? This is a hard problem. | |
| 206 | x_move = clip_width * float( param.getElementsByTagName('value')[0].childNodes[1].firstChild.data )
| |
| 207 | y_move = clip_height * float( param.getElementsByTagName('value')[0].childNodes[3].firstChild.data )
| |
| 208 | print effect_name, param_id, x_move, y_move | |
| 209 | ||
| 210 | ||
| 211 | # Gets the shot name, which is the formatted clip_name with clip# and reel#, with the sequence clipnumber. | |
| 212 | # uses camera type (Red, Alexa, etc), and the clip_name string (the filename of the clip used in FCP) | |
| 213 | # Also takes the seq_clip_number for returning the correct shot_name (the name that will be used to name the nuke script) | |
| 214 | if clip_name_format == 'RED': | |
| 215 | # This works for Red footage of format: A###_C###_RANDOMDATA | |
| 216 | reel_number = clip_name.split('_')[0][1:]
| |
| 217 | clip_number = clip_name.split('_')[1][1:]
| |
| 218 | ||
| 219 | if clip_name_format == 'Alexa': | |
| 220 | # Alexa footage is A###C###_######_R#### | |
| 221 | reel_number = int(clip_name.split('C')[0][1:])
| |
| 222 | clip_number = int( clip_name.split('C')[1].split('_')[0] )
| |
| 223 | ||
| 224 | if clip_name_format == 'Bypass': | |
| 225 | shot_name = "%02d0_%s" %(seq_clip_number, os.path.splitext(clip_name)[0]) | |
| 226 | else: | |
| 227 | # shot_name is the string that defines the name that the nuke script is saved to. seq_clip_number+0_A{reelnumber}_C{clipnumber}
| |
| 228 | shot_name = "%02d0_A%sC%s" %(seq_clip_number, reel_number, clip_number) | |
| 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 | ||
| 231 | ############################ | |
| 232 | # Build Nuke Script | |
| 233 | - | if scale_value or x_move or y_move: |
| 233 | + | |
| 234 | ||
| 235 | # if the subdirectories checkbox is checked, set the output_shotdir to be a subdirectory named with the shot_name | |
| 236 | if subdirectories: | |
| 237 | output_shotdir = output_dir | |
| 238 | output_shotdir = os.path.join(output_dir, shot_name) | |
| 239 | else: | |
| 240 | output_shotdir = output_dir | |
| 241 | # If the output_shotdir does not exist, create it (auto-creates subdirectories) | |
| 242 | - | scale {2}
|
| 242 | + | |
| 243 | - | center {{{3} {4}}}
|
| 243 | + | |
| 244 | ||
| 245 | # Compute Handles and set first_frame and last_frame | |
| 246 | first_frame = in_point - handle_length | |
| 247 | - | '''.format( x_move, y_move, scale_value/100, seq_res_x/2, seq_res_y/2 ) ) |
| 247 | + | |
| 248 | if timeremap_value: | |
| 249 | first_frame = round(first_frame/(100/timeremap_value)) | |
| 250 | last_frame = round(last_frame/(100/timeremap_value)) | |
| 251 | ||
| 252 | if seq_res_x == 1920 and seq_res_y == 1080: | |
| 253 | fcp_xml_resolution = 'HD' | |
| 254 | else: | |
| 255 | fcp_xml_resolution = 'from_xml' | |
| 256 | ||
| 257 | #!!! 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. | |
| 258 | # 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
| |
| 259 | nuke_file = os.path.join(output_shotdir, "%s_v001.nk"%(shot_name)) | |
| 260 | nuke_script = open(nuke_file, 'a+') | |
| 261 | # Create Root node | |
| 262 | 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))
| |
| 263 | # Create Read node | |
| 264 | 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))
| |
| 265 | # Create FrameRange node | |
| 266 | 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))
| |
| 267 | ||
| 268 | # Create a Transform node with pans and scales, if they exist | |
| 269 | if scale_value or x_move or y_move or rotation_value: | |
| 270 | if not scale_value: | |
| 271 | scale_value = 1 | |
| 272 | if not x_move: | |
| 273 | x_move = 0 | |
| 274 | if not y_move: | |
| 275 | y_move = 0 | |
| 276 | if not rotation_value: | |
| 277 | rotation_value = 0 | |
| 278 | # Create a reformat node if there are pans or scales | |
| 279 | nuke_script.write('''Reformat {{
| |
| 280 | resize none | |
| 281 | name Reformat1 | |
| 282 | selected true | |
| 283 | }} | |
| 284 | '''.format()) | |
| 285 | ||
| 286 | nuke_script.write( '''Transform {{
| |
| 287 | translate {{{0} {1}}}
| |
| 288 | rotate {2}
| |
| 289 | scale {3}
| |
| 290 | center {{{4} {5}}}
| |
| 291 | name Transform1 | |
| 292 | selected true | |
| 293 | }} | |
| 294 | '''.format( x_move, y_move, rotation_value, scale_value/100, seq_res_x/2, seq_res_y/2 ) ) | |
| 295 | # Create TimeRemap if there is retiming on the clip | |
| 296 | if timeremap_value: | |
| 297 | nuke_script.write('''Group {{
| |
| 298 | name RetimeFromFrame | |
| 299 | selected true | |
| 300 | addUserKnob {{20 Retime t "Retime From Frame Parameters"}}
| |
| 301 | 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}}
| |
| 302 | 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}}
| |
| 303 | }} | |
| 304 | Input {{
| |
| 305 | inputs 0 | |
| 306 | name Input1 | |
| 307 | xpos 0 | |
| 308 | }} | |
| 309 | TimeOffset {{
| |
| 310 | time_offset {{{{-RetimeControls.StartFrame/RetimeScreen.timingSpeed}}}}
| |
| 311 | name Retime_TimeOffset | |
| 312 | tile_color 0xff0000ff | |
| 313 | xpos 0 | |
| 314 | ypos 132 | |
| 315 | }} | |
| 316 | OFXuk.co.thefoundry.time.oflow_v100 {{
| |
| 317 | method Motion | |
| 318 | timing Speed | |
| 319 | timingFrame 1 | |
| 320 | timingSpeed {{{{RetimeControls.PlaybackSpeed}}}}
| |
| 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 | warpMode Normal | |
| 323 | correctLuminance false | |
| 324 | automaticShutterTime false | |
| 325 | shutterTime 0 | |
| 326 | shutterSamples 1 | |
| 327 | vectorDetail 0.2 | |
| 328 | smoothness 0.5 | |
| 329 | blockSize 6 | |
| 330 | Tolerances 0 | |
| 331 | weightRed 0.3 | |
| 332 | weightGreen 0.6 | |
| 333 | weightBlue 0.1 | |
| 334 | showVectors false | |
| 335 | cacheBreaker false | |
| 336 | name RetimeScreen | |
| 337 | tile_color 0xff0000ff | |
| 338 | selected true | |
| 339 | xpos 0 | |
| 340 | ypos 156 | |
| 341 | }} | |
| 342 | TimeOffset {{
| |
| 343 | time_offset {{{{root.first_frame}}}}
| |
| 344 | name GlobalStart_Offset | |
| 345 | tile_color 0xff0000ff | |
| 346 | xpos 0 | |
| 347 | ypos 180 | |
| 348 | }} | |
| 349 | Output {{
| |
| 350 | name Output1 | |
| 351 | xpos 0 | |
| 352 | ypos 393 | |
| 353 | }} | |
| 354 | NoOp {{
| |
| 355 | inputs 0 | |
| 356 | name RetimeControls | |
| 357 | xpos -174 | |
| 358 | ypos 126 | |
| 359 | addUserKnob {{20 User}}
| |
| 360 | addUserKnob {{7 PlaybackSpeed l "Playback Speed" R 0 100}}
| |
| 361 | PlaybackSpeed {0}
| |
| 362 | addUserKnob {{3 StartFrame l "Start Frame" t "Offset video start frame"}}
| |
| 363 | StartFrame {1}
| |
| 364 | }} | |
| 365 | end_group\n'''.format( timeremap_value/100, first_frame )) | |
| 366 | ||
| 367 | # Create Write node | |
| 368 | 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)) ) )
| |
| 369 | # Create Viewer node | |
| 370 | nuke_script.write('''Viewer {\n name Viewer1\n selected true\n}\n''')
| |
| 371 | # Create Backdrop node | |
| 372 | 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))
| |
| 373 | ||
| 374 | ||
| 375 | ||
| 376 | ||
| 377 | seq_clip_number += 1 | |
| 378 | # Reset option effect parameters to false for next clip iteration | |
| 379 | timeremap_value = False | |
| 380 | scale_value = False | |
| 381 | x_move = False | |
| 382 | y_move = False | |
| 383 | rotation_value = False | |
| 384 | ### End of for loop which processes each clip in timeline. | |
| 385 | ||
| 386 | ||
| 387 | nuke.message('All clips processed successfully!')
| |
| 388 | return | |
| 389 | ||
| 390 | ||
| 391 | ||
| 392 | ''' | |
| 393 | ### ??? ALTERNATIVE METHOD FOR CREATING THE SCRIPT FILES | |
| 394 | ### 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. | |
| 395 | ### 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. | |
| 396 | for node in nuke.allNodes(): | |
| 397 | node.setSelected(True) | |
| 398 | nuke.nodeDelete() | |
| 399 | ||
| 400 | # Create Read node | |
| 401 | read = nuke.createNode("Read")
| |
| 402 | read.knob("file").setValue(file_path)
| |
| 403 | read.knob("frame_mode").setValue('offset')
| |
| 404 | read.knob("frame").setValue('1')
| |
| 405 | read.knob("first").setValue(0)
| |
| 406 | read.knob("last").setValue(clip_duration)
| |
| 407 | ||
| 408 | # Create a NoOp node to hold shot info | |
| 409 | #!!! This is not needed | |
| 410 | #shotInfo = nuke.createNode('NoOp')
| |
| 411 | #shotInfo.knob('name').setValue(shot_name+"_info")
| |
| 412 | #shotInfo.addKnob( nuke.String_Knob("The original name of the clip", "clip_name", clip_name) )
| |
| 413 | #shotInfo.addKnob( nuke.String_Knob("Base name of the nuke script", "shot_name", shot_name) )
| |
| 414 | ||
| 415 | # Create FrameRange node | |
| 416 | frame_range = nuke.createNode("FrameRange")
| |
| 417 | frame_range.knob('label').setValue('[knob first_frame]-[knob last_frame]')
| |
| 418 | frame_range.knob('first_frame').setValue( in_point - handle_length )
| |
| 419 | frame_range.knob('last_frame').setValue( out_point-1 + handle_length )
| |
| 420 | ||
| 421 | # Create Write node | |
| 422 | write = nuke.createNode("Write")
| |
| 423 | write.knob('file').setValue('{0}{1}_v001.mov'.format(render_dir, shot_name))
| |
| 424 | #write.knob("file_type").setValue("mov")
| |
| 425 | #write.knob("mov.codec").setValue("apch")
| |
| 426 | #write.knob("mov.fps").setValue("23.976")
| |
| 427 | ||
| 428 | # Create Viewer Node | |
| 429 | nuke.createNode('Viewer')
| |
| 430 | ||
| 431 | # Informational Backdrop Node | |
| 432 | bd_node = nuke.createNode("BackdropNode")
| |
| 433 | bd_node.knob("tile_color").setValue(0x26434dff)
| |
| 434 | bd_node.knob("note_font_size").setValue(30)
| |
| 435 | bd_node.knob("bdwidth").setValue(234)
| |
| 436 | bd_node.knob("bdheight").setValue(254)
| |
| 437 | 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)))
| |
| 438 | ||
| 439 | # Set root script values | |
| 440 | #nuke.toNode('root').knob('project_directory').setValue('[python {nuke.script_directory()}]')
| |
| 441 | #nuke.toNode('root').knob('first_frame').setValue( in_point-handle_length)
| |
| 442 | #nuke.toNode('root').knob('first_frame').setValue( (out_point-1) + handle_length)
| |
| 443 | #nuke.toNode('root').knob('format').setValue('HD')
| |
| 444 | ||
| 445 | # Select all created nodes and copy them into a new script | |
| 446 | for node in nuke.allNodes(): | |
| 447 | node.setSelected(True) | |
| 448 | nuke.nodeCopy(nuke_file) | |
| 449 | ''' |