refactor(downloads): split download streaming into focused helpers and reduce complexity

This commit is contained in:
2026-02-18 04:12:54 +01:00
parent 4ba2c43c93
commit ecdd953ff4

View File

@@ -3,6 +3,7 @@
import re import re
import unicodedata import unicodedata
from pathlib import Path from pathlib import Path
from typing import Any
from urllib.parse import urlparse from urllib.parse import urlparse
import audible import audible
@@ -30,7 +31,7 @@ class DownloadManager:
chunk_size: int = DEFAULT_CHUNK_SIZE, chunk_size: int = DEFAULT_CHUNK_SIZE,
) -> None: ) -> None:
self.auth = auth self.auth = auth
self.client = client self.client: Any = client
self.cache_dir = cache_dir self.cache_dir = cache_dir
self.cache_dir.mkdir(parents=True, exist_ok=True) self.cache_dir.mkdir(parents=True, exist_ok=True)
self.chunk_size = chunk_size self.chunk_size = chunk_size
@@ -244,19 +245,7 @@ class DownloadManager:
with self._download_client.stream("GET", url) as response: with self._download_client.stream("GET", url) as response:
response.raise_for_status() response.raise_for_status()
total_size = int(response.headers.get("content-length", 0)) total_size = int(response.headers.get("content-length", 0))
downloaded = 0 self._stream_to_file(response, dest_path, total_size, notify)
with open(dest_path, "wb") as file_handle:
for chunk in response.iter_bytes(chunk_size=self.chunk_size):
file_handle.write(chunk)
downloaded += len(chunk)
if total_size > 0 and notify:
percent = (downloaded / total_size) * 100
downloaded_mb = downloaded / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
notify(
f"Downloading: {percent:.1f}% ({downloaded_mb:.1f}/{total_mb:.1f} MB)"
)
return dest_path return dest_path
except httpx.HTTPStatusError as exc: except httpx.HTTPStatusError as exc:
@@ -264,31 +253,56 @@ class DownloadManager:
notify( notify(
f"Download HTTP error: {exc.response.status_code} {exc.response.reason_phrase}" f"Download HTTP error: {exc.response.status_code} {exc.response.reason_phrase}"
) )
try: self._cleanup_partial_file(dest_path)
if dest_path.exists() and dest_path.stat().st_size < MIN_FILE_SIZE:
dest_path.unlink()
except OSError:
pass
return None return None
except httpx.HTTPError as exc: except httpx.HTTPError as exc:
if notify: if notify:
notify(f"Download network error: {exc!s}") notify(f"Download network error: {exc!s}")
try: self._cleanup_partial_file(dest_path)
if dest_path.exists() and dest_path.stat().st_size < MIN_FILE_SIZE:
dest_path.unlink()
except OSError:
pass
return None return None
except (OSError, ValueError, KeyError) as exc: except (OSError, ValueError, KeyError) as exc:
if notify: if notify:
notify(f"Download error: {exc!s}") notify(f"Download error: {exc!s}")
try: self._cleanup_partial_file(dest_path)
if dest_path.exists() and dest_path.stat().st_size < MIN_FILE_SIZE:
dest_path.unlink()
except OSError:
pass
return None return None
def _stream_to_file(
self,
response: httpx.Response,
dest_path: Path,
total_size: int,
notify: StatusCallback | None = None,
) -> None:
"""Write streamed response bytes to disk and emit progress messages."""
downloaded = 0
with open(dest_path, "wb") as file_handle:
for chunk in response.iter_bytes(chunk_size=self.chunk_size):
file_handle.write(chunk)
downloaded += len(chunk)
self._notify_download_progress(downloaded, total_size, notify)
def _notify_download_progress(
self,
downloaded: int,
total_size: int,
notify: StatusCallback | None = None,
) -> None:
"""Emit a formatted progress message when total size is known."""
if total_size <= 0 or not notify:
return
percent = (downloaded / total_size) * 100
downloaded_mb = downloaded / (1024 * 1024)
total_mb = total_size / (1024 * 1024)
notify(f"Downloading: {percent:.1f}% ({downloaded_mb:.1f}/{total_mb:.1f} MB)")
def _cleanup_partial_file(self, dest_path: Path) -> None:
"""Remove undersized partial download files after transfer failures."""
try:
if dest_path.exists() and dest_path.stat().st_size < MIN_FILE_SIZE:
dest_path.unlink()
except OSError:
return
def close(self) -> None: def close(self) -> None:
"""Close internal HTTP clients. Safe to call multiple times.""" """Close internal HTTP clients. Safe to call multiple times."""
if hasattr(self, "_http_client"): if hasattr(self, "_http_client"):