humeo-space / src /humeo /reframe_ffmpeg.py
moonlantern1's picture
Publish Humeo Docker Space
67ce4ed verified
"""Thin adapter from the product pipeline to the reusable render primitive."""
from __future__ import annotations
import logging
from pathlib import Path
from humeo_core.primitives import compile as compile_mod
from humeo_core.schemas import (
Clip,
LayoutInstruction,
LayoutKind,
RenderRequest,
)
logger = logging.getLogger(__name__)
def layout_for_clip(
clip: Clip,
default_layout: LayoutKind = LayoutKind.SIT_CENTER,
zoom: float = 1.0,
) -> LayoutInstruction:
"""Build the layout instruction for a clip using the shared schema."""
layout = clip.layout or default_layout
return LayoutInstruction(clip_id=clip.clip_id, layout=layout, zoom=zoom)
def reframe_clip_ffmpeg(
input_path: Path | str,
output_path: Path | str,
clip: Clip,
*,
zoom: float = 1.0,
layout_instruction: LayoutInstruction | None = None,
subtitle_path: Path | str | None = None,
subtitle_font_size: int = 48,
subtitle_margin_v: int = 160,
title_text: str = "",
dry_run: bool = False,
) -> RenderRequest:
"""Render a single clip to 9:16 via one ffmpeg call.
If ``layout_instruction`` is set (e.g. from Gemini vision), it is used in full
including ``person_x_norm``, ``chart_x_norm``, and optional split bbox fields.
Otherwise defaults are derived from ``clip.layout`` via ``layout_for_clip``.
"""
instr = layout_instruction if layout_instruction is not None else layout_for_clip(clip, zoom=zoom)
req = RenderRequest(
source_path=str(input_path),
clip=clip,
layout=instr,
output_path=str(output_path),
subtitle_path=str(subtitle_path) if subtitle_path else None,
subtitle_font_size=subtitle_font_size,
subtitle_margin_v=subtitle_margin_v,
title_text=title_text,
mode="dry_run" if dry_run else "normal",
)
result = compile_mod.render_clip(req)
if not result.success and not dry_run:
raise RuntimeError(f"ffmpeg failed for clip {clip.clip_id}: {result.error}")
logger.info(
"reframe_clip_ffmpeg: clip=%s layout=%s output=%s success=%s",
clip.clip_id,
instr.layout.value,
output_path,
result.success,
)
return req