Files
congsh 5c028d7952 Initial commit: snapAna 截图智能整理工具
包含 FastAPI 后端、React 前端、队列/OCR/标签/待办等完整功能。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 15:45:50 +08:00

49 lines
1.5 KiB
Python

"""生成并缓存缩略图。"""
from __future__ import annotations
import hashlib
from pathlib import Path
from PIL import Image
from app.core.config import settings
from app.core.path_utils import path_to_storage
SUPPORTED_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".tif", ".tiff"}
def is_supported(path: Path) -> bool:
"""是否为我们支持的图片格式。"""
return path.suffix.lower() in SUPPORTED_EXTS
def generate_thumbnail(image_path: Path, max_side: int | None = None) -> Path:
"""生成 webp 缩略图,落到缓存目录。返回缓存路径。"""
max_side = max_side or settings.thumb_size
# 用文件路径 + mtime 哈希作为缓存键,源文件变化会自动生成新缩略图
stat = image_path.stat()
key = hashlib.md5(
f"{path_to_storage(image_path)}|{stat.st_mtime_ns}|{max_side}".encode("utf-8")
).hexdigest()
out = settings.thumb_dir / f"{key}.webp"
if out.exists():
return out
with Image.open(image_path) as img:
img = img.convert("RGB")
img.thumbnail((max_side, max_side), Image.LANCZOS)
img.save(out, format="WEBP", quality=80)
return out
def file_hash(image_path: Path, chunk: int = 1024 * 1024) -> str:
"""计算文件 sha256,用作去重键。"""
h = hashlib.sha256()
with open(image_path, "rb") as f:
while True:
data = f.read(chunk)
if not data:
break
h.update(data)
return h.hexdigest()