43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
|
|
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
|