So I would like to attempt to write a plugin to do this.
I am a (former) developer, I know C++ and Perl among others, but I have seen that Python could be useful which is good because I have wanted to learn Python for a long time. However, only Perl is mentioned on the Create you own plugin page.
What would be the best option to create a plugin that would import the CUE file and create labels at the specified time indexes? Ideally it would also set the label name to the title of each track.
Maybe it would be easier to simply convert the cue file to a Label track that could be imported in Audacity?
Note: I have opened a cue file with Audacity (2.3.2). It opened the corresponding flac file but then gave an error about not recognizing the cue file (… try ffmpeg…)
Unrelated question: can the artist name be set from the label when exporting multiple? From what I know only the Track field is set.
Example of cue file:
REM DISCID 930E000A
REM COMMENT "ExactAudioCopy v0.99pb4"
PERFORMER "Georges Cziffra"
TITLE "CD1 - Cziffra Improvisations et transcriptions"
FILE "CD1.flac" WAVE
TRACK 01 AUDIO
TITLE "01 Danse du sabre (Khatchaturian, Gayaneh)"
PERFORMER "Georges Cziffra"
INDEX 01 00:00:00
TRACK 02 AUDIO
TITLE "02 Le vol du bourdon (Rimsky-Korsakov, Tsar Saltan)"
PERFORMER "Georges Cziffra"
INDEX 00 03:11:07
INDEX 01 03:14:08
TRACK 03 AUDIO
TITLE "03 La Danza (Rossini)"
PERFORMER "Georges Cziffra"
INDEX 00 06:09:66
INDEX 01 06:12:67
TRACK 04 AUDIO
TITLE "04 Improvisation sur des thmes de Guillaume Tell (Rossini)"
PERFORMER "Georges Cziffra"
INDEX 00 09:27:20
INDEX 01 09:30:21
TRACK 05 AUDIO
TITLE "05 Valse triste (von Vecsey)"
PERFORMER "Georges Cziffra"
INDEX 00 23:24:14
INDEX 01 23:27:44
TRACK 06 AUDIO
TITLE "06 Tritsch-Tratsch polka (J. Strauss)"
PERFORMER "Georges Cziffra"
INDEX 00 26:25:11
INDEX 01 26:27:53
TRACK 07 AUDIO
TITLE "07 Rminiscences de Johann Strauss"
PERFORMER "Georges Cziffra"
INDEX 00 29:52:56
INDEX 01 29:55:38
TRACK 08 AUDIO
TITLE "08 Le beau Danube bleu, paraphrase (J. Strauss)"
PERFORMER "Georges Cziffra"
INDEX 00 36:36:64
INDEX 01 36:40:20
TRACK 09 AUDIO
TITLE "09 Danse hongroise nбу5 (Brahms)"
PERFORMER "Georges Cziffra"
INDEX 00 45:08:14
INDEX 01 45:11:19
TRACK 10 AUDIO
TITLE "10 Fantaisie roumaine, improvisation sur deux airs folkloriques dans le style tzigane"
PERFORMER "Georges Cziffra"
INDEX 00 48:42:71
INDEX 01 48:46:15
I created a Python script that worked for me (i.e. the sample CUE file above).
It just uses the TITLE and INDEX 01 of each track to create the labels.
I’m sharing it here.
Note: I used Thonny to create it. https://thonny.org/
Copy or move the file in the same folder as python.exe (not necessary if typing python.exe is added to default path, in other words if it works from any folder)
Open a terminal or command prompt
Type python.exe cue2audacity.py directory where directory is the path where the CUE files are stored. Example for me on Windows: cue2audacity.py “C:\tempMusique\Complete - George Cziffra - 40CD” (the quotes are needed because there are spaces in the directory name).
#
# This program reads 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: Fabrice Baro
# Date: 2019-10-24
#
import argparse
import glob
import re
# 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(thePath + "\\*.cue"):
#print(cueFileName)
cueFile = open(cueFileName,"r")
cueFileContent = cueFile.readlines()
cueFile.close()
#print(cueFileContent)
# Create label file
labelFileName = re.search(".*\.",cueFileName).group(0) + "txt"
labelFile = open(labelFileName, "w") # this will overwrite any existing file with same directory/name
#print ("###" + labelFileName + "###")
# Process file
theTitle = "" # initialize title now in order to keep it available when scanning subsequent INDEX line
for line in cueFileContent:
# Match TITLE "Danse du sabre (Khatchaturian, Gayaneh)" and capture the text between " "
theMatchTitle = re.search("^ TITLE \"([^\"]+)\"", line)
if theMatchTitle is not None:
theTitle = theMatchTitle.group(1)
# Match INDEX 01 00:00:00 and capture the time 00:00:00
theMatchIndex = re.search("^ INDEX 01 ([\d]{2}):([\d]{2}):([\d]{2})", line)
if theMatchIndex is not None:
theMinutes = int(theMatchIndex.group(1))
theSeconds = int(theMatchIndex.group(2))
theFrames = int(theMatchIndex.group(3))
# Convert minutes, seconds and frames (mm:ss) to track start time (in seconds)
theStartTime = (theMinutes*60) + theSeconds + (theFrames*75)/10000 # there are 75 frames in 1 second; divide by 100 for seconds, and divide by 100 to convert to hundreths of a second
# format theStartTime
theStartTime = "{:.6f}".format(theStartTime)
#print (theStartTime + "\t" + theStartTime + "\t" + theTitle)
# Write time and title to label file
labelFile.write(theStartTime + "\t" + theStartTime + "\t" + theTitle + "\n")
# end for line in cueFileContent
# Close label file
labelFile.close()
# end for cueFileName in glob.glob(thePath + "\\*.cue")
For it to be cross-platform compatible, you need to change the hard-coded file path separator inthe line:
for cueFileName in glob.glob(thePath + "\\*.cue"):
It works for me adding:
import os
with the other imports, then rewriting that line as:
for cueFileName in glob.glob(thePath + os.sep + "*.cue"):
(it might actually be “better” to use pathlib, but the above works for me.)
For Linux / macOS, it is usual to add a “shebang” at the top of the file. There are a number of different forms this can take.
If the script will run on Python 2 or 3 (this one will):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
If the script requires Python 3
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
If a shebang is provided (and an appropriate version of Python is available), then Linux / macOS users can run the script by adding “executable” to the file permissions, then just using the file name. For example, if a terminal window is open in the same location as the script:
./test.py "path-to-files"
Also worth noting for Linux users, Python 3 may already be installed, but if not, then it’s recommended to install it from your distribution’s repository.
Here is an updated version. Works with Python 3.12.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This program reads CUE files and outputs a label track to be imported in Audacity.
# This is useful if you have a single FLAC or APE file containing many tracks.
#
# Author: Fabrice Baro
# 2019-10-24 Version 0.1 (Created using Thonny)
# 2023-12-01 Version 0.2
# Used os.sep in order to make it cross-platform, as per steve's suggestion https://forum.audacityteam.org/t/import-cue-sheets-to-labels-create-plugin/54147/3
# Updated reg ex matching: re.search(".*\." -> re.search(r'.*\.' (r is for raw matching) in order to fix SyntaxWarning: invalid escape sequence '\.'
#
#
# How to use:
# 1. Download and install Python https://www.python.org/downloads/
# 2. (for Windows) Copy or move the file in the same folder as python.exe (not necessary if typing python.exe is added to default path, in other words if it works from any folder)
# 3. Open a terminal or command prompt
# 4. Go to the directory where this file is stored, e.g. type:
# cd "C:\Users\Fabrice Baro\OneDrive\Documents\Code"
# 5. Type
# cue2audacity.py directory
# where directory is the path where the CUE files are stored.
# Example for me: cue2audacity.py “C:\tempMusique\Complete - George Cziffra - 40CD” (the quotes are needed because there are spaces in the directory name).
# 6. In Audacity, open the .flac or .ape file, then File > Import > Labels... then choose the file generated by this progam
# 7. Do File > Export > Export Multiple
#
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(thePath + "\\*.cue"):
for cueFileName in glob.glob(thePath + os.sep + "*.cue"):
#print("cuFileName: " + cueFileName)
cueFile = open(cueFileName,"r")
cueFileContent = cueFile.readlines()
cueFile.close()
#print(cueFileContent)
# Create label file
#labelFileName = re.search(".*\.",cueFileName).group(0) + "txt"
labelFileName = re.search(r'.*\.',cueFileName).group(0) + "txt"
labelFile = open(labelFileName, "w") # this will overwrite any existing file with same directory/name
print ("### Creating file: " + labelFileName + "###")
# Process file
theTitle = "" # initialize title now in order to keep it available when scanning subsequent INDEX line
for line in cueFileContent:
# Match TITLE "Danse du sabre (Khatchaturian, Gayaneh)" and capture the text between " "
theMatchTitle = re.search('^ TITLE \"([^\"]+)\"', line)
if theMatchTitle is not None:
theTitle = theMatchTitle.group(1)
# Match INDEX 01 00:00:00 and capture the time 00:00:00
theMatchIndex = re.search(r'^ INDEX 01 ([\d]{2}):([\d]{2}):([\d]{2})', line)
if theMatchIndex is not None:
theMinutes = int(theMatchIndex.group(1))
theSeconds = int(theMatchIndex.group(2))
theFrames = int(theMatchIndex.group(3))
# Convert minutes, seconds and frames (mm:ss) to track start time (in seconds)
theStartTime = (theMinutes*60) + theSeconds + (theFrames*75)/10000 # there are 75 frames in 1 second; divide by 100 for seconds, and divide by 100 to convert to hundreths of a second
# format theStartTime
theStartTime = "{:.6f}".format(theStartTime)
#print (theStartTime + "\t" + theStartTime + "\t" + theTitle)
# Write time and title to label file
labelFile.write(theStartTime + "\t" + theStartTime + "\t" + theTitle + "\n")
# end for line in cueFileContent
# Close label file
labelFile.close()
# end for cueFileName in glob.glob(thePath + "\\*.cue")