mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Implement --manual
This commit is contained in:
		| @@ -4,8 +4,8 @@ from spotdl import command_line | |||||||
| import sys | import sys | ||||||
|  |  | ||||||
| def match_arguments(arguments): | def match_arguments(arguments): | ||||||
|     if arguments.tracks: |     if arguments["song"]: | ||||||
|         for track in arguments.tracks: |         for track in arguments["song"]: | ||||||
|             if track == "-": |             if track == "-": | ||||||
|                 for line in sys.stdin: |                 for line in sys.stdin: | ||||||
|                     command_line.helpers.download_track( |                     command_line.helpers.download_track( | ||||||
| @@ -14,36 +14,36 @@ def match_arguments(arguments): | |||||||
|                     ) |                     ) | ||||||
|             else: |             else: | ||||||
|                 command_line.helpers.download_track(track, arguments) |                 command_line.helpers.download_track(track, arguments) | ||||||
|     elif arguments.list: |     elif arguments["list"]: | ||||||
|         if arguments.write_m3u: |         if arguments["write_m3u"]: | ||||||
|             youtube_tools.generate_m3u( |             youtube_tools.generate_m3u( | ||||||
|                 track_file=arguments.list |                 track_file=arguments["list"] | ||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             list_download = { |             list_download = { | ||||||
|                 "sequential": command_line.helpers.download_tracks_from_file, |                 "sequential": command_line.helpers.download_tracks_from_file, | ||||||
|                 "threaded"  : command_line.helpers.download_tracks_from_file_threaded, |                 "threaded"  : command_line.helpers.download_tracks_from_file_threaded, | ||||||
|             }[arguments.processor] |             }[arguments["processor"]] | ||||||
|  |  | ||||||
|             list_download( |             list_download( | ||||||
|                 arguments.list, |                 arguments["list"], | ||||||
|                 arguments, |                 arguments, | ||||||
|             ) |             ) | ||||||
|     elif arguments.playlist: |     elif arguments["playlist"]: | ||||||
|         spotify_tools.write_playlist( |         spotify_tools.write_playlist( | ||||||
|             playlist_url=arguments.playlist, text_file=arguments.write_to |             playlist_url=arguments["playlist"], text_file=arguments["write_to"] | ||||||
|         ) |         ) | ||||||
|     elif arguments.album: |     elif arguments["album"]: | ||||||
|         spotify_tools.write_album( |         spotify_tools.write_album( | ||||||
|             album_url=arguments.album, text_file=arguments.write_to |             album_url=arguments["album"], text_file=arguments["write_to"] | ||||||
|         ) |         ) | ||||||
|     elif arguments.all_albums: |     elif arguments.all_albums: | ||||||
|         spotify_tools.write_all_albums_from_artist( |         spotify_tools.write_all_albums_from_artist( | ||||||
|             artist_url=arguments.all_albums, text_file=arguments.write_to |             artist_url=arguments["all_albums"], text_file=arguments["write_to"] | ||||||
|         ) |         ) | ||||||
|     elif arguments.username: |     elif arguments.username: | ||||||
|         spotify_tools.write_user_playlist( |         spotify_tools.write_user_playlist( | ||||||
|             username=arguments.username, text_file=arguments.write_to |             username=arguments["username"], text_file=arguments["write_to"] | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -59,7 +59,7 @@ def main(): | |||||||
|     # logzero.setup_default_logger(formatter=const._formatter, level=const.args.log_level) |     # logzero.setup_default_logger(formatter=const._formatter, level=const.args.log_level) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         match_arguments(arguments) |         match_arguments(arguments.__dict__) | ||||||
|     except KeyboardInterrupt as e: |     except KeyboardInterrupt as e: | ||||||
|         # log.exception(e) |         # log.exception(e) | ||||||
|         sys.exit(2) |         sys.exit(2) | ||||||
|   | |||||||
| @@ -51,18 +51,10 @@ def get_arguments(argv=None, to_merge=True): | |||||||
|  |  | ||||||
|     group = parser.add_mutually_exclusive_group(required=True) |     group = parser.add_mutually_exclusive_group(required=True) | ||||||
|  |  | ||||||
|     # TODO: --song is deprecated. Remove in future versions. |  | ||||||
|     #       Use --tracks instead. |  | ||||||
|     group.add_argument( |     group.add_argument( | ||||||
|         "-s", |         "-s", | ||||||
|         "--song", |         "--song", | ||||||
|         nargs="+", |         nargs="+", | ||||||
|         help=argparse.SUPPRESS |  | ||||||
|     ) |  | ||||||
|     group.add_argument( |  | ||||||
|         "-t", |  | ||||||
|         "--tracks", |  | ||||||
|         nargs="+", |  | ||||||
|         help="download track(s) by spotify link or name" |         help="download track(s) by spotify link or name" | ||||||
|     ) |     ) | ||||||
|     group.add_argument( |     group.add_argument( | ||||||
| @@ -115,13 +107,6 @@ def get_arguments(argv=None, to_merge=True): | |||||||
|         help="do not embed metadata in tracks", |         help="do not embed metadata in tracks", | ||||||
|         action="store_true", |         action="store_true", | ||||||
|     ) |     ) | ||||||
|     parser.add_argument( |  | ||||||
|         "-nf", |  | ||||||
|         "--no-fallback-metadata", |  | ||||||
|         default=config["no-fallback-metadata"], |  | ||||||
|         help="do not use YouTube as fallback for metadata if track not found on Spotify", |  | ||||||
|         action="store_true", |  | ||||||
|     ) |  | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         "-a", |         "-a", | ||||||
|         "--avconv", |         "--avconv", | ||||||
| @@ -190,13 +175,6 @@ def get_arguments(argv=None, to_merge=True): | |||||||
|         "are to be surrounded by curly braces. Possible tags: " |         "are to be surrounded by curly braces. Possible tags: " | ||||||
|         # "{}".format([spotdl.util.formats[x] for x in spotdl.util.formats]), |         # "{}".format([spotdl.util.formats[x] for x in spotdl.util.formats]), | ||||||
|     ) |     ) | ||||||
|     parser.add_argument( |  | ||||||
|         "-dm", |  | ||||||
|         "--download-only-metadata", |  | ||||||
|         default=config["download-only-metadata"], |  | ||||||
|         help="download tracks only whose metadata is found", |  | ||||||
|         action="store_true", |  | ||||||
|     ) |  | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         "-d", |         "-d", | ||||||
|         "--dry-run", |         "--dry-run", | ||||||
| @@ -210,15 +188,17 @@ def get_arguments(argv=None, to_merge=True): | |||||||
|         "--music-videos-only", |         "--music-videos-only", | ||||||
|         default=config["music-videos-only"], |         default=config["music-videos-only"], | ||||||
|         help="search only for music videos on Youtube (works only " |         help="search only for music videos on Youtube (works only " | ||||||
|         "when YouTube API key is set", |         "when YouTube API key is set)", | ||||||
|         action="store_true", |         action="store_true", | ||||||
|     ) |     ) | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         "--processor", |         "--processor", | ||||||
|         default=config["processor"], |         default=config["processor"], | ||||||
|  |         choices={"synchronous", "threaded"}, | ||||||
|         help='list downloading strategy: - "synchronous" downloads ' |         help='list downloading strategy: - "synchronous" downloads ' | ||||||
|         'tracks one-by-one. - "threaded" (highly experimental!) pre-fetches ' |         'tracks one-by-one. - "threaded" (highly experimental at the ' | ||||||
|         'the next track\'s metadata for more efficient downloading' |         'moment! expect it to slash & burn) pre-fetches the next ' | ||||||
|  |         'track\'s metadata for more efficient downloading' | ||||||
|     ) |     ) | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         "-ns", |         "-ns", | ||||||
| @@ -321,13 +301,6 @@ def run_errands(parser, parsed, config): | |||||||
|         #          "output format".format(parsed.encoder)) |         #          "output format".format(parsed.encoder)) | ||||||
|         parsed.encoder = "null" |         parsed.encoder = "null" | ||||||
|  |  | ||||||
|     song_parameter_passed = parsed.song is not None and parsed.tracks is None |  | ||||||
|     if song_parameter_passed: |  | ||||||
|         # log.warn("-s / --song is deprecated and will be removed in future versions. " |  | ||||||
|         #          "Use -t / --tracks instead.") |  | ||||||
|         setattr(parsed, "tracks", parsed.song) |  | ||||||
|     del parsed.song |  | ||||||
|  |  | ||||||
|     if parsed.output_file == "-" and parsed.no_metadata is False: |     if parsed.output_file == "-" and parsed.no_metadata is False: | ||||||
|         # log.warn( |         # log.warn( | ||||||
|         #     "Cannot write metadata when target file is STDOUT. Pass " |         #     "Cannot write metadata when target file is STDOUT. Pass " | ||||||
|   | |||||||
| @@ -40,7 +40,23 @@ def search_metadata_on_spotify(query): | |||||||
|     return metadata |     return metadata | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def prompt_for_youtube_search_result(videos): | ||||||
|  |     urls = [] | ||||||
|  |     print("0. Skip downloading this track") | ||||||
|  |     for index, video in enumerate(videos, 1): | ||||||
|  |         video_repr = "{index}. {title} [{duration}] ({url})".format( | ||||||
|  |             index=index, | ||||||
|  |             title=video["title"], | ||||||
|  |             duration=video["duration"], | ||||||
|  |             url=video["url"] | ||||||
|  |         ) | ||||||
|  |         print(video_repr) | ||||||
|  |         urls.append(video["url"]) | ||||||
|  |     return spotdl.util.prompt_user_for_selection(urls) | ||||||
|  |  | ||||||
|  |  | ||||||
| def search_metadata(track, search_format="{artist} - {track-name} lyrics", manual=False): | def search_metadata(track, search_format="{artist} - {track-name} lyrics", manual=False): | ||||||
|  |     # TODO: Clean this function | ||||||
|     youtube = ProviderYouTube() |     youtube = ProviderYouTube() | ||||||
|     youtube_searcher = YouTubeSearch() |     youtube_searcher = YouTubeSearch() | ||||||
|  |  | ||||||
| @@ -52,16 +68,16 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua | |||||||
|             spotify_metadata, |             spotify_metadata, | ||||||
|         ) |         ) | ||||||
|         search_query = spotdl.metadata.format_string(search_format, spotify_metadata) |         search_query = spotdl.metadata.format_string(search_format, spotify_metadata) | ||||||
|         youtube_urls = youtube_searcher.search(search_query) |         youtube_videos = youtube_searcher.search(search_query) | ||||||
|         if not youtube_urls: |         if not youtube_videos: | ||||||
|             raise NoYouTubeVideoError( |             raise NoYouTubeVideoError( | ||||||
|                 'No videos found for the search query: "{}"'.format(search_query) |                 'No videos found for the search query: "{}"'.format(search_query) | ||||||
|             ) |             ) | ||||||
|         if manual: |         if manual: | ||||||
|             pass |             youtube_video = prompt_for_youtube_search_result(youtube_videos) | ||||||
|         else: |         else: | ||||||
|             youtube_url = youtube_urls.bestmatch()["url"] |             youtube_video = youtube_videos.bestmatch()["url"] | ||||||
|         youtube_metadata = youtube.from_url(youtube_url) |         youtube_metadata = youtube.from_url(youtube_video) | ||||||
|         metadata = spotdl.util.merge( |         metadata = spotdl.util.merge( | ||||||
|             youtube_metadata, |             youtube_metadata, | ||||||
|             spotify_metadata |             spotify_metadata | ||||||
| @@ -81,17 +97,17 @@ def search_metadata(track, search_format="{artist} - {track-name} lyrics", manua | |||||||
|             args=(track,) |             args=(track,) | ||||||
|         ) |         ) | ||||||
|         spotify_metadata.start() |         spotify_metadata.start() | ||||||
|         youtube_urls = youtube_searcher.search(track) |         youtube_videos = youtube_searcher.search(track) | ||||||
|         if not youtube_urls: |         if not youtube_videos: | ||||||
|         #     raise NoYouTubeVideoError( |             raise NoYouTubeVideoError( | ||||||
|         #         'No videos found for the search query: "{}"'.format(track) |                 'No videos found for the search query: "{}"'.format(track) | ||||||
|         #     ) |             ) | ||||||
|             return |             return | ||||||
|         if manual: |         if manual: | ||||||
|             pass |             youtube_video = prompt_for_youtube_search_result(youtube_videos) | ||||||
|         else: |         else: | ||||||
|             youtube_url = youtube_urls.bestmatch()["url"] |             youtube_video = youtube_videos.bestmatch()["url"] | ||||||
|         youtube_metadata = youtube.from_url(youtube_url) |         youtube_metadata = youtube.from_url(youtube_video) | ||||||
|         metadata = spotdl.util.merge( |         metadata = spotdl.util.merge( | ||||||
|             youtube_metadata, |             youtube_metadata, | ||||||
|             spotify_metadata.join() |             spotify_metadata.join() | ||||||
| @@ -110,11 +126,15 @@ def download_track(track, arguments): | |||||||
|     track_splits = track.split(":") |     track_splits = track.split(":") | ||||||
|     if len(track_splits) == 2: |     if len(track_splits) == 2: | ||||||
|         youtube_track, spotify_track = track_splits |         youtube_track, spotify_track = track_splits | ||||||
|     metadata = search_metadata(track, search_format=arguments.search_format) |     metadata = search_metadata( | ||||||
|  |         track, | ||||||
|  |         search_format=arguments["search_format"], | ||||||
|  |         manual=arguments["manual"], | ||||||
|  |     ) | ||||||
|     log_fmt = spotdl.metadata.format_string( |     log_fmt = spotdl.metadata.format_string( | ||||||
|         arguments.output_file, |         arguments["output_file"], | ||||||
|         metadata, |         metadata, | ||||||
|         output_extension=arguments.output_ext, |         output_extension=arguments["output_ext"], | ||||||
|     ) |     ) | ||||||
|     # log.info(log_fmt) |     # log.info(log_fmt) | ||||||
|     download_track_from_metadata(metadata, arguments) |     download_track_from_metadata(metadata, arguments) | ||||||
| @@ -122,41 +142,41 @@ def download_track(track, arguments): | |||||||
|  |  | ||||||
| def download_track_from_metadata(metadata, arguments): | def download_track_from_metadata(metadata, arguments): | ||||||
|     # TODO: Add `-m` flag |     # TODO: Add `-m` flag | ||||||
|     track = Track(metadata, cache_albumart=(not arguments.no_metadata)) |     track = Track(metadata, cache_albumart=(not arguments["no_metadata"])) | ||||||
|     print(metadata["name"]) |     print(metadata["name"]) | ||||||
|  |  | ||||||
|     stream = metadata["streams"].get( |     stream = metadata["streams"].get( | ||||||
|         quality=arguments.quality, |         quality=arguments["quality"], | ||||||
|         preftype=arguments.input_ext, |         preftype=arguments["input_ext"], | ||||||
|     ) |     ) | ||||||
|     # log.info(stream) |     # log.info(stream) | ||||||
|  |  | ||||||
|     Encoder = { |     Encoder = { | ||||||
|         "ffmpeg": EncoderFFmpeg, |         "ffmpeg": EncoderFFmpeg, | ||||||
|         "avconv": EncoderAvconv, |         "avconv": EncoderAvconv, | ||||||
|     }.get(arguments.encoder) |     }.get(arguments["encoder"]) | ||||||
|  |  | ||||||
|     if Encoder is None: |     if Encoder is None: | ||||||
|         output_extension = stream["encoding"] |         output_extension = stream["encoding"] | ||||||
|     else: |     else: | ||||||
|         output_extension = arguments.output_ext |         output_extension = arguments["output_ext"] | ||||||
|  |  | ||||||
|     filename = spotdl.metadata.format_string( |     filename = spotdl.metadata.format_string( | ||||||
|         arguments.output_file, |         arguments["output_file"], | ||||||
|         metadata, |         metadata, | ||||||
|         output_extension=output_extension, |         output_extension=output_extension, | ||||||
|         sanitizer=lambda s: spotdl.util.sanitize( |         sanitizer=lambda s: spotdl.util.sanitize( | ||||||
|             s, spaces_to_underscores=arguments.no_spaces |             s, spaces_to_underscores=arguments["no_spaces"] | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
|     print(filename) |     print(filename) | ||||||
|     # log.info(filename) |     # log.info(filename) | ||||||
|  |  | ||||||
|     to_skip = arguments.dry_run |     to_skip = arguments["dry_run"] | ||||||
|     if not to_skip and os.path.isfile(filename): |     if not to_skip and os.path.isfile(filename): | ||||||
|         if arguments.overwrite == "force": |         if arguments["overwrite"] == "force": | ||||||
|             to_skip = False |             to_skip = False | ||||||
|         elif arguments.overwrite == "prompt": |         elif arguments["overwrite"] == "prompt": | ||||||
|             to_skip = not input("overwrite? (y/N)").lower() == "y" |             to_skip = not input("overwrite? (y/N)").lower() == "y" | ||||||
|         else: |         else: | ||||||
|             to_skip = True |             to_skip = True | ||||||
| @@ -174,7 +194,7 @@ def download_track_from_metadata(metadata, arguments): | |||||||
|             encoder=Encoder() |             encoder=Encoder() | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     if not arguments.no_metadata: |     if not arguments["no_metadata"]: | ||||||
|         track.metadata["lyrics"] = track.metadata["lyrics"].join() |         track.metadata["lyrics"] = track.metadata["lyrics"].join() | ||||||
|         try: |         try: | ||||||
|             track.apply_metadata(filename, encoding=output_extension) |             track.apply_metadata(filename, encoding=output_extension) | ||||||
| @@ -206,7 +226,7 @@ def download_tracks_from_file(path, arguments): | |||||||
|  |  | ||||||
|     for number, track in enumerate(tracks, 1): |     for number, track in enumerate(tracks, 1): | ||||||
|         try: |         try: | ||||||
|             metadata = search_metadata(next_track, arguments.search_format) |             metadata = search_metadata(next_track, arguments["search_format"]) | ||||||
|             log_fmt=(str(number) + ". {artist} - {track-name}") |             log_fmt=(str(number) + ". {artist} - {track-name}") | ||||||
|             # log.info(log_fmt) |             # log.info(log_fmt) | ||||||
|             download_track_from_metadata(metadata, arguments) |             download_track_from_metadata(metadata, arguments) | ||||||
| @@ -215,8 +235,8 @@ def download_tracks_from_file(path, arguments): | |||||||
|             # log.warning("Failed. Will retry after other songs\n") |             # log.warning("Failed. Will retry after other songs\n") | ||||||
|             tracks.append(track) |             tracks.append(track) | ||||||
|         else: |         else: | ||||||
|             if arguments.write_sucessful: |             if arguments["write_sucessful"]: | ||||||
|                 with open(arguments.write_successful, "a") as fout: |                 with open(arguments["write_successful"], "a") as fout: | ||||||
|                     fout.write(track) |                     fout.write(track) | ||||||
|         finally: |         finally: | ||||||
|             with open(path, "w") as fout: |             with open(path, "w") as fout: | ||||||
| @@ -254,7 +274,7 @@ def download_tracks_from_file_threaded(path, arguments): | |||||||
|         "current_track": None, |         "current_track": None, | ||||||
|         "next_track": spotdl.util.ThreadWithReturnValue( |         "next_track": spotdl.util.ThreadWithReturnValue( | ||||||
|             target=search_metadata, |             target=search_metadata, | ||||||
|             args=(next_track, arguments.search_format) |             args=(next_track, arguments["search_format"]) | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|     metadata["next_track"].start() |     metadata["next_track"].start() | ||||||
| @@ -269,7 +289,7 @@ def download_tracks_from_file_threaded(path, arguments): | |||||||
|                 next_track = tracks.pop(0) |                 next_track = tracks.pop(0) | ||||||
|                 metadata["next_track"] = spotdl.util.ThreadWithReturnValue( |                 metadata["next_track"] = spotdl.util.ThreadWithReturnValue( | ||||||
|                     target=search_metadata, |                     target=search_metadata, | ||||||
|                     args=(next_track, arguments.search_format) |                     args=(next_track, arguments["search_format"]) | ||||||
|                 ) |                 ) | ||||||
|                 metadata["next_track"].start() |                 metadata["next_track"].start() | ||||||
|  |  | ||||||
| @@ -289,8 +309,8 @@ def download_tracks_from_file_threaded(path, arguments): | |||||||
|             tracks.append(current_track) |             tracks.append(current_track) | ||||||
|         else: |         else: | ||||||
|             tracks_count -= 1 |             tracks_count -= 1 | ||||||
|             if arguments.write_successful: |             if arguments["write_successful"]: | ||||||
|                 with open(arguments.write_successful, "a") as fout: |                 with open(arguments["write_successful"], "a") as fout: | ||||||
|                     fout.write(current_track) |                     fout.write(current_track) | ||||||
|         finally: |         finally: | ||||||
|             current_iteration += 1 |             current_iteration += 1 | ||||||
|   | |||||||
| @@ -20,6 +20,9 @@ class YouTubeVideos(Sequence): | |||||||
|         self.videos = videos |         self.videos = videos | ||||||
|         super().__init__() |         super().__init__() | ||||||
|  |  | ||||||
|  |     def __repr__(self): | ||||||
|  |         return "YouTubeVideos({})".format(self.videos) | ||||||
|  |  | ||||||
|     def __len__(self): |     def __len__(self): | ||||||
|         return len(self.videos) |         return len(self.videos) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -49,20 +49,23 @@ def merge(base, overrider): | |||||||
|     return merger |     return merger | ||||||
|  |  | ||||||
|  |  | ||||||
| def input_link(links): | def prompt_user_for_selection(items): | ||||||
|     """ Let the user input a choice. """ |     """ Let the user input a choice. """ | ||||||
|     while True: |     while True: | ||||||
|         try: |         try: | ||||||
|             log.info("Choose your number:") |             # log.info("Choose your number:") | ||||||
|  |             print("Choose your number:") | ||||||
|             the_chosen_one = int(input("> ")) |             the_chosen_one = int(input("> ")) | ||||||
|             if 1 <= the_chosen_one <= len(links): |             if 1 <= the_chosen_one <= len(items): | ||||||
|                 return links[the_chosen_one - 1] |                 return items[the_chosen_one - 1] | ||||||
|             elif the_chosen_one == 0: |             elif the_chosen_one == 0: | ||||||
|                 return None |                 return None | ||||||
|             else: |             else: | ||||||
|                 log.warning("Choose a valid number!") |                 # log.warning("Choose a valid number!") | ||||||
|  |                 print("Chose a valid number!") | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             log.warning("Choose a valid number!") |             # log.warning("Choose a valid number!") | ||||||
|  |             print("Chose a valid number!") | ||||||
|  |  | ||||||
|  |  | ||||||
| def is_spotify(raw_song): | def is_spotify(raw_song): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user