Skip to content

Instantly share code, notes, and snippets.

@btgoodwin
Last active October 21, 2022 17:26
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save btgoodwin/6d9a252527495bb566fa to your computer and use it in GitHub Desktop.
Save btgoodwin/6d9a252527495bb566fa to your computer and use it in GitHub Desktop.
Final Cut Pro X FCPXML Parser
#!/usr/bin/python
'''
Author: Thomas Goodwin
Company: Geon Technologies, LLC, 2014
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
Project Purpose:
Extract markers and time codes from Final Cut Pro X's FCPXML 1.3 formatted files
Usage:
Pass an XML element into Marker.scanForMarker(). If markers are found, the resulting list is returned in the order of discovery.
'''
import sys, datetime
from xml.etree.ElementTree import parse
# Converts the '64bit/32bits' timecode format into seconds
def parseFCPTimeSeconds (timeString):
vals = [float(n) for n in timeString.replace('s','').split('/')]
if 1 == len(vals):
val = vals[0]
else:
val = vals[0]/vals[1]
return val
class Marker:
def __init__(self, name, startTime):
self._name = name
self._startTime = startTime
@property
def startTime(self):
return self._startTime
@property
def name(self):
return self._name
@staticmethod
def scanForMarker(element, time=[]):
start = offset = 0
try:
start = parseFCPTimeSeconds(element.attrib['start'])
except:
pass
try:
offset = parseFCPTimeSeconds(element.attrib['offset'])
except:
pass
m = []
if 'marker' == element.tag:
m.append(Marker(element.attrib['value'], start + sum(time)))
else:
time.append(offset - start)
for el in element:
m.extend(Marker.scanForMarker(el, list(time)))
return m
# EXAMPLE:
# Import file and convert it to a list of markers sorted by ID and start time
xmlroot = parse(sys.argv[1]).getroot()
markers = sorted(Marker.scanForMarker(xmlroot), key=lambda s: s.startTime)
print "RESULTING ORDERED LIST:"
for m in markers:
print "Marker {0} with start time: {1}".format(m.name.encode('utf-8'), datetime.timedelta(seconds=m.startTime))
@ecnelson
Copy link

ecnelson commented May 7, 2019

Use m.name.encode('utf-8') to accommodate unicode characters in marker names.

@btgoodwin
Copy link
Author

Use m.name.encode('utf-8') to accommodate unicode characters in marker names.

Thanks! I've added it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment