Video import + free-run preview: Source Video plays on the Screen (source-media issue 04) - #15
Conversation
…urce-media issue 04) Drop/pick/paste a video like an image: import gates on file capability (mediabunny getPrimaryVideoTrack), records the canDecode() verdict, and rejects non-media with a toast. Preview is an off-DOM muted looping <video> feeding a VideoTexture, with requestVideoFrameCallback driving invalidate() per presented frame (ADR-0009) — pause freezes the frame and renders zero GL frames. Oversized sources downscale per-frame onto a canvas clamped to the texture cap; mipmaps stay off. Fit/crop and screen stylization apply through the existing Screen material path.
…ardening Address code-review findings on issue 04: use mediabunny's InputVideoTrack type instead of an inferred Awaited<ReturnType<...>>, say "image or video" in upload chrome (CONTEXT.md avoids bare "media"), seed the oversized canvas on loadeddata so a rejected autoplay still shows the first frame, and time out a wedged metadata probe so the import fails with the toast instead of hanging and leaking the object URL.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 65ff8ca. Configure here.
| export function useSourceMediaTexture(media: SourceMedia | null): ResolvedSourceMedia { | ||
| const image = useSourceImageTexture(media) | ||
| const video = useSourceVideoTexture(media) | ||
| const loaded = image ?? video |
There was a problem hiding this comment.
Stale media shown crossing kind switch
Medium Severity
useSourceMediaTexture picks the displayed pair with image ?? video, so when media.kind changes the inactive hook can still return its previous loaded state until a useEffect clears it. The screen then samples the wrong kind (e.g. video after an image replace) and displayedSourceAspect can follow that stale pair.
Reviewed by Cursor Bugbot for commit 65ff8ca. Configure here.
The wrapper merged the two kind hooks with `image ?? video`, so on the render right after a cross-kind replace (or a clear) the previous kind's hook still returned its stale pair and the Screen sampled it — with displayedSourceAspect following. Gate the pick on the bound media's kind (exhaustive switch): same-kind replacements still hold the previous pair while the new file decodes, but a kind switch falls to the placeholder until the new kind resolves. (Bugbot finding on PR #15.)


Summary
track.canDecode()verdict is recorded on the Source Video record (a playable-but-undecodable file still imports). Non-media files reject with a toast and leave the editor untouched.<video>feeds aVideoTexture, withrequestVideoFrameCallbackdriving oneinvalidate()per presented frame — neverframeloop="always". Pause (new ephemeralvideoPreviewPlayingflag + a chip beside the replace control) freezes the frame and renders zero GL frames; a fresh bind always autoplays.min(GPU limit, 4096)instead of being rejected; mipmaps stay off on the video texture. Fit/crop and screen stylization apply through the existing Screen material path via the newuseSourceMediaTexturewrapper (which now owns the placeholder fallback and thedisplayedSourceAspectpublish for both kinds).Verification
.scratch/source-media/evidence/04/(untracked): autoplay on the Device Screen; paused ⇒ 0 draw calls over 2 s; Cover + crop zoom and Pixels/CRT stylization visibly applied to the moving video; rejection toast for a text file; a generated 4320×2160 video previewing through the clamped-canvas path; still PNG export completing with playback still running.Test plan
Note
Medium Risk
New async import and GPU texture lifecycle for video (object URLs, rVFC, oversized canvas path) touch core preview rendering; document/undo paths are unchanged.
Overview
Adds Source Video alongside images: drag, paste, and file picker accept video through the same flow as images, with capability-based import (mediabunny + readable video track,
canDecoderecorded on the record) and a toast when a file isn’t usable media.Scene:
useSourceMediaTextureroutes image vs video, keeps the placeholder anddisplayedSourceAspectpublish; newuseSourceVideoTexturedrives preview with a muted looping<video>,VideoTexture(or per-frame canvas when oversized), andrequestVideoFrameCallback→invalidate()under demand rendering (ADR-0009). Pause uses ephemeralvideoPreviewPlaying; binding media resets autoplay.UI:
SourceMediaDropzonereplaces the image-only dropzone; a play/pause chip appears next to replace when a video is bound.Reviewed by Cursor Bugbot for commit 65ff8ca. Bugbot is set up for automated code reviews on this repo. Configure here.