diff --git a/spotdl/internals.py b/spotdl/internals.py index 375cc3a..d255215 100644 --- a/spotdl/internals.py +++ b/spotdl/internals.py @@ -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 diff --git a/spotdl/metadata.py b/spotdl/metadata.py index b214d27..d67fabc 100644 --- a/spotdl/metadata.py +++ b/spotdl/metadata.py @@ -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"] diff --git a/spotdl/patcher.py b/spotdl/patcher.py new file mode 100644 index 0000000..fc7efca --- /dev/null +++ b/spotdl/patcher.py @@ -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 = "http://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 diff --git a/spotdl/spotify_tools.py b/spotdl/spotify_tools.py index c0e6b74..2a60284 100644 --- a/spotdl/spotify_tools.py +++ b/spotdl/spotify_tools.py @@ -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"] diff --git a/spotdl/youtube_tools.py b/spotdl/youtube_tools.py index 9c1705f..8f19d3d 100644 --- a/spotdl/youtube_tools.py +++ b/spotdl/youtube_tools.py @@ -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: @@ -49,6 +55,8 @@ def match_video_and_metadata(track, force_pafy=True): track = slugify(content.title).replace("-", " ") if not const.args.no_metadata: meta_tags = spotify_tools.generate_metadata(track) + if meta_tags is None: # and const.args.allow_youtube: + meta_tags = generate_metadata(content) else: # Let it generate metadata, youtube doesn't know spotify slang if not const.args.no_metadata or internals.is_spotify(track): @@ -56,11 +64,37 @@ def match_video_and_metadata(track, force_pafy=True): if force_pafy: content = go_pafy(track, meta_tags) + if meta_tags is None: # and const.args.allow_youtube: + meta_tags = generate_metadata(content) else: content = None 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