from __future__ import annotations import re from copy import deepcopy from typing import Any PLACEHOLDER = re.compile(r"{{\s*([A-Za-z_][A-Za-z0-9_.-]*)\s*}}") AUTOMATION_TEMPLATES: dict[str, dict[str, Any]] = { "vertical-captioned-clip": { "preset": "tiktok_9_16_fast", "output_name": "{{output_name}}", "scenes": "{{scenes}}", "voiceover": "{{voiceover}}", "background_music": "{{background_music}}", "auto_subtitles": True, "audio_normalize": True, }, "podcast-clip": { "preset": "podcast_square", "output_name": "{{output_name}}", "scenes": "{{scenes}}", "auto_subtitles": True, "audio_normalize": True, }, "product-promo": { "preset": "capcut_product_launch", "output_name": "{{output_name}}", "scenes": "{{scenes}}", "watermark": "{{watermark}}", "background_music": "{{background_music}}", }, } def list_automation_templates() -> list[str]: return sorted(AUTOMATION_TEMPLATES) def automation_template_catalog() -> dict[str, dict[str, Any]]: return deepcopy(AUTOMATION_TEMPLATES) def resolve_render_template( template: str | dict[str, Any], variables: dict[str, Any], overrides: dict[str, Any] | None = None, ) -> dict[str, Any]: if isinstance(template, str): if template not in AUTOMATION_TEMPLATES: raise ValueError(f"Unknown automation template: {template}") source = deepcopy(AUTOMATION_TEMPLATES[template]) elif isinstance(template, dict): source = deepcopy(template) else: raise ValueError("Template must be a registered name or JSON object") rendered = _substitute(source, variables) if not isinstance(rendered, dict): raise ValueError("Rendered template must produce a JSON object") return _deep_merge(rendered, overrides or {}) def _substitute(value: Any, variables: dict[str, Any]) -> Any: if isinstance(value, dict): return {key: _substitute(item, variables) for key, item in value.items()} if isinstance(value, list): return [_substitute(item, variables) for item in value] if not isinstance(value, str): return value exact = PLACEHOLDER.fullmatch(value) if exact: return deepcopy(_lookup(variables, exact.group(1))) return PLACEHOLDER.sub(lambda match: str(_lookup(variables, match.group(1))), value) def _lookup(variables: dict[str, Any], key: str) -> Any: value: Any = variables for part in key.split("."): if not isinstance(value, dict) or part not in value: raise ValueError(f"Missing template variable: {key}") value = value[part] return value def _deep_merge(base: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]: output = deepcopy(base) for key, value in overrides.items(): if isinstance(value, dict) and isinstance(output.get(key), dict): output[key] = _deep_merge(output[key], value) else: output[key] = deepcopy(value) return output