Skip to content

Add podcli ui + backend hardening and audit fixes#30

Merged
nmbrthirteen merged 4 commits into
mainfrom
code-audit-fixes
Jun 27, 2026
Merged

Add podcli ui + backend hardening and audit fixes#30
nmbrthirteen merged 4 commits into
mainfrom
code-audit-fixes

Conversation

@nmbrthirteen

@nmbrthirteen nmbrthirteen commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the podcli ui command and a batch of backend hardening / output-quality fixes from a full code audit. All tests green (341 pytest, 47 vitest, tsc, go build+test); full pipeline verified end-to-end (renders a 1080×1920 clip).

Changes

  • podcli ui (alias webui) — launches the Studio dashboard (http://localhost:3847); wired in the Python CLI and the Go launcher (help + runtime gating). The landing site already documents this command.
  • Transcription — run face analysis on the whisper.cpp engine too, so face-based framing works on native installs. whisper.cpp stays torch-free (importing torch for diarization hard-crashes native runtimes); diarization remains whisper-py-only. Regression test added.
  • Clip selection — select suggestions by score before truncating (was earliest-by-timeline); dedupe by temporal overlap instead of exact range.
  • Captions (ASS) — hold each chunk through short pauses to stop boundary flicker.
  • Face samplinggrab()/retrieve() instead of a per-frame seek (~5× faster, identical crop output); robust to under-reported frame counts and VFR.
  • TS bridge — kill the process group on timeout (no orphaned ffmpeg/whisper), handle stdin EPIPE, buffer stderr across chunks, scan stdout for the result line.
  • Security/api/stream-source serves only user-selected/uploaded media via an allowlist + realpath + extension gate (blocks arbitrary file reads via a forged ui-state); image types allowed so logo previews still work.
  • CLI — require PODCLI_INSECURE_SSL=1 before disabling TLS verification.
  • Misc — atomic-save temp cleanup, clean EADDRINUSE exit, dead-code removal, /prep-episode/produce-shorts docs.

Test plan

  • pytest tests/ → 341 passed
  • npm test → 47 passed; tsc --noEmit clean
  • cd cli && go build && go test ./... → pass
  • podcli ui → serves localhost:3847 (verified)
  • podcli process sample.mp4 --top 1 → renders a valid 1080×1920 h264/aac clip
  • stream-source: forged ui-state path → 403; legit select-file → 200

Summary by CodeRabbit

  • New Features

    • Added a new UI/Web UI command for launching the web interface.
    • Updated documentation to use the new produce shorts workflow command.
  • Bug Fixes

    • Improved transcription reliability and speaker handling, with clearer fallback behavior.
    • Better clip selection now avoids near-duplicate suggestions and favors higher-quality matches.
    • Fixed caption timing so rendered text can carry smoothly across short gaps.
    • Hardened file serving, startup error handling, and Python process cleanup for more stable operation.

- cli: add `podcli ui` (alias `webui`) to launch the Studio dashboard
- transcription: run diarization + face analysis for the whisper.cpp engine
  too, so speaker-aware framing works on native installs (not just whisper-py)
- clips: select suggestions by score before truncating (was earliest-by-
  timeline); dedupe by temporal overlap instead of exact rounded range
- captions: hold each chunk through short pauses to stop boundary flicker
- bridge: kill the process group on timeout (no orphaned ffmpeg/whisper),
  handle stdin EPIPE, buffer stderr across chunks, scan stdout for result
- studio: stream-source serves only user-selected/uploaded media + a media-
  extension gate (blocks arbitrary file reads via a forged ui-state)
- cli: require PODCLI_INSECURE_SSL=1 before disabling TLS verification
- ui: round clip-card media top corners
- face_analysis: sample frames via grab()/retrieve() instead of a POS_MSEC
  seek per frame — ~5x faster (1.3s vs 6.6s on a 29s clip), identical crop
  output; offsets the face-analysis cost the whisper.cpp path now incurs
- clips-history: clean up the temp file if the atomic save fails
- web-server: exit with a clear message on EADDRINUSE instead of crashing
- video_processor: drop a dead always-false branch
- CLAUDE.md: /prep-episode -> /produce-shorts (command was renamed)
The shared post-transcription stage imported speaker_detection (torch) for
diarization on every engine. On native installs the whisper.cpp path has no
working torch, and the import hard-crashes the process (uncatchable, exit 144)
mid-transcription. whisper.cpp is the no-torch path by design, so skip
diarization there and keep only face analysis (OpenCV). Caught by an
end-to-end render smoke test; locked with a regression test.
- stream-source: allow image MIME types so logo/asset previews work again
  (still gated by the allowlist; secrets stay non-streamable)
- face_analysis: derive frame step from max(reported, duration*fps) so
  under-reported frame counts don't confine sampling to the video's start;
  label observations by real playback timestamp (VFR-safe)
- README: /prep-episode -> /produce-shorts (matches .claude/commands)
- strip restating comments added in the audit; keep only the why
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR renames the /prep-episode command to /produce-shorts across docs, adds ui/webui CLI subcommands backed by a new launch_webui() helper, hardens /api/stream-source with an allowlist-based access control model, refactors transcription post-processing into a shared _attach_speakers_and_faces helper, upgrades clip deduplication to overlap-based logic with score-first selection, extends caption gap-filling to all renderers, and improves Python subprocess lifecycle robustness.

Changes

Command rename: /prep-episode → /produce-shorts

Layer / File(s) Summary
Docs rename
CLAUDE.md, README.md
All references to /prep-episode replaced with /produce-shorts in pipeline diagrams, feature lists, and project structure docs.

Web UI command and stream-source access control

Layer / File(s) Summary
ui/webui CLI subcommand and launch_webui
backend/cli.py, cli/main.go
Adds ui subcommand (alias webui), launch_webui() function wired into dispatch and interactive menu, and extends Go launcher's wantsRuntime and help text.
SSL verification opt-in gate
backend/cli.py
_ensure_ssl_certs() now requires PODCLI_INSECURE_SSL to be explicitly set instead of silently disabling SSL.
allowedSourcePaths allowlist and /api/stream-source rewrite
src/ui/web-server.ts
Introduces registerSourcePath() called at upload/select/browse/transcribe, rewrites /api/stream-source to enforce allowlist or uploads-directory membership with extension gating and realpathSync resolution. Server startup now handles EADDRINUSE.

Transcription refactor and face/speaker post-processing

Layer / File(s) Summary
_attach_speakers_and_faces helper
backend/services/transcription.py
New shared helper centralizes diarization, speaker label assignment, face analysis, and final dict assembly.
Engine selection and whisper-py path refactor
backend/services/transcription.py, tests/test_transcription_engine.py
use_cpp flag cleanly branches engine paths; whisper-py delegates to helper. Regression test asserts whispercpp skips diarization.
Face analysis frame sampling via grab/retrieve
backend/services/face_analysis.py
Replaces seek-based sampling with grab()/retrieve() stepping; timestamps derived from CAP_PROP_POS_MSEC.

Clip suggestion ranking and deduplication

Layer / File(s) Summary
Overlap-based dedupe and score-first selection
backend/services/claude_suggest.py
Adds _dedupe_clips_by_range (50% overlap threshold) and _select_top_by_score (truncate by score, return by start time). All three suggest entry points updated to use these helpers.

Caption renderer gap-filling

Layer / File(s) Summary
_hold_through_gap and renderer updates
backend/services/caption_renderer.py
Introduces CAPTION_GAP_FILL_MAX and _hold_through_gap; applied to end-time calculation in hormozi, karaoke, subtle, and branded renderers.

Process lifecycle and storage robustness

Layer / File(s) Summary
PythonExecutor robustness overhaul
src/services/python-executor.ts
Adds killProcessTree and parseResultLine helpers, detached spawning, centralized finish guard, buffered stderr, EPIPE tolerance, and staged SIGTERM→SIGKILL timeout.
ClipsHistory cleanup and dead-code removal
src/services/clips-history.ts, backend/services/video_processor.py
save() deletes temp file on write/rename failure; unreachable branch removed from _track_and_crop.
Clip card media border styling
src/ui/public/css/styles.css
.clip-card-media gets top-only border-radius; inset box-shadow border narrowed to thumb images only.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • nmbrthirteen/podcli#1: Modifies the same /api/stream-source access-control logic in web-server.ts that this PR rewrites with the allowlist model.
  • nmbrthirteen/podcli#5: Touches the same backend/cli.py web UI entrypoints and studio/webui command wiring updated in this PR.
  • nmbrthirteen/podcli#16: Modifies backend/services/transcription.py engine selection and diarization handling refactored by this PR.

Poem

🐇 Hopping through the code at dawn,
/produce-shorts has now been born!
Allowlists guard the stream so neat,
Gap-filled captions, ranked and sweet.
SIGTERM first, then SIGKILL's might—
This rabbit ships it clean and right! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main changes: adding podcli ui and several backend hardening/audit fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch code-audit-fixes

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nmbrthirteen nmbrthirteen merged commit 2396637 into main Jun 27, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant