mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	A bit of thread refactoring
This commit is contained in:
		| @@ -68,31 +68,37 @@ def get_arguments(argv=None, to_merge=True): | ||||
|     group.add_argument( | ||||
|         "-l", | ||||
|         "--list", | ||||
|         help="download tracks from a file" | ||||
|         help="download tracks from a file (WARNING: this file will be modified!)" | ||||
|     ) | ||||
|     group.add_argument( | ||||
|         "-p", | ||||
|         "--playlist", | ||||
|         help="load tracks from playlist URL into <playlist_name>.txt", | ||||
|         help="load tracks from playlist URL into <playlist_name>.txt or " | ||||
|              "if `--write-to=<path/to/file.txt>` has been passed", | ||||
|     ) | ||||
|     group.add_argument( | ||||
|         "-b", "--album", help="load tracks from album URL into <album_name>.txt" | ||||
|         "-b" | ||||
|         "--album", | ||||
|         help="load tracks from album URL into <album_name>.txt or if " | ||||
|              "`--write-to=<path/to/file.txt>` has been passed" | ||||
|     ) | ||||
|     group.add_argument( | ||||
|         "-ab", | ||||
|         "--all-albums", | ||||
|         help="load all tracks from artist URL into <artist_name>.txt", | ||||
|         help="load all tracks from artist URL into <artist_name>.txt " | ||||
|              "or if `--write-to=<path/to/file.txt>` has been passed" | ||||
|     ) | ||||
|     group.add_argument( | ||||
|         "-u", | ||||
|         "--username", | ||||
|         help="load tracks from user's playlist into <playlist_name>.txt", | ||||
|         help="load tracks from user's playlist into <playlist_name>.txt " | ||||
|              "or if `--write-to=<path/to/file.txt>` has been passed" | ||||
|     ) | ||||
|  | ||||
|     parser.add_argument( | ||||
|         "--write-m3u", | ||||
|         help="generate an .m3u playlist file with youtube links given " | ||||
|             "a text file containing tracks", | ||||
|              "a text file containing tracks", | ||||
|         action="store_true", | ||||
|     ) | ||||
|     parser.add_argument( | ||||
| @@ -102,13 +108,6 @@ def get_arguments(argv=None, to_merge=True): | ||||
|         help="choose the track to download manually from a list of matching tracks", | ||||
|         action="store_true", | ||||
|     ) | ||||
|     parser.add_argument( | ||||
|         "-nr", | ||||
|         "--no-remove-original", | ||||
|         default=config["no-remove-original"], | ||||
|         help="do not remove the original file after conversion", | ||||
|         action="store_true", | ||||
|     ) | ||||
|     parser.add_argument( | ||||
|         "-nm", | ||||
|         "--no-metadata", | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| from spotdl.metadata.providers import ProviderSpotify | ||||
| from spotdl.metadata.providers import ProviderYouTube | ||||
| from spotdl.metadata.embedders import EmbedderDefault | ||||
| from spotdl.encode.encoders import EncoderFFmpeg, EncoderAvconv | ||||
|  | ||||
| from spotdl.encode.encoders import EncoderFFmpeg | ||||
| from spotdl.encode.encoders import EncoderAvconv | ||||
|  | ||||
| from spotdl.lyrics.providers import LyricWikia | ||||
| from spotdl.lyrics.providers import Genius | ||||
|  | ||||
| @@ -11,7 +14,6 @@ import spotdl.util | ||||
|  | ||||
| import os | ||||
| import urllib.request | ||||
| import threading | ||||
|  | ||||
|  | ||||
| def search_metadata(track, lyrics=True, search_format="{artist} - {track-name}"): | ||||
| @@ -46,7 +48,7 @@ def download_track(track, arguments): | ||||
|  | ||||
| def download_track_from_metadata(metadata, arguments): | ||||
|     track = Track(metadata, cache_albumart=(not arguments.no_metadata)) | ||||
|     # log.info(log_fmt) | ||||
|     print(metadata["name"]) | ||||
|  | ||||
|     stream = metadata["streams"].get( | ||||
|         quality=arguments.quality, | ||||
| @@ -114,45 +116,41 @@ def download_tracks_from_file(path, arguments): | ||||
|  | ||||
|     # Remove duplicates and empty elements | ||||
|     # Also strip whitespaces from elements (if any) | ||||
|     spotdl.util.remove_duplicates(tracks, condition=lambda x: x, operation=str.strip) | ||||
|     spotdl.util.remove_duplicates( | ||||
|         tracks, | ||||
|         condition=lambda x: x, | ||||
|         operation=str.strip | ||||
|     ) | ||||
|  | ||||
|     # Overwrite file | ||||
|     with open(path, "w") as fout: | ||||
|         fout.writelines(tracks) | ||||
|  | ||||
|     next_track_metadata = threading.Thread(target=lambda: None) | ||||
|     next_track_metadata.start() | ||||
|     tracks_count = len(tracks) | ||||
|     current_iteration = 1 | ||||
|  | ||||
|     def mutable_assignment(mutable_resource, track): | ||||
|         mutable_resource["next_track"] = search_metadata( | ||||
|             track, | ||||
|             search_format=arguments.search_format | ||||
|         ) | ||||
|  | ||||
|     next_track = tracks.pop(0) | ||||
|     metadata = { | ||||
|         "current_track": None, | ||||
|         "next_track": None, | ||||
|         "next_track": spotdl.util.ThreadWithReturnValue( | ||||
|             target=search_metadata, | ||||
|             args=(next_track, True, arguments.search_format) | ||||
|         ) | ||||
|     } | ||||
|     metadata["next_track"].start() | ||||
|     while tracks_count > 0: | ||||
|         current_track = tracks.pop(0) | ||||
|         tracks_count -= 1 | ||||
|         metadata["current_track"] = metadata["next_track"] | ||||
|         metadata["current_track"] = metadata["next_track"].join() | ||||
|         metadata["next_track"] = None | ||||
|         try: | ||||
|             if metadata["current_track"] is None: | ||||
|                 metadata["current_track"] = search_metadata( | ||||
|                     current_track, | ||||
|                     search_format=arguments.search_format | ||||
|                 ) | ||||
|             if tracks_count > 0: | ||||
|                 next_track = tracks[0] | ||||
|                 next_track_metadata = threading.Thread( | ||||
|                     target=mutable_assignment, | ||||
|                     args=(metadata, next_track) | ||||
|                 current_track = next_track | ||||
|                 next_track = tracks.pop(0) | ||||
|                 metadata["next_track"] = spotdl.util.ThreadWithReturnValue( | ||||
|                     target=search_metadata, | ||||
|                     args=(next_track, True, arguments.search_format) | ||||
|                 ) | ||||
|                 next_track_metadata.start() | ||||
|                 metadata["next_track"].start() | ||||
|  | ||||
|             log_fmt=(str(current_iteration) + ". {artist} - {track-name}") | ||||
|             # log.info(log_fmt) | ||||
| @@ -161,7 +159,6 @@ def download_tracks_from_file(path, arguments): | ||||
|                 arguments | ||||
|             ) | ||||
|             current_iteration += 1 | ||||
|             next_track_metadata.join() | ||||
|         except (urllib.request.URLError, TypeError, IOError) as e: | ||||
|             # log.exception(e.args[0]) | ||||
|             # log.warning("Failed. Will retry after other songs\n") | ||||
|   | ||||
| @@ -6,7 +6,6 @@ import spotdl.util | ||||
|  | ||||
| DEFAULT_CONFIGURATION = { | ||||
|     "spotify-downloader": { | ||||
|         "no-remove-original": False, | ||||
|         "manual": False, | ||||
|         "no-metadata": False, | ||||
|         "no-fallback-metadata": False, | ||||
|   | ||||
| @@ -2,11 +2,12 @@ import tqdm | ||||
|  | ||||
| import urllib.request | ||||
| import subprocess | ||||
| import threading | ||||
|  | ||||
| from spotdl.encode.encoders import EncoderFFmpeg | ||||
| from spotdl.metadata.embedders import EmbedderDefault | ||||
|  | ||||
| import spotdl.util | ||||
|  | ||||
| CHUNK_SIZE= 16 * 1024 | ||||
|  | ||||
| class Track: | ||||
| @@ -14,25 +15,18 @@ class Track: | ||||
|         self.metadata = metadata | ||||
|         self._chunksize = CHUNK_SIZE | ||||
|  | ||||
|         self._cache_resources = { | ||||
|             "albumart": {"content": None, "threadinstance": None } | ||||
|         } | ||||
|         if cache_albumart: | ||||
|             self._albumart_thread = self._cache_albumart() | ||||
|  | ||||
|     def _fetch_response_content_threaded(self, mutable_resource, url): | ||||
|         content = urllib.request.urlopen(url).read() | ||||
|         mutable_resource["content"] = content | ||||
|         self._cache_albumart = cache_albumart | ||||
|  | ||||
|     def _cache_albumart(self): | ||||
|         # A hack to get a thread's return value | ||||
|         albumart_thread = threading.Thread( | ||||
|             target=self._fetch_response_content_threaded, | ||||
|             args=(self._cache_resources["albumart"], | ||||
|                   self.metadata["album"]["images"][0]["url"]), | ||||
|         albumart_thread = spotdl.util.ThreadWithReturnValue( | ||||
|             target=lambda url: urllib.request.urlopen(url).read(), | ||||
|             args=(self.metadata["album"]["images"][0]["url"],) | ||||
|         ) | ||||
|         albumart_thread.start() | ||||
|         self._cache_resources["albumart"]["threadinstance"] = albumart_thread | ||||
|         return albumart_thread | ||||
|  | ||||
|     def _calculate_total_chunks(self, filesize): | ||||
|         return (filesize // self._chunksize) + 1 | ||||
| @@ -79,14 +73,15 @@ class Track: | ||||
|         process.wait() | ||||
|  | ||||
|     def apply_metadata(self, input_path, encoding=None, embedder=EmbedderDefault()): | ||||
|         albumart = self._cache_resources["albumart"] | ||||
|         if albumart["threadinstance"]: | ||||
|             albumart["threadinstance"].join() | ||||
|         if self._cache_albumart: | ||||
|             albumart = self._albumart_thread.join() | ||||
|         else: | ||||
|             albumart = None | ||||
|  | ||||
|         embedder.apply_metadata( | ||||
|             input_path, | ||||
|             self.metadata, | ||||
|             cached_albumart=albumart["content"], | ||||
|             cached_albumart=albumart, | ||||
|             encoding=encoding, | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,8 @@ import sys | ||||
| import math | ||||
| import urllib.request | ||||
|  | ||||
| import threading | ||||
|  | ||||
|  | ||||
| try: | ||||
|     import winreg | ||||
| @@ -18,6 +20,26 @@ except ImportError: | ||||
|     sys.exit(5) | ||||
|  | ||||
|  | ||||
| # This has been referred from | ||||
| # https://stackoverflow.com/a/6894023/6554943 | ||||
| # It's because threaded functions do not return by default | ||||
| # Whereas this will return the value when `join` method | ||||
| # is called. | ||||
| class ThreadWithReturnValue(threading.Thread): | ||||
|     def __init__(self, target=lambda: None, args=()): | ||||
|         super().__init__(target=target, args=args) | ||||
|         self._return = None | ||||
|     def run(self): | ||||
|         if self._target is not None: | ||||
|             self._return = self._target( | ||||
|                 *self._args, | ||||
|                 **self._kwargs | ||||
|             ) | ||||
|     def join(self, *args): | ||||
|         super().join(*args) | ||||
|         return self._return | ||||
|  | ||||
|  | ||||
| def merge(base, overrider): | ||||
|     """ Override default dict with config dict. """ | ||||
|     merger = base.copy() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user