Доработка вывода. Небольшие улучшения.

- Основная информация выводится через print. Это позволяет избавиться
от громоздкого параметра --log-level и заменить его на лаконичный
флаг --debug.
- Возвращена переменая albums_count. Она отображает реальное кол-во
альбомов, после фильтрации.
- Добавлен флаг --only-music. Позволяет загружать только музыкальные
альбомы, игнорируя, например, аудиокниги и подкасты.
This commit is contained in:
Lev Plyusnin 2023-08-02 20:14:49 +05:00
parent 531c8ce8a2
commit 03470a7402
No known key found for this signature in database
GPG key ID: 21C6C2C9C0A4460D
2 changed files with 37 additions and 33 deletions

View file

@ -84,8 +84,7 @@ python3 main.py --session-id "<ID Сессии>" --url "https://music.yandex.ru/
``` ```
usage: main.py [-h] [--hq] [--skip-existing] [--add-lyrics] [--embed-cover] usage: main.py [-h] [--hq] [--skip-existing] [--add-lyrics] [--embed-cover]
[--stick-to-artist] [--cover-resolution <Разрешение обложки>] [--stick-to-artist] [--cover-resolution <Разрешение обложки>]
[--delay <Задержка>] [--delay <Задержка>] [--only-music] [--debug]
[--log-level {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET,VERBOSE}]
(--artist-id <ID исполнителя> | --album-id <ID альбома> | --track-id <ID трека> | --playlist-id <владелец плейлиста>/<тип плейлиста> | -u URL) (--artist-id <ID исполнителя> | --album-id <ID альбома> | --track-id <ID трека> | --playlist-id <владелец плейлиста>/<тип плейлиста> | -u URL)
[--unsafe-path] [--dir <Папка>] [--path-pattern <Паттерн>] [--unsafe-path] [--dir <Папка>] [--path-pattern <Паттерн>]
--session-id <ID сессии> [--user-agent <User-Agent>] --session-id <ID сессии> [--user-agent <User-Agent>]
@ -104,7 +103,8 @@ options:
--cover-resolution <Разрешение обложки> --cover-resolution <Разрешение обложки>
по умолчанию: 400 по умолчанию: 400
--delay <Задержка> Задержка между запросами, в секундах (по умолчанию: 3) --delay <Задержка> Задержка между запросами, в секундах (по умолчанию: 3)
--log-level {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET,VERBOSE} --only-music Загружать только музыкальные альбомы
--debug Включить отладочный вывод
ID: ID:
--artist-id <ID исполнителя> --artist-id <ID исполнителя>

62
main.py
View file

@ -34,6 +34,8 @@ FILENAME_CLEAR_RE = re.compile(r'[^\w\-\'() ]+')
TITLE_TEMPLATE = '{title} ({version})' TITLE_TEMPLATE = '{title} ({version})'
TRACK_URL_TEMPLATE = 'https://music.yandex.ru/album/{album_id}/track/{track_id}' TRACK_URL_TEMPLATE = 'https://music.yandex.ru/album/{album_id}/track/{track_id}'
logger = logging.getLogger('yandex-music-downloader')
@dataclass @dataclass
class CoverInfo: class CoverInfo:
@ -43,8 +45,8 @@ class CoverInfo:
if self.cover_url_template is None: if self.cover_url_template is None:
return return
return self.cover_url_template.replace( return self.cover_url_template.replace('%%',
'%%', f'{resolution}x{resolution}') f'{resolution}x{resolution}')
@classmethod @classmethod
def from_json(cls, data: dict) -> 'CoverInfo': def from_json(cls, data: dict) -> 'CoverInfo':
@ -107,13 +109,10 @@ class BasicAlbumInfo:
release_date: Optional[dt.datetime] release_date: Optional[dt.datetime]
year: Optional[int] year: Optional[int]
artists: list[BasicArtistInfo] artists: list[BasicArtistInfo]
meta_type: str
@classmethod @classmethod
def from_json(cls, data: dict) -> Optional['BasicAlbumInfo']: def from_json(cls, data: dict) -> Optional['BasicAlbumInfo']:
if data['metaType'] != 'music':
logging.info('"%s" пропущен т.к. не является музыкальным альбомом',
data['title'])
return
artists = parse_artists(data['artists']) artists = parse_artists(data['artists'])
title = parse_title(data) title = parse_title(data)
if release_date := data.get('releaseDate'): if release_date := data.get('releaseDate'):
@ -121,6 +120,7 @@ class BasicAlbumInfo:
return cls(id=data['id'], return cls(id=data['id'],
title=title, title=title,
year=data.get('year'), year=data.get('year'),
meta_type=data['metaType'],
artists=artists, artists=artists,
release_date=release_date) release_date=release_date)
@ -155,6 +155,7 @@ class BasicTrackInfo:
title=title, title=title,
release_date=None, release_date=None,
year=None, year=None,
meta_type='music',
artists=artists) artists=artists)
if album is None: if album is None:
raise ValueError raise ValueError
@ -312,7 +313,6 @@ def set_id3_tags(path: Path, track: BasicTrackInfo, lyrics: Optional[str],
if __name__ == '__main__': if __name__ == '__main__':
eyed3.log.setLevel("ERROR")
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Загрузчик музыки с сервиса Яндекс.Музыка') description='Загрузчик музыки с сервиса Яндекс.Музыка')
@ -350,9 +350,12 @@ if __name__ == '__main__':
metavar='<Задержка>', metavar='<Задержка>',
type=int, type=int,
help=help_str('Задержка между запросами, в секундах')) help=help_str('Задержка между запросами, в секундах'))
common_group.add_argument('--log-level', common_group.add_argument('--only-music',
default=DEFAULT_LOG_LEVEL, action='store_true',
choices=logging._nameToLevel.keys()) help='Загружать только музыкальные альбомы')
common_group.add_argument('--debug',
action='store_true',
help='Включить отладочный вывод')
def args_playlist_id(arg: str) -> PlaylistId: def args_playlist_id(arg: str) -> PlaylistId:
arr = arg.split('/') arr = arg.split('/')
@ -398,15 +401,16 @@ if __name__ == '__main__':
help=help_str()) help=help_str())
args = parser.parse_args() args = parser.parse_args()
logging.basicConfig(format='%(asctime)s |%(levelname)s| %(message)s', logging.basicConfig(
format='%(asctime)s |%(levelname)s| %(name)s: %(message)s',
datefmt='%H:%M:%S', datefmt='%H:%M:%S',
level=args.log_level.upper()) level=logging.DEBUG if args.debug else logging.ERROR)
def response_hook(resp, *_args, **_kwargs): def response_hook(resp, **_kwargs):
if logging.root.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
target_headers = ['application/json', 'text/xml'] target_headers = ['application/json', 'text/xml']
if any(h in resp.headers['Content-Type'] for h in target_headers): if any(h in resp.headers['Content-Type'] for h in target_headers):
logging.debug(resp.text) logger.debug(resp.text)
time.sleep(args.delay) time.sleep(args.delay)
session = Session() session = Session()
@ -429,35 +433,36 @@ if __name__ == '__main__':
args.playlist_id = PlaylistId(owner=match.group(1), args.playlist_id = PlaylistId(owner=match.group(1),
kind=int(match.group(2))) kind=int(match.group(2)))
else: else:
logging.error('Параметер url указан в неверном формате') print('Параметер url указан в неверном формате')
sys.exit(1) sys.exit(1)
if args.artist_id is None and args.stick_to_artist:
logging.warning('Флаг --stick-to-artist имеет смысл только при'
' загрузке всех треков исполнителя')
if args.artist_id is not None: if args.artist_id is not None:
artist_info = get_artist_info(session, args.artist_id) artist_info = get_artist_info(session, args.artist_id)
albums_count = 0
for album in artist_info.albums: for album in artist_info.albums:
if args.stick_to_artist and album.artists[0] != artist_info.name: if args.stick_to_artist and album.artists[0] != artist_info.name:
logging.info( print(f'Альбом "{album.title}" пропущен'
'Альбом "%s" пропущен из-за флага --stick-to-artist', ' из-за флага --stick-to-artist')
album.title) continue
if args.only_music and album.meta_type != 'music':
print(f'Альбом "{album.title}" пропущен'
' т.к. не является музыкальным')
continue continue
full_album = get_full_album_info(session, album.id) full_album = get_full_album_info(session, album.id)
result_tracks.extend(full_album.tracks) result_tracks.extend(full_album.tracks)
logging.info(artist_info.name) albums_count += 1
logging.info('Альбомов: %d', len(artist_info.albums)) print(artist_info.name)
print(f'Альбомов: {albums_count}')
elif args.album_id is not None: elif args.album_id is not None:
album = get_full_album_info(session, args.album_id) album = get_full_album_info(session, args.album_id)
logging.info(album.title) print(album.title)
result_tracks = album.tracks result_tracks = album.tracks
elif args.track_id is not None: elif args.track_id is not None:
result_tracks = [get_full_track_info(session, args.track_id)] result_tracks = [get_full_track_info(session, args.track_id)]
elif args.playlist_id is not None: elif args.playlist_id is not None:
result_tracks = get_playlist(session, args.playlist_id) result_tracks = get_playlist(session, args.playlist_id)
logging.info('Треков: %d', len(result_tracks)) print(f'Треков: {len(result_tracks)}')
covers: dict[str, bytes] = {} covers: dict[str, bytes] = {}
for track in result_tracks: for track in result_tracks:
@ -472,7 +477,7 @@ if __name__ == '__main__':
save_dir.mkdir(parents=True) save_dir.mkdir(parents=True)
url = get_track_download_url(session, track, args.hq) url = get_track_download_url(session, track, args.hq)
logging.info('Загружается %s', save_path) print(f'Загружается {save_path}')
download_file(session, url, save_path) download_file(session, url, save_path)
lyrics = None lyrics = None
@ -487,7 +492,6 @@ if __name__ == '__main__':
cover_url = track.cover_info.cover_url(args.cover_resolution) cover_url = track.cover_info.cover_url(args.cover_resolution)
if cover_url is not None: if cover_url is not None:
if args.embed_cover: if args.embed_cover:
cover = covers.get(album.id)
if cached_cover := covers.get(album.id): if cached_cover := covers.get(album.id):
cover = cached_cover cover = cached_cover
else: else: