|
- import argparse
- import csv
- import pathlib
- import re
- import sys
-
- from download_xinje_file import request, quote_url
- import urllib.request
-
-
- INVALID_FILENAME_CHARS = r'<>:"/\|?*'
-
-
- def clean_filename(value: str) -> str:
- value = "".join("_" if ch in INVALID_FILENAME_CHARS else ch for ch in value)
- value = re.sub(r"\s+", " ", value).strip(" .")
- return value or "xinje-download"
-
-
- def build_filename(row: dict[str, str]) -> str:
- base = row.get("download_name") or row.get("title") or row["id"]
- base = clean_filename(base)
- version = clean_filename(row.get("version") or "")
- update_date = clean_filename(row.get("update_date") or "")
- suffix_parts = []
- if version:
- suffix_parts.append(f"v{version}")
- if update_date:
- suffix_parts.append(update_date)
- suffix = f" ({', '.join(suffix_parts)})" if suffix_parts else ""
- extension = (row.get("file_type") or "pdf").lower().lstrip(".")
- return f"{base}{suffix}.{extension}"
-
-
- def load_rows(catalog: pathlib.Path) -> dict[str, dict[str, str]]:
- with catalog.open("r", encoding="utf-8-sig", newline="") as handle:
- return {row["id"]: row for row in csv.DictReader(handle)}
-
-
- def download(row: dict[str, str], output_dir: pathlib.Path, skip_existing: bool) -> pathlib.Path:
- output = output_dir / build_filename(row)
- if output.exists() and skip_existing:
- print(f"skip existing {row['id']}: {output}")
- return output
-
- url = quote_url(row["url"])
- output.parent.mkdir(parents=True, exist_ok=True)
- with urllib.request.urlopen(request(url), timeout=120) as response:
- output.write_bytes(response.read())
- print(f"downloaded {row['id']}: {output} ({output.stat().st_size} bytes)")
- return output
-
-
- def main() -> None:
- parser = argparse.ArgumentParser(description="Download Xinje catalog files by ID.")
- parser.add_argument("--catalog", required=True, type=pathlib.Path)
- parser.add_argument("--output-dir", required=True, type=pathlib.Path)
- parser.add_argument("--ids", required=True, nargs="+")
- parser.add_argument("--overwrite", action="store_true")
- args = parser.parse_args()
-
- rows = load_rows(args.catalog)
- missing = [item for item in args.ids if item not in rows]
- if missing:
- raise SystemExit(f"catalog IDs not found: {', '.join(missing)}")
-
- for item in args.ids:
- download(rows[item], args.output_dir, skip_existing=not args.overwrite)
-
-
- if __name__ == "__main__":
- sys.exit(main())
|