Skip to content

Instantly share code, notes, and snippets.

@stith
Created September 28, 2023 10:39
Rekordbox .flac to .aiff conversion

quick script that, given a rekordbox library.xml file, converts all .flac files to .aiff, preserving file metadata like cue points, play count, and album art. also maintains the file's bit rate.

probably will fail in some weird cases that i didn't have to care about with my tiny baby library

import xml.etree.ElementTree as ET
import urllib.parse
from pathlib import Path
import os
import subprocess
import json
import argparse
def convert_files(input_xml_path, output_xml_path):
tree = ET.parse(input_xml_path)
logfile = open('flac-conversion-log2.txt', 'a')
tracks = tree.getroot().findall('./COLLECTION/TRACK')
for track in tracks:
track_name = f"{track.attrib['Artist']} - {track.attrib['Name']}"
if track.attrib['Kind'].lower() == 'flac file':
# Fenagle rekordbox path to something usable
filename: str = track.attrib['Location'].replace('file://localhost', '')
filename = urllib.parse.unquote(filename)
flac_path = Path(filename)
aiff_path = flac_path.with_suffix('.aiff')
# Convert flac -> wav
if not aiff_path.exists():
if not flac_path.exists():
raise RuntimeError(f"Path doesn't exist: {flac_path}")
print("Probing", track_name)
probed_data = subprocess.run(['ffprobe', '-v', 'quiet', '-print_format', 'json', '-show_streams', flac_path], capture_output=True, text=True).stdout
parsed_probe = json.loads(probed_data)
bit_depth = parsed_probe["streams"][0]["bits_per_raw_sample"]
print(f"Converting with bit depth {bit_depth}:", track_name)
convert_result = subprocess.run(['ffmpeg', '-i', flac_path, '-write_id3v2', '1', '-c:a', f"pcm_s{bit_depth}be", '-c:v', 'copy', aiff_path], capture_output=True)
if convert_result.returncode != 0:
print("Failed", convert_result.returncode, "on", flac_path)
print("STDOUT:", convert_result.stdout)
print("STDERR:", convert_result.stderr)
break
logfile.write(str(flac_path))
logfile.write("\n")
# Defenagle sensible path into rekordbox's thing
aiff_uri = aiff_path.as_uri().replace('file:///', 'file://localhost/')
track.attrib['Kind'] = 'AIFF File'
track.attrib['Location'] = aiff_uri
track.attrib['Comments'] = f"{track.attrib['Comments']} flac-converted"
track.attrib['Size'] = str(os.path.getsize(aiff_path))
print("Done with", track_name)
tree.write(output_xml_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", help="library.xml location", required=True)
parser.add_argument("-o", "--output", help="updated library.xml location", required=True)
args = parser.parse_args()
convert_files(args.input, args.output)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment