Add bundled bus fonts and shared font favorites

This commit is contained in:
2026-10-12 15:00:40 +08:00
parent c9dd03c23b
commit 4be74a34f9
181 changed files with 2078575 additions and 76 deletions
@@ -16,6 +16,7 @@ from uuid import UUID, uuid4
from app.animations.constants import DEFAULT_DURATION_MS, MAX_DURATION_MS, MIN_DURATION_MS
from app.display.text_renderer import FontResolver
from app.persistence import atomic_write_bytes
from app.fonts.render_cache import cache_stale, stamp_cache, STAMP_SUFFIX, cached_versioned_url
from app.templates.store import (
TemplateConflictError, TemplateError, TemplateNotFoundError,
TemplateStorageFullError, TemplateValidationError,
@@ -166,11 +167,12 @@ class AnimationStore:
def _write_thumbnail(self, animation_id: str, frame: dict[str, Any]) -> None:
self._thumb_dir(animation_id).mkdir(parents=True, exist_ok=True)
path = self._thumb_dir(animation_id) / frame["thumbnail"]
if path.exists():
return
scene = frame.get("scene") or self._load_scene(animation_id, frame)
if path.exists() and not cache_stale(path, scene, self._font_resolver):
return
try:
atomic_write_bytes(path, render_scene_thumbnail(scene, font_resolver=self._font_resolver))
stamp_cache(path, scene, self._font_resolver)
except OSError as exc:
self._translate(exc)
@@ -284,10 +286,10 @@ class AnimationStore:
"updated_at": str(raw.get("updated_at") or ""),
"digest": digest, "thumbnail": thumbnail, "scene_file": scene_file,
}
if self._load_scene(expected_id, frame)["version"] != 2:
checked_scene = self._load_scene(expected_id, frame)
if checked_scene["version"] != 2:
raise TemplateValidationError("animation schema v3 requires scene v2")
if not (self._thumb_dir(expected_id) / thumbnail).is_file():
self._write_thumbnail(expected_id, frame)
self._write_thumbnail(expected_id, {**frame, "scene": checked_scene})
frames.append(frame)
return {
"id": expected_id, "name": _name(document.get("name")),
@@ -315,6 +317,8 @@ class AnimationStore:
for path in directory.iterdir():
if path.is_file() and path.name not in expected and pattern.fullmatch(path.name):
path.unlink(missing_ok=True)
elif path.is_file() and path.name.endswith(STAMP_SUFFIX) and pattern.fullmatch(path.name[:-len(STAMP_SUFFIX)]) and path.name[:-len(STAMP_SUFFIX)] not in expected:
path.unlink(missing_ok=True)
def _all(self) -> list[dict[str, Any]]:
return [
@@ -386,7 +390,8 @@ class AnimationStore:
return all(r["name"].casefold() != checked.casefold() for r in self._all())
def thumbnail_url(self, animation_id: str, frame: dict[str, Any]) -> str:
return f"/api/animations/{animation_id}/frames/{frame['id']}/thumbnail?v={frame['digest']}"
return cached_versioned_url(f"/api/animations/{animation_id}/frames/{frame['id']}/thumbnail?v={frame['digest']}",
self._thumb_dir(animation_id) / frame["thumbnail"])
def _metadata(self, record: dict[str, Any]) -> dict[str, Any]:
revision = _revision(record)