import aiohttp, random, bs4, re import function as func from abc import ABC, abstractmethod from urllib.parse import quote from math import floor from importlib import import_module from typing import Optional userAgents = '''Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.11 Safari/535.19 Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11 Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.3 Safari/532.2 Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.221.0 Safari/532.2 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.220.1 Safari/532.1 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.6 Safari/532.1 Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.5 Safari/532.1 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.5 Safari/532.1 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.4 Safari/532.1 Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.3 Safari/532.1 Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.3 Safari/532.1 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/4.0.219.3 Safari/532.1 Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.0 Safari/532.0 Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.197.0 Safari/532.0 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.23 Safari/530.5 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.2 Safari/530.5 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.2 Safari/530.5 Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.4 Mozilla/5.0 (Windows; U; Windows NT 5.2; eu) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.4 Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.4 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.0 Safari/530.5 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.4 (KHTML, like Gecko) Chrome/2.0.171.0 Safari/530.4 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.170.0 Safari/530.1 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.169.0 Safari/530.1 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.168.0 Safari/530.1 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.164.0 Safari/530.1 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.0 (KHTML, like Gecko) Chrome/2.0.162.0 Safari/530.0 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.0 (KHTML, like Gecko) Chrome/2.0.160.0 Safari/530.0 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.10 (KHTML, like Gecko) Chrome/2.0.157.2 Safari/528.10 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.10 (KHTML, like Gecko) Chrome/2.0.157.2 Safari/528.10 Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/528.11 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.11 Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.9 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.9 Mozilla/5.0 (Linux; U; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13 Mozilla/5.0 (Macintosh; U; Mac OS X 10_6_1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5 Mozilla/5.0 (Macintosh; U; Mac OS X 10_5_7; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.9 (KHTML, like Gecko) Chrome/ Safari/530.9 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.6 (KHTML, like Gecko) Chrome/ Safari/530.6 Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5''' LYRIST_ENDPOINT = "https://lyrist.vercel.app/api/" LRCLIB_ENDPOINT = "https://lrclib.net/api/" class LyricsPlatform(ABC): @abstractmethod async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]: ... class A_ZLyrics(LyricsPlatform): async def get(self, url) -> str: try: async with aiohttp.ClientSession() as session: resp = await session.get(url=url, headers={'User-Agent': random.choice(userAgents)}) if resp.status != 200: return None return await resp.text() except: return "" async def get_lyrics(self, title: str, artist: str) -> dict[str, str]: link = await self.googleGet(title=title, artist=artist) if not link: return 0 page = await self.get(link) metadata = [elm.text for elm in self.htmlFindAll(page)('b')] if not metadata: return None try: title = ''.join(i for i in metadata[1][1:-1] if i not in r'<>:"/\|?*') divs = [i.text for i in self.htmlFindAll(page)('div', {'class': None})] lyrics = max(divs, key=len).strip() if not lyrics: return None lyrics_parts = re.split(r"(\[[\w\S_ ]+\:])", lyrics) lyrics_parts = [item for item in lyrics_parts if item != ""] count = len(lyrics_parts) if count > 1: if (count % 2) != 0: del lyrics_parts[count-1] return {lyrics_parts[i].replace("[", "").replace(":]", ""): self.clearText(lyrics_parts[i + 1]) for i in range(0, len(lyrics_parts), 2)} return {"default": self.clearText(lyrics_parts[0])} except: return None async def googleGet(self, acc = 0.6, artist='', title='') -> Optional[str]: data = artist + ' ' * (title != '' and artist != '') + title encoded_data = quote(data.replace(' ', '+')) google_page = await self.get('{}{}+site%3Aazlyrics.com'.format('https://duckduckgo.com/html/?q=', encoded_data)) try: results = re.findall(r'(azlyrics\.com\/lyrics\/[a-z0-9]+\/(\w+).html)', google_page) except: return None if len(results): jaro_artist = 1.0 jaro_title = 1.0 if artist: jaro_artist = self.jaro_distance(artist.replace(' ', ''), results[0][0]) if title: jaro_title = self.jaro_distance(title.replace(' ', ''), results[0][1]) if jaro_artist >= acc and jaro_title >= acc: return 'https://www.' + results[0][0] else: return None return None def jaro_distance(self, s1, s2) -> int: if (s1 == s2): return 1.0 len1, len2 = len(s1), len(s2) max_dist = floor(max(len1, len2) / 2) - 1 match = 0 hash_s1, hash_s2 = [0] * len(s1), [0] * len(s2) for i in range(len1): for j in range(max(0, i - max_dist), min(len2, i + max_dist + 1)): if (s1[i] == s2[j] and hash_s2[j] == 0): hash_s1[i], hash_s2[j] = 1, 1 match += 1 break if (match == 0): return 0.0 t = 0 point = 0 for i in range(len1): if (hash_s1[i]): while (hash_s2[point] == 0): point += 1 if (s1[i] != s2[point]): point += 1 t += 1 t = t//2 return (match/ len1 + match / len2 + (match - t + 1) / match)/ 3.0 def htmlFindAll(self, page) -> list: soup = bs4.BeautifulSoup(page, "html.parser") return soup.findAll def clearText(self, text: str) -> str: if text.startswith("\n\n"): text = text.replace("\n\n", "", 1) return text class Genius(LyricsPlatform): def __init__(self) -> None: self.module = import_module("lyricsgenius") self.genius = self.module.Genius(func.settings.genius_token) async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]: song = self.genius.search_song(title=title, artist=artist) if not song: return None return {"default": song.lyrics} class Lyrist(LyricsPlatform): async def get_lyrics(self, title: str, artist: str) -> Optional[dict[str, str]]: try: request_url = LYRIST_ENDPOINT + title + "/" + artist async with aiohttp.ClientSession() as session: resp = await session.get(url=request_url, headers={'User-Agent': random.choice(userAgents)}) if resp.status != 200: return None data = await resp.json() return {"default": data["lyrics"]} except: return None class Lrclib(LyricsPlatform): async def get(self, url, params: dict = None) -> list[dict]: try: async with aiohttp.ClientSession() as session: resp = await session.get(url=url, headers={'User-Agent': random.choice(userAgents)}, params=params) if resp.status != 200: return None return await resp.json() except: return [] async def get_lyrics(self, title, artist): params = {"q": f"{title} - {artist}"} result = await self.get(LRCLIB_ENDPOINT + "search", params) if result: return {"default": result[0].get("plainLyrics", "")} lyricsPlatform: dict[str, LyricsPlatform] = { "a_zlyrics": A_ZLyrics, "genius": Genius, "lyrist": Lyrist, "lrclib": Lrclib }