107 lines
4.7 KiB
Python
Executable File
107 lines
4.7 KiB
Python
Executable File
#
|
|
# This program reads rekordbox CUE files and output a label track to be imported in Audacity.
|
|
# Usage: cue2audacity.py directory (where directory is the path where the CUE files are stored)
|
|
# Author: Manuel Weiser
|
|
# Date: 2025-07-03
|
|
#
|
|
|
|
import argparse
|
|
import glob
|
|
import re
|
|
import os
|
|
|
|
# Get argument on command line
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("thePathOfCueFiles", help="The path of the directory with CUE files or a single CUE file to process")
|
|
thePath = parser.parse_args().thePathOfCueFiles
|
|
|
|
# Determine if input is a directory or a single file
|
|
if os.path.isfile(thePath) and thePath.lower().endswith('.cue'):
|
|
# Single CUE file
|
|
cueFiles = [thePath]
|
|
elif os.path.isdir(thePath):
|
|
# Directory - find all CUE files
|
|
cueFiles = glob.glob(os.path.join(thePath, "*.cue"))
|
|
else:
|
|
print(f"Error: '{thePath}' is neither a valid CUE file nor a directory containing CUE files.")
|
|
exit(1)
|
|
|
|
# Process each CUE file
|
|
for cueFileName in cueFiles:
|
|
#print(cueFileName)
|
|
with open(cueFileName, "r", encoding="utf-8") as cueFile:
|
|
cueFileContent = cueFile.readlines()
|
|
#print(cueFileContent)
|
|
|
|
# Create label file
|
|
labelFileName = os.path.splitext(cueFileName)[0] + "_audacity.txt"
|
|
tracklistFileName = os.path.splitext(cueFileName)[0] + "_tracklist.txt"
|
|
|
|
with open(labelFileName, "w", encoding="utf-8") as labelFile, \
|
|
open(tracklistFileName, "w", encoding="utf-8") as tracklistFile:
|
|
#print ("###" + labelFileName + "###")
|
|
|
|
# Process file
|
|
theTitle = "" # initialize title now in order to keep it available when scanning subsequent INDEX line
|
|
thePerformer = "" # initialize performer
|
|
trackCount = 0
|
|
lastTitles = [] # Track the last few titles to avoid duplicates within a few tracks
|
|
maxTrackDistance = 2 # Filter duplicates within this many tracks
|
|
|
|
for lineNum, line in enumerate(cueFileContent, 1):
|
|
originalLine = line
|
|
line = line.strip() # Remove leading/trailing whitespace
|
|
|
|
# Match TITLE - try multiple patterns for different indentation (tabs or spaces)
|
|
theMatchTitle = re.search(r'TITLE\s+"([^"]+)"', line)
|
|
if theMatchTitle is not None:
|
|
theTitle = theMatchTitle.group(1)
|
|
|
|
# Match PERFORMER - try multiple patterns for different indentation (tabs or spaces)
|
|
theMatchPerformer = re.search(r'PERFORMER\s+"([^"]+)"', line)
|
|
if theMatchPerformer is not None:
|
|
thePerformer = theMatchPerformer.group(1)
|
|
|
|
# Match INDEX 01 - try multiple patterns for different formats (tabs or spaces)
|
|
theMatchIndex = re.search(r'INDEX\s+01\s+(\d{1,2}):(\d{2}):(\d{2})', line)
|
|
if theMatchIndex is not None:
|
|
theHours = int(theMatchIndex.group(1)) # Actually hours in HH:MM:SS format
|
|
theMinutes = int(theMatchIndex.group(2)) # Minutes
|
|
theSeconds = int(theMatchIndex.group(3)) # Seconds
|
|
|
|
# Convert HH:MM:SS to total seconds
|
|
theStartTime = (theHours * 3600) + (theMinutes * 60) + theSeconds
|
|
|
|
# Check for duplicates: skip if same title appears within the last few tracks
|
|
isDuplicate = theTitle in lastTitles[-maxTrackDistance:]
|
|
|
|
if not isDuplicate:
|
|
trackCount += 1
|
|
|
|
# format theStartTime
|
|
theStartTimeFormatted = "{:.6f}".format(theStartTime)
|
|
|
|
# Format time for tracklist (MM:SS)
|
|
timeMinutes = theStartTime // 60
|
|
timeSeconds = theStartTime % 60
|
|
timeFormatted = f"{int(timeMinutes):02d}:{int(timeSeconds):02d}"
|
|
|
|
# Write time and title to Audacity label file
|
|
labelFile.write(theStartTimeFormatted + "\t" + theStartTimeFormatted + "\t" + theTitle + "\n")
|
|
|
|
# Write to tracklist file: TrackNr. Artist - Track (Time)
|
|
if thePerformer:
|
|
tracklistFile.write(f"{trackCount:02d}. {thePerformer} - {theTitle} ({timeFormatted})\n")
|
|
else:
|
|
tracklistFile.write(f"{trackCount:02d}. {theTitle} ({timeFormatted})\n")
|
|
|
|
# Add to title history
|
|
lastTitles.append(theTitle)
|
|
|
|
print(f"Created {trackCount} labels in {labelFileName}")
|
|
print(f"Created tracklist in {tracklistFileName}")
|
|
|
|
# end for line in cueFileContent
|
|
|
|
# end for cueFileName in cueFiles
|