Repository navigation
fix(export): unknown durations, stuck-progress watchdog, e2e export tests - #1832
Merged
Merged
Conversation
Production (Netlify) is cross-origin isolated, so every browser loads the multi-threaded core. With FFmpeg choosing its own thread count, core-mt's 32-worker pool deadlocks and every export sits at "Exporting 100%" forever. Reproduced locally with COOP/COEP for MP4, WebM, portrait and no-audio inputs; the single-threaded core was unaffected. Ports the export hardening from the earlier branch (33b2c5a, bf26cf1): - src/lib/ffmpegArgs.ts: one command builder used by the worker (the old copy in ffmpeg.ts was dead code); decoder, filter and encoder threads capped at 4 on the multi-threaded core only - stall watchdog with worker heartbeats: 60 s without activity fails the export with ExportStalledError, and later attempts use the single-threaded core - WebM uses VP8 + Vorbis: libvpx-vp9 and libopus trap in @ffmpeg/core 0.12.10 - SRI hashes for the multi-threaded core files; falls back to the single-threaded core if core-mt fails to load - id-less worker errors, cancel during engine load, and string rejections from wasm traps no longer leave the editor stuck Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ests - Live-recorded files (MediaRecorder screen recordings) report an Infinity duration; seek to the end to find the real one (src/lib/mediaDuration.ts), in the editor and the export. Trim works on them again. - ffmpegArgs: time values are repaired before use (withSafeTimes), -t only for a finite positive length, -shortest for looped music when the length is unknown, and a final check that no argument carries Infinity or NaN. An infinite speed no longer hangs the audio filter builder. - Watchdog: besides 60 s of silence, stop exports whose encoded time stops changing for 120 s while FFmpeg keeps logging (stuck at 100%), or that run far past the expected length. The worker reports encoded time and computes progress from it. The message says what happened and to press Retry. - Telemetry: unknown_duration failure category and stall_reason. - scripts/make-fixtures.sh (test/fixtures/, gitignored), a static server with COOP/COEP (scripts/serve-isolated.mjs), Playwright export tests on the multi- and single-threaded engines, and a Tests workflow running Vitest and Playwright in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read default 2 - Threads per stage are min(4, navigator.hardwareConcurrency || 2); the fallback for an unreported core count was 4. - When the multi-threaded engine stalls, exportVideo retries the same export (same file and edits) once on the single-threaded engine instead of failing. The overlay says so, and RUM gets engine_fallback with the stall reason; the attempt's single outcome then carries engine "single" and fell_back. - A single-threaded stall, or a stall after cancelling, is not retried; the error tells the user to press Retry. - The editor passes the duration it already read to the export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
They are untracked local copies (names ending in " 2"), not project files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion-e2e Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # docs/DATADOG.md # src/lib/ffmpeg.ts # src/lib/tests/exportTelemetry.test.ts
export_cancelled carries stuck: true when the progress percentage hadn't changed for 60 s, so monitors can count a user giving up on a hang as a failure instead of an ordinary cancellation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
👋 Thanks for your PR, @magic-peach!Welcome to Reframe — a browser-based video editor built for everyone 🎬 What happens next
Quick checklist
Useful links
Happy coding! 🎉 |
Contributor
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No content change: this branch already contains #1831's commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ Deploy Preview for reframe-os ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
magic-peach
marked this pull request as ready for review
November 3, 2026 16:02
Contributor
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
magic-peach
added a commit
that referenced
this pull request
Nov 3, 2026
No content change: main's tree equals 8e24b37, which this branch builds on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Stacked on #1831. It targets
fix/mt-export-hang, so the diff shows only this work. Merge #1831 first, then change this PR's base tomain.A correction: the earlier diagnosis was wrong
I said earlier that production exports hang because
getVideoDurationreturnsInfinityand the export gets-t Infinity. That was wrong. The worker only adds-twhen the speed isn't 1×, and the hangs I saw were at 1×.The real cause is that the multi-threaded FFmpeg core deadlocks when FFmpeg picks its own thread count. Production is cross-origin isolated, so every visitor loaded that core and every export hung, normal MP4s included. Locally, all four fixtures hang with COOP/COEP and finish without it. #1831 fixes this with a thread cap, plus a watchdog that falls back to the single-threaded core.
This PR fixes a separate bug. Browser-recorded files such as screen recordings have no duration in their header, so
<video>.durationisInfinity. The editor turned that into 0:-t;-t Infinity.None of this caused the production hang.
What changes
Durations
src/lib/mediaDuration.ts: when the duration isInfinity, seek past the end so the browser works out the real length. Used by the editor and the export.src/lib/ffmpegArgs.ts:withSafeTimesrepairs a non-finite or negative trim start, a trim end that isn't after the start, and an unusable speed before any filter is built.-tis added only for a finite, positive length.-shortestis added when looped music would otherwise never end.assertFiniteArgsthrows if any argument still containsInfinityorNaN.-t, and text and overlay positions. They all go through these builders. FPS isn't set anywhere.Stalls
Telemetry
unknown_duration: a clip whose length couldn't be found that then stalled or failed.export_failed.stall_reason:no_activity,no_progressoroverrun.export_cancelled.stuck: true when the user cancelled after 60 s with no progress. Monitors count these as failures.docs/DATADOG.md.Monitoring (already live in Datadog, not part of the diff)
340168191, "Exports start but none succeed": more than 3export_startedand 0export_succeededon production over 6 h. Email only.340137974now fires on exports not completing: started − succeeded − ordinary cancellations, over 1 h (more than 10, warning at 5). That covers failures, stuck cancellations and abandoned exports.export_failed, only cancellations or nothing at all. Note that builds before feat(telemetry): export reliability by browser and device #1829 used older event names, so these monitors only see traffic from feat(telemetry): export reliability by browser and device #1829 onward.Tests and CI
scripts/make-fixtures.shmakes the fixtures intest/fixtures/(gitignored), all with the ffmpeg CLI:-live 1, the MediaRecorder layout);scripts/serve-isolated.mjsservesout/with or without COOP/COEP.test/e2e/export.spec.ts(Playwright) runs on both the multi- and single-threaded engines:Testsworkflow: Vitest, plus Playwright in headless Chromium withffmpegfrom apt.maindidn't run unit tests in CI before.resolveDuration, the watchdog's three rules, no false stall without time reports,unknown_duration/stall_reason/stuck, and the hook passing its known duration.Checks run locally
E2E_LARGE=1, multi-threaded) also passed: trim, rotate, 2× and export in 22.9 s.NEXT_PUBLIC_DATADOG_ENV=off.