Repository navigation
fix(media-server): recover web recordings with damaged frames instead of failing them - #3527
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
| const corruptBytes = readFileSync(corrupt); | ||
| writeFileSync( | ||
| damagedTruncated, | ||
| corruptBytes.subarray(0, Math.floor(corruptBytes.length * 0.6)), | ||
| ); |
There was a problem hiding this comment.
Missing-duration inputs remain untested
The new truncation test uses a seekable WebM produced by ffmpeg. Removing its tail leaves the original duration in the header. This misses inputs without a stored duration: probeMedia falls back to computeDuration, so the recovery check can use the shortened upload as its reference. Add a damaged, truncated WebM without stored duration and check that recovery does not silently accept the missing tail.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/media-server/src/__tests__/lib/video-input-validation.integration.test.ts
Line: 101-105
Comment:
**Missing-duration inputs remain untested**
The new truncation test uses a seekable WebM produced by ffmpeg. Removing its tail leaves the original duration in the header. This misses inputs without a stored duration: `probeMedia` falls back to `computeDuration`, so the recovery check can use the shortened upload as its reference. Add a damaged, truncated WebM without stored duration and check that recovery does not silently accept the missing tail.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
hey @greptileai, please re-review the PR |
| const outputPath = processed.outputFile.path; | ||
| updateJob(jobId, { outputTempFile: processed.outputFile }); | ||
| if (options.importedOriginal) { | ||
| if (options.importedOriginal || recoveringDamagedSource) { |
There was a problem hiding this comment.
Truncated streamed inputs supply their own shortened recovery target
Medium Severity
The reference-duration defect is in probeMedia and probeWithRepairFallback: when a streamed WebM has no stored Duration, probing computes duration from the packets still present, or from the repaired container. For a recording with a damaged frame and a missing tail, that yields the shortened duration; transcoding the surviving packets can then satisfy this newly enabled completeness check. Previously strict validation rejected that damaged recording. The workflow now publishes it as complete, overwrites the browser-reported duration, and deletes the raw upload through cleanupRawUpload. Carry the independently recorded duration into recovery and compare against it rather than treating an observed or repaired duration as proof of completeness. The new streamed fixture is not truncated, while the truncated fixture retains its original header duration.
Full-length audio can mask a shortened recovered video track
Medium Severity
The output-duration defect is in probeMedia: its single container-level duration does not establish the recovered video track's endpoint. For example, if corruption prevents decoding the last ten seconds of a sixty-second video while its audio remains intact, the non-strict processVideo transcode can succeed with fifty seconds of video and sixty seconds of audio. The MP4 still reports sixty seconds, so this newly enabled check accepts it and the workflow deletes the raw source, despite losing substantially more video than the intended short keyframe freeze. Compare recovered video-track coverage against the source video timeline, and check audio coverage separately when required, rather than using the longest container duration.
|
hey @greptileai, please re-review the PR |
|
hey @greptileai, please re-review the PR |
Web recorder uploads (
raw-upload.webm) are checked with a strict ffmpeg decode (-xerror -err_detect explode) before conversion. That decode stops at the first bad packet, so a single damaged VP9 frame failed the whole recording, however long it was. The repair and resilient-transcode paths further down never ran. This PR keeps the check, but a damaged frame now triggers recovery instead of an outright failure.flowchart TD A[raw WebM] --> B{strict decode} B -- clean --> P[probe + transcode] B -- no video stream --> N["error: recording has no video"] B -- cannot open --> D["error: damaged or unreadable"] B -- decode errors --> R[probe + transcode with recovery] R --> C{output covers source duration?} C -- yes --> OK[publish] C -- no --> D R -- probe or transcode fails --> Dapps/media-server/src/lib/video-input-validation.ts:109validateVideoInputnow returns"clean" | "damaged"instead of throwing on every non-zero exit. It still throws when ffmpeg cannot open the input, and it throws a new, accurate message when the file has no video stream (:104). That happens when a browser recording stops before its first video frame. Those uploads used to be labelled "damaged".apps/media-server/src/routes/video.ts:1610A damaged source goes through the existing probe, repair and resilient transcode. Afterwards it gets the same duration check imported originals already use (isCompleteRecovery,:1680). If the recovered output falls short of the source duration, or if probing or transcoding fails (:1624), the job fails with the original "damaged or unreadable" message, so callers and the web app see no new error strings./video/processresultCases in
video-input-validation.integration.test.ts:damagednow completes through the route,damaged-truncated(a damaged frame plus a missing tail) is still rejected, andno-videois rejected with the new message before any transcode.

Confidence Score: 5/5
The PR appears safe to merge; the remaining test gap is non-blocking.Findings
Fix with agent prompt
Summary
Damaged WebM recordings can reach recovery instead of failing at the first bad frame.
Reviews (4) · Last reviewed commit: "test(media-server): cover recovered audi..." · Reviewed by Greptile