used black

This commit is contained in:
hhh
2024-11-02 00:10:24 +02:00
parent 1b1f217b75
commit e0a3d256d5
79 changed files with 658 additions and 733 deletions

View File

@@ -7,4 +7,4 @@ deezer = Deezer(
arl=config.tokens.deezer.arl,
)
__all__ = ['deezer', 'DeezerBytestream']
__all__ = ["deezer", "DeezerBytestream"]

View File

@@ -17,10 +17,7 @@ class DeezerBytestream:
@classmethod
def from_bytestream(
cls,
bytestream: BytesIO,
filename: str,
full_song: FullSongItem
cls, bytestream: BytesIO, filename: str, full_song: FullSongItem
):
bytestream.seek(0)
return cls(
@@ -38,21 +35,18 @@ class Downloader:
song: FullSongItem
@classmethod
async def build(
cls,
song_id: str,
driver: DeezerDriver
):
async def build(cls, song_id: str, driver: DeezerDriver):
track = await driver.reverse_get_track(song_id)
try:
return cls(
song_id=str(song_id),
driver=driver,
track=track['results'],
song=await FullSongItem.from_deezer(track)
track=track["results"],
song=await FullSongItem.from_deezer(track),
)
except KeyError:
from icecream import ic
ic(track)
await driver.renew_engine()
return await cls.build(song_id, driver)
@@ -65,7 +59,7 @@ class Downloader:
audio = BytesIO()
async for chunk in self.driver.engine.get_data_iter(
await self._get_download_url(quality=quality)
await self._get_download_url(quality=quality)
):
if i % 3 > 0 or len(chunk) < 2 * 1024:
audio.write(chunk)
@@ -76,18 +70,16 @@ class Downloader:
return DeezerBytestream.from_bytestream(
filename=self.song.full_name + track_formats.TRACK_FORMAT_MAP[quality].ext,
bytestream=audio,
full_song=self.song
full_song=self.song,
)
async def _get_download_url(self, quality: str = 'MP3_128'):
async def _get_download_url(self, quality: str = "MP3_128"):
md5_origin = self.track["MD5_ORIGIN"]
track_id = self.track["SNG_ID"]
media_version = self.track["MEDIA_VERSION"]
url_decrypter = UrlDecrypter(
md5_origin=md5_origin,
track_id=track_id,
media_version=media_version
md5_origin=md5_origin, track_id=track_id, media_version=media_version
)
return url_decrypter.get_url_for(track_formats.TRACK_FORMAT_MAP[quality])
@@ -98,7 +90,4 @@ class DownloaderBuilder:
driver: DeezerDriver
async def from_id(self, song_id: str):
return await Downloader.build(
song_id=song_id,
driver=self.driver
)
return await Downloader.build(song_id=song_id, driver=self.driver)

View File

@@ -10,30 +10,19 @@ class DeezerDriver:
engine: DeezerEngine
async def get_track(self, track_id: int | str):
data = await self.engine.call_legacy_api(
f'track/{track_id}'
)
data = await self.engine.call_legacy_api(f"track/{track_id}")
return data
async def reverse_get_track(self, track_id: str):
return await self.engine.call_api(
'song.getData',
params={
'SNG_ID': track_id
}
)
return await self.engine.call_api("song.getData", params={"SNG_ID": track_id})
async def search(self, query: str, limit: int = 30):
data = await self.engine.call_legacy_api(
'search/track',
params={
'q': clean_query(query),
'limit': limit
}
"search/track", params={"q": clean_query(query), "limit": limit}
)
return data['data']
return data["data"]
async def renew_engine(self):
self.engine = await self.engine.from_arl(self.engine.arl)

View File

@@ -7,13 +7,13 @@ from attrs import define
HTTP_HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"(KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
"Content-Language": "en-US",
"Cache-Control": "max-age=0",
"Accept": "*/*",
"Accept-Charset": "utf-8,ISO-8859-1;q=0.7,*;q=0.3",
"Accept-Language": "en-US,en;q=0.9,en-US;q=0.8,en;q=0.7",
"Connection": 'keep-alive'
"Connection": "keep-alive",
}
@@ -25,28 +25,22 @@ class DeezerEngine:
@classmethod
async def from_arl(cls, arl: str):
cookies = {'arl': arl}
cookies = {"arl": arl}
data, cookies = await cls(cookies).call_api(
'deezer.getUserData', get_cookies=True
"deezer.getUserData", get_cookies=True
)
data = data['results']
token = data['checkForm']
data = data["results"]
token = data["checkForm"]
return cls(
cookies=cookies,
arl=arl,
token=token
)
return cls(cookies=cookies, arl=arl, token=token)
async def call_legacy_api(
self, request_point: str, params: dict = None
):
async def call_legacy_api(self, request_point: str, params: dict = None):
async with aiohttp.ClientSession(cookies=self.cookies) as session:
async with session.get(
f"https://api.deezer.com/{request_point}",
params=params,
headers=HTTP_HEADERS
f"https://api.deezer.com/{request_point}",
params=params,
headers=HTTP_HEADERS,
) as r:
return await r.json()
@@ -63,31 +57,26 @@ class DeezerEngine:
async def get_data_iter(self, url: str):
async with aiohttp.ClientSession(
cookies=self.cookies,
headers=HTTP_HEADERS
cookies=self.cookies, headers=HTTP_HEADERS
) as session:
r = await session.get(
url,
allow_redirects=True
)
r = await session.get(url, allow_redirects=True)
async for chunk in self._iter_exact_chunks(r):
yield chunk
async def call_api(
self, method: str, params: dict = None,
get_cookies: bool = False
self, method: str, params: dict = None, get_cookies: bool = False
):
async with aiohttp.ClientSession(cookies=self.cookies) as session:
async with session.post(
f"https://www.deezer.com/ajax/gw-light.php",
params={
'method': method,
'api_version': '1.0',
'input': '3',
'api_token': self.token or 'null',
},
headers=HTTP_HEADERS,
json=params
f"https://www.deezer.com/ajax/gw-light.php",
params={
"method": method,
"api_version": "1.0",
"input": "3",
"api_token": self.token or "null",
},
headers=HTTP_HEADERS,
json=params,
) as r:
if not get_cookies:
return await r.json()

