├── .github └── dependabot.yml ├── README.md ├── lyricspy ├── __init__.py └── aio │ └── __init__.py └── setup.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "11:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | LyricsPy 3 |

4 | 5 |

LyricsPy

6 | 7 |

A library to search for music lyrics.

8 | 9 | ## Installation 10 | 11 | LyricsPy can be installed using `pip` from PyPI or from GitHub. 12 | 13 | ### via PyPI 14 | 15 | ```bash 16 | pip install -U lyricspy 17 | ``` 18 | 19 | #### via GitHub using pip+git 20 | 21 | ```bash 22 | pip install -U git+https://github.com/AmanoTeam/LyricsPy 23 | ``` 24 | 25 | ## Usage 26 | 27 | Using LyricsPy is easy, but let's see some examples: 28 | 29 | ### Musixmatch example 30 | 31 | ```python 32 | from lyricspy import Musixmatch 33 | import json 34 | 35 | def search_lyrics_and_translation_musixmatch(query, lang="pt", limit=1): 36 | # Initializes the Musixmatch class 37 | musixmatch = Musixmatch() 38 | # Note: after the 2.2.0 update the token is optional 39 | 40 | # Performs an automatic search to obtain the lyrics and their translations 41 | search_results = musixmatch.auto(query, lang, limit) 42 | 43 | # Saves the results in a JSON file for viewing 44 | with open("musixmatch_results.json", "w") as f: 45 | json.dump(search_results, f) 46 | 47 | # Example of use 48 | search_lyrics_and_translation_musixmatch("Hello") 49 | ``` 50 | 51 | ### Lyrics example 52 | 53 | ```python 54 | from lyricspy import Lyrics 55 | 56 | def search_lyrics_and_translation(query): 57 | # Initializes the Lyrics class 58 | lyrics = Lyrics() 59 | 60 | # Performs the initial search to obtain the links to the lyrics 61 | search_results = lyrics.search(query) 62 | 63 | # Iterates through the search results 64 | for result in search_results: 65 | # Extracts the link to the lyrics 66 | lyrics_link = result["link"] 67 | 68 | # Performs the search for the lyrics on the page of the obtained link 69 | lyrics_details = lyrics.lyric(result) 70 | 71 | # Prints the title of the song, the lyrics, and the translation (if available) 72 | print(f"Title: {lyrics_details['music']}") 73 | print(f"Lyrics: \n{lyrics_details['lyric']}\n") 74 | if lyrics_details['translation']: 75 | print(f"Translation: \n{lyrics_details['translation']}\n") 76 | 77 | # Example of use 78 | search_lyrics_and_translation("Hello") 79 | ``` -------------------------------------------------------------------------------- /lyricspy/__init__.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | from typing import Union 4 | 5 | import httpx 6 | from bs4 import BeautifulSoup 7 | from uuid import uuid4 8 | 9 | headers = { 10 | "Connection": "Keep-Alive", 11 | "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Musixmatch/0.19.4 Chrome/58.0.3029.110 Electron/1.7.6 Safari/537.36", 12 | } 13 | 14 | 15 | class CaptchaError(Exception): 16 | def __init__(self, message): 17 | super(CaptchaError, self).__init__(message) 18 | 19 | 20 | class Musixmatch: 21 | def __init__(self, usertoken: Union[str, list] = None): 22 | self.token = usertoken 23 | self.http = httpx.Client(http2=True, follow_redirects=True) 24 | 25 | def gen_token(self): 26 | print("gen_token") 27 | a = self.http.get( 28 | "https://apic.musixmatch.com/ws/1.1/token.get", 29 | params=dict( 30 | app_id="web-desktop-app-v1.0", guid=str(uuid4()), format="json" 31 | ), 32 | headers=headers, 33 | ) 34 | print(a.text) 35 | ajson = a.json() 36 | if ajson["message"]["header"]["status_code"] == 401 and ajson["message"]["header"]["hint"] == "captcha": 37 | raise CaptchaError("Captcha required") 38 | else: 39 | return ajson["message"]["body"]["user_token"] 40 | 41 | def search(self, q, limit): 42 | if not self.token: 43 | utoken = self.gen_token() 44 | else: 45 | utoken = random.choice(self.token) if type(self.token) is list else self.token 46 | a = self.http.get( 47 | "https://apic.musixmatch.com/ws/1.1/macro.search", 48 | params=dict( 49 | app_id="web-desktop-app-v1.0", 50 | usertoken=utoken, 51 | q=q, 52 | page=0, 53 | page_size=limit + 1, 54 | format="json", 55 | ), 56 | headers=headers, 57 | ) 58 | return a.json() 59 | 60 | def lyrics(self, id=None, artist=None, track=None): 61 | if not self.token: 62 | utoken = self.gen_token() 63 | else: 64 | utoken = random.choice(self.token) if type(self.token) is list else self.token 65 | a = self.http.get( 66 | "https://apic.musixmatch.com/ws/1.1/macro.subtitles.get", 67 | params=dict( 68 | app_id="web-desktop-app-v1.0", 69 | usertoken=utoken, 70 | q_artist=artist, 71 | q_track=track, 72 | track_id=id, 73 | format="json", 74 | ), 75 | headers=headers, 76 | follow_redirects=True, 77 | ) 78 | 79 | return a.json() 80 | 81 | def translation(self, id, lang): 82 | if not self.token: 83 | utoken = self.gen_token() 84 | else: 85 | utoken = random.choice(self.token) if type(self.token) is list else self.token 86 | a = self.http.get( 87 | "https://apic.musixmatch.com/ws/1.1/crowd.track.translations.get", 88 | params=dict( 89 | app_id="web-desktop-app-v1.0", 90 | usertoken=utoken, 91 | selected_language=lang, 92 | track_id=id, 93 | part="user", 94 | format="json", 95 | ), 96 | headers=headers, 97 | ) 98 | return a.json() 99 | 100 | def auto(self, q, lang, limit=5): 101 | a = self.search(q, limit) 102 | res = ( 103 | a["message"]["body"]["macro_result_list"]["track_list"] 104 | if limit != 1 105 | else [a["message"]["body"]["macro_result_list"]["best_match"]] 106 | ) 107 | ret = [] 108 | for i in res: 109 | id = i["track"]["track_id"] if "track" in i else i["id"] 110 | b = self.lyrics(id) 111 | letra = b["message"]["body"]["macro_calls"]["track.lyrics.get"]["message"]["body"]["lyrics"]["lyrics_body"] 112 | c = self.translation(id, lang) 113 | tr = letra 114 | for i in c["message"]["body"]["translations_list"]: 115 | escape = re.escape(i["translation"]["snippet"]) 116 | tr = re.sub( 117 | f"^{escape}$", i["translation"]["description"], tr, flags=re.M 118 | ) 119 | b["translate"] = tr 120 | ret.append(b) 121 | return ret 122 | 123 | def parce(self, q): 124 | autor = q['message']['body']['macro_calls']['matcher.track.get']['message']['body']['track']['artist_name'] 125 | musica = q['message']['body']['macro_calls']['matcher.track.get']['message']['body']['track']['track_name'] 126 | letra = q['message']['body']['macro_calls']['track.lyrics.get']['message']['body']['lyrics']['lyrics_body'] 127 | link = q['message']['body']['macro_calls']['track.lyrics.get']['message']['body']['lyrics']['backlink_url'].split('?')[0] 128 | traducao = q['translate'] if 'translate' in q else None 129 | id = q['message']['body']['macro_calls']['matcher.track.get']['message']['body']['track']['track_id'] 130 | ret = {'autor': autor, 'musica': musica, 'letra': letra, 'link': link, 'traducao':traducao, 'id':id} 131 | return ret 132 | 133 | 134 | class Letras: 135 | def __init__(self): 136 | self.http = httpx.Client(http2=True) 137 | 138 | def letra(self, query, **kwargs): 139 | link = query["link"].replace("www.letras", "m.letras") 140 | r = self.http.get(link, params=dict(**kwargs)) 141 | tr = None 142 | soup = BeautifulSoup(r.text, "html.parser") 143 | for br in soup.find_all("br"): 144 | br.replace_with("\n") 145 | a = soup.find("div", "lyric-cnt g-1") 146 | # Songs with translation 147 | if a is None: 148 | a = soup.find("div", "lyric-tra_l") 149 | tr = soup.find("div", "lyric-tra_r") 150 | b = "\n\n".join(i.get_text() for i in a.find_all("p")) 151 | query.update({"letra": b}) 152 | if tr is not None: 153 | b = "\n\n".join(i.get_text() for i in a.find_all("p")) 154 | query.update({"traducao": b}) 155 | else: 156 | query.update({"traducao": None}) 157 | return query 158 | 159 | def search(self, query): 160 | r = self.http.get( 161 | "https://studiosolsolr-a.akamaihd.net/letras/app2/", params=dict(q=query) 162 | ) 163 | a = r.json() 164 | x = [] 165 | n = 0 166 | for i in a["highlighting"]: 167 | if "mus" in i: 168 | g = a["response"]["docs"][n] 169 | g.update({"link": f"https://www.letras.mus.br/{g['dns']}/{g['url']}"}) 170 | x.append(g) 171 | n += 1 172 | return x 173 | 174 | def auto(self, query, limit=4): 175 | result = [] 176 | n = 0 177 | for i in self.search(query): 178 | a = self.letra(i) 179 | result.append(a) 180 | n += 1 181 | if n == limit: 182 | break 183 | return result 184 | 185 | def parce(self, q): 186 | autor = q["art"] 187 | musica = q["txt"] 188 | letra = q["letra"] 189 | link = q["link"] 190 | traducao = q["traducao"] 191 | id = q["id"] 192 | ret = { 193 | "autor": autor, 194 | "musica": musica, 195 | "letra": letra, 196 | "link": link, 197 | "traducao": traducao, 198 | "id": id, 199 | } 200 | return ret 201 | 202 | class Genius: 203 | def __init__(self, token=str) -> None: 204 | self.http = httpx.Client(http2=True) 205 | self.token = token 206 | 207 | def search(self, query, limit=5) -> dict: 208 | a = self.http.get( 209 | "https://api.genius.com/search", 210 | params=dict(q=query, access_token=self.token), 211 | headers=headers, 212 | ) 213 | return a.json() 214 | 215 | def lyrics(self, id) -> dict: 216 | a = self.http.get( 217 | f"https://api.genius.com/songs/{id}", 218 | params=dict(access_token=self.token), 219 | headers=headers, 220 | ) 221 | return a.json() 222 | 223 | def auto(self, query, limit=5) -> dict: 224 | a = self.search(query, limit) 225 | res = a["response"]["hits"] 226 | ret = [] 227 | for i in res: 228 | id = i["result"]["id"] 229 | b = self.lyrics(id) 230 | letra = b["response"]["song"]["lyrics"] 231 | ret.append({"letra": letra}) 232 | return ret 233 | 234 | def parce(self, q): 235 | autor = q["response"]["song"]["primary_artist"]["name"] 236 | musica = q["response"]["song"]["title"] 237 | letra = q["response"]["song"]["lyrics"]["plain"] 238 | link = q["response"]["song"]["url"] 239 | traducao = None 240 | id = q["response"]["song"]["id"] 241 | ret = { 242 | "autor": autor, 243 | "musica": musica, 244 | "letra": letra, 245 | "link": link, 246 | "traducao": traducao, 247 | "id": id, 248 | } 249 | return ret 250 | -------------------------------------------------------------------------------- /lyricspy/aio/__init__.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | from typing import Union 4 | from uuid import uuid4 5 | 6 | import httpx 7 | from bs4 import BeautifulSoup 8 | 9 | headers = { 10 | "Connection": "Keep-Alive", 11 | "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.37", 12 | } 13 | 14 | 15 | class Musixmatch: 16 | """ 17 | A class for interacting with the Musixmatch API. 18 | 19 | Provides methods for generating tokens, searching for songs, retrieving lyrics, 20 | and translating lyrics. 21 | 22 | Attributes: 23 | token (str|list): The API token(s) for authenticating requests to Musixmatch. 24 | """ 25 | 26 | def __init__(self, usertoken: Union[str, list] = None): 27 | self.token = usertoken 28 | self.http = httpx.AsyncClient(http2=True, follow_redirects=True) 29 | 30 | async def gen_token(self) -> str: 31 | """ 32 | enerate a token for authenticating requests to Musixmatch. 33 | 34 | Returns: 35 | str: The generated API token. 36 | """ 37 | a = await self.http.get( 38 | "https://apic.musixmatch.com/ws/1.1/token.get", 39 | params=dict(app_id="community-app-v1.0", guid=str(uuid4()), format="json"), 40 | headers=headers, 41 | ) 42 | return a.json()["message"]["body"]["user_token"] 43 | 44 | async def search(self, q, limit) -> dict: 45 | """ 46 | Search for songs on Musixmatch. 47 | 48 | Args: 49 | q (str): The search query. 50 | limit (int): The maximum number of results to return. 51 | 52 | Returns: 53 | dict: The search results. 54 | """ 55 | if not self.token: 56 | utoken = await self.gen_token() 57 | else: 58 | utoken = ( 59 | random.choice(self.token) if type(self.token) is list else self.token 60 | ) 61 | a = await self.http.get( 62 | "https://apic.musixmatch.com/ws/1.1/macro.search", 63 | params=dict( 64 | app_id="android-player-v1.0", 65 | usertoken=utoken, 66 | q=q, 67 | page=0, 68 | page_size=limit + 1, 69 | format="json", 70 | ), 71 | headers=headers, 72 | ) 73 | return a.json() 74 | 75 | async def lyrics(self, id) -> dict: 76 | """ 77 | Retrieve the lyrics for a song on Musixmatch. 78 | 79 | Args: 80 | id (str): The ID of the song. 81 | 82 | Returns: 83 | dict: The lyrics for the song. 84 | """ 85 | if not self.token: 86 | utoken = await self.gen_token() 87 | else: 88 | utoken = ( 89 | random.choice(self.token) if type(self.token) is list else self.token 90 | ) 91 | a = await self.http.get( 92 | "https://apic.musixmatch.com/ws/1.1/macro.subtitles.get", 93 | params=dict( 94 | app_id="android-player-v1.0", 95 | usertoken=utoken, 96 | track_id=id, 97 | format="json", 98 | ), 99 | headers=headers, 100 | follow_redirects=True, 101 | ) 102 | 103 | return a.json() 104 | 105 | async def spotify_lyrics(self, artist, track) -> dict: 106 | """ 107 | Retrieve the lyrics for a song on Musixmatch using Spotify data. 108 | 109 | Args: 110 | artist (str): The name of the artist. 111 | track (str): The name of the track. 112 | 113 | Returns: 114 | dict: The lyrics for the song. 115 | """ 116 | if not self.token: 117 | utoken = await self.gen_token() 118 | else: 119 | utoken = ( 120 | random.choice(self.token) if type(self.token) is list else self.token 121 | ) 122 | a = await self.http.get( 123 | "https://apic.musixmatch.com/ws/1.1/macro.subtitles.get", 124 | params=dict( 125 | app_id="android-player-v1.0", 126 | usertoken=utoken, 127 | q_artist=artist, 128 | q_track=track, 129 | format="json", 130 | ), 131 | headers=headers, 132 | follow_redirects=True, 133 | ) 134 | 135 | return a.json() 136 | 137 | async def translation(self, id, lang, letra=None) -> Union[str, None]: 138 | """ 139 | Translate the lyrics for a song on Musixmatch. 140 | 141 | Args: 142 | id (str): The ID of the song. 143 | lang (str): The language to translate the lyrics to. 144 | letra (str): The lyrics to translate. 145 | 146 | Returns: 147 | str|None: The translated lyrics. 148 | """ 149 | if not self.token: 150 | utoken = await self.gen_token() 151 | else: 152 | utoken = ( 153 | random.choice(self.token) if type(self.token) is list else self.token 154 | ) 155 | a = await self.http.get( 156 | "https://apic.musixmatch.com/ws/1.1/crowd.track.translations.get", 157 | params=dict( 158 | app_id="android-player-v1.0", 159 | usertoken=utoken, 160 | selected_language=lang, 161 | track_id=id, 162 | part="user", 163 | format="json", 164 | ), 165 | headers=headers, 166 | ) 167 | c = a.json() 168 | if c["message"]["body"]["translations_list"] and letra: 169 | tr = letra 170 | for i in c["message"]["body"]["translations_list"]: 171 | escape = re.escape(i["translation"]["snippet"]) 172 | tr = re.sub( 173 | f"^{escape}$", i["translation"]["description"], tr, flags=re.M 174 | ) 175 | elif c["message"]["body"]["translations_list"]: 176 | tr = True 177 | else: 178 | tr = None 179 | 180 | return tr 181 | 182 | async def auto(self, q=None, lang="pt", limit=5, id=None) -> list: 183 | """ 184 | Retrieve the lyrics for a song on Musixmatch. 185 | 186 | Args: 187 | q (str): The search query. 188 | lang (str): The language to translate the lyrics to. 189 | limit (int): The maximum number of results to return. 190 | id (str): The ID of the song. 191 | 192 | Returns: 193 | list: The lyrics for the song. 194 | """ 195 | if q: 196 | a = await self.search(q, limit) 197 | res = ( 198 | a["message"]["body"]["macro_result_list"]["track_list"] 199 | if limit != 1 200 | else [a["message"]["body"]["macro_result_list"]["best_match"]] 201 | ) 202 | else: 203 | res = [id] 204 | ret = [] 205 | for i in res: 206 | if not id: 207 | id = i["track"]["track_id"] if "track" in i else i["id"] 208 | b = await self.lyrics(id) 209 | letra = b["message"]["body"]["macro_calls"]["track.lyrics.get"]["message"][ 210 | "body" 211 | ]["lyrics"]["lyrics_body"] 212 | c = await self.translation(id, lang, letra) 213 | b["translate"] = c 214 | ret.append(b) 215 | return ret 216 | 217 | def parce(self, q): 218 | autor = q["message"]["body"]["macro_calls"]["matcher.track.get"]["message"][ 219 | "body" 220 | ]["track"]["artist_name"] 221 | musica = q["message"]["body"]["macro_calls"]["matcher.track.get"]["message"][ 222 | "body" 223 | ]["track"]["track_name"] 224 | letra = q["message"]["body"]["macro_calls"]["track.lyrics.get"]["message"][ 225 | "body" 226 | ]["lyrics"]["lyrics_body"] 227 | link = q["message"]["body"]["macro_calls"]["track.lyrics.get"]["message"][ 228 | "body" 229 | ]["lyrics"]["backlink_url"].split("?")[0] 230 | traducao = q["translate"] if "translate" in q else None 231 | id = q["message"]["body"]["macro_calls"]["matcher.track.get"]["message"][ 232 | "body" 233 | ]["track"]["track_id"] 234 | ret = { 235 | "autor": autor, 236 | "musica": musica, 237 | "letra": letra, 238 | "link": link, 239 | "traducao": traducao, 240 | "id": id, 241 | } 242 | return ret 243 | 244 | 245 | class Letras: 246 | """ 247 | A class for interacting with the Letras API. 248 | 249 | Provides methods for searching songs and retrieving lyrics from Letras. 250 | """ 251 | 252 | def __init__(self): 253 | self.http = httpx.AsyncClient(http2=True) 254 | 255 | async def letra(self, query, **kwargs) -> dict: 256 | """ 257 | Retrieve the lyrics for a song on Letras. 258 | 259 | Args: 260 | query (dict): The search query. 261 | kwargs: Additional keyword arguments. 262 | 263 | Returns: 264 | dict: The lyrics for the song. 265 | """ 266 | link = query["link"].replace("www.letras", "m.letras") 267 | r = await self.http.get(link, params=dict(**kwargs)) 268 | tr = None 269 | soup = BeautifulSoup(r.text, "html.parser") 270 | for br in soup.find_all("br"): 271 | br.replace_with("\n") 272 | a = soup.find("div", "lyric-cnt g-1") 273 | # Songs with translation 274 | if a is None: 275 | a = soup.find("div", "lyric-tra_l") 276 | tr = soup.find("div", "lyric-tra_r") 277 | b = "\n\n".join(i.get_text() for i in a.find_all("p")) 278 | query.update({"letra": b}) 279 | if tr is not None: 280 | b = "\n\n".join(i.get_text() for i in a.find_all("p")) 281 | query.update({"traducao": b}) 282 | else: 283 | query.update({"traducao": None}) 284 | return query 285 | 286 | async def search(self, query) -> dict: 287 | """ 288 | Search for songs on Letras. 289 | 290 | Args: 291 | query (str): The search query. 292 | 293 | Returns: 294 | dict: The search results. 295 | """ 296 | r = await self.http.get( 297 | "https://studiosolsolr-a.akamaihd.net/letras/app2/", params=dict(q=query) 298 | ) 299 | a = r.json() 300 | x = [] 301 | n = 0 302 | for i in a["highlighting"]: 303 | if "mus" in i: 304 | g = a["response"]["docs"][n] 305 | g.update({"link": f"https://www.letras.mus.br/{g['dns']}/{g['url']}"}) 306 | x.append(g) 307 | n += 1 308 | return x 309 | 310 | async def auto(self, query, limit=4) -> list: 311 | """ 312 | Retrieve the lyrics for a song on Letras. 313 | 314 | Args: 315 | query (str): The search query. 316 | limit (int): The maximum number of results to return. 317 | 318 | Returns: 319 | list: The lyrics for the song. 320 | """ 321 | result = [] 322 | n = 0 323 | for i in await self.search(query): 324 | a = await self.letra(i) 325 | result.append(a) 326 | n += 1 327 | if n == limit: 328 | break 329 | return result 330 | 331 | def parce(self, q) -> dict: 332 | """ 333 | Parse the search results from Letras. 334 | 335 | Args: 336 | q (dict): The search results. 337 | 338 | Returns: 339 | dict: The parsed search results. 340 | """ 341 | autor = q["art"] 342 | musica = q["txt"] 343 | letra = q["letra"] 344 | link = q["link"] 345 | traducao = q["traducao"] 346 | id = q["id"] 347 | ret = { 348 | "autor": autor, 349 | "musica": musica, 350 | "letra": letra, 351 | "link": link, 352 | "traducao": traducao, 353 | "id": id, 354 | } 355 | return ret 356 | 357 | 358 | class Genius: 359 | """ 360 | Class for interacting with the Genius API. 361 | 362 | Provides methods for searching songs and retrieving lyrics from Genius. 363 | 364 | Attributes: 365 | token (str): The API token for authenticating requests to Genius. 366 | """ 367 | 368 | def __init__(self, token=None): 369 | self.token = ( 370 | token 371 | if token 372 | else "ZTejoT_ojOEasIkT9WrMBhBQOz6eYKK5QULCMECmOhvwqjRZ6WbpamFe3geHnvp3" 373 | ) 374 | self.http = httpx.AsyncClient(http2=True) 375 | 376 | async def lyrics(self, id) -> dict: 377 | """ 378 | Retrieve the lyrics for a song on Genius. 379 | 380 | Args: 381 | id (str): The ID of the song. 382 | 383 | Returns: 384 | dict: The lyrics for the song. 385 | """ 386 | async with httpx.AsyncClient(http2=True, timeout=50) as client: 387 | response = await client.get( 388 | f"https://api.genius.com/songs/{id}", 389 | params={"text_format": "plain"}, 390 | headers={"Authorization": f"Bearer {self.token}"}, 391 | ) 392 | 393 | return response.json() 394 | 395 | async def spotify_lyrics(self, artist, track) -> dict: 396 | """ 397 | Retrieve the lyrics for a song on Genius using Spotify data. 398 | 399 | Args: 400 | artist (str): The name of the artist. 401 | track (str): The name of the track. 402 | 403 | Returns: 404 | dict: The lyrics for the song. 405 | """ 406 | res = await self.search(f"{artist} {track}") 407 | 408 | if not res: 409 | return None 410 | 411 | return res[0]["result"]["id"] 412 | 413 | return None 414 | 415 | async def search(self, query) -> dict: 416 | """ 417 | Search for songs on Genius. 418 | 419 | Args: 420 | query (str): The search query. 421 | 422 | Returns: 423 | dict: The search results. 424 | """ 425 | async with httpx.AsyncClient(http2=True, follow_redirects=True) as client: 426 | response = await client.get( 427 | "https://api.genius.com/search", 428 | params={"q": query}, 429 | headers={"Authorization": f"Bearer {self.token}"}, 430 | ) 431 | 432 | return response.json()["response"]["hits"] 433 | 434 | async def auto(self, query, limit=4) -> list: 435 | """ 436 | Retrieve the lyrics for a song on Genius. 437 | 438 | Args: 439 | query (str): The search query. 440 | limit (int): The maximum number of results to return. 441 | 442 | Returns: 443 | list: The lyrics for the song. 444 | """ 445 | result = [] 446 | hits = await self.search(query) 447 | for i in hits: 448 | lyrics = await self.lyrics(i["result"]["id"]) 449 | result.append(lyrics) 450 | if len(result) == limit: 451 | break 452 | return result 453 | 454 | def parse(self, q) -> dict: 455 | """ 456 | Parse the search results from Genius. 457 | 458 | Args: 459 | q (dict): The search results. 460 | 461 | Returns: 462 | dict: The parsed search results. 463 | """ 464 | autor = q["response"]["song"]["primary_artist"]["name"] 465 | musica = q["response"]["song"]["title"] 466 | letra = q["response"]["song"]["lyrics"]["plain"] 467 | link = q["response"]["song"]["url"] 468 | traducao = None 469 | id = q["response"]["song"]["id"] 470 | ret = { 471 | "autor": autor, 472 | "musica": musica, 473 | "letra": letra, 474 | "link": link, 475 | "traducao": traducao, 476 | "id": id, 477 | } 478 | return ret 479 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md") as f: 4 | long_desc = f.read() 5 | 6 | setuptools.setup( 7 | name='LyricsPy', 8 | version='2.2.2', 9 | packages=["lyricspy", "lyricspy.aio"], 10 | url='https://github.com/amanoteam/LyricsPy', 11 | author='Amano Team', 12 | install_requires=['httpx'], 13 | author_email='contact@amanoteam.com', 14 | description='search lyrics on musixmatch.com', 15 | long_description=long_desc, 16 | long_description_content_type="text/markdown" 17 | ) 18 | --------------------------------------------------------------------------------