mirror of
https://github.com/Horizontal42/Sort_YM.git
synced 2026-11-10 15:42:02 +03:00
apply --yes падал целиком на первом же самозалитом/пиратском треке без альбома (105 из 1984) - Яндекс отвечает BadRequestError (wrong-json), т.к. albumId обязателен, а with_retries лечил это как временный сетевой сбой и вслепую повторял заведомо безнадёжный запрос 4 раза. Теперь BadRequestError (как и NotFoundError) не ретраится нигде - это окончательный ответ сервера, а не таймаут. Треки без album_id пропускаются проактивно с понятным сообщением, а неожиданный BadRequestError на отдельном треке больше не прерывает весь прогон - пропускается со счётчиком, остальные треки обрабатываются дальше.
45 lines
1.8 KiB
Python
45 lines
1.8 KiB
Python
from __future__ import annotations
|
||
|
||
import time
|
||
from typing import Callable, Iterator, List, TypeVar
|
||
|
||
from yandex_music import Client
|
||
from yandex_music.exceptions import BadRequestError, NetworkError, NotFoundError
|
||
|
||
T = TypeVar("T")
|
||
|
||
|
||
def make_client(token: str, request_timeout: float = 20) -> Client:
|
||
client = Client(token=token)
|
||
client.request.set_timeout(request_timeout)
|
||
with_retries(lambda: client.init())
|
||
return client
|
||
|
||
|
||
def chunked(items: List[T], size: int) -> Iterator[List[T]]:
|
||
for i in range(0, len(items), size):
|
||
yield items[i : i + size]
|
||
|
||
|
||
def with_retries(fn: Callable[[], T], max_attempts: int = 4, base_delay: float = 2.0) -> T:
|
||
"""Повторяет вызов при временных сетевых сбоях (таймауты и т.п.) с экспоненциальной паузой.
|
||
|
||
NotFoundError и BadRequestError не повторяются - это окончательные ответы сервера
|
||
(данные точно не найдены / запрос точно невалиден), а не сбои сети, хотя формально
|
||
оба тоже унаследованы от NetworkError. Повтор такого запроса даст тот же результат.
|
||
"""
|
||
last_exc: NetworkError | None = None
|
||
for attempt in range(max_attempts):
|
||
try:
|
||
return fn()
|
||
except (NotFoundError, BadRequestError):
|
||
raise
|
||
except NetworkError as e:
|
||
last_exc = e
|
||
if attempt < max_attempts - 1:
|
||
wait = base_delay * (2**attempt)
|
||
print(f" сетевая ошибка ({e}), повтор через {wait:.0f}с...")
|
||
time.sleep(wait)
|
||
|
||
assert last_exc is not None
|
||
raise last_exc
|