View File

@@ -10,11 +10,11 @@ class SongItem(BaseSongItem):
@classmethod
def from_deezer(cls, song_item: dict):
return cls(
name=song_item['title'],
id=str(song_item['id']),
artists=[song_item['artist']['name']],
preview_url=song_item.get('preview'),
thumbnail=song_item['album']['cover_medium']
name=song_item["title"],
id=str(song_item["id"]),
artists=[song_item["artist"]["name"]],
preview_url=song_item.get("preview"),
thumbnail=song_item["album"]["cover_medium"],
)
@@ -25,21 +25,23 @@ class FullSongItem(BaseSongItem):
@classmethod
async def from_deezer(cls, song_item: dict):
if song_item.get('results'):
song_item = song_item['results']
if song_item.get("results"):
song_item = song_item["results"]
return cls(
name=song_item['SNG_TITLE'],
id=song_item['SNG_ID'],
artists=[artist['ART_NAME'] for artist in song_item['ARTISTS']],
preview_url=(song_item.get('MEDIA').get('HREF')
if type(song_item.get('MEDIA')) is dict and
song_item.get('MEDIA').get('TYPE') == 'preview'
else None),
thumbnail=f'https://e-cdns-images.dzcdn.net/images/cover/'
f'{song_item["ALB_PICTURE"]}/320x320.jpg',
duration=int(song_item['DURATION']),
track_dict=song_item
name=song_item["SNG_TITLE"],
id=song_item["SNG_ID"],
artists=[artist["ART_NAME"] for artist in song_item["ARTISTS"]],
preview_url=(
song_item.get("MEDIA").get("HREF")
if type(song_item.get("MEDIA")) is dict
and song_item.get("MEDIA").get("TYPE") == "preview"
else None
),
thumbnail=f"https://e-cdns-images.dzcdn.net/images/cover/"
f'{song_item["ALB_PICTURE"]}/320x320.jpg',
duration=int(song_item["DURATION"]),
track_dict=song_item,
)

View File

@@ -19,32 +19,11 @@ class TrackFormat:
TRACK_FORMAT_MAP = {
FLAC: TrackFormat(
code=9,
ext=".flac"
),
MP3_128: TrackFormat(
code=1,
ext=".mp3"
),
MP3_256: TrackFormat(
code=5,
ext=".mp3"
),
MP3_320: TrackFormat(
code=3,
ext=".mp3"
),
MP4_RA1: TrackFormat(
code=13,
ext=".mp4"
),
MP4_RA2: TrackFormat(
code=14,
ext=".mp4"
),
MP4_RA3: TrackFormat(
code=15,
ext=".mp3"
)
FLAC: TrackFormat(code=9, ext=".flac"),
MP3_128: TrackFormat(code=1, ext=".mp3"),
MP3_256: TrackFormat(code=5, ext=".mp3"),
MP3_320: TrackFormat(code=3, ext=".mp3"),
MP4_RA1: TrackFormat(code=13, ext=".mp4"),
MP4_RA2: TrackFormat(code=14, ext=".mp4"),
MP4_RA3: TrackFormat(code=15, ext=".mp3"),
}

View File

@@ -34,20 +34,20 @@ class UrlDecrypter:
media_version: str
def get_url_for(self, track_format: TrackFormat):
step1 = (f'{self.md5_origin}¤{track_format.code}¤'
f'{self.track_id}¤{self.media_version}')
step1 = (
f"{self.md5_origin}¤{track_format.code}¤"
f"{self.track_id}¤{self.media_version}"
)
m = hashlib.md5()
m.update(bytes([ord(x) for x in step1]))
step2 = f'{m.hexdigest()}¤{step1}¤'
step2 = f"{m.hexdigest()}¤{step1}¤"
step2 = step2.ljust(80, " ")
cipher = Cipher(
algorithm=algorithms.AES(
key=bytes('jo6aey6haid2Teih', 'ascii')
),
algorithm=algorithms.AES(key=bytes("jo6aey6haid2Teih", "ascii")),
mode=modes.ECB(),
backend=default_backend()
backend=default_backend(),
)
encryptor = cipher.encryptor()
@@ -55,7 +55,7 @@ class UrlDecrypter:
cdn = self.md5_origin[0]
return f'https://e-cdns-proxy-{cdn}.dzcdn.net/mobile/1/{step3}'
return f"https://e-cdns-proxy-{cdn}.dzcdn.net/mobile/1/{step3}"
@define
@@ -69,12 +69,10 @@ class ChunkDecrypter:
cipher = Cipher(
algorithms.Blowfish(get_blowfish_key(track_id)),
modes.CBC(bytes([i for i in range(8)])),
default_backend()
default_backend(),
)
return cls(
cipher=cipher
)
return cls(cipher=cipher)
def decrypt_chunk(self, chunk: bytes):
decryptor = self.cipher.decryptor()
@@ -82,7 +80,7 @@ class ChunkDecrypter:
def get_blowfish_key(track_id: str):
secret = 'g4el58wc0zvf9na1'
secret = "g4el58wc0zvf9na1"
m = hashlib.md5()
m.update(bytes([ord(x) for x in track_id]))