mirror of
https://github.com/KevinMidboe/spotify-downloader.git
synced 2025-10-29 18:00:15 +00:00
Use YouTube as fallback for track metadata if not found on Spotify
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
from logzero import logger as log
|
from logzero import logger as log
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
from spotdl import const
|
from spotdl import const
|
||||||
|
|
||||||
@@ -253,3 +254,12 @@ def remove_duplicates(tracks):
|
|||||||
local_set = set()
|
local_set = set()
|
||||||
local_set_add = local_set.add
|
local_set_add = local_set.add
|
||||||
return [x for x in tracks if not (x in local_set or local_set_add(x))]
|
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):
|
def __init__(self, music_file, meta_tags):
|
||||||
self.music_file = music_file
|
self.music_file = music_file
|
||||||
self.meta_tags = meta_tags
|
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):
|
def as_mp3(self):
|
||||||
""" Embed metadata to MP3 files. """
|
""" Embed metadata to MP3 files. """
|
||||||
@@ -62,7 +64,7 @@ class EmbedMetadata:
|
|||||||
audiofile["lyricist"] = meta_tags["artists"][0]["name"]
|
audiofile["lyricist"] = meta_tags["artists"][0]["name"]
|
||||||
audiofile["arranger"] = meta_tags["artists"][0]["name"]
|
audiofile["arranger"] = meta_tags["artists"][0]["name"]
|
||||||
audiofile["performer"] = 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"])
|
audiofile["length"] = str(meta_tags["duration"])
|
||||||
if meta_tags["publisher"]:
|
if meta_tags["publisher"]:
|
||||||
audiofile["encodedby"] = meta_tags["publisher"]
|
audiofile["encodedby"] = meta_tags["publisher"]
|
||||||
@@ -78,7 +80,7 @@ class EmbedMetadata:
|
|||||||
audiofile["TYER"] = TYER(encoding=3, text=meta_tags["year"])
|
audiofile["TYER"] = TYER(encoding=3, text=meta_tags["year"])
|
||||||
if meta_tags["publisher"]:
|
if meta_tags["publisher"]:
|
||||||
audiofile["TPUB"] = TPUB(encoding=3, text=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"]:
|
if meta_tags["lyrics"]:
|
||||||
audiofile["USLT"] = USLT(
|
audiofile["USLT"] = USLT(
|
||||||
encoding=3, desc=u"Lyrics", text=meta_tags["lyrics"]
|
encoding=3, desc=u"Lyrics", text=meta_tags["lyrics"]
|
||||||
@@ -106,7 +108,7 @@ class EmbedMetadata:
|
|||||||
audiofile = MP4(music_file)
|
audiofile = MP4(music_file)
|
||||||
self._embed_basic_metadata(audiofile, preset=M4A_TAG_PRESET)
|
self._embed_basic_metadata(audiofile, preset=M4A_TAG_PRESET)
|
||||||
audiofile[M4A_TAG_PRESET["year"]] = meta_tags["year"]
|
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"]:
|
if meta_tags["lyrics"]:
|
||||||
audiofile[M4A_TAG_PRESET["lyrics"]] = meta_tags["lyrics"]
|
audiofile[M4A_TAG_PRESET["lyrics"]] = meta_tags["lyrics"]
|
||||||
try:
|
try:
|
||||||
@@ -127,7 +129,7 @@ class EmbedMetadata:
|
|||||||
audiofile = FLAC(music_file)
|
audiofile = FLAC(music_file)
|
||||||
self._embed_basic_metadata(audiofile)
|
self._embed_basic_metadata(audiofile)
|
||||||
audiofile["year"] = meta_tags["year"]
|
audiofile["year"] = meta_tags["year"]
|
||||||
audiofile["comment"] = meta_tags["external_urls"]["spotify"]
|
audiofile["comment"] = meta_tags["external_urls"][self.provider]
|
||||||
if meta_tags["lyrics"]:
|
if meta_tags["lyrics"]:
|
||||||
audiofile["lyrics"] = meta_tags["lyrics"]
|
audiofile["lyrics"] = meta_tags["lyrics"]
|
||||||
|
|
||||||
@@ -147,7 +149,8 @@ class EmbedMetadata:
|
|||||||
meta_tags = self.meta_tags
|
meta_tags = self.meta_tags
|
||||||
audiofile[preset["artist"]] = meta_tags["artists"][0]["name"]
|
audiofile[preset["artist"]] = meta_tags["artists"][0]["name"]
|
||||||
audiofile[preset["albumartist"]] = meta_tags["album"]["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["title"]] = meta_tags["name"]
|
||||||
audiofile[preset["date"]] = meta_tags["release_date"]
|
audiofile[preset["date"]] = meta_tags["release_date"]
|
||||||
audiofile[preset["originaldate"]] = 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 = "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
|
||||||
@@ -81,6 +81,7 @@ def generate_metadata(raw_song):
|
|||||||
# Some sugar
|
# Some sugar
|
||||||
meta_tags["year"], *_ = meta_tags["release_date"].split("-")
|
meta_tags["year"], *_ = meta_tags["release_date"].split("-")
|
||||||
meta_tags["duration"] = meta_tags["duration_ms"] / 1000.0
|
meta_tags["duration"] = meta_tags["duration_ms"] / 1000.0
|
||||||
|
meta_tags["spotify_metadata"] = True
|
||||||
# Remove unwanted parameters
|
# Remove unwanted parameters
|
||||||
del meta_tags["duration_ms"]
|
del meta_tags["duration_ms"]
|
||||||
del meta_tags["available_markets"]
|
del meta_tags["available_markets"]
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ from spotdl import const
|
|||||||
# Read more on mps-youtube/pafy#199
|
# Read more on mps-youtube/pafy#199
|
||||||
pafy.g.opener.addheaders.append(("Range", "bytes=0-"))
|
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():
|
def set_api_key():
|
||||||
if const.args.youtube_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("-", " ")
|
track = slugify(content.title).replace("-", " ")
|
||||||
if not const.args.no_metadata:
|
if not const.args.no_metadata:
|
||||||
meta_tags = spotify_tools.generate_metadata(track)
|
meta_tags = spotify_tools.generate_metadata(track)
|
||||||
|
if meta_tags is None: # and const.args.allow_youtube:
|
||||||
|
meta_tags = generate_metadata(content)
|
||||||
else:
|
else:
|
||||||
# Let it generate metadata, youtube doesn't know spotify slang
|
# Let it generate metadata, youtube doesn't know spotify slang
|
||||||
if not const.args.no_metadata or internals.is_spotify(track):
|
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:
|
if force_pafy:
|
||||||
content = go_pafy(track, meta_tags)
|
content = go_pafy(track, meta_tags)
|
||||||
|
if meta_tags is None: # and const.args.allow_youtube:
|
||||||
|
meta_tags = generate_metadata(content)
|
||||||
else:
|
else:
|
||||||
content = None
|
content = None
|
||||||
return content, 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):
|
def get_youtube_title(content, number=None):
|
||||||
""" Get the YouTube video's title. """
|
""" Get the YouTube video's title. """
|
||||||
title = content.title
|
title = content.title
|
||||||
|
|||||||
Reference in New Issue
Block a user