Jump to: Board index » General » Fusion

.fcpxml

Learn about 3D compositing, animation, broadcast design and VFX workflows.
  • Author
  • Message
Offline

Kirill Mukha

  • Posts: 5
  • Joined: Sat Oct 03, 2015 8:47 pm

.fcpxml

PostSat Oct 03, 2015 8:54 pm

Do Blackmagic Design planning support FCP X .fcpxml 1.5 in Fusion and Fusion Studio?
Offline
User avatar

Rony Soussan

  • Posts: 727
  • Joined: Tue Nov 11, 2014 5:33 pm

Re: .fcpxml

PostMon Oct 05, 2015 8:12 pm

No, not directly. I wrote my own fcpxml parser for Generation (Fusion Suite)

consider it an example of how to parse, but it's only part of a complete pipeline solution
UPDATE: had only a method pasted before, here is the complete example class

Code: Select all
import xml.etree.ElementTree as ET
from pprint import pprint as pp
import idEyeon
import os
import tkFileDialog
from Tkinter import *
import easygui

fusion = idEyeon.getFusion()
generation = idEyeon.getGeneration()

# pp (generation.GetPathMap())

proj = generation.ActiveProject
sub = proj.SubGet(0) 
track = sub.TrackGet(0)

_ROOT = './resources'
_FORMAT = '%s/format'%_ROOT
_ASSETS = '%s/asset'%_ROOT
_LIBRARY = './library'
_EVENT = '%s/event'%_LIBRARY
_PROJECT = '%s/project'%_EVENT
_SEQUENCE = '%s/sequence'%_PROJECT
_SPINE = '%s/spine'%_SEQUENCE
_CLIP = '%s/clip'%_SPINE
_VIDEO = '%s/video'%_CLIP

template = r"X:\OneDrive\VfxWork\BMD\Templates\FusionScript_TemplateComps\FXPXML_Template.comp"
# myXml = tkFileDialog.askopenfilename()
# myXml = r"x:\OneDrive\VfxWork\_Dev1\ScriptDev\LearningXML\CitizenChain (Resolve).fcpxml"

