mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Merge pull request #457 from ritiek/youtube-metadata
Use YouTube as fallback for track metadata if not found on Spotify
This commit is contained in:
		| @@ -27,11 +27,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. | ||||
|  | ||||
| ## [1.1.1] - 2019-01-03 | ||||
| ### Added | ||||
| - Use YouTube as fallback metadata if track not found on Spotify. Also added `--no-fallback-metadata` | ||||
|   to preserve old behaviour ([@ritiek](https://github.com/ritiek)) (#457) | ||||
| - Output informative message in case of no result found in YouTube search ([@Amit-L](https://github.com/Amit-L)) (#452) | ||||
| - Ability to pass multiple tracks with `-s` option ([@ritiek](https://github.com/ritiek)) (#442) | ||||
|  | ||||
| ### Changed | ||||
| - Allowed to fetch metadata from Spotify upon searching Spotify-URL and  `--no-metadata` to gather YouTube custom-search fields ([@Amit-L](https://github.com/Amit-L)) (#452) | ||||
| - Allowed to fetch metadata from Spotify upon searching Spotify-URL and  `--no-metadata` to gather YouTube | ||||
|   custom-search fields ([@Amit-L](https://github.com/Amit-L)) (#452) | ||||
| - Change FFmpeg to use the built-in encoder `aac` instead of 3rd party `libfdk-aac` which does not | ||||
|   ship with the apt package ([@ritiek](https://github.com/ritiek)) (#448) | ||||
| - Monkeypatch ever-changing network-relying tests ([@ritiek](https://github.com/ritiek)) (#448) | ||||
|   | ||||
| @@ -163,8 +163,6 @@ class Downloader: | ||||
|             if not refined_songname == " - ": | ||||
|                 songname = refined_songname | ||||
|         else: | ||||
|             if not const.args.no_metadata: | ||||
|                 log.warning("Could not find metadata") | ||||
|             songname = internals.sanitize_title(songname) | ||||
|  | ||||
|         return songname | ||||
|   | ||||
| @@ -17,6 +17,7 @@ default_conf = { | ||||
|     "spotify-downloader": { | ||||
|         "manual": False, | ||||
|         "no-metadata": False, | ||||
|         "no-fallback-metadata": False, | ||||
|         "avconv": False, | ||||
|         "folder": internals.get_music_dir(), | ||||
|         "overwrite": "prompt", | ||||
| @@ -132,7 +133,7 @@ def get_arguments(raw_args=None, to_group=True, to_merge=True): | ||||
|         "-m", | ||||
|         "--manual", | ||||
|         default=config["manual"], | ||||
|         help="choose the track to download manually from a list " "of matching tracks", | ||||
|         help="choose the track to download manually from a list of matching tracks", | ||||
|         action="store_true", | ||||
|     ) | ||||
|     parser.add_argument( | ||||
| @@ -142,6 +143,13 @@ def get_arguments(raw_args=None, to_group=True, to_merge=True): | ||||
|         help="do not embed metadata in tracks", | ||||
|         action="store_true", | ||||
|     ) | ||||
|     parser.add_argument( | ||||
|         "-nf", | ||||
|         "--no-fallback-metadata", | ||||
|         default=config["no-fallback-metadata"], | ||||
|         help="use YouTube metadata as fallback if track not found on Spotify", | ||||
|         action="store_true", | ||||
|     ) | ||||
|     parser.add_argument( | ||||
|         "-a", | ||||
|         "--avconv", | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| from logzero import logger as log | ||||
| import os | ||||
| import sys | ||||
| import urllib.request | ||||
|  | ||||
| from spotdl import const | ||||
|  | ||||
| @@ -253,3 +254,12 @@ def remove_duplicates(tracks): | ||||
|     local_set = set() | ||||
|     local_set_add = local_set.add | ||||
|     return [x for x in tracks if not (x in local_set or local_set_add(x))] | ||||
|  | ||||
|  | ||||
| def content_available(url): | ||||
|     try: | ||||
|         response = urllib.request.urlopen(url) | ||||
|     except HTTPError: | ||||
|         return False | ||||
|     else: | ||||
|         return response.getcode() < 300 | ||||
|   | ||||
| @@ -46,6 +46,8 @@ class EmbedMetadata: | ||||
|     def __init__(self, music_file, meta_tags): | ||||
|         self.music_file = music_file | ||||
|         self.meta_tags = meta_tags | ||||
|         self.spotify_metadata = meta_tags["spotify_metadata"] | ||||
|         self.provider = "spotify" if meta_tags["spotify_metadata"] else "youtube" | ||||
|  | ||||
|     def as_mp3(self): | ||||
|         """ Embed metadata to MP3 files. """ | ||||
| @@ -62,7 +64,7 @@ class EmbedMetadata: | ||||
|         audiofile["lyricist"] = meta_tags["artists"][0]["name"] | ||||
|         audiofile["arranger"] = meta_tags["artists"][0]["name"] | ||||
|         audiofile["performer"] = meta_tags["artists"][0]["name"] | ||||
|         audiofile["website"] = meta_tags["external_urls"]["spotify"] | ||||
|         audiofile["website"] = meta_tags["external_urls"][self.provider] | ||||
|         audiofile["length"] = str(meta_tags["duration"]) | ||||
|         if meta_tags["publisher"]: | ||||
|             audiofile["encodedby"] = meta_tags["publisher"] | ||||
| @@ -78,7 +80,7 @@ class EmbedMetadata: | ||||
|         audiofile["TYER"] = TYER(encoding=3, text=meta_tags["year"]) | ||||
|         if meta_tags["publisher"]: | ||||
|             audiofile["TPUB"] = TPUB(encoding=3, text=meta_tags["publisher"]) | ||||
|         audiofile["COMM"] = COMM(encoding=3, text=meta_tags["external_urls"]["spotify"]) | ||||
|         audiofile["COMM"] = COMM(encoding=3, text=meta_tags["external_urls"][self.provider]) | ||||
|         if meta_tags["lyrics"]: | ||||
|             audiofile["USLT"] = USLT( | ||||
|                 encoding=3, desc=u"Lyrics", text=meta_tags["lyrics"] | ||||
| @@ -106,7 +108,7 @@ class EmbedMetadata: | ||||
|         audiofile = MP4(music_file) | ||||
|         self._embed_basic_metadata(audiofile, preset=M4A_TAG_PRESET) | ||||
|         audiofile[M4A_TAG_PRESET["year"]] = meta_tags["year"] | ||||
|         audiofile[M4A_TAG_PRESET["comment"]] = meta_tags["external_urls"]["spotify"] | ||||
|         audiofile[M4A_TAG_PRESET["comment"]] = meta_tags["external_urls"][self.provider] | ||||
|         if meta_tags["lyrics"]: | ||||
|             audiofile[M4A_TAG_PRESET["lyrics"]] = meta_tags["lyrics"] | ||||
|         try: | ||||
| @@ -127,7 +129,7 @@ class EmbedMetadata: | ||||
|         audiofile = FLAC(music_file) | ||||
|         self._embed_basic_metadata(audiofile) | ||||
|         audiofile["year"] = meta_tags["year"] | ||||
|         audiofile["comment"] = meta_tags["external_urls"]["spotify"] | ||||
|         audiofile["comment"] = meta_tags["external_urls"][self.provider] | ||||
|         if meta_tags["lyrics"]: | ||||
|             audiofile["lyrics"] = meta_tags["lyrics"] | ||||
|  | ||||
| @@ -147,7 +149,8 @@ class EmbedMetadata: | ||||
|         meta_tags = self.meta_tags | ||||
|         audiofile[preset["artist"]] = meta_tags["artists"][0]["name"] | ||||
|         audiofile[preset["albumartist"]] = meta_tags["album"]["artists"][0]["name"] | ||||
|         audiofile[preset["album"]] = meta_tags["album"]["name"] | ||||
|         if meta_tags["album"]["name"]: | ||||
|             audiofile[preset["album"]] = meta_tags["album"]["name"] | ||||
|         audiofile[preset["title"]] = meta_tags["name"] | ||||
|         audiofile[preset["date"]] = meta_tags["release_date"] | ||||
|         audiofile[preset["originaldate"]] = meta_tags["release_date"] | ||||
|   | ||||
							
								
								
									
										31
									
								
								spotdl/patcher.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								spotdl/patcher.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import pafy | ||||
|  | ||||
| from spotdl import internals | ||||
|  | ||||
|  | ||||
| def _getbestthumb(self): | ||||
|     url = self._ydl_info["thumbnails"][0]["url"] | ||||
|     if url: | ||||
|         return url | ||||
|  | ||||
|     part_url = "https://i.ytimg.com/vi/%s/" % self.videoid | ||||
|     # Thumbnail resolution sorted in descending order | ||||
|     thumbs = ("maxresdefault.jpg", | ||||
|               "sddefault.jpg", | ||||
|               "hqdefault.jpg", | ||||
|               "mqdefault.jpg", | ||||
|               "default.jpg") | ||||
|     for thumb in thumbs: | ||||
|         url = part_url + thumb | ||||
|         if self._content_available(url): | ||||
|             return url | ||||
|  | ||||
|  | ||||
| @classmethod | ||||
| def _content_available(cls, url): | ||||
|     return internals.content_available(url) | ||||
|  | ||||
| def patch_pafy(): | ||||
|     pafy.backend_shared.BasePafy._bestthumb = None | ||||
|     pafy.backend_shared.BasePafy._content_available = _content_available | ||||
|     pafy.backend_shared.BasePafy.getbestthumb = _getbestthumb | ||||
| @@ -81,6 +81,7 @@ def generate_metadata(raw_song): | ||||
|     # Some sugar | ||||
|     meta_tags["year"], *_ = meta_tags["release_date"].split("-") | ||||
|     meta_tags["duration"] = meta_tags["duration_ms"] / 1000.0 | ||||
|     meta_tags["spotify_metadata"] = True | ||||
|     # Remove unwanted parameters | ||||
|     del meta_tags["duration_ms"] | ||||
|     del meta_tags["available_markets"] | ||||
|   | ||||
| @@ -14,6 +14,12 @@ from spotdl import const | ||||
| # Read more on mps-youtube/pafy#199 | ||||
| pafy.g.opener.addheaders.append(("Range", "bytes=0-")) | ||||
|  | ||||
| # Implement unreleased methods on Pafy object | ||||
| # More info: https://github.com/mps-youtube/pafy/pull/211 | ||||
| if pafy.__version__ <= "0.5.4": | ||||
|     from spotdl import patcher | ||||
|     patcher.patch_pafy() | ||||
|  | ||||
|  | ||||
| def set_api_key(): | ||||
|     if const.args.youtube_api_key: | ||||
| @@ -39,28 +45,75 @@ def go_pafy(raw_song, meta_tags=None): | ||||
|     return track_info | ||||
|  | ||||
|  | ||||
| def match_video_and_metadata(track, force_pafy=True): | ||||
| def match_video_and_metadata(track): | ||||
|     """ Get and match track data from YouTube and Spotify. """ | ||||
|     meta_tags = None | ||||
|  | ||||
|  | ||||
|     def fallback_metadata(meta_tags): | ||||
|         fallback_metadata_info = "Track not found on Spotify, falling back on YouTube metadata" | ||||
|         skip_fallback_metadata_warning = "Fallback condition not met, shall not embed metadata" | ||||
|         if meta_tags is None: | ||||
|             if const.args.no_fallback_metadata: | ||||
|                 log.warning(skip_fallback_metadata_warning) | ||||
|             else: | ||||
|                 log.info(fallback_metadata_info) | ||||
|                 meta_tags = generate_metadata(content) | ||||
|         return meta_tags | ||||
|  | ||||
|  | ||||
|     if internals.is_youtube(track): | ||||
|         log.debug("Input song is a YouTube URL") | ||||
|         content = go_pafy(track, meta_tags=None) | ||||
|         track = slugify(content.title).replace("-", " ") | ||||
|         if not const.args.no_metadata: | ||||
|             meta_tags = spotify_tools.generate_metadata(track) | ||||
|     else: | ||||
|         # Let it generate metadata, youtube doesn't know spotify slang | ||||
|         if not const.args.no_metadata or internals.is_spotify(track): | ||||
|             meta_tags = spotify_tools.generate_metadata(track) | ||||
|             meta_tags = fallback_metadata(meta_tags) | ||||
|  | ||||
|         if force_pafy: | ||||
|             content = go_pafy(track, meta_tags) | ||||
|     elif internals.is_spotify(track): | ||||
|         log.debug("Input song is a Spotify URL") | ||||
|         # Let it generate metadata, YouTube doesn't know Spotify slang | ||||
|         meta_tags = spotify_tools.generate_metadata(track) | ||||
|         content = go_pafy(track, meta_tags) | ||||
|         if const.args.no_metadata: | ||||
|             meta_tags = None | ||||
|  | ||||
|     else: | ||||
|         log.debug("Input song is plain text based") | ||||
|         if const.args.no_metadata: | ||||
|             content = go_pafy(track, meta_tags=None) | ||||
|         else: | ||||
|             content = None | ||||
|             meta_tags = spotify_tools.generate_metadata(track) | ||||
|             content = go_pafy(track, meta_tags=meta_tags) | ||||
|             meta_tags = fallback_metadata(meta_tags) | ||||
|  | ||||
|     return content, meta_tags | ||||
|  | ||||
|  | ||||
| def generate_metadata(content): | ||||
|     """ Fetch a song's metadata from YouTube. """ | ||||
|     meta_tags = {"spotify_metadata": False, | ||||
|                  "name": content.title, | ||||
|                  "artists": [{"name": content.author}], | ||||
|                  "duration": content.length, | ||||
|                  "external_urls": {"youtube": content.watchv_url}, | ||||
|                  "album": {"images" : [{"url": content.getbestthumb()}], "name": None}, | ||||
|                  "year": content.published.split("-")[0], | ||||
|                  "release_date": content.published.split(" ")[0], | ||||
|                  "type": "track", | ||||
|                  "disc_number": 1, | ||||
|                  "track_number": 1, | ||||
|                  "total_tracks": 1, | ||||
|                  "publisher": None, | ||||
|                  "external_ids": {"isrc": None}, | ||||
|                  "lyrics": None, | ||||
|                  "copyright": None, | ||||
|                  "genre": None, | ||||
|                  } | ||||
|  | ||||
|     return meta_tags | ||||
|  | ||||
|  | ||||
| def get_youtube_title(content, number=None): | ||||
|     """ Get the YouTube video's title. """ | ||||
|     title = content.title | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| from spotdl import const | ||||
| from spotdl import handle | ||||
| from spotdl import spotdl | ||||
| import urllib | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| @@ -13,3 +14,13 @@ def load_defaults(): | ||||
|     spotdl.log = const.logzero.setup_logger( | ||||
|         formatter=const._formatter, level=const.args.log_level | ||||
|     ) | ||||
|  | ||||
|  | ||||
| # GIST_URL is the monkeypatched version of: https://www.youtube.com/results?search_query=janji+-+heroes | ||||
| # so that we get same results even if YouTube changes the list/order of videos on their page. | ||||
| GIST_URL = "https://gist.githubusercontent.com/ritiek/e731338e9810e31c2f00f13c249a45f5/raw/c11a27f3b5d11a8d082976f1cdd237bd605ec2c2/search_results.html" | ||||
|  | ||||
| def monkeypatch_youtube_search_page(*args, **kwargs): | ||||
|     fake_urlopen = urllib.request.urlopen(GIST_URL) | ||||
|     return fake_urlopen | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import urllib | ||||
| import subprocess | ||||
| import os | ||||
|  | ||||
| @@ -39,7 +38,7 @@ def metadata_fixture(): | ||||
|  | ||||
|  | ||||
| def test_metadata(metadata_fixture): | ||||
|     expect_number = 23 | ||||
|     expect_number = 24 | ||||
|     assert len(metadata_fixture) == expect_number | ||||
|  | ||||
|  | ||||
| @@ -54,16 +53,11 @@ class TestFileFormat: | ||||
|         assert title == EXPECTED_SPOTIFY_TITLE.replace(" ", "_") | ||||
|  | ||||
|  | ||||
| def monkeypatch_youtube_search_page(*args, **kwargs): | ||||
|     fake_urlopen = urllib.request.urlopen(GIST_URL) | ||||
|     return fake_urlopen | ||||
|  | ||||
|  | ||||
| def test_youtube_url(metadata_fixture, monkeypatch): | ||||
|     monkeypatch.setattr( | ||||
|         youtube_tools.GenerateYouTubeURL, | ||||
|         "_fetch_response", | ||||
|         monkeypatch_youtube_search_page, | ||||
|         loader.monkeypatch_youtube_search_page, | ||||
|     ) | ||||
|     url = youtube_tools.generate_youtube_url(SPOTIFY_TRACK_URL, metadata_fixture) | ||||
|     assert url == EXPECTED_YOUTUBE_URL | ||||
| @@ -73,7 +67,7 @@ def test_youtube_title(metadata_fixture, monkeypatch): | ||||
|     monkeypatch.setattr( | ||||
|         youtube_tools.GenerateYouTubeURL, | ||||
|         "_fetch_response", | ||||
|         monkeypatch_youtube_search_page, | ||||
|         loader.monkeypatch_youtube_search_page, | ||||
|     ) | ||||
|     content = youtube_tools.go_pafy(SPOTIFY_TRACK_URL, metadata_fixture) | ||||
|     pytest.content_fixture = content | ||||
| @@ -107,22 +101,16 @@ class TestDownload: | ||||
|  | ||||
|     def test_m4a(self, monkeypatch, filename_fixture): | ||||
|         expect_download = True | ||||
|         monkeypatch.setattr( | ||||
|             "pafy.backend_shared.BaseStream.download", self.blank_audio_generator | ||||
|         ) | ||||
|         download = youtube_tools.download_song( | ||||
|             filename_fixture + ".m4a", pytest.content_fixture | ||||
|         ) | ||||
|         monkeypatch.setattr("pafy.backend_shared.BaseStream.download", self.blank_audio_generator) | ||||
|         monkeypatch.setattr("pafy.backend_youtube_dl.YtdlStream.download", self.blank_audio_generator) | ||||
|         download = youtube_tools.download_song(filename_fixture + ".m4a", pytest.content_fixture) | ||||
|         assert download == expect_download | ||||
|  | ||||
|     def test_webm(self, monkeypatch, filename_fixture): | ||||
|         expect_download = True | ||||
|         monkeypatch.setattr( | ||||
|             "pafy.backend_shared.BaseStream.download", self.blank_audio_generator | ||||
|         ) | ||||
|         download = youtube_tools.download_song( | ||||
|             filename_fixture + ".webm", pytest.content_fixture | ||||
|         ) | ||||
|         monkeypatch.setattr("pafy.backend_shared.BaseStream.download", self.blank_audio_generator) | ||||
|         monkeypatch.setattr("pafy.backend_youtube_dl.YtdlStream.download", self.blank_audio_generator) | ||||
|         download = youtube_tools.download_song(filename_fixture + ".webm", pytest.content_fixture) | ||||
|         assert download == expect_download | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										35
									
								
								test/test_patcher.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								test/test_patcher.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| from spotdl import patcher | ||||
| import pafy | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| patcher.patch_pafy() | ||||
|  | ||||
| class TestPafyContentAvailable: | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class TestMethodAssignment: | ||||
|     def test_pafy_getbestthumb(self): | ||||
|         pafy.backend_shared.BasePafy.getbestthumb == patcher._getbestthumb | ||||
|  | ||||
|  | ||||
| class TestMethodCalls: | ||||
|     @pytest.fixture(scope="module") | ||||
|     def content_fixture(self): | ||||
|         content = pafy.new("http://youtube.com/watch?v=3nQNiWdeH2Q") | ||||
|         return content | ||||
|  | ||||
|     def test_pafy_getbestthumb(self, content_fixture): | ||||
|         thumbnail = patcher._getbestthumb(content_fixture) | ||||
|         assert thumbnail == "https://i.ytimg.com/vi/3nQNiWdeH2Q/maxresdefault.jpg" | ||||
|  | ||||
|     def test_pafy_getbestthumb_without_ytdl(self, content_fixture): | ||||
|         content_fixture._ydl_info["thumbnails"][0]["url"] = None | ||||
|         thumbnail = patcher._getbestthumb(content_fixture) | ||||
|         assert thumbnail == "https://i.ytimg.com/vi/3nQNiWdeH2Q/maxresdefault.jpg" | ||||
|  | ||||
|  | ||||
|     def test_pafy_content_available(self): | ||||
|         TestPafyContentAvailable._content_available = patcher._content_available | ||||
|         assert TestPafyContentAvailable()._content_available("https://youtube.com/") | ||||
| @@ -2,6 +2,9 @@ from spotdl import spotify_tools | ||||
|  | ||||
| import os | ||||
| import pytest | ||||
| import loader | ||||
|  | ||||
| loader.load_defaults() | ||||
|  | ||||
|  | ||||
| def test_generate_token(): | ||||
| @@ -23,7 +26,7 @@ class TestGenerateMetadata: | ||||
|         return metadata | ||||
|  | ||||
|     def test_len(self, metadata_fixture): | ||||
|         assert len(metadata_fixture) == 23 | ||||
|         assert len(metadata_fixture) == 24 | ||||
|  | ||||
|     def test_trackname(self, metadata_fixture): | ||||
|         assert metadata_fixture["name"] == "Spectre" | ||||
|   | ||||
| @@ -17,7 +17,6 @@ YT_API_KEY = "AIzaSyAnItl3udec-Q1d5bkjKJGL-RgrKO_vU90" | ||||
| TRACK_SEARCH = "Tony's Videos VERY SHORT VIDEO 28.10.2016" | ||||
| EXPECTED_TITLE = TRACK_SEARCH | ||||
| EXPECTED_YT_URL = "http://youtube.com/watch?v=qOOcy2-tmbk" | ||||
| EXPECTED_YT_URLS = (EXPECTED_YT_URL, "http://youtube.com/watch?v=5USR1Omo7f0") | ||||
|  | ||||
| RESULT_COUNT_SEARCH = "she is still sleeping SAO" | ||||
|  | ||||
| @@ -71,8 +70,7 @@ class TestYouTubeURL: | ||||
|     def test_only_music_category(self, metadata_fixture): | ||||
|         const.args.music_videos_only = True | ||||
|         url = youtube_tools.generate_youtube_url(TRACK_SEARCH, metadata_fixture) | ||||
|         # YouTube keeps changing its results | ||||
|         assert url in EXPECTED_YT_URLS | ||||
|         assert url == EXPECTED_YT_URL | ||||
|  | ||||
|     def test_all_categories(self, metadata_fixture): | ||||
|         const.args.music_videos_only = False | ||||
| @@ -99,6 +97,56 @@ def content_fixture(metadata_fixture): | ||||
|     return content | ||||
|  | ||||
|  | ||||
| # True = Metadata must be fetched from Spotify | ||||
| # False = Metadata must be fetched from YouTube | ||||
| # None = Metadata must be `None` | ||||
|  | ||||
| MATCH_METADATA_NO_FALLBACK_TEST_TABLE = [ | ||||
|     ("https://open.spotify.com/track/5nWduGwBGBn1PSqYTJUDbS", True), | ||||
|     ("http://youtube.com/watch?v=3nQNiWdeH2Q", None), | ||||
|     ("Linux Talk | Working with Drives and Filesystems", None) | ||||
| ] | ||||
|  | ||||
| MATCH_METADATA_FALLBACK_TEST_TABLE = [ | ||||
|     ("https://open.spotify.com/track/5nWduGwBGBn1PSqYTJUDbS", True), | ||||
|     ("http://youtube.com/watch?v=3nQNiWdeH2Q", False), | ||||
|     ("Linux Talk | Working with Drives and Filesystems", False) | ||||
| ] | ||||
|  | ||||
| MATCH_METADATA_NO_METADATA_TEST_TABLE = [ | ||||
|     ("https://open.spotify.com/track/5nWduGwBGBn1PSqYTJUDbS", None), | ||||
|     ("http://youtube.com/watch?v=3nQNiWdeH2Q", None), | ||||
|     ("Linux Talk | Working with Drives and Filesystems", None) | ||||
| ] | ||||
|  | ||||
|  | ||||
| class TestMetadataOrigin: | ||||
|     def match_metadata(self, track, metadata_type): | ||||
|         _, metadata = youtube_tools.match_video_and_metadata(track) | ||||
|         if metadata_type is None: | ||||
|             assert metadata == metadata_type | ||||
|         else: | ||||
|             assert metadata["spotify_metadata"] == metadata_type | ||||
|  | ||||
|     @pytest.mark.parametrize("track, metadata_type", MATCH_METADATA_NO_FALLBACK_TEST_TABLE) | ||||
|     def test_match_metadata_with_no_fallback(self, track, metadata_type, content_fixture, monkeypatch): | ||||
|         monkeypatch.setattr(youtube_tools, "go_pafy", lambda track, meta_tags: content_fixture) | ||||
|         const.args.no_fallback_metadata = True | ||||
|         self.match_metadata(track, metadata_type) | ||||
|  | ||||
|     @pytest.mark.parametrize("track, metadata_type", MATCH_METADATA_FALLBACK_TEST_TABLE) | ||||
|     def test_match_metadata_with_fallback(self, track, metadata_type, content_fixture, monkeypatch): | ||||
|         monkeypatch.setattr(youtube_tools, "go_pafy", lambda track, meta_tags: content_fixture) | ||||
|         const.args.no_fallback_metadata = False | ||||
|         self.match_metadata(track, metadata_type) | ||||
|  | ||||
|     @pytest.mark.parametrize("track, metadata_type", MATCH_METADATA_NO_METADATA_TEST_TABLE) | ||||
|     def test_match_metadata_with_no_metadata(self, track, metadata_type, content_fixture, monkeypatch): | ||||
|         monkeypatch.setattr(youtube_tools, "go_pafy", lambda track, meta_tags: content_fixture) | ||||
|         const.args.no_metadata = True | ||||
|         self.match_metadata(track, metadata_type) | ||||
|  | ||||
|  | ||||
| @pytest.fixture(scope="module") | ||||
| def title_fixture(content_fixture): | ||||
|     title = youtube_tools.get_youtube_title(content_fixture) | ||||
| @@ -136,6 +184,26 @@ def test_check_exists(metadata_fixture, filename_fixture, tmpdir): | ||||
|     assert check == expect_check | ||||
|  | ||||
|  | ||||
| def test_generate_m3u(tmpdir, monkeypatch): | ||||
|     monkeypatch.setattr(youtube_tools.GenerateYouTubeURL, "_fetch_response", loader.monkeypatch_youtube_search_page) | ||||
|     expect_m3u = ( | ||||
|         "#EXTM3U\n\n" | ||||
|         "#EXTINF:208,Janji - Heroes Tonight (feat. Johnning) [NCS Release]\n" | ||||
|         "http://www.youtube.com/watch?v=3nQNiWdeH2Q\n" | ||||
|         "#EXTINF:226,Alan Walker - Spectre [NCS Release]\n" | ||||
|         "http://www.youtube.com/watch?v=AOeY-nDp7hI\n" | ||||
|     ) | ||||
|     m3u_track_file = os.path.join(str(tmpdir), "m3u_test.txt") | ||||
|     with open(m3u_track_file, "w") as track_file: | ||||
|         track_file.write("\nhttps://open.spotify.com/track/3SipFlNddvL0XNZRLXvdZD") | ||||
|         track_file.write("\nhttp://www.youtube.com/watch?v=AOeY-nDp7hI") | ||||
|     youtube_tools.generate_m3u(m3u_track_file) | ||||
|     m3u_file = "{}.m3u".format(m3u_track_file.split(".")[0]) | ||||
|     with open(m3u_file, "r") as m3u_in: | ||||
|         m3u = m3u_in.readlines() | ||||
|     assert "".join(m3u) == expect_m3u | ||||
|  | ||||
|  | ||||
| class TestDownload: | ||||
|     def test_webm(self, content_fixture, filename_fixture, monkeypatch): | ||||
|         # content_fixture does not have any .webm audiostream | ||||
|   | ||||
		Reference in New Issue
	
	Block a user