feat: render audiograms for audio-only episodes - #153
Conversation
An episode recorded as audio has no frame to crop, no face to follow and nothing to burn captions onto, and the pipeline found that out the expensive way: transcription ran to completion, then get_dimensions raised "No video stream found". An hour of audio paid for its full Whisper run to be told it was never going to produce a clip, and the message read like a bug in the tool rather than a fact about the file. The header knew from the start. Checked there instead, right after the file is confirmed to exist. The check is deliberately stricter than "no video stream found", because this one is allowed to stop a run. Cover art counts as a video stream to ffprobe and is not something to crop, so it does not save a file from the message. A file ffprobe cannot read at all is the opposite case: a stub, a truncated download or a container this build has no demuxer for is not evidence of anything, and refusing it with a message about audio would swap one unhelpful failure for another. Anything short of "read it, has sound, has no moving picture" goes down the path it always took, which is what keeps the existing tests passing on their stub files. Rendering an audiogram for these episodes is the obvious next step and is not here. This only makes the refusal immediate and true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An audio podcast had no path through this tool. Transcription ran to completion, then get_dimensions raised "No video stream found", so an hour of audio paid for its full Whisper run to be told there was never going to be a clip, in a message that read like a bug rather than a fact about the file. There is no frame to crop and no face to follow, so the picture is made instead of found: the show's own artwork behind, the voice drawn as bars, and the same captions every other clip gets. The caption components are reused untouched, which is why an audiogram looks like the rest of the show rather than like a different product. The bars are computed here and handed over already reduced, one row of levels per frame. These samples are read on this side for moment detection anyway, and shipping an hour of PCM into a browser to average it there would be the same arithmetic somewhere slower and harder to test. RMS rather than peak, because peaks put every bar at full height on any voice that clips, and normalised against the window's own loudest bar so a quietly recorded episode still moves. The branch lives at the top of generate_clip rather than at its five call sites, so process, the MCP tools and batch rendering all get it without knowing anything changed: what comes back is the same dict describing the same window. Verified by rendering: 1080x1920, audio muxed, bars tracking the waveform, captions in step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The .gitignore rule is node_modules/, which matches a directory. My build worktree linked the real one in as a symlink, and a symlink is a file, so the rule did not catch it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 21 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
What
An episode recorded as audio now produces clips: captions and a moving waveform over the show's artwork, instead of a crash.
Why
There was no path through the tool for an audio podcast. Transcription ran to completion, then
get_dimensionsraised "No video stream found". An hour of audio paid for its full Whisper run to be told there was never going to be a clip, in a message that reads like a bug rather than a fact about the file.How
There is no frame to crop and no face to follow, so the picture is made instead of found: artwork behind, the voice as bars, and the same captions every other clip gets.
HormoziCaptionsand friends are reused untouched, which is why an audiogram looks like the rest of the show rather than like a different product.The bar levels are computed in Python and handed over already reduced, one row per frame:
@remotion/media-utils.The branch sits at the top of
generate_cliprather than at its five call sites, soprocess, the MCP tools and batch rendering all get it without knowing anything changed. What comes back is the same dict describing the same window, withcrop_strategy: "audiogram".Detection is deliberately strict. Cover art is a video stream to ffprobe and does not count as a picture to crop. A file ffprobe cannot read at all is not evidence of anything, so it goes down the video road exactly as before, which is what keeps the existing stub-file tests passing.
Verified by rendering
Not just by exit code. A real mp3 through
generate_clipproduced a 1080x1920 clip with audio muxed, bars tracking the waveform across the window, and captions in step with the words.Tests
615 pass, 12 new.
tests/test_ai_fallback.py::test_find_cli_falls_back_to_shell_lookupfails on my machine and fails identically on untouchedmain: it finds a realclaudeat~/.local/binthat the mock does not isolate.🤖 Generated with Claude Code