Обработка сетевых ошибок
This commit is contained in:
parent
2ffd45d36e
commit
e435328907
4 changed files with 76 additions and 13 deletions
|
|
@ -74,6 +74,8 @@ usage: yandex-music-downloader [-h] [--quality <Качество>] [--skip-exist
|
||||||
[--only-music]
|
[--only-music]
|
||||||
[--compatibility-level <Уровень совместимости>]
|
[--compatibility-level <Уровень совместимости>]
|
||||||
[--timeout <Время ожидания>]
|
[--timeout <Время ожидания>]
|
||||||
|
[--tries <Количество попыток>]
|
||||||
|
[--retry-delay <Задержка>]
|
||||||
(--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 <Папка>]
|
[--unsafe-path] [--dir <Папка>]
|
||||||
[--path-pattern <Паттерн>] --token <Токен>
|
[--path-pattern <Паттерн>] --token <Токен>
|
||||||
|
|
@ -100,8 +102,14 @@ options:
|
||||||
--only-music Загружать только музыкальные альбомы (пропускать подкасты и аудиокниги)
|
--only-music Загружать только музыкальные альбомы (пропускать подкасты и аудиокниги)
|
||||||
--compatibility-level <Уровень совместимости>
|
--compatibility-level <Уровень совместимости>
|
||||||
Уровень совместимости, от 0 до 1. См. README для подробного описания (по умолчанию: 1)
|
Уровень совместимости, от 0 до 1. См. README для подробного описания (по умолчанию: 1)
|
||||||
|
|
||||||
|
Сетевые параметры:
|
||||||
--timeout <Время ожидания>
|
--timeout <Время ожидания>
|
||||||
Время ожидания ответа от сервера, в секундах. Увеличьте если возникают сетевые ошибки (по умолчанию: 20)
|
Время ожидания ответа от сервера, в секундах. Увеличьте если возникают сетевые ошибки (по умолчанию: 20)
|
||||||
|
--tries <Количество попыток>
|
||||||
|
Количество попыток при возникновении сетевых ошибок. 0 - бесконечное количество попыток (по умолчанию: 20)
|
||||||
|
--retry-delay <Задержка>
|
||||||
|
Задержка между повторными запросами при сетевых ошибках (по умолчанию: 5)
|
||||||
|
|
||||||
ID:
|
ID:
|
||||||
--artist-id <ID исполнителя>
|
--artist-id <ID исполнителя>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "yandex-music-downloader"
|
name = "yandex-music-downloader"
|
||||||
version = "3.4.10"
|
version = "3.5.0"
|
||||||
description = "Загрузчик музыки с сервиса Яндекс.Музыка"
|
description = "Загрузчик музыки с сервиса Яндекс.Музыка"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
||||||
53
ymd/cli.py
53
ymd/cli.py
|
|
@ -6,7 +6,7 @@ import re
|
||||||
import time
|
import time
|
||||||
import typing
|
import typing
|
||||||
from argparse import ArgumentTypeError
|
from argparse import ArgumentTypeError
|
||||||
from collections.abc import Generator, Iterable
|
from collections.abc import Callable, Generator, Iterable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
from urllib.parse import urlparse
|
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(
|
||||||
aint = int(astr)
|
min_value: int, max_value: Optional[int] = None
|
||||||
if aint > 0:
|
) -> Callable[[str], int]:
|
||||||
return aint
|
def func(astr: str) -> int:
|
||||||
raise ArgumentTypeError("Значение должен быть > 0")
|
aint = int(astr)
|
||||||
|
if aint >= min_value and (max_value is None or aint <= max_value):
|
||||||
|
return aint
|
||||||
|
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:
|
def cover_resolution_arg(astr: str) -> int:
|
||||||
if astr == "original":
|
if astr == "original":
|
||||||
return -1
|
return -1
|
||||||
return int(astr)
|
return checked_int_arg(100)(astr)
|
||||||
|
|
||||||
|
|
||||||
def lyrics_format_arg(astr: str) -> core.LyricsFormat:
|
def lyrics_format_arg(astr: str) -> core.LyricsFormat:
|
||||||
|
|
@ -115,7 +123,7 @@ def main():
|
||||||
"--delay",
|
"--delay",
|
||||||
default=DEFAULT_DELAY,
|
default=DEFAULT_DELAY,
|
||||||
metavar="<Задержка>",
|
metavar="<Задержка>",
|
||||||
type=int,
|
type=checked_int_arg(0),
|
||||||
help=show_default("Задержка между запросами, в секундах"),
|
help=show_default("Задержка между запросами, в секундах"),
|
||||||
)
|
)
|
||||||
common_group.add_argument(
|
common_group.add_argument(
|
||||||
|
|
@ -137,15 +145,33 @@ def main():
|
||||||
f"Уровень совместимости, от {core.MIN_COMPATIBILITY_LEVEL} до {core.MAX_COMPATIBILITY_LEVEL}. См. README для подробного описания"
|
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",
|
"--timeout",
|
||||||
metavar="<Время ожидания>",
|
metavar="<Время ожидания>",
|
||||||
default=20,
|
default=20,
|
||||||
type=natural_int_arg,
|
type=checked_int_arg(1),
|
||||||
help=show_default(
|
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)
|
common_group.add_argument("--debug", action="store_true", help=argparse.SUPPRESS)
|
||||||
|
|
||||||
id_group_meta = parser.add_argument_group("ID")
|
id_group_meta = parser.add_argument_group("ID")
|
||||||
|
|
@ -221,7 +247,12 @@ def main():
|
||||||
print("Параметер url указан в неверном формате")
|
print("Параметер url указан в неверном формате")
|
||||||
return 1
|
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] = []
|
result_tracks: Iterable[Track] = []
|
||||||
|
|
||||||
def album_tracks_gen(album_ids: Iterable[Union[int, str]]) -> Generator[Track]:
|
def album_tracks_gen(album_ids: Iterable[Union[int, str]]) -> Generator[Track]:
|
||||||
|
|
|
||||||
26
ymd/core.py
26
ymd/core.py
|
|
@ -1,6 +1,7 @@
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
import typing
|
import typing
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
@ -33,6 +34,7 @@ from yandex_music import (
|
||||||
Track,
|
Track,
|
||||||
YandexMusicModel,
|
YandexMusicModel,
|
||||||
)
|
)
|
||||||
|
from yandex_music.exceptions import NetworkError
|
||||||
|
|
||||||
from ymd import api
|
from ymd import api
|
||||||
from ymd.api import (
|
from ymd.api import (
|
||||||
|
|
@ -91,9 +93,31 @@ class AlbumCover:
|
||||||
mime_type: MimeType
|
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 = Client(token)
|
||||||
client.request.set_timeout(timeout)
|
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()
|
return client.init()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue