├── .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 |
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 |
--------------------------------------------------------------------------------