What problem does this address?
The client-side GIF→video conversion works well for typical GIFs, but for large ones it can degrade badly. During testing on #80260, @andrewserong uploaded raw screen-recording GIFs (large pixel dimensions, ~20 MB) and hit conversions that appeared hung - one eventually finished after 30 minutes on a MacBook Air (comment, test GIFs).
As discussed in this comment and this one, the feature ceases to be useful once conversion takes longer than a "fairly quick" amount of time. The GIF itself uploads fine; the companion video is a progressive enhancement - so when conversion is expensive we should skip or abandon it rather than churn the user's CPU for minutes.
What is your proposed solution?
Add two guardrails so the conversion is seamless: fast when it can be, invisible when it can't.
1. Conversion timeout (default: 30 seconds)
If the GIF→video conversion has not completed within a timeout (default 30s), abandon it and keep only the uploaded GIF:
- Cancel the in-flight conversion. The worker loop in
packages/video-conversion/src/index.ts already checks inProgressOperations at every async boundary (including per frame), so an existing cancellation path can be reused.
- Silently cancel the companion-video sideload, exactly like the existing "unsupported" graceful-skip path in
convertGifToVideoOperation (packages/upload-media/src/store/private-actions.ts). No user-facing error - from the user's perspective they uploaded an image and it worked.
- Log the skip to the console for debuggability, following the existing unconditional
console.error( '[video-conversion] …' ) pattern used for real engine failures in the same catch block (client-side code has no WP_DEBUG signal; the plain-console pattern is the established precedent here).
The timeout should be filterable/configurable so hosts or plugins can raise it (e.g. 0 to disable).
2. Maximum size gate (don't even try)
Before running the encode loop, check whether the GIF is plausibly convertible in a reasonable time. The cost is roughly proportional to width × height × frameCount (total decoded pixels), so a single pixel-budget threshold covers all three dimensions.
This can be checked cheaply: in packages/video-conversion/src/index.ts, frameCount and the frame dimensions are available right after decoder.tracks.ready resolves, before any frame is decoded or encoded. If the budget is exceeded:
- Throw the existing
Unsupported-prefixed error (or a new dedicated prefix) so the caller takes the graceful-skip path - GIF stays as-is, no companion video, no user-facing error.
- Log a console warning explaining the skip and the threshold, so developers testing large GIFs understand why no video was produced.
The default budget needs benchmarking against real-world GIFs (Andrew's test files above are a good starting corpus), but the goal is: anything that would plausibly blow the 30s timeout on modest hardware shouldn't start at all. Like the timeout, the budget should be filterable.
Interaction with related work
Step-by-step reproduction instructions
- Enable client-side media processing.
- Upload a large raw screen-recording GIF, e.g.
2026-07-10 14.44.24.gif from this comment.
- Observe the conversion runs for many minutes (up to ~30 minutes on a MacBook Air) with the upload appearing hung.
Expected with this enhancement: the GIF uploads normally; the video conversion either finishes within ~30s or is skipped/abandoned silently, with a console message explaining why.
What problem does this address?
The client-side GIF→video conversion works well for typical GIFs, but for large ones it can degrade badly. During testing on #80260, @andrewserong uploaded raw screen-recording GIFs (large pixel dimensions, ~20 MB) and hit conversions that appeared hung - one eventually finished after 30 minutes on a MacBook Air (comment, test GIFs).
As discussed in this comment and this one, the feature ceases to be useful once conversion takes longer than a "fairly quick" amount of time. The GIF itself uploads fine; the companion video is a progressive enhancement - so when conversion is expensive we should skip or abandon it rather than churn the user's CPU for minutes.
What is your proposed solution?
Add two guardrails so the conversion is seamless: fast when it can be, invisible when it can't.
1. Conversion timeout (default: 30 seconds)
If the GIF→video conversion has not completed within a timeout (default 30s), abandon it and keep only the uploaded GIF:
packages/video-conversion/src/index.tsalready checksinProgressOperationsat every async boundary (including per frame), so an existing cancellation path can be reused.convertGifToVideoOperation(packages/upload-media/src/store/private-actions.ts). No user-facing error - from the user's perspective they uploaded an image and it worked.console.error( '[video-conversion] …' )pattern used for real engine failures in the same catch block (client-side code has noWP_DEBUGsignal; the plain-console pattern is the established precedent here).The timeout should be filterable/configurable so hosts or plugins can raise it (e.g.
0to disable).2. Maximum size gate (don't even try)
Before running the encode loop, check whether the GIF is plausibly convertible in a reasonable time. The cost is roughly proportional to width × height × frameCount (total decoded pixels), so a single pixel-budget threshold covers all three dimensions.
This can be checked cheaply: in
packages/video-conversion/src/index.ts,frameCountand the frame dimensions are available right afterdecoder.tracks.readyresolves, before any frame is decoded or encoded. If the budget is exceeded:Unsupported-prefixed error (or a new dedicated prefix) so the caller takes the graceful-skip path - GIF stays as-is, no companion video, no user-facing error.The default budget needs benchmarking against real-world GIFs (Andrew's test files above are a good starting corpus), but the goal is: anything that would plausibly blow the 30s timeout on modest hardware shouldn't start at all. Like the timeout, the budget should be filterable.
Interaction with related work
resizeImage, making a wedged worker recoverable via an RPC-layer timeout, and passingmaxDimensionstoconvertGifToVideoso large GIFs downscale below encoder limits. Those are worth evaluating alongside this - the RPC-layer timeout in particular could be the mechanism that implements guardrail 1 while also making a wedged worker recoverable for everything queued behind it (see also Upload queue: a vips worker failure leaves uploads hanging forever (worker RPC promises never settle) #79953).Step-by-step reproduction instructions
2026-07-10 14.44.24.giffrom this comment.Expected with this enhancement: the GIF uploads normally; the video conversion either finishes within ~30s or is skipped/abandoned silently, with a console message explaining why.