feat: Smarter ffmpeg/ffprobe resolution, until we bundle it#141
Draft
Hazer wants to merge 27 commits into
Draft
Conversation
- PlaybackAnalysisState enum: refuse to decide media never probed by ffprobe (NULL metadata_json), instead of returning an untrustworthy 'transcode required' - count_media_files_needing_reprobe + reprobe_media_files: targeted UPDATE of codec/container/metadata_json columns, independent of the hash-skip scan guard - Re-probe driver (try_spawn_reprobe) + activity progress (Dashboard bar) - Triggers: startup (ffprobe now available + unprobed files), manual route POST /api/v1/system/tools/reprobe (+ GET status). Settings-change trigger dropped (DbConn can't be cloned into a spawn from the route) - Drop the buggy client-side capabilities-cache preflight; the server-side analysis_state guard replaces it with single-source-of-truth - Remove debug telemetry from create_session / get_session_stream - Client: analysis_state rendering in player, Re-probe button in settings
db.run borrows &self, so the save doesn't consume db; move it into the spawn afterward (same pattern as scan_library). No clone needed.
|
|
|
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.




Description
Fixes two interrelated transcoding failures and the deeper root cause behind them, plus adds a guided FFmpeg/ffprobe setup experience. Minor refactors or code changes made while developing this new ffmpeg resolution experience.
The reported bugs: playing an HEVC-in-MKV item (a) failed to start transcoding (
No such file or directory/ ENOENT at spawn) and (b) left the web player on a black screen with no error.Root cause (deeper than it first appeared):
ffmpeg_pathis the bare nameffmpeg. On macOS, GUI-launched.appbundles do not inherit the user's shellPATH, so a bare-name lookup fails even when ffmpeg is installed at/opt/homebrew/bin/ffmpeg. The same applies to ffprobe.extract_metadatareturnsdefault_metadata(all-NULL fields), so every cataloged file getsvideo_codec=NULL, audio_codec=NULL, container=NULL, metadata_json=NULL. The playability decision then falls back to an extension check, returns an untrustworthy "transcode required," and playback opens a doomed player that black-screens.What this PR does:
ffmpeg_resolvemodule — a single source of truth for locating ffmpeg/ffprobe, used by both capability display and the actual spawn. Resolution is literal (configured-absolute → PATH → Missing); it does not silently substitute a binary. A hardcoded binary-name allow-list (ffmpeg,ffprobe) prevents path-lookup abuse.Command::argsbypasses the shell, so source paths with spaces/brackets are passed verbatim (the "spaces in path" framing in the original report was a red herring — pinned by a test).PlaybackDecisiongainsanalysis_state: analyzed | awaiting_analysis. When a source file was never probed (metadata_json IS NULL),get_playback_decisionreturnsawaiting_analysisinstead of an untrustworthy "transcode required" — the client refuses to open a doomed player and surfaces a setup hint. This replaces an earlier, buggy client-side capabilities-cache preflight (single source of truth now lives on the server).reprobe_media_filesruns ffprobe over rows with NULLmetadata_jsonand writes only the codec/container/stream columns via a targeted UPDATE (independent of the scan path's hash-skip guard, which would otherwise skip content-unchanged files). Triggers: server startup (if ffprobe is now available and unprobed files exist), ffprobe settings change, and a manual "Re-probe media info" button in Settings. Progress surfaces in Dashboard → Activities. Resumable — a crash mid-re-probe simply resumes on the next start.TranscodeErrorBody { code, message, action }(e.g.transcode_executable_missingwithaction: open_settings) instead of a bare 500. The client surfaces these via the in-player overlay and the global banner (with a conditional "Open settings" button for actionable codes). Two inert seams (per-session error store + shared error-mapper) are laid for a deferred lifecycle/mid-stream-failure phase.POST /api/v1/system/tools/discover(admin),GET /api/v1/sessions/<id>/status,GET/POST /api/v1/system/tools/reprobe(admin).Tests: 340 passing (was ~290 baseline). New coverage: resolver resolution (hermetic, tempfile shims), transcode arg generation (incl. the spaces-in-path invariant),
probe_encodersparsing, structured error mapping, route integration (discover auth-gating, session-status 404). A real-ffmpeg smoke test sits behind an opt-inreal-ffmpeg-testsfeature flag. (TODO: I'll re-check this, AI claim here that I believe is not entirely correct)Design docs included (can be deleted, included by mistake originally):
docs/superpowers/specs/(the shipped phase + a fully-drafted deferred lifecycle discovery/plan),docs/refactoring/routes-module-split-plan.md(a planned, separately-delegated split of the 4.8k-lineroutes/media.rs).Out of scope (deliberately deferred): transcode mid-stream failure handling (first-byte race, mid-stream watcher — drafted in the lifecycle spec); the routes-module split; a planned DB-abstraction + actor-model refactor.
Screenshot
Settings → FFmpeg with the Detect table (versions, encoder tags, per-path validation) and the Re-probe button:
Issues Fixed or Closed
Roadmap Issues
Type of Change
Checklist
AI Usage