mirror of
https://github.com/beetbox/beets.git
synced 2025-12-06 08:39:17 +01:00
Append source to the lyrics
This commit is contained in:
parent
bdc564a573
commit
734bcc28a8
2 changed files with 33 additions and 15 deletions
|
|
@ -266,7 +266,7 @@ class Backend(RequestHandler, metaclass=BackendClass):
|
|||
|
||||
def fetch(
|
||||
self, artist: str, title: str, album: str, length: int
|
||||
) -> str | None:
|
||||
) -> tuple[str, str] | None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
|
@ -277,6 +277,7 @@ class LRCLyrics:
|
|||
DURATION_DIFF_TOLERANCE = 0.05
|
||||
|
||||
target_duration: float
|
||||
id: int
|
||||
duration: float
|
||||
instrumental: bool
|
||||
plain: str
|
||||
|
|
@ -292,6 +293,7 @@ class LRCLyrics:
|
|||
) -> LRCLyrics:
|
||||
return cls(
|
||||
target_duration,
|
||||
candidate["id"],
|
||||
candidate["duration"] or 0.0,
|
||||
candidate["instrumental"],
|
||||
candidate["plainLyrics"],
|
||||
|
|
@ -374,14 +376,15 @@ class LRCLib(Backend):
|
|||
|
||||
def fetch(
|
||||
self, artist: str, title: str, album: str, length: int
|
||||
) -> str | None:
|
||||
) -> tuple[str, str] | None:
|
||||
"""Fetch lyrics text for the given song data."""
|
||||
evaluate_item = partial(LRCLyrics.make, target_duration=length)
|
||||
|
||||
for group in self.fetch_candidates(artist, title, album, length):
|
||||
candidates = [evaluate_item(item) for item in group]
|
||||
if item := self.pick_best_match(candidates):
|
||||
return item.get_text(self.config["synced"])
|
||||
lyrics = item.get_text(self.config["synced"])
|
||||
return lyrics, f"{self.GET_URL}/{item.id}"
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -420,7 +423,7 @@ class MusiXmatch(DirectBackend):
|
|||
|
||||
return quote(unidecode(text))
|
||||
|
||||
def fetch(self, artist: str, title: str, *_) -> str | None:
|
||||
def fetch(self, artist: str, title: str, *_) -> tuple[str, str] | None:
|
||||
url = self.build_url(artist, title)
|
||||
|
||||
html = self.fetch_text(url)
|
||||
|
|
@ -442,7 +445,7 @@ class MusiXmatch(DirectBackend):
|
|||
# sometimes there are non-existent lyrics with some content
|
||||
if "Lyrics | Musixmatch" in lyrics:
|
||||
return None
|
||||
return lyrics
|
||||
return lyrics, url
|
||||
|
||||
|
||||
class Html:
|
||||
|
|
@ -543,13 +546,13 @@ class SearchBackend(SoupMixin, Backend):
|
|||
if check_match(candidate):
|
||||
yield candidate
|
||||
|
||||
def fetch(self, artist: str, title: str, *_) -> str | None:
|
||||
def fetch(self, artist: str, title: str, *_) -> tuple[str, str] | None:
|
||||
"""Fetch lyrics for the given artist and title."""
|
||||
for result in self.get_results(artist, title):
|
||||
if (html := self.fetch_text(result.url)) and (
|
||||
lyrics := self.scrape(html)
|
||||
):
|
||||
return lyrics
|
||||
return lyrics, result.url
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -604,11 +607,15 @@ class Tekstowo(SoupMixin, DirectBackend):
|
|||
def encode(cls, text: str) -> str:
|
||||
return cls.non_alpha_to_underscore(unidecode(text.lower()))
|
||||
|
||||
def fetch(self, artist: str, title: str, *_) -> str | None:
|
||||
def fetch(self, artist: str, title: str, *_) -> tuple[str, str] | None:
|
||||
url = self.build_url(artist, title)
|
||||
# We are expecting to receive a 404 since we are guessing the URL.
|
||||
# Thus suppress the error so that it does not end up in the logs.
|
||||
with suppress(NotFoundError):
|
||||
return self.scrape(self.fetch_text(self.build_url(artist, title)))
|
||||
if lyrics := self.scrape(self.fetch_text(url)):
|
||||
return lyrics, url
|
||||
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -1014,8 +1021,9 @@ class LyricsPlugin(RequestHandler, plugins.BeetsPlugin):
|
|||
self.info("Fetching lyrics for {} - {}", artist, title)
|
||||
for backend in self.backends:
|
||||
with backend.handle_request():
|
||||
if lyrics := backend.fetch(artist, title, *args):
|
||||
return lyrics
|
||||
if lyrics_info := backend.fetch(artist, title, *args):
|
||||
lyrics, url = lyrics_info
|
||||
return f"{lyrics}\n\nSource: {url}"
|
||||
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -279,11 +279,12 @@ class TestLyricsSources(LyricsBackendTest):
|
|||
|
||||
def test_backend_source(self, lyrics_plugin, lyrics_page: LyricsPage):
|
||||
"""Test parsed lyrics from each of the configured lyrics pages."""
|
||||
lyrics = lyrics_plugin.get_lyrics(
|
||||
lyrics_info = lyrics_plugin.get_lyrics(
|
||||
lyrics_page.artist, lyrics_page.track_title, "", 186
|
||||
)
|
||||
|
||||
assert lyrics
|
||||
assert lyrics_info
|
||||
lyrics, _ = lyrics_info.split("\n\nSource: ")
|
||||
assert lyrics == lyrics_page.lyrics
|
||||
|
||||
|
||||
|
|
@ -400,6 +401,7 @@ LYRICS_DURATION = 950
|
|||
|
||||
def lyrics_match(**overrides):
|
||||
return {
|
||||
"id": 1,
|
||||
"instrumental": False,
|
||||
"duration": LYRICS_DURATION,
|
||||
"syncedLyrics": "synced",
|
||||
|
|
@ -428,7 +430,9 @@ class TestLRCLibLyrics(LyricsBackendTest):
|
|||
[({"synced": True}, "synced"), ({"synced": False}, "plain")],
|
||||
)
|
||||
def test_synced_config_option(self, fetch_lyrics, expected_lyrics):
|
||||
assert fetch_lyrics() == expected_lyrics
|
||||
lyrics, _ = fetch_lyrics()
|
||||
|
||||
assert lyrics == expected_lyrics
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"response_data, expected_lyrics",
|
||||
|
|
@ -490,4 +494,10 @@ class TestLRCLibLyrics(LyricsBackendTest):
|
|||
)
|
||||
@pytest.mark.parametrize("plugin_config", [{"synced": True}])
|
||||
def test_fetch_lyrics(self, fetch_lyrics, expected_lyrics):
|
||||
assert fetch_lyrics() == expected_lyrics
|
||||
lyrics_info = fetch_lyrics()
|
||||
if lyrics_info is None:
|
||||
assert expected_lyrics is None
|
||||
else:
|
||||
lyrics, _ = fetch_lyrics()
|
||||
|
||||
assert lyrics == expected_lyrics
|
||||
|
|
|
|||
Loading…
Reference in a new issue