class parseFcpXML(object):
  _MAX_KEY = 0
  _maxCount = 0
  _tcMode = 0

  def __init__(self, filepath):
    self._filePath = filepath
    self._ok = False

    try:
      self._tree = ET.ElementTree(file=filepath)
      self._root = self._tree.getroot()
      self._ok = True
    except Exception, err:
      self.errors().append( 'Failed to parse file: %s'%filepath)
      self.errors().append( '%s'%err)
      return # verifies if there is an XML

  def getTcFormat(self):
    """evaluates if project is in drop rame mode or 23.9"""


    if self._root.find(_SEQUENCE).get('tcFormat') == "NDF":
      fps= 24
    else:
      fps = 23.978
    return fps

  def _frameCalc(self,rawDuration):
    """converts the Final CutX duration formula in frames and seconds"""

    fps = self.getTcFormat() # 24v call method to get projects fps format
    split = rawDuration.split("/") # spliting the format ##/#s into parts
    splitA = int(split[0]) # first number in the format
    splitB = int(split[1].replace("s","")) # striping the s off the format
    mulF = ((fps) / splitB)
    durInFF = (mulF * splitA) # results of above against first number for actuall frame duration
    durInTC = self.frame_to_tc(durInFF)
    return durInFF

  def getEvent(self):
        """ returns the name of timeline even from exported timeline."""

     return self._root.find(_EVENT).get('name')

  def getSeqDur(self):
    return self._root.find(_SEQUENCE).get('duration')

  def getProject(self):
     return self._root.find(_PROJECT).get('name')

  def getClipCount(self):
     count = 0
     for item in self._root.findall(_CLIP):
        count = count +1
   return count

  def frame_to_tc(self,frames):
     return '{0:02d}:{1:02d}:{2:02d}:{3:02d}'.format(frames / (3600*24),
                                                    frames / (60*24) % 60,
                                                    frames / 24 % 60,
                                                    frames % 24)
  def _getSource(self,id):
    """ gets a count of how many clips by ID count """

    for asset in self._root.findall(_ASSETS):
      if asset.get('id') == id:
        src = asset.get('src')
        src = src.replace("%20", " ") # replace with windows friendly space
        src = src.replace("file://localhost/", "")
        return src

  def getDict(self):
    """ build a dict from clip data in xml """


    clipDict = {}
    formatDict = {}
    listDict = []
    shotNumber = int(10)
    for clip in self._root.findall(_CLIP):

      clipRef = (clip.find('video')).get('ref') # establish relationship between the clip and it's source from the ref attribute.
      filePath = self._getSource(clipRef) # var for found sources
      clipDict["filePath"] = filePath
      name = clip.get('name')
      clipDict["Name"] = name
      duration = self._frameCalc (clip.get('duration'))
      clipDict["Duration"] = duration

      clip_offset_TC = (self.frame_to_tc(self._frameCalc (clip.get('offset'))))
      clip_Offset_FF = (self._frameCalc (clip.get('offset')))
      clipDict["(inpoint timeline) clip_offset_TC"] = clip_offset_TC # physical location on the timeline
      clipDict["(inpoint timeline) clip_Offset_FF"] = clip_Offset_FF - 86400

      video_offset_FF = self._frameCalc((clip.find('video')).get('offset'))
      video_offset_TC = self.frame_to_tc(video_offset_FF)
      clipDict["(seq starting FF) video_offset_FF"] = video_offset_FF
      clipDict["(seq starting TC) video_offset_TC"] = video_offset_TC

      clip_Start_FF = (self._frameCalc (clip.get('start')))
      clip_Start_TC = self.frame_to_tc(clip_Start_FF)
      clipDict ["(Clip_trim_in) clip_Start_FF"] = clip_Start_FF
      clipDict ["(Clip_trim_in) clip_Start_TC"] = clip_Start_TC

      fileTrim_OUT_FF = clip_Start_FF + duration
      fileTrim_OUT_TC = self.frame_to_tc(fileTrim_OUT_FF)
      clipDict ["fileTrim_OUT_FF"] = fileTrim_OUT_FF
      clipDict ["fileTrim_OUT_TC"] = fileTrim_OUT_TC

      listDict.append(dict(clipDict))
      # we have a list that contains a dict of every flound clip and it's attributes.

    return (listDict)

  def addClip(self,dicList):
    print ""
    pp(dicList)
    for path in dicList:

      filePath = path["filePath"]
      print filePath
      clip = proj.DropSequence(filePath)
      clipM = track.ClipInsert(clip[1])
      clipV = clipM.VersionGet()

      loader = clipV.GetLoader()
      inPVar = path['(Clip_trim_in) clip_Start_FF'] - path['(seq starting FF) video_offset_FF']
      clipV.InPoint = inPVar
      out = path["(inpoint timeline) clip_Offset_FF"] + path["Duration"]
      clipM.SetOutPoint(out)

  def printData(self,data):

    pad = 35
    for i in data:
      for k,v in i.iteritems():
        offset = pad - len(k)
        print"-"*offset,("%s: %s") % (k,v)
      print "="*160
    return

  def makeDir(self,path):

    try:
       os.makedirs( path )
    except:
       pass

  def saveProject(self,path):

    dirName = os.path.basename(path)
    print dirName
    projPath = os.path.join(path,"Generation")
    # fullPath = os.path.join(projPath,dirName+".genproj")
    print projPath
    # print fullPath
    self.makeDir(projPath)

    fullPath = (projPath+"\\"+dirName+".genproj")
    print fullPath
    print proj
    proj.SaveAs(fullPath)

  # def myDialog(self,root):

  #       top = self.top = Toplevel(parent)

  #       Label(top, text="Value").pack()

  #       self.e = Entry(top)
  #       self.e.pack(padx=5)

  #       b = Button(top, text="OK", command=self.ok)
  #       b.pack(pady=5)

  #       print "value is", self.e.get()

  #       self.top.destroy()



class mkComp(object):

  def __init__(self):
    # self._filePath = filepath
    self._ok = False

  def getClipData(self):

    for ClipV in proj.SelectedVersions().values():

      ClipM = ClipV.GetSlotClip()
      path = (ClipV.GetLoader().Filename)
      trimIn = int(ClipV.InPoint)
      length = int(ClipM.Length)
      trimOut = trimIn + length
      clipData =[path,trimIn,trimOut]


      return clipData
 

  def createFld(self,rootFld,shotNumber):

    prefix = "_VFX_"
    baseName = os.path.basename(rootFld)
    # var = os.path.join(rootFld,baseName+prefix)
    # shotPath = var+ str(shotNumber)

    for folder in folderList:
      # shotPath = var+"10"
      # print "DFLJKSDJFLOJ"
      var = os.path.join(rootFld,baseName+prefix)
      shotPath = var+ str(shotNumber)
      fldName = os.path.join(shotPath,folder)
      self.makeDir(fldName)
      print fldName
    return shotPath
  def getPath(self):
    # dirPath = tkFileDialog.askdirectory()
    dirPath = r"X:\Projects\TST"
    return dirPath

  def makeDir(self,path):

    try:
       os.makedirs( path )
    except:
       pass


  def saveComp(self,assetRoot,filePath,trimIn,trimOut):


    shotName = os.path.basename(assetRoot)
    compPath = os.path.join(assetRoot,"comps")
    newComp = fusion.LoadComp( template , False )
    ld = newComp.LD
    sv = newComp.SV
    ld.Clip = filePath
    # ld.DPXFormat.BypassConversion = 1
    ld.ClipTimeStart = trimIn
    ld.ClipTimeEnd = trimOut -1
    ldLenght = (ld.ClipTimeEnd[0] - ld.ClipTimeStart[0])
    newComp.SetAttrs({'COMPN_GlobalEnd':ldLenght})
    baseCompName = os.path.basename(assetRoot)
    compName = baseCompName + "_comp_v01.comp"
    fullPathComp = os.path.join(compPath,compName)
    saveFileName = baseCompName + "_comp_v01..jpg"
    print "saver file name ",saveFileName
    saverPath = os.path.join(assetRoot,"renders","v01")

    print "saverPath ",saverPath
    self.makeDir(saverPath)
    saverFull = os.path.join(saverPath,compName)
    sv.Clip = saverFull
    # print saverFull
    newComp.Save(fullPathComp)
    print "compname",compName
    print "fullPathComp" ,fullPathComp
    newComp.Close()
    return fullPathComp



  def insertVer(self,compPath):

    for clipV in proj.SelectedVersions().values():

      print "HJD"

      # clip = proj.DropSequence(compPath)
      # clipM = track.ClipInsert(clip[1])
      # clipV = clipM.VersionGet()


