Обработка сетевых ошибок

This commit is contained in:
Lev Plyusnin 2025-06-15 11:02:18 +07:00
parent 2ffd45d36e
commit e435328907
No known key found for this signature in database
GPG key ID: 21C6C2C9C0A4460D
4 changed files with 76 additions and 13 deletions

View file

@ -74,6 +74,8 @@ usage: yandex-music-downloader [-h] [--quality <Качество>] [--skip-exist
[--only-music]
[--compatibility-level <Уровень совместимости>]
[--timeout <Время ожидания>]
[--tries <Количество попыток>]
[--retry-delay <Задержка>]
(--artist-id <ID исполнителя> | --album-id <ID альбома> | --track-id <ID трека> | --playlist-id <владелец плейлиста>/<тип плейлиста> | -u URL)
[--unsafe-path] [--dir <Папка>]
[--path-pattern <Паттерн>] --token <Токен>
@ -100,8 +102,14 @@ options:
--only-music Загружать только музыкальные альбомы (пропускать подкасты и аудиокниги)
--compatibility-level <Уровень совместимости>
Уровень совместимости, от 0 до 1. См. README для подробного описания (по умолчанию: 1)
Сетевые параметры:
--timeout <Время ожидания>
Время ожидания ответа от сервера, в секундах. Увеличьте если возникают сетевые ошибки (по умолчанию: 20)
--tries <Количество попыток>
Количество попыток при возникновении сетевых ошибок. 0 - бесконечное количество попыток (по умолчанию: 20)
--retry-delay <Задержка>
Задержка между повторными запросами при сетевых ошибках (по умолчанию: 5)
ID:
--artist-id <ID исполнителя>

View file

@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "yandex-music-downloader"
version = "3.4.10"
version = "3.5.0"
description = "Загрузчик музыки с сервиса Яндекс.Музыка"
requires-python = ">=3.9"
readme = "README.md"

View file

@ -6,7 +6,7 @@ import re
import time
import typing
from argparse import ArgumentTypeError
from collections.abc import Generator, Iterable
from collections.abc import Callable, Generator, Iterable
from pathlib import Path
from typing import Optional, Union
from urllib.parse import urlparse
@ -52,17 +52,25 @@ def compatibility_level_arg(astr: str) -> int:
)
def natural_int_arg(astr: str) -> int:
def checked_int_arg(
min_value: int, max_value: Optional[int] = None
) -> Callable[[str], int]:
def func(astr: str) -> int:
aint = int(astr)
if aint > 0:
if aint >= min_value and (max_value is None or aint <= max_value):
return aint
raise ArgumentTypeError("Значение должен быть > 0")
error_text = f"Значение должен быть >= {min_value}"
if max_value is not None:
error_text += f" и <= {max_value}"
raise ArgumentTypeError(error_text)
return func
def cover_resolution_arg(astr: str) -> int:
if astr == "original":
return -1
return int(astr)
return checked_int_arg(100)(astr)
def lyrics_format_arg(astr: str) -> core.LyricsFormat:
@ -115,7 +123,7 @@ def main():
"--delay",
default=DEFAULT_DELAY,
metavar="<Задержка>",
type=int,
type=checked_int_arg(0),
help=show_default("Задержка между запросами, в секундах"),
)
common_group.add_argument(
@ -137,15 +145,33 @@ def main():
f"Уровень совместимости, от {core.MIN_COMPATIBILITY_LEVEL} до {core.MAX_COMPATIBILITY_LEVEL}. См. README для подробного описания"
),
)
common_group.add_argument(
network_group = parser.add_argument_group("Сетевые параметры")
network_group.add_argument(
"--timeout",
metavar="<Время ожидания>",
default=20,
type=natural_int_arg,
type=checked_int_arg(1),
help=show_default(
"Время ожидания ответа от сервера, в секундах. Увеличьте если возникают сетевые ошибки"
),
)
network_group.add_argument(
"--tries",
metavar="<Количество попыток>",
default=20,
type=checked_int_arg(0),
help=show_default(
"Количество попыток при возникновении сетевых ошибок. 0 - бесконечное количество попыток"
),
)
network_group.add_argument(
"--retry-delay",
metavar="<Задержка>",
default=5,
type=checked_int_arg(0),
help=show_default("Задержка между повторными запросами при сетевых ошибках"),
)
common_group.add_argument("--debug", action="store_true", help=argparse.SUPPRESS)
id_group_meta = parser.add_argument_group("ID")
@ -221,7 +247,12 @@ def main():
print("Параметер url указан в неверном формате")
return 1
client = core.init_client(args.token, args.timeout)
client = core.init_client(
token=args.token,
timeout=args.timeout,
max_try_count=args.tries,
retry_delay=args.retry_delay,
)
result_tracks: Iterable[Track] = []
def album_tracks_gen(album_ids: Iterable[Union[int, str]]) -> Generator[Track]:

View file

@ -1,6 +1,7 @@
import datetime as dt
import hashlib
import re
import time
import typing
from collections.abc import Callable
from dataclasses import dataclass
@ -33,6 +34,7 @@ from yandex_music import (
Track,
YandexMusicModel,
)
from yandex_music.exceptions import NetworkError
from ymd import api
from ymd.api import (
@ -91,9 +93,31 @@ class AlbumCover:
mime_type: MimeType
def init_client(token: str, timeout: int) -> Client:
def init_client(
token: str, timeout: int, max_try_count: int, retry_delay: int
) -> Client:
assert timeout > 0
assert max_try_count >= 0
assert retry_delay >= 0
client = Client(token)
client.request.set_timeout(timeout)
original_wrapper = client.request._request_wrapper
def retry_wrapper(*args, **kwargs):
try_count = 0
while True:
try:
return original_wrapper(*args, **kwargs)
except NetworkError as error:
if max_try_count == 0 or try_count < max_try_count:
try_count += 1
time.sleep(retry_delay)
continue
raise error
client.request._request_wrapper = retry_wrapper
return client.init()