@@ -255,7 +255,7 @@ def __init__(self, config, log):
255
255
256
256
def fetch (
257
257
self , artist : str , title : str , album : str , length : int
258
- ) -> str | None :
258
+ ) -> tuple [ str , str ] | None :
259
259
raise NotImplementedError
260
260
261
261
@@ -266,6 +266,7 @@ class LRCLyrics:
266
266
DURATION_DIFF_TOLERANCE = 0.05
267
267
268
268
target_duration : float
269
+ id : int
269
270
duration : float
270
271
instrumental : bool
271
272
plain : str
@@ -281,6 +282,7 @@ def make(
281
282
) -> LRCLyrics :
282
283
return cls (
283
284
target_duration ,
285
+ candidate ["id" ],
284
286
candidate ["duration" ] or 0.0 ,
285
287
candidate ["instrumental" ],
286
288
candidate ["plainLyrics" ],
@@ -360,18 +362,20 @@ def pick_best_match(cls, lyrics: Iterable[LRCLyrics]) -> LRCLyrics | None:
360
362
361
363
def fetch (
362
364
self , artist : str , title : str , album : str , length : int
363
- ) -> str | None :
365
+ ) -> tuple [ str , str ] | None :
364
366
"""Fetch lyrics text for the given song data."""
365
367
fetch = partial (self .fetch_candidates , artist , title , album , length )
366
368
make = partial (LRCLyrics .make , target_duration = length )
367
369
pick = self .pick_best_match
368
370
try :
369
- return next (
371
+ item = next (
370
372
filter (None , map (pick , (map (make , x ) for x in fetch ())))
371
- ). get_text ( self . config [ "synced" ])
373
+ )
372
374
except StopIteration :
373
375
return None
374
376
377
+ return item .get_text (self .config ["synced" ]), f"{ self .GET_URL } /{ item .id } "
378
+
375
379
376
380
class DirectBackend (Backend ):
377
381
"""A backend for fetching lyrics directly."""
@@ -407,7 +411,7 @@ def encode(cls, text: str) -> str:
407
411
408
412
return quote (unidecode (text ))
409
413
410
- def fetch (self , artist : str , title : str , * _ ) -> str | None :
414
+ def fetch (self , artist : str , title : str , * _ ) -> tuple [ str , str ] | None :
411
415
url = self .build_url (artist , title )
412
416
413
417
html = self .fetch_text (url )
@@ -429,7 +433,7 @@ def fetch(self, artist: str, title: str, *_) -> str | None:
429
433
# sometimes there are non-existent lyrics with some content
430
434
if "Lyrics | Musixmatch" in lyrics :
431
435
return None
432
- return lyrics
436
+ return lyrics , url
433
437
434
438
435
439
class Html :
@@ -530,13 +534,13 @@ def get_results(self, artist: str, title: str) -> Iterable[SearchResult]:
530
534
if check_match (candidate ):
531
535
yield candidate
532
536
533
- def fetch (self , artist : str , title : str , * _ ) -> str | None :
537
+ def fetch (self , artist : str , title : str , * _ ) -> tuple [ str , str ] | None :
534
538
"""Fetch lyrics for the given artist and title."""
535
539
for result in self .get_results (artist , title ):
536
540
if (html := self .fetch_text (result .url )) and (
537
541
lyrics := self .scrape (html )
538
542
):
539
- return lyrics
543
+ return lyrics , result . url
540
544
541
545
return None
542
546
@@ -594,11 +598,15 @@ class Tekstowo(SoupMixin, DirectBackend):
594
598
def encode (cls , text : str ) -> str :
595
599
return cls .non_alpha_to_underscore (unidecode (text .lower ()))
596
600
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 )
598
603
# We are expecting to receive a 404 since we are guessing the URL.
599
604
# Thus suppress the error so that it does not end up in the logs.
600
605
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
602
610
603
611
return None
604
612
@@ -1004,8 +1012,9 @@ def get_lyrics(self, artist: str, title: str, *args) -> str | None:
1004
1012
self .info ("Fetching lyrics for {} - {}" , artist , title )
1005
1013
for backend in self .backends :
1006
1014
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 \n Source: { url } "
1009
1018
1010
1019
return None
1011
1020
0 commit comments