Move large example public assets to remotion.media#7481
Conversation
Delete unreferenced public files (long-video, spiral_, video.mp4) and host the heaviest referenced fixtures on R2 instead of in the repo. Point example and example-videos at https://remotion.media URLs. Run `bun run upload-public-to-r2` in packages/example with R2 credentials before merging so CI can fetch the assets. Co-authored-by: Cursor <[email protected]>
There was a problem hiding this comment.
Caution
The upload script can't be run from this branch as documented — the files it reads from packages/example/public/ are deleted in the same commit. And exampleVideos.bigBuckBunny / corrupted / iphonevideo / variablefps / iphonehevc / stretchedVp8 / music silently change from local-filesystem paths to https:// URLs, breaking tests across media-parser, renderer, webcodecs, and it-tests that consumed them as paths.
TL;DR — Moves the largest fixtures out of packages/example/public/ and into R2 behind https://remotion.media/..., exposes them via a new remoteExampleMedia map in @remotion/example-videos, and updates every Studio composition that referenced them via staticFile(). The diff also reroutes the existing exampleVideos keys to the new URLs, which has downstream consequences not covered by the PR.
Key changes
- Add
remoteExampleMediaandREMOTION_MEDIA_URL— new map in@remotion/example-videos/src/remote-example-media.tsthat hard-codes URLs forbigBuckBunny,corrupted,iphonevideo,iphonehevc,variablefps,stretchedVp8,vid1,audio16khz,music, andsounds/1–9. - Repoint
exampleVideos.*to the new URLs — same keys in the existingexampleVideosmap now resolve toremoteExampleMediastrings instead of local paths underpackages/example/public/. - Migrate
packages/exampleStudio compositions — replacesstaticFile('bigbuckbunny.mp4')etc. withremoteExampleMedia.bigBuckBunny(via a smallexample-media-urls.tsre-export) across 18 Studio compositions andRoot.tsx. - Delete the migrated binaries from
packages/example/public/, plus 3 unreferenced ones (long-video.mp4,spiral_.mp4,video.mp4). - Add
upload-public-to-r2.ts— a Bun-based S3 client that uploads the same set frompackages/example/public/to theparser-mediaR2 bucket.
Summary | 45 files | 1 commit | base: main ← move-example-public-to-r2
Upload script is unrunnable from this branch as documented
Before: Files lived in
packages/example/public/.
After: Same commit deletes them and adds an upload script that reads them frompackages/example/public/.
The PR body instructs maintainers to cd packages/example && bun run upload-public-to-r2 before merging. But upload-public-to-r2.ts reads from path.join(import.meta.dir, 'public') and the binaries it iterates over (bigbuckbunny.mp4, music.mp3, sounds/*.wav, …) are deleted in this same commit, so statSync(localPath).isFile() throws ENOENT on the first file. Either split the upload into a pre-merge commit while the files still exist, restore the files from git show main:packages/example/public/<file> inside the script, or document the git checkout main -- packages/example/public workaround.
exampleVideos.* semantics silently change from path to URL
Before:
exampleVideos.bigBuckBunnywaspath.join(examplePackage, 'public', 'bigbuckbunny.mp4')— an absolute local file path consumed directly by test code.
After: It is'https://remotion.media/bigbuckbunny.mp4', a remote URL.
The repo already has a clear contract for remote test fixtures: getRemoteExampleVideo() in remote-videos.ts downloads the URL into node_modules/.videos and returns a local path. By contrast, this PR keeps the property names in exampleVideos identical but quietly changes their type from "local path" to "URL", which breaks consumers that string-manipulate or filesystem-touch them. Concrete fallout:
packages/renderer/src/test/extract-audio.test.ts:7,20—audioOutput = exampleVideos.bigBuckBunny.replace('bigbuckbunny.mp4', 'bigbuckbunny.aac')now resolves tohttps://remotion.media/bigbuckbunny.aac, which is then passed toextractAudio({audioOutput})andunlinkSync(audioOutput). Both fail on a URL string.packages/renderer/src/test/extract-frame-rust.test.ts,rust-memory-usage.test.ts— passexampleVideos.bigBuckBunny/corrupted/iphonevideo/variablefpsas bothsrcandoriginal_srcto the Rust compositor. The compositor accepts URLs, but every run now downloads ~14 MB+ from R2 instead of reading a local file, andexpect(data.length).toBe(2764854)style byte-exact assertions are now dependent on network behavior and HTTP-range support.packages/media-parser/src/test/**— manyparseMedia({src: exampleVideos.bigBuckBunny | music | stretchedVp8 | iphonevideo | iphonehevc})callsites now hit the network on every test run.packages/it-tests/src/rendering/bundle-renderer.test.ts:67,71— passes the URL as anexecSyncargv to a Node script; the Node script's behavior on a URL vs path depends on its internals.
Either keep exampleVideos.bigBuckBunny resolving to a local path (e.g. lazily download via getRemoteExampleVideo-style caching the way remoteExampleVideos already does) and only expose the raw URL under a new namespace like remoteExampleMedia for in-browser/Studio use, or update every test consumer in this PR and explicitly accept the network dependency.
packages/example-videos/src/index.ts · packages/example-videos/src/remote-example-media.ts
Indirection through packages/example/src/example-media-urls.ts
Before: Studio code imported
staticFilefromremotion.
After: Studio code importsremoteExampleMediafrom a local one-line re-export of@remotion/example-videos.
packages/example/src/example-media-urls.ts only does export {remoteExampleMedia} from '@remotion/example-videos';. Worth inlining the import directly — every consumer already lists the dep transitively, and the indirection adds a hop without earning anything (no rebinding, no narrowing).
Claude Opus | 𝕏
| for (const key of FILES_TO_UPLOAD) { | ||
| const localPath = path.join(publicDir, key); | ||
| if (!statSync(localPath).isFile()) { | ||
| throw new Error(`Missing file: ${localPath}`); |
There was a problem hiding this comment.
This script is unrunnable from this branch — the same commit that adds it deletes bigbuckbunny.mp4, music.mp3, sounds/*.wav, etc. from packages/example/public/, so statSync(localPath).isFile() throws ENOENT on the first file. Either land the upload as a separate prior commit while the files still exist, or have the script restore the files from the parent commit (e.g. git show main:packages/example/public/<file>).
| const localPath = path.join(publicDir, key); | ||
| if (!statSync(localPath).isFile()) { |
There was a problem hiding this comment.
statSync throws when the path doesn't exist, so the if (!isFile()) branch is unreachable — a missing file produces ENOENT instead of the friendlier Missing file: message. Use existsSync(localPath) (or statSync(localPath, {throwIfNoEntry: false})) before the isFile() check.
| const localPath = path.join(publicDir, key); | |
| if (!statSync(localPath).isFile()) { | |
| if (!existsSync(localPath) || !statSync(localPath).isFile()) { | |
| throw new Error(`Missing file: ${localPath}`); | |
| } |
| @@ -43,15 +44,15 @@ export const exampleVideos = { | |||
| 'transparent-with-dar.webm', | |||
| ), | |||
| prores: path.join(examplePackage, 'public', 'prores.mov'), | |||
| iphonehevc: path.join(examplePackage, 'public', 'iphone-hevc.mov'), | |||
| iphonehevc: remoteExampleMedia.iphonehevc, | |||
There was a problem hiding this comment.
Repointing existing exampleVideos.* keys from local paths to URLs is a silent semantic break for every test that consumes them as filesystem paths (e.g. packages/renderer/src/test/extract-audio.test.ts does .replace('bigbuckbunny.mp4', 'bigbuckbunny.aac') and feeds the result to unlinkSync). Either keep these resolving to a local path (lazy download into node_modules/.videos the way getRemoteExampleVideo already does for remoteExampleVideos) and only expose the raw URL under remoteExampleMedia, or update the test consumers in this PR.
| sounds: { | ||
| '1': `${REMOTION_MEDIA_URL}/sounds/1.wav`, | ||
| '2': `${REMOTION_MEDIA_URL}/sounds/2.wav`, | ||
| '3': `${REMOTION_MEDIA_URL}/sounds/3.wav`, | ||
| '4': `${REMOTION_MEDIA_URL}/sounds/4.wav`, | ||
| '5': `${REMOTION_MEDIA_URL}/sounds/5.wav`, | ||
| '6': `${REMOTION_MEDIA_URL}/sounds/6.wav`, | ||
| '7': `${REMOTION_MEDIA_URL}/sounds/7.wav`, | ||
| '8': `${REMOTION_MEDIA_URL}/sounds/8.wav`, | ||
| '9': `${REMOTION_MEDIA_URL}/sounds/9.wav`, |
There was a problem hiding this comment.
Numeric-string keys on an object are awkward at the callsite (remoteExampleMedia.sounds['1']). Since the consumers always have a known fixed index, an array would read more naturally — sounds[1] — and the object spelling forces a quoted-key lookup at every callsite (see ComplexSounds/index.tsx and AudioTesting/Amplify.tsx).
| @@ -0,0 +1 @@ | |||
| export {remoteExampleMedia} from '@remotion/example-videos'; | |||
There was a problem hiding this comment.
This file only re-exports a single symbol. Importing remoteExampleMedia directly from @remotion/example-videos in each consumer is one fewer hop with no loss of locality, and removes a file that adds no behaviour.
Import browser-safe URLs from @remotion/example-videos/remote-example-media so bundling does not pull in node-only remote-videos code. Remove the one-time R2 upload script and fix example-videos formatting. Co-authored-by: Cursor <[email protected]>
Restore exampleVideos.* to filesystem paths under node_modules/.videos, downloaded from remotion.media during @remotion/example-videos make. Studio compositions keep using remoteExampleMedia URLs directly. Co-authored-by: Cursor <[email protected]>
tsgo typechecks all files under src/; top-level await in the download script failed CI. Run it from the package root instead. Co-authored-by: Cursor <[email protected]>
Use universalReader for HTTP example URLs, add example-videos subpath exports and cached media for Node tests. Co-authored-by: Cursor <[email protected]>

Summary
packages/example/public/(~14 MB):long-video.mp4,spiral_.mp4,video.mp4(code already usedhttps://remotion.media/video.mp4).sounds/1.wav–9.wavtohttps://remotion.media/...URLs via@remotion/example-videos(remoteExampleMedia).public/; folder is now ~35 MB).packages/example/upload-public-to-r2.ts(same R2 bucket/endpoint aspackages/remotion-media).Before merge (required for CI)
Upload the assets to R2 once (skips files that already exist with the same size):
cd packages/example AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... bun run upload-public-to-r2Uses the same credentials as
packages/remotion-mediabuild.Test plan
curl -I https://remotion.media/bigbuckbunny.mp4)bunx turbo @remotion/example#bundleMade with Cursor