# shot = mkComp()
# print shot
# pp(shot)

# clipData = shot.getClipPath()
# pp(clipData)
# # # #
# filePath, trimIn, trimOut = clipData
# # print filePath,trimIn,trimOut

# compPath = shot.getPath()
# # print "comppath",compPath


# shotNumber = integerbox(msg='enter shot number for clip', title='New Shot ', default='10')
# assetRoot = shot.createFld(compPath,shotNumber)
# # print "asset root",assetRoot





# newCompPath = shot.saveComp(assetRoot,filePath,trimIn,trimOut)

# # shot.insertVer(newCompPath)

Last edited by Rony Soussan on Tue Oct 06, 2015 5:25 pm, edited 1 time in total.
Offline

Jeff Ha

  • Posts: 160
  • Joined: Tue Apr 14, 2015 10:38 am

Re: .fcpxml

PostTue Oct 06, 2015 11:00 am

just run it through resolve for an export xml. With apple constantly changing their own spec with each patch, it's a PITA to support fcpxml, which is typical Apple. I also heard Automatic Duck is back in the game but I think for Adobe products.
Offline

Kirill Mukha

  • Posts: 5
  • Joined: Sat Oct 03, 2015 8:47 pm

Re: .fcpxml

PostThu Oct 08, 2015 5:17 pm

Thank you, Rony! I will try your script.

Thank you, Jeff! I also export xml from DaVinci Resolve.

AutomaticDuck not actual now, for After Effects and Nuke better choice is ClipExporter, imho.
May be later ClipExporter will support Fusion and may be DaVinci Resolve also will have "Send To Fusion" :D
Offline

Milos Labski

  • Posts: 10
  • Joined: Thu Aug 06, 2015 4:31 am
  • Location: Cologne, Germany

Re: .fcpxml

PostFri Oct 09, 2015 4:26 pm

Hi,
first beta version of "XML to Fusion" script was published today on "We suck less" forum. Supports FCP XML up to version 5.
Attachments
07.PNG
script created flow
07.PNG (69.66 KiB) Viewed 2533 times
Offline

Kirill Mukha

  • Posts: 5
  • Joined: Sat Oct 03, 2015 8:47 pm

Re: .fcpxml

PostFri Oct 09, 2015 6:28 pm

Very interesting, Milos!
Why you use scale? Or this transformation (scale/move/crop)?
How about retime? Colour Correction? Keying?
Do you can share script?
Offline

Milos Labski

  • Posts: 10
  • Joined: Thu Aug 06, 2015 4:31 am
  • Location: Cologne, Germany

Re: .fcpxml

PostSun Oct 11, 2015 8:48 am

Why you use scale?

Rescale nodes are created, if resolution of imported clip differs from fusion composition resolution - mainly for preview purposes. You have still the option to delete all rescale nodes with two clicks. :D
Ok, other option would be change of scale values on merge nodes - this is probably the better way to do it. Thanks.
How about retime? Colour Correction? Keying?

Time remapping effects are not processed. maybe in next version. Color correction - analysing of all proprietary color-correction tools from 3 applications and translate them to fusion tools would be probably a task too big for any software company on the market. For me too.
The main purpose of the script is the import of trimmed clips from sequences, created by editing applications such Premiere and Final cut - or even Resolve. I think, deeper integration between Fusion and Resolve can and should be done by BMD only.
Do you can share script?

It's a free script, so you can download it anytime from "We suck less". There are also some screenshots, showing how the script is working. I would appreciate any suggestion or improvement ideas.
Offline

Kirill Mukha

  • Posts: 5
  • Joined: Sat Oct 03, 2015 8:47 pm

Re: .fcpxml

PostSun Oct 11, 2015 5:52 pm

Milos Labski wrote:It's a free script, so you can download it anytime


Thank you! How about github version? :) Other fusioner may help improve script.

Return to Fusion

Who is online

Users browsing this forum: Bryan Ray and 34 guests