From 915cdf836d602a0626023b9c88dcc285470d3d8e Mon Sep 17 00:00:00 2001 From: Jack Clifton Date: Sun, 8 Nov 2026 11:10:07 +0000 Subject: [PATCH] more refactoring and made source file stable - WIP albums stored in source.txt not in repo --- source.json | 143 +++++++++++++---------------- ytaml.py | 260 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 221 insertions(+), 182 deletions(-) diff --git a/source.json b/source.json index 1612989..a688882 100644 --- a/source.json +++ b/source.json @@ -133,43 +133,43 @@ "url": "https://www.youtube.com/watch?v=rY0WxgSXdEE" }, { "track": 3, "title": "Killer Queen", - "url": "" }, + "url": "https://www.youtube.com/watch?v=2ZBtPf7FOoM" }, { "track": 4, "title": "Fat Bottomed Girls", "url": "https://www.youtube.com/watch?v=VMnjF1O4eH0" }, { "track": 5, "title": "Bicycle Race", - "url": "https://www.youtube.com/watch?v=GugsCdLHm-Q" }, + "url": "https://www.youtube.com/watch?v=xt0V0_1MS0Q" }, { "track": 6, "title": "You're My Best Friend", - "url": "" }, + "url": "https://www.youtube.com/watch?v=HaZpZQG2z10" }, { "track": 7, "title": "Don't Stop Me Now", "url": "https://www.youtube.com/watch?v=HgzGwKwLmgM" }, { "track": 8, "title": "Save Me", - "url": "" }, + "url": "https://www.youtube.com/watch?v=Iw3izcZd9zU" }, { "track": 9, "title": "Crazy Little Thing Called Love", - "url": "" }, + "url": "https://www.youtube.com/watch?v=zO6D_BAuYCI" }, { "track": 10, "title": "Somebody to Love", "url": "https://www.youtube.com/watch?v=kijpcUv-b8M" }, { "track": 11, "title": "Now I'm Here", - "url": "" }, + "url": "https://www.youtube.com/watch?v=AH9mdnogLKk" }, { "track": 12, "title": "Good Old-Fashioned Lover Boy", - "url": "https://www.youtube.com/watch?v=PI3LAgGBxqU" }, + "url": "https://www.youtube.com/watch?v=iAutGC_uX-k" }, { "track": 13, "title": "Play the Game", - "url": "" }, + "url": "https://www.youtube.com/watch?v=6_5O-nUiZ_0" }, { "track": 14, "title": "Flash", - "url": "" }, + "url": "https://www.youtube.com/watch?v=LfmrHTdXgK4" }, { "track": 15, "title": "Seven Seas of Rhye", - "url": "" }, + "url": "https://www.youtube.com/watch?v=0e4Odk-v3oU" }, { "track": 16, "title": "We Will Rock You", "url": "https://www.youtube.com/watch?v=-tJYN-eG1zk" }, @@ -186,34 +186,34 @@ "tracks": [ { "track": 1, "title": "A Kind of Magic", - "url": "" }, + "url": "https://www.youtube.com/watch?v=0p_1QSUsbsM" }, { "track": 2, "title": "Under Pressure", "url": "https://www.youtube.com/watch?v=a01QQZyl-_I" }, { "track": 3, "title": "Radio Ga Ga", - "url": "" }, + "url": "https://www.youtube.com/watch?v=azdwsXLmrHE" }, { "track": 4, "title": "I Want It All", "url": "https://www.youtube.com/watch?v=hFDcoX7s6rE" }, { "track": 5, "title": "I Want to Break Free", - "url": "" }, + "url": "https://www.youtube.com/watch?v=WUOtCLOXgm8" }, { "track": 6, "title": "Innuendo", - "url": "" }, + "url": "https://www.youtube.com/watch?v=g2N0TkfrQhY" }, { "track": 7, "title": "It's a Hard Life", "url": "https://www.youtube.com/watch?v=uHP-qgzUVLM" }, { "track": 8, "title": "Breakthru", - "url": "" }, + "url": "https://www.youtube.com/watch?v=CEjU9KVABao" }, { "track": 9, "title": "Who Wants to Live Forever", - "url": "" }, + "url": "https://www.youtube.com/watch?v=_Jtpf8N5IDE" }, { "track": 10, "title": "Headlong", - "url": "" }, + "url": "https://www.youtube.com/watch?v=zhyaAPsT1LU" }, { "track": 11, "title": "The Miracle", "url": "https://www.youtube.com/watch?v=2DaY8-Mui0I" }, @@ -222,78 +222,19 @@ "url": "https://www.youtube.com/watch?v=Od6hY_50Dh0" }, { "track": 13, "title": "The Invisible Man", - "url": "" }, + "url": "https://www.youtube.com/watch?v=zKdxd718WXg" }, { "track": 14, "title": "Hammer to Fall", "url": "https://www.youtube.com/watch?v=JU5LMG3WFBw" }, { "track": 15, "title": "Friends Will Be Friends", - "url": "" }, + "url": "https://www.youtube.com/watch?v=0AIlz08fZos" }, { "track": 16, "title": "The Show Must Go On", "url": "https://www.youtube.com/watch?v=t99KH0TR-J4" }, { "track": 17, "title": "One Vision", - "url": "" } - ] - }, - { - "artist": "Queen", - "album": "Greatest Hits III", - "year": "1999", - "cover_img_url": "https://image-cdn-ak.spotifycdn.com/image/ab67616d00001e024f27ae739d2040962d8b4c4f", - "tracks": [ - { "track": 1, - "title": "The Show Must Go On (Live featuring Elton John)", - "url": "" }, - { "track": 2, - "title": "Under Pressure (Rah Mix with David Bowie)", - "url": "" }, - { "track": 3, - "title": "Barcelona (Freddie Mercury & Montserrat Caballé)", - "url": "https://www.youtube.com/watch?v=Y1fiOJDXA-E" }, - { "track": 4, - "title": "Too Much Love Will Kill You", - "url": "" }, - { "track": 5, - "title": "Somebody to Love (Live featuring George Michael)", - "url": "" }, - { "track": 6, - "title": "You Don't Fool Me", - "url": "" }, - { "track": 7, - "title": "Heaven for Everyone", - "url": "" }, - { "track": 8, - "title": "Las Palabras de Amor (The Words of Love)", - "url": "" }, - { "track": 9, - "title": "Driven by You (Brian May)", - "url": "" }, - { "track": 10, - "title": "Living on My Own (Freddie Mercury)", - "url": "" }, - { "track": 11, - "title": "Let Me Live", - "url": "" }, - { "track": 12, - "title": "The Great Pretender (Freddie Mercury)", - "url": "" }, - { "track": 13, - "title": "Princes of the Universe", - "url": "" }, - { "track": 14, - "title": "Another One Bites the Dust (featuring Wyclef Jean)", - "url": "" }, - { "track": 15, - "title": "No-One but You (Only the Good Die Young)", - "url": "" }, - { "track": 16, - "title": "These Are the Days of Our Lives", - "url": "" }, - { "track": 17, - "title": "Thank God It's Christmas", - "url": "" } + "url": "https://www.youtube.com/watch?v=0_1IMZmJe-U" } ] }, { @@ -387,5 +328,49 @@ "url": "https://www.youtube.com/watch?v=noWUQci7BsU" } ] + }, + { + "artist": "Ashley Parker Angel", + "album": "Soundtrack To Your Life", + "year": "2006", + "cover_img_url": "https://image-cdn-ak.spotifycdn.com/image/ab67616d00001e02cf144905cb38295a2e14a4d8", + "tracks": [ + { "track": 1, + "title": "Let U Go", + "url": "https://www.youtube.com/watch?v=PeR5zqbWYH8&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=2" }, + { "track": 2, + "title": "I'm Better", + "url": "https://www.youtube.com/watch?v=hClsmvAE5qA&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=3" }, + { "track": 3, + "title": "Soundtrack to Your Life", + "url": "https://www.youtube.com/watch?v=UVhgkf8jqWM&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=4" }, + { "track": 4, + "title": "Feel So Alive", + "url": "https://www.youtube.com/watch?v=f-Fm04G7WVQ&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=5" }, + { "track": 5, + "title": "Crazy Beautiful", + "url": "https://www.youtube.com/watch?v=2wYZQUlThbo&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=6" }, + { "track": 6, + "title": "Who Cares?", + "url": "https://www.youtube.com/watch?v=GXPOwt7rExk&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=7" }, + { "track": 7, + "title": "Shades of Blue", + "url": "https://www.youtube.com/watch?v=3xWbi_DFb7I&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=9" }, + { "track": 8, + "title": "Beautiful Lie", + "url": "https://www.youtube.com/watch?v=GOlhXWDaiLU&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=9" }, + { "track": 9, + "title": "Perfect Now", + "url": "https://www.youtube.com/watch?v=8RVfieLxmjU&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=10" }, + { "track": 10, + "title": "Where Did You Go?", + "url": "https://www.youtube.com/watch?v=o7Me7K1w4w0&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=11" }, + { "track": 11, + "title": "Along the Way", + "url": "https://www.youtube.com/watch?v=Z4Eqdl0PXxE&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=12" }, + { "track": 12, + "title": "Apology", + "url": "https://www.youtube.com/watch?v=cdboQaj3k-E&list=OLAK5uy_nACGMEPpZ5SqH8yuOCSnBU6DYrCIKzQGQ&index=13" } + ] } ] \ No newline at end of file diff --git a/ytaml.py b/ytaml.py index 753770e..1af3f8b 100644 --- a/ytaml.py +++ b/ytaml.py @@ -4,135 +4,189 @@ import json import yt_dlp -def download_playlist_for_cd(playlist_url: str, output_folder: str = "cd_audio"): - """Downloads YouTube audio directly into 44.1kHz 16-bit WAV files +g_OUTPUT_FOLDER = "cd_audio" - ideal for burning directly to Audio CDs without secondary re-encoding loss. - """ - if not os.path.exists(output_folder): - os.makedirs(output_folder) - ydl_opts = { - # Grab best audio stream available - "format": "bestaudio/best", - # Prefix with track index (01, 02...) so CD track order matches playlist order - "outtmpl": os.path.join(output_folder, "%(playlist_index)s - %(title)s.%(ext)s"), - "postprocessors": [ - { - "key": "FFmpegExtractAudio", - "preferredcodec": "wav", # Lossless format native to CD Burning - } - ], - # Enforce Red Book CD Standard audio resampling (44.1 kHz) - "postprocessor_args": [ - "-ar", "44100", # Set sample rate to 44.1 kHz (CD Standard) - "-ac", "2", # Force 2 channels (Stereo) - ], - "ignoreerrors": True, - } - print(f"Downloading audio optimized for CD burning: {playlist_url}") - with yt_dlp.YoutubeDL(ydl_opts) as ydl: - ydl.download([playlist_url]) +def generate_ytdl_opts(outtmpl: str, metadata_artist: str = "", metadata_title: str = ""): + return { + "format": "bestaudio/best", + "noplaylist": True, + "outtmpl": outtmpl, + "postprocessors": [ + { + "key": "FFmpegExtractAudio", + "preferredcodec": "wav", + } + ], + "postprocessor_args": [ + "-ar", "44100", + "-ac", "2", + "-metadata", f"artist={metadata_artist}", + "-metadata", f"title={metadata_title}", + ], + "ignoreerrors": True, + } - print("\nDownload complete! Import these .wav files directly into your CD burner.") + + +def download_playlist_for_cd(playlist_url: str): + ytdl_opts = generate_ytdl_opts( + os.path.join(g_OUTPUT_FOLDER, "%(playlist_index)s - %(title)s.%(ext)s") + ) + + print(f"Downloading audio optimized for CD burning: {playlist_url}") + with yt_dlp.YoutubeDL(ytdl_opts) as ytdl: + ytdl.download([playlist_url]) + + print("\nDownload complete! Import these .wav files directly into your CD burner.") def download_single_video(url, song_num, artist, title): - output_folder = "cd_audio" - if not os.path.exists(output_folder): - os.makedirs(output_folder) + ytdl_opts = generate_ytdl_opts( + os.path.join(g_OUTPUT_FOLDER, f"{song_num} - {artist} - {title}.%(ext)s"), + artist, title + ) - ytdl_opts = { - "format": "bestaudio/best", - "noplaylist": True, - "outtmpl": os.path.join(output_folder, f"{song_num} - {artist} - {title}.%(ext)s"), - "postprocessors": [ - { - "key": "FFmpegExtractAudio", - "preferredcodec": "wav", - } - ], - "postprocessor_args": [ - "-ar", "44100", # Set sample rate to 44.1 kHz (CD Standard) - "-ac", "2", # Force 2 channels (Stereo) - "-metadata", f"artist={artist}", - "-metadata", f"title={title}", - ], - "ignoreerrors": True, - } + with yt_dlp.YoutubeDL(ytdl_opts) as ytdl: + ytdl.download([url]) - with yt_dlp.YoutubeDL(ytdl_opts) as ytdl: - ytdl.download([url]) def parse_file(filename): - class Track: - def __init__(self, track: int, title: str, url: str): - self.track = track - self.title = title - self.url = url - - class Album: - def __init__(self, artist: str, album: str): - self.artist = artist - self.album = album - self.tracks = [] - def add_track(self, track: Track): - self.tracks.append(track) - - with open(filename, "r") as file: - data = json.load(file) + class Track: + def __init__(self, track: int, title: str, url: str): + self.track = track + self.title = title + self.url = url - - albums = [] + class Album: + def __init__(self, artist: str, album: str): + self.artist = artist + self.album = album + self.tracks = [] + def add_track(self, track: Track): + self.tracks.append(track) - for album in data: + with open(filename, "r") as file: + data = json.load(file) - try: - new_album = Album( - album["artist"], - album["album"]) - for video in album["tracks"]: - new_album.add_track(Track( - video["track"], - video["title"], - video["url"] - )) - except KeyError as e: - print(e) - continue - albums.append(new_album) + albums = [] - return albums + for album in data: + + try: + new_album = Album( + album["artist"], + album["album"]) + for video in album["tracks"]: + new_album.add_track(Track( + video["track"], + video["title"], + video["url"] + )) + except KeyError as e: + print(e) + continue + + albums.append(new_album) + + return albums + + + +def print_help(): + print("-h print help and exit") + print("-f select a file (required)") + print("-a download all") + print("-d download a specific album") + + +def parse_args(argv): + + class Ret: + file = None + downloads = None + ret = Ret() + + cache_arg_count = len(argv) + arg_idx = 1 + + while arg_idx < cache_arg_count: + + if argv[arg_idx] == "-h": + print_help() + exit(1) + + elif argv[arg_idx] == "-f": + if arg_idx + 1 >= cache_arg_count: + raise ValueError("-f - is missing the filename") + + ret.file = argv[arg_idx + 1] + + arg_idx += 2 + continue + + elif argv[arg_idx] == "-a": + + if ret.downloads != None: + raise ValueError("Cannot use -a twice, or in combination with -d") + + ret.downloads = "" + + arg_idx += 1 + continue + + elif argv[arg_idx] == "-d": + + if ret.downloads != None: + raise ValueError("Cannot use -d twice, or in combination with -a") + + if arg_idx + 1 >= cache_arg_count: + raise ValueError("-d - is missing the album name") + + ret.downloads = argv[arg_idx + 1] + + arg_idx += 2 + continue + + else: + print_help() + exit(2) + + return ret if __name__ == "__main__": - if len(sys.argv) == 1: - url = input("Enter YouTube playlist URL: ").strip() - if url: - download_playlist_for_cd(url) + config = parse_args(sys.argv) - else: - data = parse_file(" ".join(sys.argv[1:])) - for album in data: - print(f"Band: {album.artist}") - print(f"Album: {album.album}") + if not os.path.exists(g_OUTPUT_FOLDER): + os.makedirs(g_OUTPUT_FOLDER) - for video in album.tracks: - print(f"\tTrack {video.track}: {video.title}") - if album.album == "Even In Arcadia": - download_single_video( - video.url, - video.track, - f"{album.artist} - {album.album}", - video.title - ) + data = parse_file(config.file) + + + for album in data: + + if not (config.downloads == "" or config.downloads == album.album): + continue + + print(f"Band: {album.artist}") + print(f"Album: {album.album}") + + for video in album.tracks: + print(f"\tTrack {video.track}: {video.title}") + download_single_video( + video.url, + video.track, + f"{album.artist} - {album.album}", + video.title + )