# # 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 the CUE files to process") thePath = parser.parse_args().thePathOfCueFiles # Read *.cue files in specified directory for cueFileName in glob.glob(os.path.join(thePath, "*.cue")): #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) else: print(f"Skipping duplicate: '{theTitle}' at {theHours:02d}:{theMinutes:02d}:{theSeconds:02d} (appeared within last {maxTrackDistance} tracks)") print(f"Created {trackCount} labels in {labelFileName}") print(f"Created tracklist in {tracklistFileName}") # end for line in cueFileContent # end for cueFileName in glob.glob(thePath + "\\*.cue")