Found in code review of #637. Adjacent to what that issue fixed, and invisible to its test matrix.
Problem
#637 routes a source with no video stream to an audio-only conversion, gated on SourceVideo::Absent, which comes from MediaInfo::has_video.
But probe sets has_video = true for any stream of type video — including an attached_pic cover-art still (crates/rdlp-ffmpeg/src/ffmpeg/probe.rs:201-203). So an .m4a/.mp3/.flac that already carries embedded artwork is not SourceVideo::Absent. It classifies as a video source, can_remux_video answers about the mjpeg still as though it were the video track, and a transcode re-encodes a single cover image as a video stream.
Why #637's matrix didn't catch it
The fixture is built with ffmpeg -f lavfi -i sine ... -c:a aac — no cover art. Every container in that matrix is genuinely audio-only, so the Absent branch is always taken.
Why it isn't reachable through rdlp's own pipeline (today)
ThumbnailStage (index 7) runs after RecodeStage (index 4) — see crates/rdlp-api/src/orchestrator/mod.rs. So rdlp never embeds art before recoding.
It is reachable when the source file already has cover art, which is common:
- an audio file downloaded from a site that ships embedded artwork
- a local file passed to
process_local_file
- any second rdlp run over an already-thumbnailed output
Expected
An attached-picture stream should not count as video for routing purposes. AV_DISPOSITION_ATTACHED_PIC is the discriminator — MediaInfo would need to either exclude such streams from has_video or expose the disposition so SourceVideo::from_probe can.
Care needed: has_video is consumed elsewhere (thumbnail embedding genuinely cares that an attached pic exists), so this is likely a new field rather than a change to has_video's meaning — changing it in place risks breaking the embed paths that rely on it.
Acceptance
Refs #637
Found in code review of #637. Adjacent to what that issue fixed, and invisible to its test matrix.
Problem
#637routes a source with no video stream to an audio-only conversion, gated onSourceVideo::Absent, which comes fromMediaInfo::has_video.But
probesetshas_video = truefor any stream of type video — including anattached_piccover-art still (crates/rdlp-ffmpeg/src/ffmpeg/probe.rs:201-203). So an.m4a/.mp3/.flacthat already carries embedded artwork is notSourceVideo::Absent. It classifies as a video source,can_remux_videoanswers about the mjpeg still as though it were the video track, and a transcode re-encodes a single cover image as a video stream.Why #637's matrix didn't catch it
The fixture is built with
ffmpeg -f lavfi -i sine ... -c:a aac— no cover art. Every container in that matrix is genuinely audio-only, so theAbsentbranch is always taken.Why it isn't reachable through rdlp's own pipeline (today)
ThumbnailStage(index 7) runs afterRecodeStage(index 4) — seecrates/rdlp-api/src/orchestrator/mod.rs. So rdlp never embeds art before recoding.It is reachable when the source file already has cover art, which is common:
process_local_fileExpected
An attached-picture stream should not count as video for routing purposes.
AV_DISPOSITION_ATTACHED_PICis the discriminator —MediaInfowould need to either exclude such streams fromhas_videoor expose the disposition soSourceVideo::from_probecan.Care needed:
has_videois consumed elsewhere (thumbnail embedding genuinely cares that an attached pic exists), so this is likely a new field rather than a change tohas_video's meaning — changing it in place risks breaking the embed paths that rely on it.Acceptance
--recode-videoon an audio file with embedded cover art takes the audio-only route.has_video's existing consumers (thumbnail embed) are unaffected.Refs #637