# Plan: Cinematic Highlight Render

## Context

Add a new render type: a 20-second cinematic product highlight video with procedural multi-shot camera animation. Unlike turntable (constant orbit), this has varying camera distances, focal lengths, elevations, and bezier-eased transitions between 4 segments.

## Tasks

### [ ] Task 1: Cinematic Blender script

- **File**: `render-worker/scripts/cinematic_render.py` (new)
- **What**: Blender Python script that:
  1. Imports product geometry (GLB or USD, same as turntable_render.py)
  2. Applies materials (same pipeline as turntable)
  3. Computes bounding sphere from imported meshes
  4. Creates a procedural 4-segment camera animation (480 frames @ 24fps = 20s):
     - Segment 1 (0-120): Establishing — slow 45° orbit + push-in, 50mm lens
     - Segment 2 (121-240): Detail sweep — low-angle close arc, 85mm lens, shallow DOF
     - Segment 3 (241-360): Crane up — rising from 30° to 60° elevation, 35mm wide
     - Segment 4 (361-480): Hero close — slow push-in, 65mm, ease-out to still
  5. Each segment: camera position from spherical coords (azimuth, elevation, distance), bezier interpolation
  6. Depth of field enabled (f-stop scales with product size)
  7. Renders all frames as PNG to temp directory
  8. FFmpeg assembles frames → MP4 (H.264, yuv420p, 24fps)

  CLI args (same pattern as turntable_render.py):
  ```
  blender --background --python cinematic_render.py -- \
    <glb_path> <output_mp4> <width> <height> <engine> <samples> \
    <smooth_angle> <cycles_device> <transparent_bg> <template_path> \
    <target_collection> <material_library_path> <material_map_json> \
    <part_names_ordered_json> <lighting_only> <shadow_catcher> \
    <rotation_x> <rotation_y> <rotation_z> \
    [--usd-path ...] [--focal-length ...] [--material-override ...]
  ```
- **Acceptance gate**: Script renders a 20s MP4 from a STEP file
- **Dependencies**: None

### [ ] Task 2: Render service function

- **File**: `backend/app/services/render_blender.py`
- **What**: Add `render_cinematic_to_file()` function with same signature as `render_turntable_to_file()` but:
  - Calls `cinematic_render.py` instead of `turntable_render.py`
  - Fixed 24fps, 480 frames (20s)
  - Output format: mp4
  - Passes all the same material/template/position args
- **Acceptance gate**: Function callable, builds correct subprocess command
- **Dependencies**: Task 1

### [ ] Task 3: Pipeline integration

- **File**: `backend/app/domains/pipeline/tasks/render_order_line.py`
- **What**: In the render task, detect when output type has a cinematic flag. Add a check:
  - If `output_type.render_settings.get("cinematic")` is True → call `render_cinematic_to_file()` instead of `render_turntable_to_file()`
  - OR: if output_type name contains "Cinematic" → route to cinematic
- **Acceptance gate**: Order line with cinematic output type renders via the new script
- **Dependencies**: Task 2

### [ ] Task 4: Output type + test

- **What**: Create the "Cinematic Highlight" output type via API:
  ```json
  {
    "name": "Cinematic Highlight",
    "renderer": "blender",
    "output_format": "mp4",
    "render_backend": "celery",
    "is_animation": true,
    "transparent_bg": false,
    "cycles_device": "gpu",
    "render_settings": {
      "width": 1920,
      "height": 1080,
      "engine": "cycles",
      "samples": 128,
      "cinematic": true,
      "fps": 24,
      "frame_count": 480
    }
  }
  ```
  Link to BlenderStudio template. Test with a real product.
- **Acceptance gate**: A cinematic MP4 renders successfully for a TRB product
- **Dependencies**: Task 3

## Migration Check
**No** — no DB changes needed.
