Skip to content

Instantly share code, notes, and snippets.

@Grezzo
Last active October 10, 2016 12:34
Show Gist options
  • Save Grezzo/6130285ac3357e6177758335d59aeb48 to your computer and use it in GitHub Desktop.
Save Grezzo/6130285ac3357e6177758335d59aeb48 to your computer and use it in GitHub Desktop.
Opens an fcpxml (from Apple FCP X) and modifies the multicam clips to add all audio angles. It doesn't really work very well, and I've run out of time/inclination to get it working properly
#! /usr/bin/env python
#Todo:
#Update link to latest version
#check that video only angles are changed to all
#check that video only is "vidoe" and audio only is "audio" and both is "all
#check that angles with no audio are not added
import logging
def get_assets_dict(asset_elements):
#Create dictionary
asset_elements_dict = {}
#Add each asset to dictionary with id as key
for asset_element in asset_elements:
asset_id = asset_element.getAttribute("id")
asset_elements_dict[asset_id] = asset_element
return asset_elements_dict
def fix_asset(asset_element, audio_element):
logging.debug("Processing audio element that references asset " + asset_element.getAttribute("id") + " (" + asset_element.getAttribute("name") + ")")
#Add hasAudio attribute
asset_element.setAttribute("hasAudio", "1")
#Get highest audio channel used by audio element
if audio_element.hasAttribute("srcCh"):
#Get the last channel number in the comma separated list
used_channels = audio_element.getAttribute("srcCh").split(", ")
highest_used_channel = int(used_channels[-1])
else:
#If no attribute, channel must be 1
highest_used_channel = 1
#Get number of channels currently in asset
if asset_element.hasAttribute("audioChannels"):
asset_audio_channels = int(asset_element.getAttribute("audioChannels"))
else:
asset_audio_channels = 0
if asset_audio_channels < highest_used_channel:
#Update audioChannels attribute
asset_element.setAttribute("audioChannels", str(highest_used_channel))
logging.info("Changed asset " + str(asset_element.getAttribute("id")) + " (" + asset_element.getAttribute("name") + ") to have " + str(asset_element.getAttribute("audioChannels")) + " channels")
#Get audio id used by audio element
if audio_element.hasAttribute("srcID"):
used_id = int(audio_element.getAttribute("srcID"))
else:
#If no attribute, id must be 1
used_id = 1
#Get number of ids currently in asset
if asset_element.hasAttribute("audioSources"):
asset_audio_ids = int(asset_element.getAttribute("audioSources"))
else:
asset_audio_ids = 0
if asset_audio_ids < used_id:
#Update audioSources attribute
asset_element.setAttribute("audioSources", str(used_id))
logging.info("Changed asset " + str(asset_element.getAttribute("id")) + " (" + asset_element.getAttribute("name") + ") to have " + str(asset_element.getAttribute("audioSources")) + " sources")
def main():
import argparse, os, codecs, sys
from xml.dom.minidom import parse
#Set up argument parser
parser = argparse.ArgumentParser(description="Enable all audio angles in multicam clips in fcpxml",
epilog="Latest version available at https://gist.github.com/Grezzo/6130285ac3357e6177758335d59aeb48")
parser.add_argument("input", metavar='SOURCE', help="an invalid fcpxml file")
parser.add_argument("output", metavar='DESTINATION', help="the name of the fcpxml file to create")
parser.add_argument("-v", "--verbose", help="print verbose logging information", action="store_true")
parser.add_argument("-d", "--debug", help="print debug logging information", action="store_true")
args = parser.parse_args()
#Turn on logging if specified
if args.debug:
logging.getLogger().setLevel("DEBUG")
elif args.verbose:
logging.getLogger().setLevel("INFO")
#Check that input file exists
if not os.path.isfile(args.input):
print(os.path.basename(__file__) + ": error: " + args.input + " does not exist")
sys.exit(2)
#Parse fcpxml and catch any errors
try:
xmldom = parse(args.input)
except:
print(os.path.basename(__file__) + ": error: " + args.input + " cannot be parsed")
sys.exit(2)
#Check if it is really an fcpxml
if not xmldom.getElementsByTagName("fcpxml"):
print(os.path.basename(__file__) + ": error: " + args.input + " xml is not an fcpxml")
sys.exit(2)
#Check if destination folder exists
if not os.path.exists(os.path.dirname(args.output)):
print(os.path.basename(__file__) + ": error: " + os.path.dirname(args.output) + " does not exist")
sys.exit(2)
#Get a dictionary containing multicams and their angle ids that contain audio
multicams = xmldom.getElementsByTagName("media")
multicams_dict = {}
for multicam in multicams:
if multicam.getElementsByTagName("multicam"):
multicam_id = multicam.getAttribute("id")
mc_angles = multicam.getElementsByTagName("mc-angle")
angle_ids = []
for mc_angle in mc_angles:
if mc_angle.getElementsByTagName("audio"):
angle_id = mc_angle.getAttribute("angleID")
angle_ids.append(angle_id)
multicams_dict[multicam_id] = angle_ids
#Go through mc-clips adding any necesarry angles
mc_clips = xmldom.getElementsByTagName("mc-clip")
for mc_clip in mc_clips:
ref = mc_clip.getAttribute("ref")
mc_sources = mc_clip.getElementsByTagName("mc-source")
angles = []
for mc_source in mc_sources:
angle = mc_source.getAttribute("angleID")
angles.append(angle)
for angle_id in multicams_dict[ref]:
if angle_id in angles:
if mc_source.getAttribute("srcEnable") == "video":
logging.info("Enabling audio on video-only angle in multicam clip")
mc_source.setAttribute("srcEnable", "all")
else:
logging.info("Adding audio angle to multicam clip")
new_mc_source = xmldom.createElement("mc-source")
new_mc_source.setAttribute("angleID", angle_id)
new_mc_source.setAttribute("srcEnable", "audio")
mc_clip.appendChild(new_mc_source)
#Write out new fcpxml file
with codecs.open(args.output, "w", "utf-8") as new_xml:
xmldom.writexml(new_xml)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment