diff --git a/ingest/radieo/canonicalizer.py b/ingest/radieo/canonicalizer.py index 4ce0373..f9fd348 100644 --- a/ingest/radieo/canonicalizer.py +++ b/ingest/radieo/canonicalizer.py @@ -32,6 +32,7 @@ class Canonicalizer: timeout=httpx.Timeout(connect=10.0, read=30.0, write=10.0, pool=10.0), headers={"User-Agent": config.USER_AGENT}, follow_redirects=True, + transport=httpx.HTTPTransport(retries=config.HTTP_RETRIES), ) self._rate_lock = threading.Lock() self._last_call = 0.0 diff --git a/ingest/radieo/config.py b/ingest/radieo/config.py index 55cb6e0..2aa28f0 100644 --- a/ingest/radieo/config.py +++ b/ingest/radieo/config.py @@ -18,6 +18,10 @@ STATE_DB = STATE_DIR / "radieo.db" HTTP_HOST = os.environ.get("RADIEO_HTTP_HOST", "0.0.0.0") HTTP_PORT = int(os.environ.get("RADIEO_HTTP_PORT", "8080")) +# Transport-level retries for transient connection errors on outgoing HTTP +# (Navidrome, MusicBrainz, ListenBrainz). Applies to connect failures only. +HTTP_RETRIES = int(os.environ.get("RADIEO_HTTP_RETRIES", "2")) + # --- Prefetching / retention --- # How many downloaded tracks to keep ready ahead of playback. PREFETCH = int(os.environ.get("RADIEO_PREFETCH", "3")) diff --git a/ingest/radieo/providers/listenbrainz.py b/ingest/radieo/providers/listenbrainz.py index 4772484..5ed89c4 100644 --- a/ingest/radieo/providers/listenbrainz.py +++ b/ingest/radieo/providers/listenbrainz.py @@ -66,6 +66,7 @@ class ListenBrainzProvider: timeout=httpx.Timeout(connect=10.0, read=30.0, write=10.0, pool=10.0), headers={"User-Agent": config.USER_AGENT}, follow_redirects=True, + transport=httpx.HTTPTransport(retries=config.HTTP_RETRIES), ) self._recs: list[dict] = [] self._loaded_at = 0.0 diff --git a/ingest/radieo/subsonic.py b/ingest/radieo/subsonic.py index 76a3157..8118a9f 100644 --- a/ingest/radieo/subsonic.py +++ b/ingest/radieo/subsonic.py @@ -11,6 +11,8 @@ from pathlib import Path import httpx +from . import config + log = logging.getLogger("radieo.subsonic") # Advertised API version and client name. @@ -43,7 +45,11 @@ class SubsonicClient: self._base = base_url.rstrip("/") self._user = user self._password = password - self._http = httpx.Client(timeout=30.0, follow_redirects=True) + self._http = httpx.Client( + timeout=30.0, + follow_redirects=True, + transport=httpx.HTTPTransport(retries=config.HTTP_RETRIES), + ) def _auth_params(self) -> dict[str, str]: salt = secrets.token_hex(8)