Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8d8315d

Browse files
committedJan 13, 2025
Append source to the lyrics
1 parent 9328bd7 commit 8d8315d

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed
 

‎beetsplug/lyrics.py

+21-12
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ def __init__(self, config, log):
255255

256256
def fetch(
257257
self, artist: str, title: str, album: str, length: int
258-
) -> str | None:
258+
) -> tuple[str, str] | None:
259259
raise NotImplementedError
260260

261261

@@ -266,6 +266,7 @@ class LRCLyrics:
266266
DURATION_DIFF_TOLERANCE = 0.05
267267

268268
target_duration: float
269+
id: int
269270
duration: float
270271
instrumental: bool
271272
plain: str
@@ -281,6 +282,7 @@ def make(
281282
) -> LRCLyrics:
282283
return cls(
283284
target_duration,
285+
candidate["id"],
284286
candidate["duration"] or 0.0,
285287
candidate["instrumental"],
286288
candidate["plainLyrics"],
@@ -360,18 +362,20 @@ def pick_best_match(cls, lyrics: Iterable[LRCLyrics]) -> LRCLyrics | None:
360362

361363
def fetch(
362364
self, artist: str, title: str, album: str, length: int
363-
) -> str | None:
365+
) -> tuple[str, str] | None:
364366
"""Fetch lyrics text for the given song data."""
365367
fetch = partial(self.fetch_candidates, artist, title, album, length)
366368
make = partial(LRCLyrics.make, target_duration=length)
367369
pick = self.pick_best_match
368370
try:
369-
return next(
371+
item = next(
370372
filter(None, map(pick, (map(make, x) for x in fetch())))
371-
).get_text(self.config["synced"])
373+
)
372374
except StopIteration:
373375
return None
374376

377+
return item.get_text(self.config["synced"]), f"{self.GET_URL}/{item.id}"
378+
375379

376380
class DirectBackend(Backend):
377381
"""A backend for fetching lyrics directly."""
@@ -407,7 +411,7 @@ def encode(cls, text: str) -> str:
407411

408412
return quote(unidecode(text))
409413

410-
def fetch(self, artist: str, title: str, *_) -> str | None:
414+
def fetch(self, artist: str, title: str, *_) -> tuple[str, str] | None:
411415
url = self.build_url(artist, title)
412416

413417
html = self.fetch_text(url)
@@ -429,7 +433,7 @@ def fetch(self, artist: str, title: str, *_) -> str | None:
429433
# sometimes there are non-existent lyrics with some content
430434
if "Lyrics | Musixmatch" in lyrics:
431435
return None
432-
return lyrics
436+
return lyrics, url
433437

434438

435439
class Html:
@@ -530,13 +534,13 @@ def get_results(self, artist: str, title: str) -> Iterable[SearchResult]:
530534
if check_match(candidate):
531535
yield candidate
532536

533-
def fetch(self, artist: str, title: str, *_) -> str | None:
537+
def fetch(self, artist: str, title: str, *_) -> tuple[str, str] | None:
534538
"""Fetch lyrics for the given artist and title."""
535539
for result in self.get_results(artist, title):
536540
if (html := self.fetch_text(result.url)) and (
537541
lyrics := self.scrape(html)
538542
):
539-
return lyrics
543+
return lyrics, result.url
540544

541545
return None
542546

@@ -594,11 +598,15 @@ class Tekstowo(SoupMixin, DirectBackend):
594598
def encode(cls, text: str) -> str:
595599
return cls.non_alpha_to_underscore(unidecode(text.lower()))
596600

597-
def fetch(self, artist: str, title: str, *_) -> str | None:
601+
def fetch(self, artist: str, title: str, *_) -> tuple[str, str] | None:
602+
url = self.build_url(artist, title)
598603
# We are expecting to receive a 404 since we are guessing the URL.
599604
# Thus suppress the error so that it does not end up in the logs.
600605
with suppress(NotFoundError):
601-
return self.scrape(self.fetch_text(self.build_url(artist, title)))
606+
if lyrics := self.scrape(self.fetch_text(url)):
607+
return lyrics, url
608+
609+
return None
602610

603611
return None
604612

@@ -1004,8 +1012,9 @@ def get_lyrics(self, artist: str, title: str, *args) -> str | None:
10041012
self.info("Fetching lyrics for {} - {}", artist, title)
10051013
for backend in self.backends:
10061014
with backend.handle_request():
1007-
if lyrics := backend.fetch(artist, title, *args):
1008-
return lyrics
1015+
if lyrics_info := backend.fetch(artist, title, *args):
1016+
lyrics, url = lyrics_info
1017+
return f"{lyrics}\n\nSource: {url}"
10091018

10101019
return None
10111020

‎test/plugins/test_lyrics.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,12 @@ def _patch_google_search(self, requests_mock, lyrics_page):
279279

280280
def test_backend_source(self, lyrics_plugin, lyrics_page: LyricsPage):
281281
"""Test parsed lyrics from each of the configured lyrics pages."""
282-
lyrics = lyrics_plugin.get_lyrics(
282+
lyrics_info = lyrics_plugin.get_lyrics(
283283
lyrics_page.artist, lyrics_page.track_title, "", 186
284284
)
285285

286-
assert lyrics
286+
assert lyrics_info
287+
lyrics, _ = lyrics_info.split("\n\nSource: ")
287288
assert lyrics == lyrics_page.lyrics
288289

289290

@@ -400,6 +401,7 @@ def test_scrape(self, backend, lyrics_html, expecting_lyrics):
400401

401402
def lyrics_match(**overrides):
402403
return {
404+
"id": 1,
403405
"instrumental": False,
404406
"duration": LYRICS_DURATION,
405407
"syncedLyrics": "synced",
@@ -428,7 +430,9 @@ def fetch_lyrics(self, backend, requests_mock, response_data):
428430
[({"synced": True}, "synced"), ({"synced": False}, "plain")],
429431
)
430432
def test_synced_config_option(self, fetch_lyrics, expected_lyrics):
431-
assert fetch_lyrics() == expected_lyrics
433+
lyrics, _ = fetch_lyrics()
434+
435+
assert lyrics == expected_lyrics
432436

433437
@pytest.mark.parametrize(
434438
"response_data, expected_lyrics",
@@ -475,4 +479,10 @@ def test_synced_config_option(self, fetch_lyrics, expected_lyrics):
475479
)
476480
@pytest.mark.parametrize("plugin_config", [{"synced": True}])
477481
def test_fetch_lyrics(self, fetch_lyrics, expected_lyrics):
478-
assert fetch_lyrics() == expected_lyrics
482+
lyrics_info = fetch_lyrics()
483+
if lyrics_info is None:
484+
assert expected_lyrics is None
485+
else:
486+
lyrics, _ = fetch_lyrics()
487+
488+
assert lyrics == expected_lyrics

0 commit comments

Comments
 (0)
Please sign in to comment.