Skip to content

Instantly share code, notes, and snippets.

@r-a-y
Forked from joshbarrass/cuemaker.py
Last active February 1, 2024 02:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save r-a-y/01873769c8722f786f94ff92e89f8d5c to your computer and use it in GitHub Desktop.
Save r-a-y/01873769c8722f786f94ff92e89f8d5c to your computer and use it in GitHub Desktop.
Python code to generate cue sheets (.cue files) from YouTube timestamps
"""
A script for automatically generating CUE files from a list of timestamps and titles, like
those commonly seen in YouTube descriptions.
Usage:
python cuemaker.py --output="Filename without extension" "timestamps.txt" "Album name" "Artist name"
"timestamps.txt" should be a text file containing a list of timestamps and titles like:
00:00 Track 1
03:00 Track 2
The FILE in the generated CUE file defaults to the .opus extension.
If your audio file is .m4a, add the following flag: --ext="m4a"
Original Author: JoshBarrass
Contributing Authors: TheScienceOtter (https://gitlab.com/TheScienceOtter/cuemaker)), r-a-y
Last Edit: 05/25/2022
"""
import re
import argparse
def pad_number(number,length=2,padding="0"):
str_number = str(number)
if len(str_number) < length:
str_number = padding+str_number
return str_number
def make_cue_tracks(inp,pattern="(\[)?((\\d{1,2}):)?(\\d{1,2}):(\\d{1,2})(\])? (.*)",
hr=2,m=3,s=4,title=6,artist=None):
matcher = re.compile(pattern)
output = ""
lines = inp.strip("\n").split("\n")
if len(lines) > 99:
raise ValueError("A cue sheet cannot contain more than 99 tracks!")
for line in range(len(lines)):
lines[line] = lines[line].strip()
str_track = pad_number(line+1)
match = matcher.match(lines[line])
groups = list(match.groups())
if groups[hr] == None: groups[hr] = "00"
output += "\n TRACK {n} AUDIO\n".format(n=str_track)
output += " TITLE \"{title}\"\n".format(title=groups[title])
if isinstance(artist,int):
output += " PERFORMER {artist}\n".format(artist=groups[artist])
output += " INDEX 01 {m}:{s}:00".format(m=pad_number(int(groups[hr])*60+int(groups[m])),s=pad_number(groups[s]))
return output
def make_cue(inp,performer,album,filename,ext,rems={},*args,**kwargs):
"""Takes input text, artist, and album name, filename and type to produce a cue sheet. Optionally takes 'pattern' argument with 'hr', 'm', 's', and 'title' arguments to specify new regex patterns and group indices. 'artist' argument specifies artist regex index for tracks that have different artists."""
output = "PERFORMER \"{performer}\"\nTITLE \"{album}\"\n".format(performer=performer,album=album)
for key,item in rems.items():
output+="REM {k} {i}\n".format(k=key,i=item)
output += "FILE \"{f}.{e}\" WAVE".format(f=filename,e=ext)
output += make_cue_tracks(inp,*args,**kwargs)
output += "\n"
return output
def read_description(path):
f = open(path, "r")
description = f.read()
f.close()
return description
def save_cue(path, data):
with open(path, "w") as f:
f.write(data)
f.close()
return True
if __name__ == "__main__":
# python3 cuemaker "description.txt" "album name" "performer"
parser = argparse.ArgumentParser()
parser.add_argument("description_path", help="Path to the description file containing timestamps.")
parser.add_argument("album", help="Display name of the album enclosed in quotes.")
parser.add_argument("performer", help="Display name of the artist/performer, enclosed in quotes.")
parser.add_argument("--pattern", default="(\[)?((\\d{1,2}):)?(\\d{1,2}):(\\d{1,2})(\])? (.*)", nargs='?', help="A Regex pattern to match on the description file. If this is changed the --hr, --m, --s, and --title, options should also be defined to capture the correct regex groups.")
parser.add_argument("--hr", default=2, nargs='?', help="Specify the Regex group corresponding to the hour digit(s).")
parser.add_argument("--m", default=3, nargs='?', help="Specify the Regex group corresponding to the minutes digit(s).")
parser.add_argument("--s", default=4, nargs='?', help="Specify the Regex group corresponding to the seconds digit(s).")
parser.add_argument("--title", default=6, nargs='?', help="Specify the Regex group corresponding to the title.")
parser.add_argument("--ext", default="opus", nargs='?', help="Extension of your audio file. Defaults to \"opus\"")
parser.add_argument("--output", default="output", nargs='?', help="THe name of the output file. Defaults to \"output\"")
args = parser.parse_args()
# Try read description file
description = read_description(args.description_path)
# Read given data
if args.output == "output": args.output = args.description_path.rsplit('.', 1)[0]
filename = args.output
# make .cue data
output = make_cue(description, args.performer, args.album, filename, args.ext,
pattern=args.pattern, hr=args.hr,
m=args.m, s=args.s, title=args.title)
# Save .cue file
save_cue(filename + ".cue", output)
print("Done!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment