Go to file
JSLMPR 8b00b41d7c fix(director): select the decisive moment, include its outcome, honest overlay
The highlight could cut off before the actual action and then assert an action
that was never shown (a "KICK" overlay over a stop-before-the-kick cut). Root
causes, all generic and fixed here:

- Localization (A): the climax search only scanned the central 25-80% band on
  audio(+semantic), structurally excluding a late payoff and often landing on the
  anticipation. Now it searches nearly the whole clip and localizes primarily by
  MOTION (the decisive action is a motion event), audio equal, semantic a weak
  tie-breaker, with a robust percentile-based camera-whip guard.
- Outcome (B): the window ended at a fixed climax+offset. It now extends past the
  climax until motion settles (the action AND its result), whip-guarded and capped.
- Honest overlay (C): a DENSE pass over the shown payoff window (not 7 sparse
  whole-clip frames) picks the best action frame; an honesty rule never asserts an
  action the window does not show -- anticipation gets a grounded teaser question
  instead (HighlightVisionDirector.groundOverlay/honestOverlayText/isAnticipatory).
  The VLM no longer picks the moment (it mislocalized onto anticipation), only
  grounds the overlay.
- Pacing cap: montage-max-build-seconds bounds the single pre-climax build shot so
  a distant action spike on a long source can't create one runaway shot (dead air
  plus an impractically long generated score).

Also: re-ingesting a source whose name already exists in processed/ no longer
fails -- moveToDirectory picks a unique "<name>-<n>.<ext>" instead of refusing.

Verified end-to-end on a new 63s landscape soccer clip: the director now finds the
scoring kick (ball in net), keeps the outcome, honestly labels "KICK", 24fps/420p/
-16 LUFS, beat-synced, subject-followed. mvn verify: 292 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:50:57 +02:00
.claude feat(cinematic): R10–R14 grade/cadence/edit rules + 420p compat fix 2026-07-25 12:36:18 +02:00
.github/workflows Add Maven Wrapper (pin Maven 3.9.9); CI uses ./mvnw 2026-07-24 15:41:17 +02:00
.idea I added an importable Grafana dashboard at dashboards/video-clipping-overview-grafana.json:1 and the companion setup note in docs/dashboards.md:1. The dashboard covers queue backlog and age, DLQ and processing counts, API 2026-07-10 00:14:13 +02:00
.mvn/wrapper Add Maven Wrapper (pin Maven 3.9.9); CI uses ./mvnw 2026-07-24 15:41:17 +02:00
Ultralytics feat(cinematic): R10–R14 grade/cadence/edit rules + 420p compat fix 2026-07-25 12:36:18 +02:00
dashboards I added an importable Grafana dashboard at dashboards/video-clipping-overview-grafana.json:1 and the companion setup note in docs/dashboards.md:1. The dashboard covers queue backlog and age, DLQ and processing counts, API 2026-07-10 00:14:13 +02:00
docs feat(cinematic): R10–R14 grade/cadence/edit rules + 420p compat fix 2026-07-25 12:36:18 +02:00
input/source • Added missing tests and a 100% coverage gate for the new folder scheduler package. 2026-07-10 01:02:08 +02:00
output - Implement end-to-end cinematic highlight rendering pipeline 2026-07-12 01:20:18 +02:00
src fix(director): select the decisive moment, include its outcome, honest overlay 2026-07-25 16:50:57 +02:00
tools feat(cinematic): R10–R14 grade/cadence/edit rules + 420p compat fix 2026-07-25 12:36:18 +02:00
.dockerignore Add Dockerfile + .dockerignore (app image; models mounted at runtime) 2026-07-24 15:13:29 +02:00
.gitignore Fix local CV visual-analysis client (HTTP/1.1) to enable YOLO selection 2026-07-22 11:14:48 +02:00
AGENTS.md feat(cinematic): R10–R14 grade/cadence/edit rules + 420p compat fix 2026-07-25 12:36:18 +02:00
Dockerfile Add Dockerfile + .dockerignore (app image; models mounted at runtime) 2026-07-24 15:13:29 +02:00
README.md feat(cinematic): R10–R14 grade/cadence/edit rules + 420p compat fix 2026-07-25 12:36:18 +02:00
mvnw Add Maven Wrapper (pin Maven 3.9.9); CI uses ./mvnw 2026-07-24 15:41:17 +02:00
mvnw.cmd Add Maven Wrapper (pin Maven 3.9.9); CI uses ./mvnw 2026-07-24 15:41:17 +02:00
pom.xml Configure folder scheduler in YAML 2026-07-10 12:24:16 +02:00

README.md

Video Editing Service — local cinematic highlight generator

Turn a single source video into a cinematic highlight entirely with local, offline models: it selects the moment, cuts a story-structured montage, generates the music/SFX/voiceover, applies a cinematic grade, and masters the audio — no external AI services, no runtime downloads.

