Добавление кэширования (#16)

This commit is contained in:
Lev Plyusnin 2023-08-04 14:17:27 +05:00
parent 03470a7402
commit 896f35327e
No known key found for this signature in database
GPG key ID: 21C6C2C9C0A4460D
3 changed files with 57 additions and 28 deletions

View file

@ -83,8 +83,8 @@ 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 <Разрешение обложки>] [--cover-resolution <Разрешение обложки>] [--delay <Задержка>]
[--delay <Задержка>] [--only-music] [--debug] [--stick-to-artist] [--only-music] [--enable-caching]
(--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>]
@ -99,12 +99,16 @@ options:
--skip-existing Пропускать уже загруженные треки --skip-existing Пропускать уже загруженные треки
--add-lyrics Загружать тексты песен --add-lyrics Загружать тексты песен
--embed-cover Встраивать обложку в .mp3 файл --embed-cover Встраивать обложку в .mp3 файл
--stick-to-artist Загружать только альбомы созданные данным исполнителем
--cover-resolution <Разрешение обложки> --cover-resolution <Разрешение обложки>
по умолчанию: 400 по умолчанию: 400
--delay <Задержка> Задержка между запросами, в секундах (по умолчанию: 3) --delay <Задержка> Задержка между запросами, в секундах (по умолчанию: 3)
--only-music Загружать только музыкальные альбомы --stick-to-artist Загружать альбомы, созданные только данным
--debug Включить отладочный вывод исполнителем
--only-music Загружать только музыкальные альбомы (пропускать
подкасты и аудиокниги)
--enable-caching Включить кэширование. Данная опция полезна при
нестабильном интернете. (кэш хранится в папке
/tmp/ymd)
ID: ID:
--artist-id <ID исполнителя> --artist-id <ID исполнителя>

70
main.py
View file

@ -5,6 +5,7 @@ import hashlib
import logging import logging
import re import re
import sys import sys
import tempfile
import time import time
import urllib.parse import urllib.parse
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -15,6 +16,7 @@ from typing import Optional
import eyed3 import eyed3
from eyed3.id3.frames import ImageFrame from eyed3.id3.frames import ImageFrame
from requests import Session from requests import Session
from requests_cache import CachedSession, FileCache
MD5_SALT = 'XGRlBW9FXlekgbPrRHuSiA' MD5_SALT = 'XGRlBW9FXlekgbPrRHuSiA'
ENCODED_BY = 'https://github.com/llistochek/yandex-music-downloader' 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_COVER_RESOLUTION = 400
DEFAULT_LOG_LEVEL = 'INFO' DEFAULT_LOG_LEVEL = 'INFO'
CACHE_EXPIRE_AFTER = dt.timedelta(hours=8)
CACHE_DIR = Path(tempfile.gettempdir()) / 'ymd'
TRACK_RE = re.compile(r'track/(\d+)$') TRACK_RE = re.compile(r'track/(\d+)$')
ALBUM_RE = re.compile(r'album/(\d+)$') ALBUM_RE = re.compile(r'album/(\d+)$')
ARTIST_RE = re.compile(r'artist/(\d+)$') ARTIST_RE = re.compile(r'artist/(\d+)$')
@ -335,10 +340,6 @@ if __name__ == '__main__':
common_group.add_argument('--embed-cover', common_group.add_argument('--embed-cover',
action='store_true', action='store_true',
help='Встраивать обложку в .mp3 файл') help='Встраивать обложку в .mp3 файл')
common_group.add_argument(
'--stick-to-artist',
action='store_true',
help='Загружать только альбомы созданные данным исполнителем')
common_group.add_argument('--cover-resolution', common_group.add_argument('--cover-resolution',
default=DEFAULT_COVER_RESOLUTION, default=DEFAULT_COVER_RESOLUTION,
metavar='<Разрешение обложки>', metavar='<Разрешение обложки>',
@ -350,12 +351,22 @@ if __name__ == '__main__':
metavar='<Задержка>', metavar='<Задержка>',
type=int, type=int,
help=help_str('Задержка между запросами, в секундах')) help=help_str('Задержка между запросами, в секундах'))
common_group.add_argument('--stick-to-artist',
action='store_true',
help='Загружать альбомы, созданные'
' только данным исполнителем')
common_group.add_argument('--only-music', common_group.add_argument('--only-music',
action='store_true', action='store_true',
help='Загружать только музыкальные альбомы') help='Загружать только музыкальные альбомы'
' (пропускать подкасты и аудиокниги)')
common_group.add_argument('--enable-caching',
action='store_true',
help='Включить кэширование. Данная опция полезна'
' при нестабильном интернете.'
f' (кэш хранится в папке {CACHE_DIR})')
common_group.add_argument('--debug', common_group.add_argument('--debug',
action='store_true', action='store_true',
help='Включить отладочный вывод') help=argparse.SUPPRESS)
def args_playlist_id(arg: str) -> PlaylistId: def args_playlist_id(arg: str) -> PlaylistId:
arr = arg.split('/') arr = arg.split('/')
@ -406,19 +417,32 @@ if __name__ == '__main__':
datefmt='%H:%M:%S', datefmt='%H:%M:%S',
level=logging.DEBUG if args.debug else logging.ERROR) 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 = Session()
session.hooks = {'response': response_hook} cached_session = Session()
session.cookies.set('Session_id', args.session_id, domain='yandex.ru') if args.enable_caching:
session.headers['User-Agent'] = args.user_agent cached_session = CachedSession(backend=FileCache(cache_name=CACHE_DIR),
session.headers['X-Retpath-Y'] = urllib.parse.quote_plus( expire_after=CACHE_EXPIRE_AFTER)
'https://music.yandex.ru')
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] = [] result_tracks: list[BasicTrackInfo] = []
@ -437,7 +461,7 @@ if __name__ == '__main__':
sys.exit(1) sys.exit(1)
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(cached_session, args.artist_id)
albums_count = 0 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:
@ -448,19 +472,19 @@ if __name__ == '__main__':
print(f'Альбом "{album.title}" пропущен' print(f'Альбом "{album.title}" пропущен'
' т.к. не является музыкальным') ' т.к. не является музыкальным')
continue 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) result_tracks.extend(full_album.tracks)
albums_count += 1 albums_count += 1
print(artist_info.name) print(artist_info.name)
print(f'Альбомов: {albums_count}') 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(cached_session, args.album_id)
print(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(cached_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(cached_session, args.playlist_id)
print(f'Треков: {len(result_tracks)}') print(f'Треков: {len(result_tracks)}')
covers: dict[str, bytes] = {} covers: dict[str, bytes] = {}

View file

@ -1,2 +1,3 @@
requests requests
requests-cache
eyed3 eyed3