Lazy-load mediabunny on web (#11094)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-07-08 16:21:16 +03:00
committed by GitHub
parent ea3d6827c7
commit b41624edea
3 changed files with 39 additions and 12 deletions
+17 -11
View File
@@ -1,17 +1,8 @@
import {type ImagePickerAsset} from 'expo-image-picker'
import {
ALL_FORMATS,
type AudioCodec,
BlobSource,
BufferTarget,
canEncodeAudio,
canEncodeVideo,
Conversion,
Input,
Mp4OutputFormat,
Output,
type Input as MediaBunnyInput,
type VideoCodec,
WebMOutputFormat,
} from 'mediabunny'
import {VIDEO_MAX_SIZE} from '#/lib/constants'
@@ -23,6 +14,7 @@ import {
COMPRESSION_MIN_SIZE_BYTES,
COMPRESSION_TARGET_BITRATE,
} from './constants'
import {loadMediaBunny} from './mediabunny'
import {type CompressedVideo, type ProbedMetadata} from './types'
// Codecs to try in order of preference
@@ -112,6 +104,7 @@ export async function compressVideo(
}
async function probeWithMediaBunny(blob: Blob): Promise<ProbedMetadata> {
const {ALL_FORMATS, BlobSource, Input} = await loadMediaBunny()
const input = new Input({source: new BlobSource(blob), formats: ALL_FORMATS})
try {
const videoTrack = await input.getPrimaryVideoTrack()
@@ -153,6 +146,7 @@ async function findEncodableVideoCodec(
width: number,
height: number,
): Promise<{codec: VideoCodec; useWebM: boolean} | null> {
const {canEncodeVideo} = await loadMediaBunny()
for (const codec of VIDEO_CODECS) {
const canEncode = await canEncodeVideo(codec, {
width,
@@ -179,9 +173,10 @@ const AUDIO_CODECS_MP4: AudioCodec[] = ['aac']
const AUDIO_CODECS_WEBM: AudioCodec[] = ['opus', 'vorbis']
async function findEncodableAudioCodec(
audioTrack: Awaited<ReturnType<Input['getPrimaryAudioTrack']>>,
audioTrack: Awaited<ReturnType<MediaBunnyInput['getPrimaryAudioTrack']>>,
useWebM: boolean,
): Promise<{codec: AudioCodec} | null> {
const {canEncodeAudio} = await loadMediaBunny()
if (!audioTrack) {
return null
}
@@ -228,6 +223,17 @@ async function doCompression(
): Promise<CompressedVideo> {
const {onProgress, signal} = opts
const {
ALL_FORMATS,
BlobSource,
BufferTarget,
Conversion,
Input,
Mp4OutputFormat,
Output,
WebMOutputFormat,
} = await loadMediaBunny()
const input = new Input({
source: new BlobSource(blob),
formats: ALL_FORMATS,
+20
View File
@@ -0,0 +1,20 @@
type MediaBunnyModule = typeof import('mediabunny')
let promise: Promise<MediaBunnyModule> | undefined
/**
* Lazily loads mediabunny into its own webpack chunk and caches the resulting
* promise so the module is fetched at most once. If the import fails (e.g. a
* transient chunk-load error), the cached promise is reset so a later call can
* retry the load.
*/
export function loadMediaBunny(): Promise<MediaBunnyModule> {
promise ??= import(/* webpackChunkName: "mediabunny" */ 'mediabunny').catch(
e => {
// reset so a later attempt can retry after a transient chunk-load failure
promise = undefined
throw e
},
)
return promise
}
+2 -1
View File
@@ -1,6 +1,6 @@
import {type ImagePickerAsset} from 'expo-image-picker'
import {ALL_FORMATS, BlobSource, Input} from 'mediabunny'
import {loadMediaBunny} from '#/lib/media/video/mediabunny'
import {logger} from '#/logger'
export function hasWebCodecs(): boolean {
@@ -55,6 +55,7 @@ async function getMetadataWithWebCodecs(
file: File,
blobUrl: string,
): Promise<ImagePickerAsset> {
const {ALL_FORMATS, BlobSource, Input} = await loadMediaBunny()
const input = new Input({
source: new BlobSource(file),
formats: ALL_FORMATS,