Repository navigation
M2: the native renderer - #3
Merged
Merged
Conversation
GVC renders a VideoSpec to an MP4, with what the format promised and Notes' renderer refused: - a soundtrack that changes through the video; - a default transition with per-scene overrides; - Ken Burns moves between two frames of a picture; - trimmed GAM clips, cards, narration, chapters and subtitles. The engine is copied from Notes (app/render/: ffmpeg, compose, narration, pause markup, shot cache) with its tests. It is driven by GVC's own compile step rather than Notes' shot list: - compile.py cuts scenes at quote/code beats and cascades defaults field by field; - each join gets its own transition (blends overlap, dips draw inside the shots, cuts cut); - structural anchors resolve to seconds, and an orphaned anchor drops its element with a warning. New builders: - a zoompan move between two picture frames; - trimmed clips cut out once, so a loop repeats the trim; - a stitch with a transition per join; - a mix of any number of tracks, ducked ones compressed against the narration. The stitch normalises every input's timebase first: concat re-times its output, and xfade refused a cut followed by a blend, a latent bug in Notes' copy too. Media comes from GAM through a cache keyed on GAM id and size. Placeholders draw as slates in a preview and refuse a full render; files still in a note refuse both, with the fix. Narration is read through Notes, one beat at a time, so beat anchors are exact. The engine and voice are pinned after the first chunk (gecko-notes#211). A Notes without that refuses, so a video never changes voice partway through. Renders run on GAM's job runner as their requester. The token is held in memory only, so a restart ends a render instead of storing a credential. Finished files are served at signed, ranged URLs. The editor gains: - per-scene transition and Ken Burns controls, with a frame editor; - a Soundtrack tab, with crossfade in one click; - a Render tab with the estimate, what blocks a render, progress, the result and past renders. Deployment: - all of data/ is mounted (renders, media cache); - GAM_BASE_URL defaults to assets.geckopico.com; - the prod overlay points the backend at the Docker host for the sibling names. GVC's server calling GAM by its public name from inside the same machine failed with gam_unavailable (hairpin NAT). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016fiRJAt34ag62iPiuMkNhm
The renderer's compose module imports PIL. It was installed in the local venv but never pinned, so CI and the Docker image lacked it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016fiRJAt34ag62iPiuMkNhm
davior
marked this pull request as ready for review
October 16, 2026 05:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M2 of the plan of attack. GVC now renders a project to an MP4, including what the VideoSpec described and Notes' renderer refused:
Needs davior/gecko-notes#211 deployed in Notes. Narration is read through Notes, and GVC refuses to narrate against a Notes without it rather than risk a video that changes voice partway through.
What you can do
.srt, and past renders.How
app/render/: ffmpeg, compose, narration, pause markup, shot cache), together with Notes' own tests for it (359 of them).compile.pyis the spec doc's §5.8 compile step, done natively.zoompanmove between two picture frames;sidechaincompressed against the narration.concatre-times its output, so a cut followed by a blend gavexfadetwo different timebases and the stitch failed to build. Every input is now normalised first. Notes' copy has the same bug in its cut-then-crossfade path.data/cache/media, keyed on GAM id and size, so a second render fetches nothing. A GAM clip plays from its parent's file at itsin_point./media/renders/…), because<video>can't send a header.Deployment notes
docker-compose.prod.ymlnow mapsassets.geckopico.comandnotes.geckopico.comtohost-gatewayfor the backend. From inside the server, GVC calling GAM by its public name hit hairpin NAT and every search failed withgam_unavailable; via the Docker host it reaches Caddy directly, with the same names and certificates.data/is mounted now (database, renders, media cache).GAM_BASE_URLdefaults tohttps://assets.geckopico.com..env.example:RENDER_CONCURRENCY(1),RENDER_MAX_SEGMENTS,RENDER_MAX_NARRATION_CHARS,RENDER_CACHE_MAX_GB,MEDIA_URL_TTL_SECONDS.render_job), run by the entrypoint as usual.Tests
pytest -qpasses, 445 tests. That includes Notes' engine tests and these new ones:s_ruins.npm test(89 tests) and build all pass.Not in this PR (M2b)
🤖 Generated with Claude Code
https://claude.ai/code/session_016fiRJAt34ag62iPiuMkNhm
Generated by Claude Code