Backend-B2V / backend /app /services /template_service.py
Muhammad-Mehdi's picture
Backend and remotion-video for HF Space
cfe45d5
Raw
History Blame Contribute Delete
4.14 kB
"""
TemplateService β€” Reads template metadata and prompts from backend/templates/.
All template-specific logic lives in meta.json and prompt.md. This service
is template-agnostic: it discovers templates from the registry and reads
their files. Unknown IDs fall back to "default".
"""
import json
import os
from pathlib import Path
from typing import Any
# Path to backend/templates/ (relative to this file: app/services/template_service.py)
_TEMPLATES_DIR = Path(__file__).resolve().parent.parent.parent / "templates"
def _load_registry() -> list[str]:
"""Load template IDs from registry.json."""
path = _TEMPLATES_DIR / "registry.json"
if not path.exists():
return ["default"]
with open(path, encoding="utf-8") as f:
data = json.load(f)
return data if isinstance(data, list) else ["default"]
def _load_meta(template_id: str) -> dict[str, Any] | None:
"""Load meta.json for a template. Returns None if not found."""
path = _TEMPLATES_DIR / template_id / "meta.json"
if not path.exists():
return None
with open(path, encoding="utf-8") as f:
return json.load(f)
def _load_prompt(template_id: str) -> str:
"""Load prompt.md content for a template. Returns empty string if not found."""
path = _TEMPLATES_DIR / template_id / "prompt.md"
if not path.exists():
return ""
with open(path, encoding="utf-8") as f:
return f.read()
# ─── Public API ─────────────────────────────────────────────────────
def list_templates() -> list[dict[str, Any]]:
"""Return list of all templates (meta for each)."""
registry = _load_registry()
result = []
for tid in registry:
meta = _load_meta(tid)
if meta:
result.append(meta)
return result
def get_meta(template_id: str) -> dict[str, Any] | None:
"""Get meta.json for one template."""
return _load_meta(template_id)
def get_prompt(template_id: str) -> str:
"""Get prompt.md content for one template. Reads fresh on each call."""
return _load_prompt(template_id)
def get_valid_layouts(template_id: str) -> set[str]:
"""Get the set of valid layout IDs for a template."""
meta = _load_meta(template_id)
if not meta:
return set()
layouts = meta.get("valid_layouts", [])
return set(layouts) if isinstance(layouts, list) else set()
def get_hero_layout(template_id: str) -> str:
"""Get the hero layout ID (scene 0). Default: hero_image."""
meta = _load_meta(template_id)
if not meta:
return "hero_image"
return meta.get("hero_layout", "hero_image")
def get_fallback_layout(template_id: str) -> str:
"""Get the fallback layout when DSPy output is invalid."""
meta = _load_meta(template_id)
if not meta:
return "text_narration"
return meta.get("fallback_layout", "text_narration")
def get_composition_id(template_id: str) -> str:
"""Get the Remotion composition ID for rendering."""
meta = _load_meta(template_id)
if not meta:
return "DefaultVideo"
return meta.get("composition_id", "DefaultVideo")
def get_image_layout(template_id: str) -> str | None:
"""Get the layout ID for image-heavy scenes (blog images). None = no override."""
meta = _load_meta(template_id)
if not meta:
return None
return meta.get("image_layout")
def get_preview_colors(template_id: str) -> dict[str, str] | None:
"""Get preview_colors (accent, bg, text) for template. None = use request defaults."""
meta = _load_meta(template_id)
if not meta:
return None
pc = meta.get("preview_colors")
if not isinstance(pc, dict):
return None
return pc
def validate_template_id(template_id: str | None) -> str:
"""Return template_id if valid, else 'default'."""
if not template_id or not isinstance(template_id, str):
return "default"
tid = template_id.strip().lower()
registry = _load_registry()
if tid in registry:
return tid
return "default"