Обработка отсутсвия обложки у трека (#10)
This commit is contained in:
parent
d96a288f9c
commit
4146f207bc
1 changed files with 59 additions and 52 deletions
99
main.py
99
main.py
|
|
@ -33,6 +33,19 @@ FILENAME_CLEAR_RE = re.compile(r'[^\w\-\'() ]+')
|
||||||
TITLE_FMT = '%(title)s (%(version)s)'
|
TITLE_FMT = '%(title)s (%(version)s)'
|
||||||
|
|
||||||
|
|
||||||
|
class CoverInfo:
|
||||||
|
cover_url_template: Optional[str]
|
||||||
|
|
||||||
|
def __init__(self, json_data: dict):
|
||||||
|
og_image = json_data.get('ogImage')
|
||||||
|
if og_image is not None:
|
||||||
|
self.cover_url_template = 'https://' + og_image
|
||||||
|
|
||||||
|
def cover_url(self, resolution: int) -> Optional[str]:
|
||||||
|
if self.cover_url_template is not None:
|
||||||
|
return self.cover_url_template.replace('%%', f'{resolution}x{resolution}')
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PlaylistId:
|
class PlaylistId:
|
||||||
owner: str
|
owner: str
|
||||||
|
|
@ -50,9 +63,9 @@ def parse_artists(artists: list) -> list[str]:
|
||||||
return artists_names
|
return artists_names
|
||||||
|
|
||||||
|
|
||||||
def parse_title(json: dict) -> str:
|
def parse_title(data: dict) -> str:
|
||||||
title = json['title']
|
title = data['title']
|
||||||
if version := json.get('version'):
|
if version := data.get('version'):
|
||||||
title = TITLE_FMT % {'title': title, 'version': version}
|
title = TITLE_FMT % {'title': title, 'version': version}
|
||||||
return title
|
return title
|
||||||
|
|
||||||
|
|
@ -66,16 +79,16 @@ class BasicAlbumInfo:
|
||||||
artists: list[str]
|
artists: list[str]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json: dict):
|
def from_json(data: dict):
|
||||||
if json['metaType'] != 'music':
|
if data['metaType'] != 'music':
|
||||||
logging.info('"%s" пропущен т.к. не является музыкальным альбомом', json['title'])
|
logging.info('"%s" пропущен т.к. не является музыкальным альбомом', data['title'])
|
||||||
return None
|
return None
|
||||||
artists = parse_artists(json['artists'])
|
artists = parse_artists(data['artists'])
|
||||||
title = parse_title(json)
|
title = parse_title(data)
|
||||||
release_date = json.get('releaseDate')
|
release_date = data.get('releaseDate')
|
||||||
if release_date is not None:
|
if release_date is not None:
|
||||||
release_date = dt.datetime.fromisoformat(release_date)
|
release_date = dt.datetime.fromisoformat(release_date)
|
||||||
return BasicAlbumInfo(id=json['id'], title=title, year=json.get('year'),
|
return BasicAlbumInfo(id=data['id'], title=title, year=data.get('year'),
|
||||||
artists=artists, release_date=release_date)
|
artists=artists, release_date=release_date)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -88,32 +101,28 @@ class BasicTrackInfo:
|
||||||
number: int
|
number: int
|
||||||
disc_number: int
|
disc_number: int
|
||||||
artists: list[str]
|
artists: list[str]
|
||||||
url_template: str
|
|
||||||
has_lyrics: bool
|
has_lyrics: bool
|
||||||
|
cover_info: CoverInfo
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json: dict):
|
def from_json(data: dict):
|
||||||
if not json['available']:
|
if not data['available']:
|
||||||
return None
|
return None
|
||||||
title = parse_title(json)
|
title = parse_title(data)
|
||||||
album_json = json['albums'][0]
|
album_data = data['albums'][0]
|
||||||
artists = parse_artists(json['artists'])
|
artists = parse_artists(data['artists'])
|
||||||
track_position = album_json.get('trackPosition')
|
track_position = album_data.get('trackPosition')
|
||||||
if track_position is None:
|
if track_position is None:
|
||||||
logging.warning('%s - %s не содержит информацию о позиции трека', artists[0], title)
|
logging.warning('%s - %s не содержит информацию о позиции трека', artists[0], title)
|
||||||
track_position = {'index': 1, 'volume': 1}
|
track_position = {'index': 1, 'volume': 1}
|
||||||
album = BasicAlbumInfo.from_json(album_json)
|
album = BasicAlbumInfo.from_json(album_data)
|
||||||
if album is None:
|
if album is None:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
url_template = 'https://' + json['ogImage']
|
cover_info = CoverInfo(data)
|
||||||
has_lyrics = json['lyricsInfo']['hasAvailableTextLyrics']
|
has_lyrics = data['lyricsInfo']['hasAvailableTextLyrics']
|
||||||
return BasicTrackInfo(title=title, id=str(json['id']), real_id=json['realId'],
|
return BasicTrackInfo(title=title, id=str(data['id']), real_id=data['realId'],
|
||||||
number=track_position['index'], disc_number=track_position['volume'],
|
number=track_position['index'], disc_number=track_position['volume'],
|
||||||
artists=artists, album=album, url_template=url_template,
|
artists=artists, album=album, has_lyrics=has_lyrics, cover_info=cover_info)
|
||||||
has_lyrics=has_lyrics)
|
|
||||||
|
|
||||||
def pic_url(self, resolution: int) -> str:
|
|
||||||
return self.url_template.replace('%%', f'{resolution}x{resolution}')
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -121,9 +130,9 @@ class FullTrackInfo(BasicTrackInfo):
|
||||||
lyrics: str
|
lyrics: str
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json: dict):
|
def from_json(data: dict):
|
||||||
base = BasicTrackInfo.from_json(json['track'])
|
base = BasicTrackInfo.from_json(data['track'])
|
||||||
lyrics = json['lyric'][0]['fullLyrics']
|
lyrics = data['lyric'][0]['fullLyrics']
|
||||||
return FullTrackInfo(**base.__dict__, lyrics=lyrics)
|
return FullTrackInfo(**base.__dict__, lyrics=lyrics)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -132,9 +141,9 @@ class FullAlbumInfo(BasicAlbumInfo):
|
||||||
tracks: list[BasicTrackInfo]
|
tracks: list[BasicTrackInfo]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json: dict):
|
def from_json(data: dict):
|
||||||
base = BasicAlbumInfo.from_json(json)
|
base = BasicAlbumInfo.from_json(data)
|
||||||
tracks = json.get('volumes', [])
|
tracks = data.get('volumes', [])
|
||||||
tracks = [t for v in tracks for t in v]
|
tracks = [t for v in tracks for t in v]
|
||||||
tracks = map(BasicTrackInfo.from_json, tracks)
|
tracks = map(BasicTrackInfo.from_json, tracks)
|
||||||
tracks = [t for t in tracks if t is not None]
|
tracks = [t for t in tracks if t is not None]
|
||||||
|
|
@ -146,19 +155,16 @@ class ArtistInfo:
|
||||||
id: str
|
id: str
|
||||||
name: str
|
name: str
|
||||||
albums: list[BasicAlbumInfo]
|
albums: list[BasicAlbumInfo]
|
||||||
url_template: str
|
cover_info: CoverInfo
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(json: dict):
|
def from_json(data: dict):
|
||||||
artist = json['artist']
|
artist = data['artist']
|
||||||
albums = map(BasicAlbumInfo.from_json, json.get('albums', []))
|
albums = map(BasicAlbumInfo.from_json, data.get('albums', []))
|
||||||
albums = [a for a in albums if a is not None]
|
albums = [a for a in albums if a is not None]
|
||||||
url_template = 'https://' + artist['ogImage']
|
cover_info = CoverInfo(data)
|
||||||
return ArtistInfo(id=str(artist['id']), name=artist['name'],
|
return ArtistInfo(id=str(artist['id']), name=artist['name'],
|
||||||
albums=albums, url_template=url_template)
|
cover_info=cover_info, albums=albums)
|
||||||
|
|
||||||
def pic_url(self, resolution: int) -> str:
|
|
||||||
return self.url_template.replace('%%', f'{resolution}x{resolution}')
|
|
||||||
|
|
||||||
|
|
||||||
def get_track_download_url(session: Session, track: BasicTrackInfo, hq: bool) -> str:
|
def get_track_download_url(session: Session, track: BasicTrackInfo, hq: bool) -> str:
|
||||||
|
|
@ -196,7 +202,7 @@ def get_full_track_info(session: Session, track_id: str) -> FullTrackInfo:
|
||||||
'track': track_id,
|
'track': track_id,
|
||||||
'lang': 'ru'
|
'lang': 'ru'
|
||||||
}
|
}
|
||||||
resp = session.get(f'https://music.yandex.ru/handlers/track.jsx', params=params)
|
resp = session.get('https://music.yandex.ru/handlers/track.jsx', params=params)
|
||||||
return FullTrackInfo.from_json(resp.json())
|
return FullTrackInfo.from_json(resp.json())
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -205,7 +211,7 @@ def get_full_album_info(session: Session, album_id: str) -> FullAlbumInfo:
|
||||||
'album': album_id,
|
'album': album_id,
|
||||||
'lang': 'ru'
|
'lang': 'ru'
|
||||||
}
|
}
|
||||||
resp = session.get(f'https://music.yandex.ru/handlers/album.jsx', params=params)
|
resp = session.get('https://music.yandex.ru/handlers/album.jsx', params=params)
|
||||||
return FullAlbumInfo.from_json(resp.json())
|
return FullAlbumInfo.from_json(resp.json())
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -414,15 +420,16 @@ if __name__ == '__main__':
|
||||||
lyrics = full_info.lyrics
|
lyrics = full_info.lyrics
|
||||||
|
|
||||||
cover = None
|
cover = None
|
||||||
|
cover_url = track.cover_info.cover_url(args.cover_resolution)
|
||||||
|
if cover_url is not None:
|
||||||
if args.embed_cover:
|
if args.embed_cover:
|
||||||
if cached_cover := covers.get(album.id):
|
if cached_cover := covers.get(album.id):
|
||||||
cover = cached_cover
|
cover = cached_cover
|
||||||
else:
|
else:
|
||||||
cover = covers[album.id] = download_bytes(session,
|
cover = covers[album.id] = download_bytes(session, cover_url)
|
||||||
track.pic_url(args.cover_resolution))
|
|
||||||
else:
|
else:
|
||||||
cover_path = save_dir / 'cover.jpg'
|
cover_path = save_dir / 'cover.jpg'
|
||||||
if not cover_path.is_file():
|
if not cover_path.is_file():
|
||||||
download_file(session, track.pic_url(args.cover_resolution), cover_path)
|
download_file(session, cover_url, cover_path)
|
||||||
|
|
||||||
set_id3_tags(save_path, track, lyrics, cover)
|
set_id3_tags(save_path, track, lyrics, cover)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue