Use YouTube as fallback for track metadata if not found on Spotify

This commit is contained in:
Ritiek Malhotra
2018-12-31 19:12:54 +05:30
parent ddb4b01897
commit eb77880f9f
5 changed files with 84 additions and 5 deletions

View File

@@ -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

View File

@@ -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
View 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 = "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

View File

@@ -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"]

View File

@@ -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