chore: 完善开源项目文档与结构,移除敏感原始图片

- 重写 README.md,添加在线体验、API 文档、部署指南、技术栈等
- 添加 MIT LICENSE
- 添加 CONTRIBUTING.md 贡献指南
- 添加 GitHub Issue/PR 模板
- 完善 .gitignore,防止原始图片和敏感文件误提交
- 从 git 中移除 25 个原始图片文件(.png/.jpg/非序号 webp)
- 项目结构迁移:functions/ -> src/,根目录静态文件 -> public/
- 添加 wrangler.toml 与 package.json 配置
This commit is contained in:
2026-06-06 20:52:59 +08:00
parent f68e19a3bd
commit f45a0068a2
69 changed files with 872 additions and 675 deletions

103
build.py
View File

@@ -4,6 +4,7 @@ from __future__ import annotations
import json
import os
import re
import shutil
import subprocess
import sys
import venv
@@ -11,11 +12,24 @@ from datetime import datetime, timezone
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
MANIFEST_PATH = BASE_DIR / "manifest.json"
PUBLIC_DIR = BASE_DIR / "public"
MANIFEST_PATH = PUBLIC_DIR / "manifest.json"
VARIANTS = {
"mobile": [BASE_DIR / "mobile-image"],
"desktop": [BASE_DIR / "desketop-image"],
"mobile": {
"out": PUBLIC_DIR / "mobile-image",
"scan": [
PUBLIC_DIR / "mobile-image",
BASE_DIR / "mobile-image",
],
},
"desktop": {
"out": PUBLIC_DIR / "desketop-image",
"scan": [
PUBLIC_DIR / "desketop-image",
BASE_DIR / "desketop-image",
],
},
}
INPUT_EXTENSIONS = {
@@ -81,14 +95,15 @@ def iter_source_files(directory: Path) -> list[Path]:
return files
def select_source_directory(candidates: list[Path]) -> Path:
for directory in candidates:
def select_scan_directory(scan_candidates: list[Path]) -> Path:
"""优先有原图的目录,其次有序号 webp否则退回 scan 第一项(通常为 public下的输出目录"""
for directory in scan_candidates:
if iter_source_files(directory):
return directory
for directory in candidates:
if directory.exists():
for directory in scan_candidates:
if sorted_generated_webp_names(directory):
return directory
return candidates[0]
return scan_candidates[0]
def clear_generated_files(directory: Path) -> None:
@@ -100,6 +115,23 @@ def clear_generated_files(directory: Path) -> None:
path.unlink()
def sorted_generated_webp_names(directory: Path) -> list[str]:
"""已存在的序号 webp无原图时再跑脚本不会清空"""
if not directory.exists():
return []
names = [
path.name
for path in directory.iterdir()
if path.is_file() and GENERATED_NAME_RE.match(path.name)
]
def sort_key(entry: str) -> int:
return int(Path(entry).stem)
names.sort(key=sort_key)
return names
def convert_to_webp(source: Path, target: Path, image_mod, image_ops) -> None:
with image_mod.open(source) as image:
image = image_ops.exif_transpose(image)
@@ -114,24 +146,47 @@ def convert_to_webp(source: Path, target: Path, image_mod, image_ops) -> None:
image.save(target, format="WEBP", quality=92, method=6)
def build_variant(name: str, directories: list[Path], image_mod, image_ops) -> dict:
directory = select_source_directory(directories)
directory.mkdir(parents=True, exist_ok=True)
clear_generated_files(directory)
def copy_generated_webps(source_dir: Path, out_dir: Path, names: list[str]) -> None:
"""将已有序号 webp 同步到 public 下的部署目录。"""
out_dir.mkdir(parents=True, exist_ok=True)
for entry in names:
shutil.copy2(source_dir / entry, out_dir / entry)
def build_variant(name: str, spec: dict[str, Path | list[Path]], image_mod, image_ops) -> dict:
scan_dir = select_scan_directory(list(spec["scan"]))
out_dir = spec["out"]
out_dir.mkdir(parents=True, exist_ok=True)
sources = iter_source_files(scan_dir)
sources = iter_source_files(directory)
images: list[str] = []
for index, source in enumerate(sources, start=1):
target = directory / f"{index}.webp"
try:
convert_to_webp(source, target, image_mod, image_ops)
images.append(target.name)
print(f"[{name}] {source.name} -> {target.name}")
except Exception as exc:
print(f"[{name}] skip {source.name}: {exc}", file=sys.stderr)
if sources:
clear_generated_files(out_dir)
for index, source in enumerate(sources, start=1):
target = out_dir / f"{index}.webp"
try:
convert_to_webp(source, target, image_mod, image_ops)
images.append(target.name)
rel_src = os.path.relpath(source.resolve(), BASE_DIR.resolve())
print(f"[{name}] {rel_src} -> {target.name}")
except Exception as exc:
print(f"[{name}] skip {source.name}: {exc}", file=sys.stderr)
else:
images = sorted_generated_webp_names(out_dir)
if not images and scan_dir.resolve() != out_dir.resolve():
legacy = sorted_generated_webp_names(scan_dir)
if legacy:
copy_generated_webps(scan_dir, out_dir, legacy)
images = legacy
print(f"[{name}] 已从 {scan_dir.relative_to(BASE_DIR)} 复制序号 webp ×{len(images)} -> public/")
if images:
if scan_dir.resolve() == out_dir.resolve():
print(f"[{name}] 未检测到原图,保留 public 下序号 webp ×{len(images)}")
else:
print(f"[{name}] 跳过:未发现原图,且 public/ 与归档目录均无 1.webp 等序号文件")
return {
"folder": directory.name,
"folder": out_dir.name,
"count": len(images),
"images": images,
}
@@ -149,8 +204,8 @@ def main() -> int:
"variants": {},
}
for name, directories in VARIANTS.items():
manifest["variants"][name] = build_variant(name, directories, image_mod, image_ops)
for name, variant_spec in VARIANTS.items():
manifest["variants"][name] = build_variant(name, variant_spec, image_mod, image_ops)
write_manifest(manifest)
print(f"Wrote manifest: {MANIFEST_PATH}")