26
26
import unicodedata
27
27
import warnings
28
28
from functools import partial
29
- from typing import ClassVar
29
+ from typing import TYPE_CHECKING , ClassVar
30
30
from urllib .parse import quote , urlencode
31
31
32
32
import requests
33
33
from unidecode import unidecode
34
34
35
+ import beets
36
+ from beets import plugins , ui
37
+
38
+ if TYPE_CHECKING :
39
+ from beets .importer import ImportTask
40
+ from beets .library import Item
41
+
35
42
try :
36
43
import bs4
37
44
from bs4 import SoupStrainer
47
54
except ImportError :
48
55
HAS_LANGDETECT = False
49
56
50
-
51
- import beets
52
- from beets import plugins , ui
53
-
54
57
DIV_RE = re .compile (r"<(/?)div>?" , re .I )
55
58
COMMENT_RE = re .compile (r"<!--.*-->" , re .S )
56
59
TAG_RE = re .compile (r"<[^>]*>" )
@@ -256,20 +259,27 @@ def fetch_url(self, url):
256
259
self ._log .debug ("failed to fetch: {0} ({1})" , url , r .status_code )
257
260
return None
258
261
259
- def fetch (self , artist , title , album = None , length = None ):
260
- raise NotImplementedError ()
262
+ def fetch (
263
+ self , artist : str , title : str , album : str , length : int
264
+ ) -> str | None :
265
+ raise NotImplementedError
261
266
262
267
263
268
class LRCLib (Backend ):
264
269
base_url = "https://lrclib.net/api/get"
265
270
266
- def fetch (self , artist , title , album = None , length = None ):
267
- params = {
271
+ def fetch (
272
+ self , artist : str , title : str , album : str , length : int
273
+ ) -> str | None :
274
+ params : dict [str , str | int ] = {
268
275
"artist_name" : artist ,
269
276
"track_name" : title ,
270
- "album_name" : album ,
271
- "duration" : length ,
272
277
}
278
+ if album :
279
+ params ["album_name" ] = album
280
+
281
+ if length :
282
+ params ["duration" ] = length
273
283
274
284
try :
275
285
response = requests .get (
@@ -322,7 +332,7 @@ def encode(cls, text: str) -> str:
322
332
323
333
return quote (unidecode (text ))
324
334
325
- def fetch (self , artist , title , album = None , length = None ) :
335
+ def fetch (self , artist : str , title : str , * _ ) -> str | None :
326
336
url = self .build_url (artist , title )
327
337
328
338
html = self .fetch_url (url )
@@ -370,7 +380,7 @@ def __init__(self, config, log):
370
380
"User-Agent" : USER_AGENT ,
371
381
}
372
382
373
- def fetch (self , artist , title , album = None , length = None ) :
383
+ def fetch (self , artist : str , title : str , * _ ) -> str | None :
374
384
"""Fetch lyrics from genius.com
375
385
376
386
Because genius doesn't allow accessing lyrics via the api,
@@ -501,7 +511,7 @@ class Tekstowo(DirectBackend):
501
511
def encode (cls , text : str ) -> str :
502
512
return cls .non_alpha_to_underscore (unidecode (text .lower ()))
503
513
504
- def fetch (self , artist , title , album = None , length = None ) :
514
+ def fetch (self , artist : str , title : str , * _ ) -> str | None :
505
515
if html := self .fetch_url (self .build_url (artist , title )):
506
516
return self .extract_lyrics (html )
507
517
@@ -675,7 +685,7 @@ def is_page_candidate(self, url_link, url_title, title, artist):
675
685
ratio = difflib .SequenceMatcher (None , song_title , title ).ratio ()
676
686
return ratio >= typo_ratio
677
687
678
- def fetch (self , artist , title , album = None , length = None ) :
688
+ def fetch (self , artist : str , title : str , * _ ) -> str | None :
679
689
query = f"{ artist } { title } "
680
690
url = "https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s" % (
681
691
self .api_key ,
@@ -885,10 +895,7 @@ def func(lib, opts, args):
885
895
for item in items :
886
896
if not opts .local_only and not self .config ["local" ]:
887
897
self .fetch_item_lyrics (
888
- lib ,
889
- item ,
890
- write ,
891
- opts .force_refetch or self .config ["force" ],
898
+ item , write , opts .force_refetch or self .config ["force" ]
892
899
)
893
900
if item .lyrics :
894
901
if opts .printlyr :
@@ -974,15 +981,13 @@ def writerest_indexes(self, directory):
974
981
with open (conffile , "w" ) as output :
975
982
output .write (REST_CONF_TEMPLATE )
976
983
977
- def imported (self , session , task ) :
984
+ def imported (self , _ , task : ImportTask ) -> None :
978
985
"""Import hook for fetching lyrics automatically."""
979
986
if self .config ["auto" ]:
980
987
for item in task .imported_items ():
981
- self .fetch_item_lyrics (
982
- session .lib , item , False , self .config ["force" ]
983
- )
988
+ self .fetch_item_lyrics (item , False , self .config ["force" ])
984
989
985
- def fetch_item_lyrics (self , lib , item , write , force ) :
990
+ def fetch_item_lyrics (self , item : Item , write : bool , force : bool ) -> None :
986
991
"""Fetch and store lyrics for a single item. If ``write``, then the
987
992
lyrics will also be written to the file itself.
988
993
"""
@@ -991,18 +996,17 @@ def fetch_item_lyrics(self, lib, item, write, force):
991
996
self ._log .info ("lyrics already present: {0}" , item )
992
997
return
993
998
994
- lyrics = None
995
- album = item .album
996
- length = round (item .length )
999
+ lyrics_matches = []
1000
+ album , length = item .album , round (item .length )
997
1001
for artist , titles in search_pairs (item ):
998
- lyrics = [
999
- self .get_lyrics (artist , title , album = album , length = length )
1002
+ lyrics_matches = [
1003
+ self .get_lyrics (artist , title , album , length )
1000
1004
for title in titles
1001
1005
]
1002
- if any (lyrics ):
1006
+ if any (lyrics_matches ):
1003
1007
break
1004
1008
1005
- lyrics = "\n \n ---\n \n " .join (filter (None , lyrics ))
1009
+ lyrics = "\n \n ---\n \n " .join (filter (None , lyrics_matches ))
1006
1010
1007
1011
if lyrics :
1008
1012
self ._log .info ("fetched lyrics: {0}" , item )
@@ -1027,18 +1031,20 @@ def fetch_item_lyrics(self, lib, item, write, force):
1027
1031
item .try_write ()
1028
1032
item .store ()
1029
1033
1030
- def get_lyrics (self , artist , title , album = None , length = None ) :
1034
+ def get_lyrics (self , artist : str , title : str , * args ) -> str | None :
1031
1035
"""Fetch lyrics, trying each source in turn. Return a string or
1032
1036
None if no lyrics were found.
1033
1037
"""
1034
1038
for backend in self .backends :
1035
- lyrics = backend .fetch (artist , title , album = album , length = length )
1039
+ lyrics = backend .fetch (artist , title , * args )
1036
1040
if lyrics :
1037
1041
self ._log .debug (
1038
1042
"got lyrics from backend: {0}" , backend .__class__ .__name__
1039
1043
)
1040
1044
return _scrape_strip_cruft (lyrics , True )
1041
1045
1046
+ return None
1047
+
1042
1048
def append_translation (self , text , to_lang ):
1043
1049
from xml .etree import ElementTree
1044
1050
0 commit comments