From 71bcf9ffd22d4afd83b2d626bac76db30d8a7073 Mon Sep 17 00:00:00 2001 From: Jackson Taylor Date: Sat, 6 Aug 2022 16:19:53 -0400 Subject: Use generic metadata dict This replaces the need to read the json file each time, or do weird stuff for the file name. --- jamos | 161 +++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 91 insertions(+), 70 deletions(-) diff --git a/jamos b/jamos index 587577c..9c666e2 100755 --- a/jamos +++ b/jamos @@ -9,6 +9,7 @@ from pathlib import Path import sys import youtube_dl + def cleanup_metadata_files(music_directory): files = glob.glob(os.path.join(music_directory, '*.json')) @@ -41,10 +42,22 @@ def get_all_files(directory): def get_command_line_options(): - parser = argparse.ArgumentParser(description="Download songs from YouTube Music") - parser.add_argument("url", metavar="string", type=str, help="Playlist or Song URL to download") - parser.add_argument("-c", "--cookies", metavar="string", type=str, help="Cookie file to use.") - parser.add_argument("-o", "--output", metavar="string", type=str, help="Output directory to use") + parser = argparse.ArgumentParser( + description="Download songs from YouTube Music") + parser.add_argument("url", + metavar="string", + type=str, + help="Playlist or Song URL to download") + parser.add_argument("-c", + "--cookies", + metavar="string", + type=str, + help="Cookie file to use.") + parser.add_argument("-o", + "--output", + metavar="string", + type=str, + help="Output directory to use") return parser.parse_args() @@ -66,26 +79,15 @@ def get_video_urls_in_playlist(playlist_url, ytdl): def move_file(file, metadata, output_directory): - # TODO: Pass a jamos specific metadata object/dict - # Then we can do this validation all at once - - artist = 'unknownartist' - album = 'unknownalbum' - title = 'unknownsong' - - if ('artist' in metadata.keys()) and (metadata['artist'] is not None): - if len(metadata['artist'].split(',')) > 1: - # If there are multiple artists, pick the first one - # NOTE: This may break if the artist has a comma in their name - artist = metadata['artist'].split(',')[0].replace(' ', '_').lower() - else: - artist = metadata['artist'].replace(' ', '_').lower() - - if ('album' in metadata.keys()) and (metadata['album'] is not None): - album = metadata['album'].replace(' ', '_').replace(',', '').replace("'", "").replace('"', "").lower() - - if ('title' in metadata.keys()) and (metadata['title'] is not None): - title = metadata['title'].replace(' ', '_').replace('/', '').lower() + artist = metadata['artist'].replace( + '-', ' ').replace(' ', '_').replace("'", "").replace( + "&", "and").lower() + album = metadata['album'].replace( + '-', ' ').replace(' ', '_').replace("'", "").replace( + "&", "and").lower() + title = metadata['title'].replace( + '-', ' ').replace(' ', '_').replace("'", "").replace( + "&", "and").lower() final_directory = os.path.join( output_directory, @@ -99,62 +101,78 @@ def move_file(file, metadata, output_directory): # TODO: Research better file formats over mp3? os.rename( file, - os.path.join(final_directory, artist + '_' + title + '.mp3')) + os.path.join(final_directory, '{}_{}_{}.mp3'.format(artist, + album, + title))) + + +def write_metadata_to_song_file(filename, metadata): + file = music_tag.load_file(filename) + file['name'] = metadata['title'] + file['artist'] = metadata['artist'] + file['album'] = metadata['album'] + file['year'] = metadata['year'] -def write_metadata_to_song_file(file, metadata): - f = music_tag.load_file(file) + file.save() - artist = 'unknownartist' - album = 'unknownalbum' - title = 'unknownsong' - year = 9999 - if ('artist' in metadata.keys()) and (metadata['artist'] is not None): - if len(metadata['artist'].split(',')) > 1: - # If there are multiple artists, pick the first one - # NOTE: This will break if the artist has a comma in their name - artist = metadata['artist'].split(',')[0].replace(' ', '_').lower() - else: - artist = metadata['artist'].replace(' ', '_').lower() +def get_song_metadata_from_json(json, counter): + metadata = { + 'title': 'unknownsong', + 'artist': 'unknownartist', + 'album': 'unknownalbum', + 'year': 1999, + 'jamos_filename': 'jamos_unknwon_file_number_{}.mp3'.format(counter), + } - artist = artist.replace('&', 'and') + try: + if ('artist' in json.keys()) and (json['artist'] is not None): + metadata['artist'] = json['artist'] + if len(metadata['artist'].split(',')) > 1: + # If there are multiple artists, pick the first one + # NOTE: This will break if the artist has a comma in their name + metadata['artist'] = metadata['artist'].split(',')[0] + + if ('album' in json.keys()) and (json['album'] is not None): + metadata['album'] = json['album'] - if ('album' in metadata.keys()) and (metadata['album'] is not None): - album = metadata['album'].replace(' ', '_').lower() + if ('title' in json.keys()) and (json['title'] is not None): + metadata['title'] = json['title'] - if ('title' in metadata.keys()) and (metadata['title'] is not None): - title = metadata['title'].replace(' ', '_').lower() + if ('release_date' in json.keys()): + metadata['year'] = format_youtube_date(json['release_date']) - if ('release_date' in metadata.keys()): - year = format_youtube_date(metadata['release_date']) + artist_for_filename = metadata['artist'].replace(' ', '_').lower() + title_for_filename = metadata['title'].replace(' ', '_').lower() + metadata['jamos_filename'] = '{}_{}.mp3'.format(artist_for_filename, + title_for_filename) - f['name'] = title - f['artist'] = artist - f['album'] = album - f['year'] = year + except Exception as ex: + print(ex) - f.save() + return metadata def create_downloader(music_directory, cookies): audio_options = { - 'format': 'mp3/bestaudio/best', - 'cookiefile': cookies, - 'outtmpl': music_directory + '%(title)s.%(ext)s', - 'postprocessors': [ - { - 'key': 'FFmpegExtractAudio', - 'preferredcodec': 'mp3', - 'preferredquality': '192', - }, - {'key': 'FFmpegMetadata'}, - ], - 'writeinfojson': True - } + 'format': 'mp3/bestaudio/best', + 'cookiefile': cookies, + 'outtmpl': music_directory + '%(title)s.%(ext)s', + 'postprocessors': [ + { + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': '192', + }, + {'key': 'FFmpegMetadata'}, + ], + 'writeinfojson': True + } return youtube_dl.YoutubeDL(audio_options) + def save_urls_from_playlist_to_file(filename, urls): try: f = open(filename, "w") @@ -165,6 +183,7 @@ def save_urls_from_playlist_to_file(filename, urls): print(e) raise e + if __name__ == "__main__": args = get_command_line_options() @@ -174,15 +193,16 @@ if __name__ == "__main__": music_directory = args.output or "~/Music" cookies = args.cookies or "~/cookies.txt" - # From some testing, if your playlist is public, you don't have to use a cookie file. - # Youtube-dl doesn't break or throw if the file doesn't exist. + # From some testing, if your playlist is public, you don't have to use a + # cookie file. Youtube-dl doesn't break or throw if the file doesn't exist. ytdl = create_downloader(music_directory, cookies) # TODO: Save urls to file so we can start in the # middle of the playlist if needed urls = get_video_urls_in_playlist(playlist_url, ytdl) - save_urls_from_playlist_to_file("~/jamos_urls.txt", urls) + save_urls_from_playlist_to_file(os.path.join(music_directory, + "jamos_urls.txt"), urls) for url in urls: try: @@ -193,16 +213,17 @@ if __name__ == "__main__": files = get_all_files(music_directory) + counter = 1 for f in files: try: - json_data = None with open(f.replace('.mp3', '.info.json')) as json_file: json_data = json.load(json_file) - write_metadata_to_song_file(f, json_data) - move_file(f, json_data, music_directory) + metadata = get_song_metadata_from_json(json_data, counter) + write_metadata_to_song_file(f, metadata) + move_file(f, metadata, music_directory) + counter += 1 except Exception as e: # just gonna print this and move on to the next file. print(e) cleanup_metadata_files(music_directory) - -- cgit v1.2.3