Last active
October 10, 2016 12:34
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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