Status: a working, source-adaptive proof of concept. The cinematic quality ruleset (R1R9, below) is complete and content-agnostic. It is not yet production-hardened (no auth on the render endpoint, no containers/PostgreSQL/no-egress certification, and the local models are non-commercially licensed — see Limitations). Do not deploy as-is.

What it does

For one source clip the single-source highlight pipeline runs, fully offline:

ingest ─► analyze (ffprobe, scenes, audio, frames)
       ─► candidates + category
       ─► DIRECTOR (auto):
            Tier 1  measure motion (YDIF) + audio (RMS) ─► story-structured shot list
            Tier 2  local VLM (moondream2) captions beats ─► semantic payoff selection + overlay + music mood
       ─► generate assets (Piper voice · MusicGen music · AudioLDM2 SFX)
       ─► render (portrait/landscape-aware, exposure-normalized, push-in, crossfades, slow-mo ramp,
                  bold overlay, ducked source under a swelling score)
       ─► master loudness ─► QA probes ─► final.mp4

Rendering is gated: it stays off by default and requires an explicit approval flag per project.

Cinematic quality rules (R1R9)

Every rule is source-adaptive — it measures the source and adapts, rather than hard-coding constants. Full detail in docs/cinematic-quality-rules.md.

# Rule Measure → adapt
R1 Exposure frame luma → normalize; grade never crushes the subject
R2 Orientation source rotation → portrait/landscape output, no distortion
R3 Audio balance + loudness score leads, source ducked; measured loudness corrected to 16 LUFS
R4 Duration any length, story-driven
R5 Motion push-in per-shot motion (YDIF) → adaptive in-shot zoompan
R6 Transitions cross-dissolves between beats + ease into slow-motion
R7 Overlays bold, outlined, animated entrance
R8 Music dynamics volume swell builds into the payoff
R9 Show the action Tier-1 measured + Tier-2 VLM caption-driven selection
R10 Cinematic cadence 24 fps film-standard output (was 30)
R11 Filmic grade filmic tone S-curve (lifted toe + highlight roll-off) + optional licensed lut3d
R12 Motion blur shutter-angle frame blend on styled shots (toggle)
R13 Beat-synced cuts snap cut boundaries onto the score's beat grid (librosa; toggle)
R14 Subject-tracking reframe follow the detected subject vs. static crop (YOLO/AGPL; toggle)

Build & test

Requires JDK 21 and ffmpeg/ffprobe on the PATH.

mvn -B verify          # compile, run all tests, JaCoCo gate

CI (.github/workflows/ci.yml) runs this on every push.

Run the highlight pipeline (local PoC)

The localpoc Spring profile wires the pipeline to pre-provisioned local model paths, isolates its input/output trees, keeps rendering disabled + approval-required, and never starts a network-capable bootstrap. The end-to-end command sequence (stage source → analyze → auto-direct → approve → render) is in docs/RUNBOOK-highlight-e2e.md. Outputs land under output/localpoc/highlight-projects/<project>/final.mp4.

Local models used (provisioning + version traps in docs/LOCAL-MODELS.md; each needs a provenance sidecar under models/):

Model Role License
Piper (en_US-lessac-medium) voiceover MIT / Blizzard dataset
MusicGen small music CC-BY-NC
AudioLDM2 SFX CC-BY-NC-SA
moondream2 Tier-2 vision director Apache-2.0
YOLOv8n (optional CV) visual analysis AGPL-3.0

Non-negotiable constraints

  • No automatic dependency/model downloads at runtime; models load offline from models/ + the local cache.
  • No external AI services in the media path.
  • No unlicensed assets; no placeholder silence/tones passed off as generated audio (the pipeline fails closed).
  • No rendering without an explicit approval flag.

Limitations

  • Licensing: MusicGen (CC-BY-NC), AudioLDM2 (CC-BY-NC-SA) and YOLOv8 (AGPL) are non-commercial/copyleft. Commercial use requires swapping in commercially-licensed models/assets.
  • Not production-hardened: no Spring Security/authN, no container/K8s/deployment manifests, REST persistence defaults to in-memory, and no-egress operation is not yet certified. POST /v1/edit-projects/{projectId}:render now requires an approved.flag in the project directory, but that is a basic presence gate — not yet an authenticated, digest-bound authorization.
  • VLM quality: on distant/small subjects the small local VLM is only weakly discriminative; a stronger model or closer framing improves Tier-2 selection.
  • A director can only cut what was filmed — it cannot show a moment the camera never captured.

Repository map

  • src/main/java/org/example/videoclips/editing/ — highlight analysis, two-tier director, renderer, QA.
  • tools/ — local model workers (local_asset_worker.py, vision_caption.py).
  • src/main/resources/application-localpoc.yml — the opt-in PoC profile.
  • docs/ — the PoC plan, the cinematic quality rules, acceptance review.