Files
vvveb-cms/utils/media_manager.py
2026-05-26 11:53:28 +08:00

106 lines
3.1 KiB
Python

from __future__ import annotations
import json
import os
from datetime import datetime
from pathlib import Path
from typing import Any
import uuid
def _now_iso() -> str:
return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")
def save_upload(project_dir: Path, file_storage) -> dict[str, Any]:
"""Save uploaded file under project media directory and generate thumbnail.
Returns metadata dict.
"""
uploads_dir = project_dir / "media" / "images"
date_folder = datetime.utcnow().strftime("%Y-%m")
target_dir = uploads_dir / date_folder
target_dir.mkdir(parents=True, exist_ok=True)
original_name = file_storage.filename or "upload"
ext = Path(original_name).suffix.lower() or ".bin"
safe_name = f"{uuid.uuid4().hex}{ext}"
target_path = target_dir / safe_name
# save original
file_storage.save(str(target_path))
# try to generate thumbnail for common image types
thumb_name = None
try:
from PIL import Image
if ext in [".jpg", ".jpeg", ".png", ".webp", ".gif"]:
im = Image.open(str(target_path))
im.thumbnail((400, 400))
thumb_name = f"{uuid.uuid4().hex}_thumb{ext}"
thumb_path = target_dir / thumb_name
im.save(str(thumb_path))
except Exception:
thumb_name = None
meta = {
"filename": safe_name,
"original_name": original_name,
"url": f"/sites/{{slug}}/media/images/{date_folder}/{safe_name}",
"thumb": (f"/sites/{{slug}}/media/images/{date_folder}/{thumb_name}" if thumb_name else None),
"size": target_path.stat().st_size,
"uploaded_at": _now_iso(),
}
# update uploads index
uploads_index = uploads_dir / "uploads.json"
data = {}
if uploads_index.exists():
try:
data = json.loads(uploads_index.read_text(encoding="utf-8"))
except Exception:
data = {}
data.setdefault(date_folder, []).append({
"filename": safe_name,
"original_name": original_name,
"size": meta["size"],
"uploaded_at": meta["uploaded_at"],
})
uploads_index.parent.mkdir(parents=True, exist_ok=True)
uploads_index.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
return meta
def list_media(project_dir: Path) -> list[dict[str, Any]]:
uploads_dir = project_dir / "media" / "images"
uploads_index = uploads_dir / "uploads.json"
if not uploads_index.exists():
return []
try:
data = json.loads(uploads_index.read_text(encoding="utf-8"))
except Exception:
return []
items = []
for date_folder, files in data.items():
for f in files:
items.append({
"date": date_folder,
**f,
})
return items
def delete_media(project_dir: Path, rel_path: str) -> bool:
target = (project_dir / rel_path).resolve()
if not str(target).startswith(str(project_dir.resolve())):
return False
if not target.exists():
return False
try:
target.unlink()
except Exception:
return False
return True