Want more features on Pastebin? Sign Up, it's FREE!
Guest

fcpxml_to_nuke_v1.5.1

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