Page 1 of 1
Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Tue Oct 06, 2015 10:26 am
by Jason Bowdach
Hey Fusion community,
Has anyone coded a link between these two applications? or at least have a quick workflow to go between them easily?
If Rony Soussan happens to be in the forum, we discussed this possibility at a local BMD event in Burbank recently. Was just curious if this was possible, even if some aspect of it was automated.
Many thanks,
Jason
Re: Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Tue Oct 06, 2015 3:28 pm
by Paul Ingvarsson
Just a note to say i'd be really interested in any developments on this too - i'm sure it's on the cards. The DS integration of editing and effects was incredible, it's frustrating to see these two products (Resolve / Fusion) not fused together even in the most basic way.
A simple copy clip from Resolve and paste into Fusion would be a great start...
Re: Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Tue Oct 06, 2015 5:05 pm
by Rony Soussan
Hi guys,
I wrote a simple class in python that you can use as starting point.
The code is not commented well (if any), but it's essentially can strip out the contents of the xml into a dictionary, with methods to create generation projects, and fusion comps etc..
My plan is to have a more complete setup with comments ready for studio8 release, but anyone is welcome to look at the wip.
The 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)
Re: Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Tue Oct 06, 2015 9:48 pm
by Paul Ingvarsson
Hi Rony,
Before I bash myself against a wall getting this to work... Will I be able to run this script in Fusion 8 on mac?
I've installed python and can run your script but it's calling for idEyeon module (and I guess plenty more after that)
I must point out that although I am code savvy (PHP etc) Python is totally new to me
Paul
Re: Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Tue Oct 06, 2015 10:45 pm
by Rony Soussan
You won't be able to use that script at all, it's there for other scripts to call on, so there is nothing much to run without the support scripts. I wanted to just put it out there for those who wanted to see the parsing code.
I am planning to bundle a complete working example when studio 8 is out.
Re: Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Mon Oct 12, 2015 5:47 pm
by Simon Dayan
Hi Ronny,
nice code dude!
import idEyeon???
is there any SDK for fusion that we can download?
Re: Resolve 12 & Fusion - Easy Workflow \ Script
Posted:
Mon Oct 12, 2015 7:36 pm
by Rony Soussan
Thanks, I try (although I don't comment nearly as much as I should)
here is the ideyeon.
It's just a class for binding fusion/generation without having to do it in every script.
- Code: Select all
import id_config
import pathParser_one
import re
import sys
import os
from time import strftime
from pprint import pprint as pp
import xml.etree.cElementTree as ET
def getFusion():
MAX_TRIES = 50
path_fu = r"S:\Software\eyeon\bin\Fusion\Launchers\Batch_Files\Fusion64.bat"
try:
import PeyeonScript as eyeon
except Exception, err:
print 'Failed to get eyeon PeyeonScript module: \n\t%s'%err
exit(-1)
try:
print 'Fusion: %s'%fusion
except Exception, err:
fusion = eyeon.scriptapp("Fusion")
if not fusion:
os.system(path_fu)
for i in range(MAX_TRIES):
print ("waiting for fusion to load........")
time.sleep(1)
fusion = eyeon.scriptapp("Fusion")
if fusion:
break;
if not fusion:
print 'Error: Failed to get a handle to fusion'
return fusion
def getGeneration():
try:
import PeyeonScript as eyeon
except Exception, err:
print 'Failed to get eyeon PeyeonScript module: \n\t%s'%err
try:
print 'Generation: %s'%generation
except Exception, err:
generation = eyeon.scriptapp("Generation")
return generation
def getNote(xml):
doc = ET.parse( xml )
root = doc.getroot()
lines = root.findall("./Lines/Line")
slates = {}
for ln in lines:
date = ln.attrib.get('Date')
note = ln.text
slates.update({date:note})
# pp (slates)
# pp (sorted(slates.values())[:1])
slates = sorted(slates.values())[:1]
print note
if "r'_'" in note:
print "Hrwerwerwerewr"
return note
print getGeneration()
print getFusion()