#!/usr/bin/env python3 """Build a portable v1 article package for the DWW11 userscript (standard library only).""" from __future__ import annotations import argparse import base64 import json import mimetypes from pathlib import Path def make_package(directory: str | Path, markdown: str | None = None) -> dict: """Return JSON-serializable data; no network access or publishing side effects.""" root = Path(directory).expanduser().resolve(strict=True) if not root.is_dir(): raise ValueError("文章目录不存在") choices = sorted(path for path in root.rglob("*") if path.is_file() and path.suffix.lower() == ".md") if markdown: selected = (root / markdown).resolve(strict=True) elif len(choices) == 1: selected = choices[0].resolve() else: raise ValueError("目录必须包含一个 Markdown 文件,或通过 --markdown 指定目录内相对路径") if not selected.is_relative_to(root) or selected.suffix.lower() != ".md" or not selected.is_file(): raise ValueError("Markdown 必须位于所选文章目录内") images = [] # Package local supported images. The importer uploads only those referenced by Markdown. for path in sorted(root.rglob("*")): if not path.is_file() or path.suffix.lower() not in {".jpg", ".jpeg", ".png", ".gif"}: continue if not path.resolve().is_relative_to(root): raise ValueError(f"图片符号链接指向目录外:{path.name}") if not 0 < path.stat().st_size < 20 * 1024 * 1024: raise ValueError(f"图片为空或达到 20 MB:{path.name}") images.append({"path": path.relative_to(root).as_posix(), "mime": mimetypes.guess_type(path.name)[0] or "application/octet-stream", "base64": base64.b64encode(path.read_bytes()).decode("ascii")}) return {"schemaVersion": 1, "markdownPath": selected.relative_to(root).as_posix(), "markdown": selected.read_text(encoding="utf-8-sig"), "images": images} def main() -> None: parser = argparse.ArgumentParser(description="把文章目录打包为油猴脚本可导入的 JSON;不上传或发布") parser.add_argument("directory", help="包含 Markdown 和图片的目录") parser.add_argument("--markdown", help="多个 Markdown 时指定目录内的相对路径") parser.add_argument("-o", "--output", help="输出文件路径;未指定时输出到标准输出") args = parser.parse_args() try: content = json.dumps(make_package(args.directory, args.markdown), ensure_ascii=False, indent=2) if args.output: output = Path(args.output).expanduser() # Never silently overwrite an article or an existing package. with output.open("x", encoding="utf-8") as handle: handle.write(content + "\n") print(json.dumps({"ok": True, "package": str(output.resolve())}, ensure_ascii=False)) else: print(content) except (OSError, ValueError) as error: parser.exit(1, f"打包失败:{error}\n") if __name__ == "__main__": main()