from __future__ import annotations from pathlib import Path from typing import Optional import requests def sanitize_filename(name: str, *, max_len: int = 150) -> str: text = str(name or "").strip() if not text: return "download" forbidden = set('<>:"/\\|?*') cleaned = "".join("_" if c in forbidden else c for c in text) cleaned = " ".join(cleaned.split()).strip().strip(".") if not cleaned: cleaned = "download" return cleaned[:max_len] def download_file(url: str, output_path: Path, *, session: Optional[requests.Session] = None, timeout_s: float = 30.0) -> bool: output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) s = session or requests.Session() try: with s.get(url, stream=True, timeout=timeout_s) as resp: resp.raise_for_status() with open(output_path, "wb") as f: for chunk in resp.iter_content(chunk_size=1024 * 256): if chunk: f.write(chunk) return output_path.exists() and output_path.stat().st_size > 0 except Exception: try: if output_path.exists(): output_path.unlink() except Exception: pass return False