From 896f35327e332cb43798444fc9ddd81a00eb5d5e Mon Sep 17 00:00:00 2001 From: Lev Plyusnin Date: Fri, 4 Aug 2023 14:17:27 +0500 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BA=D1=8D=D1=88=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++++---- main.py | 70 ++++++++++++++++++++++++++++++++---------------- requirements.txt | 1 + 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 7922b01..c2df07a 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,8 @@ python3 main.py --session-id "" --url "https://music.yandex.ru/ ``` usage: main.py [-h] [--hq] [--skip-existing] [--add-lyrics] [--embed-cover] - [--stick-to-artist] [--cover-resolution <Разрешение обложки>] - [--delay <Задержка>] [--only-music] [--debug] + [--cover-resolution <Разрешение обложки>] [--delay <Задержка>] + [--stick-to-artist] [--only-music] [--enable-caching] (--artist-id | --album-id | --track-id | --playlist-id <владелец плейлиста>/<тип плейлиста> | -u URL) [--unsafe-path] [--dir <Папка>] [--path-pattern <Паттерн>] --session-id [--user-agent ] @@ -99,12 +99,16 @@ options: --skip-existing Пропускать уже загруженные треки --add-lyrics Загружать тексты песен --embed-cover Встраивать обложку в .mp3 файл - --stick-to-artist Загружать только альбомы созданные данным исполнителем --cover-resolution <Разрешение обложки> по умолчанию: 400 --delay <Задержка> Задержка между запросами, в секундах (по умолчанию: 3) - --only-music Загружать только музыкальные альбомы - --debug Включить отладочный вывод + --stick-to-artist Загружать альбомы, созданные только данным + исполнителем + --only-music Загружать только музыкальные альбомы (пропускать + подкасты и аудиокниги) + --enable-caching Включить кэширование. Данная опция полезна при + нестабильном интернете. (кэш хранится в папке + /tmp/ymd) ID: --artist-id diff --git a/main.py b/main.py index b60b3ce..d387811 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ import hashlib import logging import re import sys +import tempfile import time import urllib.parse import xml.etree.ElementTree as ET @@ -15,6 +16,7 @@ from typing import Optional import eyed3 from eyed3.id3.frames import ImageFrame from requests import Session +from requests_cache import CachedSession, FileCache MD5_SALT = 'XGRlBW9FXlekgbPrRHuSiA' ENCODED_BY = 'https://github.com/llistochek/yandex-music-downloader' @@ -24,6 +26,9 @@ DEFAULT_PATH_PATTERN = Path('#album-artist', '#album', '#number - #title') DEFAULT_COVER_RESOLUTION = 400 DEFAULT_LOG_LEVEL = 'INFO' +CACHE_EXPIRE_AFTER = dt.timedelta(hours=8) +CACHE_DIR = Path(tempfile.gettempdir()) / 'ymd' + TRACK_RE = re.compile(r'track/(\d+)$') ALBUM_RE = re.compile(r'album/(\d+)$') ARTIST_RE = re.compile(r'artist/(\d+)$') @@ -335,10 +340,6 @@ if __name__ == '__main__': common_group.add_argument('--embed-cover', action='store_true', help='Встраивать обложку в .mp3 файл') - common_group.add_argument( - '--stick-to-artist', - action='store_true', - help='Загружать только альбомы созданные данным исполнителем') common_group.add_argument('--cover-resolution', default=DEFAULT_COVER_RESOLUTION, metavar='<Разрешение обложки>', @@ -350,12 +351,22 @@ if __name__ == '__main__': metavar='<Задержка>', type=int, help=help_str('Задержка между запросами, в секундах')) + common_group.add_argument('--stick-to-artist', + action='store_true', + help='Загружать альбомы, созданные' + ' только данным исполнителем') common_group.add_argument('--only-music', action='store_true', - help='Загружать только музыкальные альбомы') + help='Загружать только музыкальные альбомы' + ' (пропускать подкасты и аудиокниги)') + common_group.add_argument('--enable-caching', + action='store_true', + help='Включить кэширование. Данная опция полезна' + ' при нестабильном интернете.' + f' (кэш хранится в папке {CACHE_DIR})') common_group.add_argument('--debug', action='store_true', - help='Включить отладочный вывод') + help=argparse.SUPPRESS) def args_playlist_id(arg: str) -> PlaylistId: arr = arg.split('/') @@ -406,19 +417,32 @@ if __name__ == '__main__': datefmt='%H:%M:%S', level=logging.DEBUG if args.debug else logging.ERROR) - def response_hook(resp, **_kwargs): - if logger.isEnabledFor(logging.DEBUG): - target_headers = ['application/json', 'text/xml'] - if any(h in resp.headers['Content-Type'] for h in target_headers): - logger.debug(resp.text) - time.sleep(args.delay) - session = Session() - session.hooks = {'response': response_hook} - session.cookies.set('Session_id', args.session_id, domain='yandex.ru') - session.headers['User-Agent'] = args.user_agent - session.headers['X-Retpath-Y'] = urllib.parse.quote_plus( - 'https://music.yandex.ru') + cached_session = Session() + if args.enable_caching: + cached_session = CachedSession(backend=FileCache(cache_name=CACHE_DIR), + expire_after=CACHE_EXPIRE_AFTER) + + def setup_session(session: Session): + + def response_hook(resp, **kwargs): + del kwargs + if logger.isEnabledFor(logging.DEBUG): + target_headers = ['application/json', 'text/xml'] + if any(h in resp.headers['Content-Type'] + for h in target_headers): + logger.debug(resp.text) + if not getattr(resp, 'from_cache', False): + time.sleep(args.delay) + + session.hooks = {'response': response_hook} + session.cookies.set('Session_id', args.session_id, domain='yandex.ru') + session.headers['User-Agent'] = args.user_agent + session.headers['X-Retpath-Y'] = urllib.parse.quote_plus( + 'https://music.yandex.ru') + + setup_session(session) + setup_session(cached_session) result_tracks: list[BasicTrackInfo] = [] @@ -437,7 +461,7 @@ if __name__ == '__main__': sys.exit(1) if args.artist_id is not None: - artist_info = get_artist_info(session, args.artist_id) + artist_info = get_artist_info(cached_session, args.artist_id) albums_count = 0 for album in artist_info.albums: if args.stick_to_artist and album.artists[0] != artist_info.name: @@ -448,19 +472,19 @@ if __name__ == '__main__': print(f'Альбом "{album.title}" пропущен' ' т.к. не является музыкальным') continue - full_album = get_full_album_info(session, album.id) + full_album = get_full_album_info(cached_session, album.id) result_tracks.extend(full_album.tracks) albums_count += 1 print(artist_info.name) print(f'Альбомов: {albums_count}') elif args.album_id is not None: - album = get_full_album_info(session, args.album_id) + album = get_full_album_info(cached_session, args.album_id) print(album.title) result_tracks = album.tracks elif args.track_id is not None: - result_tracks = [get_full_track_info(session, args.track_id)] + result_tracks = [get_full_track_info(cached_session, args.track_id)] elif args.playlist_id is not None: - result_tracks = get_playlist(session, args.playlist_id) + result_tracks = get_playlist(cached_session, args.playlist_id) print(f'Треков: {len(result_tracks)}') covers: dict[str, bytes] = {} diff --git a/requirements.txt b/requirements.txt index 66afbb2..f8a53aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests +requests-cache eyed3