105 lines
3.8 KiB
Python
105 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Best-effort screenshot helpers for operation manuals."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import shutil
|
|
import re
|
|
from pathlib import Path
|
|
from urllib.parse import urljoin
|
|
|
|
from common import ensure_dir, read_json, write_json
|
|
|
|
|
|
def safe_name(path: str) -> str:
|
|
value = path.strip("/") or "home"
|
|
value = re.sub(r"[^A-Za-z0-9._-]+", "_", value)
|
|
return value[:80] or "page"
|
|
|
|
|
|
def collect_manual_screenshots(input_dir: Path, out_dir: Path) -> dict[str, object]:
|
|
out_dir = ensure_dir(out_dir)
|
|
screenshots = []
|
|
errors = []
|
|
allowed = {".png", ".jpg", ".jpeg", ".webp"}
|
|
for index, path in enumerate(sorted(input_dir.iterdir()), start=1):
|
|
if path.suffix.lower() not in allowed or not path.is_file():
|
|
continue
|
|
target = out_dir / f"{index:02d}-{safe_name(path.stem)}{path.suffix.lower()}"
|
|
if path.resolve() != target.resolve():
|
|
shutil.copy2(path, target)
|
|
screenshots.append({"route": "", "url": "", "path": str(target), "source": str(path)})
|
|
if not screenshots:
|
|
errors.append({"error": f"no screenshot images found in {input_dir}"})
|
|
manifest = {
|
|
"status": "ok" if screenshots else "empty",
|
|
"method": "user-supplied",
|
|
"screenshots": screenshots,
|
|
"errors": errors,
|
|
}
|
|
write_json(out_dir / "截图清单.json", manifest)
|
|
return manifest
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--base-url")
|
|
parser.add_argument("--analysis")
|
|
parser.add_argument("--out-dir", default="软件著作权申请资料/截图")
|
|
parser.add_argument("--max-pages", type=int, default=8)
|
|
parser.add_argument("--manual-dir", help="Collect user-supplied screenshots from this directory")
|
|
args = parser.parse_args()
|
|
|
|
if args.manual_dir:
|
|
manifest = collect_manual_screenshots(Path(args.manual_dir), Path(args.out_dir))
|
|
print(json.dumps(manifest, ensure_ascii=False, indent=2))
|
|
if not manifest["screenshots"]:
|
|
raise SystemExit(3)
|
|
return
|
|
|
|
if not args.base_url or not args.analysis:
|
|
raise SystemExit("Missing --base-url and --analysis unless --manual-dir is provided")
|
|
|
|
try:
|
|
from playwright.sync_api import sync_playwright
|
|
except Exception as exc:
|
|
print(json.dumps({"status": "error", "reason": f"playwright unavailable: {exc}"}, ensure_ascii=False))
|
|
raise SystemExit(2)
|
|
|
|
analysis = read_json(Path(args.analysis))
|
|
paths = analysis.get("routes") or ["/"]
|
|
clean_paths = []
|
|
for path in paths:
|
|
if isinstance(path, str) and path.startswith("/") and path not in clean_paths:
|
|
clean_paths.append(path)
|
|
clean_paths = clean_paths[: args.max_pages] or ["/"]
|
|
|
|
out_dir = ensure_dir(Path(args.out_dir))
|
|
screenshots = []
|
|
errors = []
|
|
with sync_playwright() as p:
|
|
browser = p.chromium.launch()
|
|
page = browser.new_page(viewport={"width": 1440, "height": 1000})
|
|
for route in clean_paths:
|
|
url = urljoin(args.base_url.rstrip("/") + "/", route.lstrip("/"))
|
|
file_path = out_dir / f"{safe_name(route)}.png"
|
|
try:
|
|
page.goto(url, wait_until="networkidle", timeout=15_000)
|
|
page.screenshot(path=str(file_path), full_page=True)
|
|
screenshots.append({"route": route, "url": url, "path": str(file_path)})
|
|
except Exception as exc:
|
|
errors.append({"route": route, "url": url, "error": str(exc)})
|
|
browser.close()
|
|
|
|
manifest = {"status": "ok" if screenshots else "partial", "screenshots": screenshots, "errors": errors}
|
|
write_json(out_dir / "截图清单.json", manifest)
|
|
print(json.dumps(manifest, ensure_ascii=False, indent=2))
|
|
if not screenshots:
|
|
raise SystemExit(3)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|