64 lines
3.1 KiB
Python
64 lines
3.1 KiB
Python
#!/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()
|