#!/usr/bin/env python3 """수령한 생활 수기집 목록의 Google Drive 공유 원본을 다운로드한다.""" from __future__ import annotations import argparse import json import re import sys from pathlib import Path FILE_ID = re.compile(r"drive\.google\.com/file/d/([^/]+)") FOLDER_ID = re.compile(r"drive\.google\.com/drive/folders/([^/?]+)") def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("manifest", type=Path) parser.add_argument("--out-dir", type=Path, required=True) parser.add_argument("--report", type=Path, required=True) return parser.parse_args() def drive_kind(url: str) -> tuple[str, str] | None: if match := FILE_ID.search(url): return "file", match.group(1) if match := FOLDER_ID.search(url): return "folder", match.group(1) return None def main() -> int: args = parse_args() try: import gdown from openpyxl import load_workbook except ImportError: print("gdown과 openpyxl이 필요합니다.", file=sys.stderr) return 2 workbook = load_workbook(args.manifest, read_only=False, data_only=False) sheet = workbook.worksheets[0] results = [] args.out_dir.mkdir(parents=True, exist_ok=True) for rownum in range(3, sheet.max_row + 1): cell = sheet.cell(row=rownum, column=10) url = cell.hyperlink.target if cell.hyperlink else "" parsed = drive_kind(url or "") if not parsed: continue kind, drive_id = parsed target = args.out_dir / f"source_{rownum:03d}" target.mkdir(parents=True, exist_ok=True) result = { "row": rownum, "kind": kind, "drive_id": drive_id, "url": url, "label": str(cell.value or ""), "status": "failed", "files": [], } try: if kind == "folder": gdown.download_folder( url=url, output=str(target), quiet=False, remaining_ok=True, ) else: gdown.download(id=drive_id, output=str(target) + "/", quiet=False) files = sorted(str(p.relative_to(args.out_dir)) for p in target.rglob("*") if p.is_file()) result["files"] = files result["status"] = "downloaded" if files else "empty" except Exception as exc: # noqa: BLE001 result["error"] = str(exc) results.append(result) report = { "manifest": args.manifest.name, "drive_links": len(results), "downloaded": sum(r["status"] == "downloaded" for r in results), "failed_or_empty": sum(r["status"] != "downloaded" for r in results), "results": results, } args.report.parent.mkdir(parents=True, exist_ok=True) args.report.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8") print(json.dumps({k: v for k, v in report.items() if k != "results"}, ensure_ascii=False)) return 0 if report["downloaded"] else 1 if __name__ == "__main__": raise SystemExit(main())