"""Version only thumbnails that use a bundled font; preserve scene revisions.""" from __future__ import annotations import hashlib import json from pathlib import Path from app.persistence import atomic_write_bytes STAMP_SUFFIX = ".font-version" def scene_font_version(scene: dict, resolver) -> str | None: owner = getattr(resolver, "__self__", None) version_for = getattr(owner, "render_version", None) if version_for is None: return None elements = scene.get("elements", []) if scene.get("version") == 1 else [ element for layer in scene.get("layers", []) if layer.get("type") == "text" for element in layer.get("elements", []) ] versions = sorted({version for element in elements if (version := version_for(element.get("font", "default")))}) if not versions: return None return hashlib.sha256(json.dumps(versions, separators=(",", ":")).encode("ascii")).hexdigest()[:16] def stamp_path(path: Path) -> Path: return path.with_name(path.name + STAMP_SUFFIX) def cache_stale(path: Path, scene: dict, resolver) -> bool: version = scene_font_version(scene, resolver) if version is None: return False try: return stamp_path(path).read_text(encoding="ascii").strip() != version except (OSError, UnicodeError): return True def stamp_cache(path: Path, scene: dict, resolver) -> None: version = scene_font_version(scene, resolver) stamp = stamp_path(path) if version is not None: atomic_write_bytes(stamp, (version + "\n").encode("ascii")) else: stamp.unlink(missing_ok=True) def versioned_url(url: str, scene: dict, resolver) -> str: version = scene_font_version(scene, resolver) return url + f"&font_v={version}" if version else url def cached_versioned_url(url: str, path: Path) -> str: try: version = stamp_path(path).read_text(encoding="ascii").strip() except (OSError, UnicodeError): return url if len(version) == 16 and all(ch in "0123456789abcdef" for ch in version): return url + f"&font_v={version}" return url