Данное изменение позволяет использовать проект в качестве библиотеки, также упрощает поддержку и добавление новых возможностей.
170 lines
5.1 KiB
Python
170 lines
5.1 KiB
Python
import datetime as dt
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
TITLE_TEMPLATE = '{title} ({version})'
|
|
|
|
|
|
@dataclass
|
|
class CoverInfo:
|
|
cover_url_template: Optional[str]
|
|
|
|
def cover_url(self, resolution: int) -> Optional[str]:
|
|
if self.cover_url_template is None:
|
|
return
|
|
|
|
return self.cover_url_template.replace('%%',
|
|
f'{resolution}x{resolution}')
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> 'CoverInfo':
|
|
og_image = data.get("ogImage")
|
|
return cls(f'https://{og_image}' if og_image is not None else None)
|
|
|
|
|
|
@dataclass
|
|
class PlaylistId:
|
|
owner: str
|
|
kind: int
|
|
|
|
|
|
@dataclass
|
|
class BasicArtistInfo:
|
|
id: str
|
|
name: str
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> 'BasicArtistInfo':
|
|
return cls(id=data['id'], name=data['name'])
|
|
|
|
|
|
@dataclass
|
|
class FullArtistInfo(BasicArtistInfo):
|
|
albums: list['BasicAlbumInfo']
|
|
cover_info: CoverInfo
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> 'FullArtistInfo':
|
|
base = BasicArtistInfo.from_json(data['artist'])
|
|
albums = map(BasicAlbumInfo.from_json, data.get('albums', []))
|
|
albums = [a for a in albums if a is not None]
|
|
cover_info = CoverInfo.from_json(data)
|
|
return cls(**base.__dict__, cover_info=cover_info, albums=albums)
|
|
|
|
|
|
@dataclass
|
|
class BasicAlbumInfo:
|
|
id: str
|
|
title: str
|
|
release_date: Optional[dt.datetime]
|
|
year: Optional[int]
|
|
artists: list[BasicArtistInfo]
|
|
meta_type: str
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> Optional['BasicAlbumInfo']:
|
|
artists = parse_artists(data['artists'])
|
|
title = parse_title(data)
|
|
if release_date := data.get('releaseDate'):
|
|
release_date = dt.datetime.fromisoformat(release_date)
|
|
return cls(id=data['id'],
|
|
title=title,
|
|
year=data.get('year'),
|
|
meta_type=data['metaType'],
|
|
artists=artists,
|
|
release_date=release_date)
|
|
|
|
|
|
@dataclass
|
|
class BasicTrackInfo:
|
|
title: str
|
|
id: str
|
|
real_id: str
|
|
album: BasicAlbumInfo
|
|
number: int
|
|
disc_number: int
|
|
artists: list[BasicArtistInfo]
|
|
has_lyrics: bool
|
|
cover_info: CoverInfo
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> Optional['BasicTrackInfo']:
|
|
if not data['available']:
|
|
return None
|
|
track_id = str(data['id'])
|
|
title = parse_title(data)
|
|
albums_data = data['albums']
|
|
artists = parse_artists(data['artists'])
|
|
track_position = {'index': 1, 'volume': 1}
|
|
if len(albums_data):
|
|
album_data = albums_data[0]
|
|
album = BasicAlbumInfo.from_json(album_data)
|
|
track_position = album_data.get('trackPosition', track_position)
|
|
else:
|
|
album = BasicAlbumInfo(id=track_id,
|
|
title=title,
|
|
release_date=None,
|
|
year=None,
|
|
meta_type='music',
|
|
artists=artists)
|
|
if album is None:
|
|
raise ValueError
|
|
cover_info = CoverInfo.from_json(data)
|
|
has_lyrics = data.get('lyricsInfo', {}).get('hasAvailableTextLyrics',
|
|
False)
|
|
return cls(title=title,
|
|
id=track_id,
|
|
real_id=data['realId'],
|
|
number=track_position['index'],
|
|
disc_number=track_position['volume'],
|
|
artists=artists,
|
|
album=album,
|
|
has_lyrics=has_lyrics,
|
|
cover_info=cover_info)
|
|
|
|
@property
|
|
def url(self) -> str:
|
|
return f'https://music.yandex.ru/album/{self.album.id}/track/{self.id}'
|
|
|
|
|
|
@dataclass
|
|
class FullTrackInfo(BasicTrackInfo):
|
|
lyrics: str
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> 'FullTrackInfo':
|
|
base = BasicTrackInfo.from_json(data['track'])
|
|
lyrics = data['lyric'][0]['fullLyrics']
|
|
return cls(**base.__dict__, lyrics=lyrics)
|
|
|
|
|
|
@dataclass
|
|
class FullAlbumInfo(BasicAlbumInfo):
|
|
tracks: list[BasicTrackInfo]
|
|
|
|
@classmethod
|
|
def from_json(cls, data: dict) -> 'FullAlbumInfo':
|
|
base = BasicAlbumInfo.from_json(data)
|
|
tracks = data.get('volumes', [])
|
|
tracks = [t for v in tracks for t in v]
|
|
tracks = map(BasicTrackInfo.from_json, tracks)
|
|
tracks = [t for t in tracks if t is not None]
|
|
return cls(**base.__dict__, tracks=tracks)
|
|
|
|
|
|
def parse_artists(data: list) -> list[BasicArtistInfo]:
|
|
artists = []
|
|
for artist in data:
|
|
artists.append(artist)
|
|
if decomposed := artist.get('decomposed'):
|
|
for d_artist in decomposed:
|
|
if isinstance(d_artist, dict):
|
|
artists.append(d_artist)
|
|
return [BasicArtistInfo.from_json(a) for a in artists]
|
|
|
|
|
|
def parse_title(data: dict) -> str:
|
|
title = data['title']
|
|
if version := data.get('version'):
|
|
title = TITLE_TEMPLATE.format(title=title, version=version)
|
|
return title
|