mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	De-clutter metadata search
This commit is contained in:
		
							
								
								
									
										275
									
								
								spotdl/metadata_search.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								spotdl/metadata_search.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| from spotdl.metadata.providers import ProviderSpotify | ||||
| from spotdl.metadata.providers import ProviderYouTube | ||||
| from spotdl.lyrics.providers import Genius | ||||
| from spotdl.lyrics.exceptions import LyricsNotFoundError | ||||
|  | ||||
| import spotdl.metadata | ||||
| import spotdl.util | ||||
| from spotdl.metadata.exceptions import SpotifyMetadataNotFoundError | ||||
|  | ||||
| from spotdl.command_line.exceptions import NoYouTubeVideoFoundError | ||||
| from spotdl.command_line.exceptions import NoYouTubeVideoMatchError | ||||
|  | ||||
| import sys | ||||
| import logging | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| PROVIDERS = { | ||||
|     "spotify": ProviderSpotify, | ||||
|     "youtube": ProviderYouTube, | ||||
| } | ||||
|  | ||||
|  | ||||
| def prompt_for_youtube_search_result(videos): | ||||
|     max_index_length = len(str(len(videos))) | ||||
|     max_title_length = max(len(v["title"]) for v in videos) | ||||
|     print(" 0. Skip downloading this track", file=sys.stderr) | ||||
|     for index, video in enumerate(videos, 1): | ||||
|         vid_details = "{index:>{max_index}}. {title:<{max_title}}\n{new_line_gap}  {url} [{duration}]".format( | ||||
|             index=index, | ||||
|             max_index=max_index_length, | ||||
|             title=video["title"], | ||||
|             max_title=max_title_length, | ||||
|             new_line_gap=" " * max_index_length, | ||||
|             url=video["url"], | ||||
|             duration=video["duration"], | ||||
|         ) | ||||
|         print(vid_details, file=sys.stderr) | ||||
|     print("", file=sys.stderr) | ||||
|  | ||||
|     selection = spotdl.util.prompt_user_for_selection(range(1, len(videos)+1)) | ||||
|  | ||||
|     if selection is None: | ||||
|         return None | ||||
|     return videos[selection-1] | ||||
|  | ||||
|  | ||||
| class MetadataSearch: | ||||
|     def __init__(self, track, lyrics=False, yt_search_format="{artist} - {track-name}", yt_manual=False, providers=PROVIDERS): | ||||
|         self.track = track | ||||
|         self.track_type = spotdl.util.track_type(track) | ||||
|         self.lyrics = lyrics | ||||
|         self.yt_search_format = yt_search_format | ||||
|         self.yt_manual = yt_manual | ||||
|         self.providers = {} | ||||
|         for provider, parent in providers.items(): | ||||
|             self.providers[provider] = parent() | ||||
|         self.lyric_provider = Genius() | ||||
|  | ||||
|     def get_lyrics(self, query): | ||||
|         try: | ||||
|             lyrics = self.lyric_provider.from_query(query) | ||||
|         except LyricsNotFoundError as e: | ||||
|             logger.warning(e.args[0]) | ||||
|             lyrics = None | ||||
|         return lyrics | ||||
|  | ||||
|     def _make_lyric_search_query(self, metadata): | ||||
|         if self.track_type == "query": | ||||
|             lyric_query = self.track | ||||
|         else: | ||||
|             lyric_search_format = "{artist} - {track-name}" | ||||
|             lyric_query = spotdl.metadata.format_string( | ||||
|                 lyric_search_format, | ||||
|                 metadata | ||||
|             ) | ||||
|         return lyric_query | ||||
|  | ||||
|     def on_youtube_and_spotify(self): | ||||
|         track_type_mapper = { | ||||
|             "spotify": self._on_youtube_and_spotify_for_type_spotify, | ||||
|             "youtube": self._on_youtube_and_spotify_for_type_youtube, | ||||
|             "query": self._on_youtube_and_spotify_for_type_query, | ||||
|         } | ||||
|         caller = track_type_mapper[self.track_type] | ||||
|         metadata = caller() | ||||
|  | ||||
|         if not self.lyrics: | ||||
|             return metadata | ||||
|  | ||||
|         lyric_query = self._make_lyric_search_query(metadata) | ||||
|         metadata["lyrics"] = spotdl.util.ThreadWithReturnValue( | ||||
|             target=self.get_lyrics, | ||||
|             args=(lyric_query,), | ||||
|         ) | ||||
|         metadata["lyrics"].start() | ||||
|  | ||||
|         return metadata | ||||
|  | ||||
|     def on_youtube(self): | ||||
|         track_type_mapper = { | ||||
|             "spotify": self._on_youtube_for_type_spotify, | ||||
|             "youtube": self._on_youtube_for_type_youtube, | ||||
|             "query": self._on_youtube_for_type_query, | ||||
|         } | ||||
|         caller = track_type_mapper[self.track_type] | ||||
|         metadata = caller(self.track) | ||||
|  | ||||
|         if not self.lyrics: | ||||
|             return metadata | ||||
|  | ||||
|         lyric_query = self._make_lyric_search_query(metadata) | ||||
|         metadata["lyrics"] = spotdl.util.ThreadWithReturnValue( | ||||
|             target=self.get_lyrics, | ||||
|             arguments=(lyric_query,), | ||||
|         ) | ||||
|         metadata["lyrics"].start() | ||||
|  | ||||
|         return metadata | ||||
|  | ||||
|     def on_spotify(self): | ||||
|         track_type_mapper = { | ||||
|             "spotify": self._on_spotify_for_type_spotify, | ||||
|             "youtube": self._on_spotify_for_type_youtube, | ||||
|             "query": self._on_spotify_for_type_query, | ||||
|         } | ||||
|         caller = track_type_mapper[self.track_type] | ||||
|         metadata = caller(self.track) | ||||
|  | ||||
|         if not self.lyrics: | ||||
|             return metadata | ||||
|  | ||||
|         lyric_query = self._make_lyric_search_query(metadata) | ||||
|         metadata["lyrics"] = spotdl.util.ThreadWithReturnValue( | ||||
|             target=self.get_lyrics, | ||||
|             arguments=(lyric_query,), | ||||
|         ) | ||||
|         metadata["lyrics"].start() | ||||
|  | ||||
|         return metadata | ||||
|  | ||||
|     def _on_youtube_and_spotify_for_type_spotify(self): | ||||
|         logger.debug("Extracting YouTube and Spotify metadata for input Spotify URI.") | ||||
|         spotify_metadata = self._on_spotify_for_type_spotify(self.track) | ||||
|         lyric_query = spotdl.metadata.format_string( | ||||
|             "{artist} - {track-name}", | ||||
|             spotify_metadata, | ||||
|         ) | ||||
|         search_query = spotdl.metadata.format_string(self.yt_search_format, spotify_metadata) | ||||
|         videos = self.providers["youtube"].search(search_query) | ||||
|         if not videos: | ||||
|             raise NoYouTubeVideoFoundError( | ||||
|                 'YouTube returned no videos for the search query "{}".'.format(search_query) | ||||
|             ) | ||||
|         if self.yt_manual: | ||||
|             youtube_video = prompt_for_youtube_search_result(videos) | ||||
|         else: | ||||
|             youtube_video = videos.bestmatch() | ||||
|  | ||||
|         if youtube_video is None: | ||||
|             raise NoYouTubeVideoMatchError( | ||||
|                 'No matching videos found on YouTube for the search query "{}".'.format( | ||||
|                     search_query | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         youtube_metadata = self.providers["youtube"].from_url(youtube_video["url"]) | ||||
|         metadata = spotdl.util.merge( | ||||
|             youtube_metadata, | ||||
|             spotify_metadata | ||||
|         ) | ||||
|         return metadata | ||||
|  | ||||
|     def _on_youtube_and_spotify_for_type_youtube(self): | ||||
|         logger.debug("Extracting YouTube and Spotify metadata for input YouTube URL.") | ||||
|         youtube_metadata = self._on_youtube_for_type_youtube(self.track) | ||||
|         search_query = spotdl.metadata.format_string(self.yt_search_format, youtube_metadata) | ||||
|         spotify_metadata = self._on_spotify_for_type_query(search_query) | ||||
|         metadata = spotdl.util.merge( | ||||
|             youtube_metadata, | ||||
|             spotify_metadata | ||||
|         ) | ||||
|         return metadata | ||||
|  | ||||
|     def _on_youtube_and_spotify_for_type_query(self): | ||||
|         logger.debug("Extracting YouTube and Spotify metadata for input track query.") | ||||
|         search_query = self.track | ||||
|         lyric_query = search_query | ||||
|         # Make use of threads here to search on both YouTube & Spotify | ||||
|         # at the same time. | ||||
|         spotify_metadata = spotdl.util.ThreadWithReturnValue( | ||||
|             target=self._on_spotify_for_type_query, | ||||
|             args=(search_query,) | ||||
|         ) | ||||
|         spotify_metadata.start() | ||||
|         youtube_metadata = self._on_youtube_for_type_query(search_query) | ||||
|         metadata = spotdl.util.merge( | ||||
|             youtube_metadata, | ||||
|             spotify_metadata.join() | ||||
|         ) | ||||
|         return metadata | ||||
|  | ||||
|     def _on_youtube_for_type_spotify(self): | ||||
|         logger.debug("Extracting YouTube metadata for input Spotify URI.") | ||||
|         spotify_metadata = self._on_spotify_for_type_spotify(self.track) | ||||
|         lyric_query = spotdl.metadata.format_string( | ||||
|             "{artist} - {track-name}", | ||||
|             spotify_metadata, | ||||
|         ) | ||||
|         search_query = spotdl.metadata.format_string(self.yt_search_format, spotify_metadata) | ||||
|         videos = self.providers["youtube"].search(search_query) | ||||
|         if not videos: | ||||
|             raise NoYouTubeVideoFoundError( | ||||
|                 'YouTube returned no videos for the search query "{}".'.format(search_query) | ||||
|             ) | ||||
|         if self.yt_manual: | ||||
|             youtube_video = prompt_for_youtube_search_result(videos) | ||||
|         else: | ||||
|             youtube_video = videos.bestmatch() | ||||
|  | ||||
|         if youtube_video is None: | ||||
|             raise NoYouTubeVideoMatchError( | ||||
|                 'No matching videos found on YouTube for the search query "{}".'.format( | ||||
|                     search_query | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         youtube_metadata = self.providers["youtube"].from_url(youtube_video["url"]) | ||||
|         return youtube_metadata | ||||
|  | ||||
|     def _on_youtube_for_type_youtube(self, url): | ||||
|         logger.debug("Extracting YouTube metadata for input YouTube URL.") | ||||
|         youtube_metadata = self.providers["youtube"].from_url(url) | ||||
|         return youtube_metadata | ||||
|  | ||||
|     def _on_youtube_for_type_query(self, query): | ||||
|         logger.debug("Extracting YouTube metadata for input track query.") | ||||
|         videos = self.providers["youtube"].search(query) | ||||
|         if not videos: | ||||
|             raise NoYouTubeVideoFoundError( | ||||
|                 'YouTube returned no videos for the search query "{}".'.format(query) | ||||
|             ) | ||||
|         if self.yt_manual: | ||||
|             youtube_video = prompt_for_youtube_search_result(videos) | ||||
|         else: | ||||
|             youtube_video = videos.bestmatch() | ||||
|         if youtube_video is None: | ||||
|             raise NoYouTubeVideoMatchError( | ||||
|                 'No matching videos found on YouTube for the search query "{}".'.format( | ||||
|                     query | ||||
|                 ) | ||||
|             ) | ||||
|         youtube_metadata = self.providers["youtube"].from_url(youtube_video["url"]) | ||||
|         return youtube_metadata | ||||
|  | ||||
|     def _on_spotify_for_type_youtube(self, url): | ||||
|         logger.debug("Extracting Spotify metadata for input YouTube URL.") | ||||
|         youtube_metadata = self.providers["youtube"].from_url(url) | ||||
|         search_query = spotdl.metadata.format_string(self.yt_search_format, youtube_metadata) | ||||
|         spotify_metadata = self.providers["spotify"].from_query(search_query) | ||||
|         return spotify_metadata | ||||
|  | ||||
|     def _on_spotify_for_type_spotify(self, url): | ||||
|         logger.debug("Extracting Spotify metadata for input Spotify URI.") | ||||
|         spotify_metadata = self.providers["spotify"].from_url(url) | ||||
|         return spotify_metadata | ||||
|  | ||||
|     def _on_spotify_for_type_query(self, query): | ||||
|         logger.debug("Extracting Spotify metadata for input track query.") | ||||
|         try: | ||||
|             spotify_metadata = self.providers["spotify"].from_query(query) | ||||
|         except SpotifyMetadataNotFoundError as e: | ||||
|             logger.warn(e.args[0]) | ||||
|             spotify_metadata = {} | ||||
|         return spotify_metadata | ||||
|  | ||||
		Reference in New Issue
	
	Block a user