UNPKG

@wordpress/upload-media

Version:
8 lines (7 loc) 6.47 kB
{ "version": 3, "sources": ["../../../src/store/utils/video-conversion.ts"], "sourcesContent": ["/**\n * Internal dependencies\n */\nimport { getFileBasename } from '../../utils';\nimport type { QueueItemId } from '../types';\n\n/**\n * Message prefix used by @wordpress/video-conversion to flag an\n * unsupported-but-graceful conversion outcome (no WebCodecs, unsupported\n * codec). This MUST mirror the package's exported `UNSUPPORTED_ERROR_PREFIX`.\n *\n * It is duplicated here rather than imported because the only path that\n * carries the constant (`@wordpress/video-conversion`) statically pulls the\n * heavy encoder library into the main bundle, defeating the lazy worker\n * load. The worker RPC layer (comctx) also serializes a thrown error to its\n * `message` string only, so the cross-boundary contract is inherently a\n * message prefix. `isUnsupportedConversionError` is unit-tested against the\n * exact strings the worker throws to catch any drift.\n */\nconst UNSUPPORTED_ERROR_PREFIX = 'Unsupported';\n\n/**\n * Whether an error from GIF-to-video conversion represents an\n * unsupported-but-graceful outcome (caller should fall back to uploading the\n * original GIF) rather than a hard failure.\n *\n * @param error Error thrown by `convertGifToVideo`.\n * @return Whether the error is a graceful \"unsupported\" outcome.\n */\nexport function isUnsupportedConversionError( error: unknown ): boolean {\n\treturn (\n\t\terror instanceof Error &&\n\t\terror.message.startsWith( UNSUPPORTED_ERROR_PREFIX )\n\t);\n}\n\n/**\n * Cached dynamic import promise for @wordpress/video-conversion/worker.\n *\n * Using a dynamic import keeps the worker module out of the main bundle; it\n * is fetched only when GIF-to-video conversion is actually triggered.\n */\nlet videoConversionModulePromise:\n\t| Promise< typeof import('@wordpress/video-conversion/worker') >\n\t| undefined;\n\n/**\n * The resolved module reference, available synchronously after first load.\n */\nlet videoConversionModule:\n\t| typeof import('@wordpress/video-conversion/worker')\n\t| undefined;\n\n/**\n * Lazily loads and caches the @wordpress/video-conversion/worker module.\n *\n * @return The video conversion worker module.\n */\nfunction loadVideoConversionModule(): Promise<\n\ttypeof import('@wordpress/video-conversion/worker')\n> {\n\tif ( ! videoConversionModulePromise ) {\n\t\tvideoConversionModulePromise = import(\n\t\t\t'@wordpress/video-conversion/worker'\n\t\t)\n\t\t\t.then( ( mod ) => {\n\t\t\t\tvideoConversionModule = mod;\n\t\t\t\treturn mod;\n\t\t\t} )\n\t\t\t.catch( ( error ) => {\n\t\t\t\t/*\n\t\t\t\t * Reset the cached promise so a transient chunk-load failure\n\t\t\t\t * does not permanently break later conversions; the next call\n\t\t\t\t * retries the import.\n\t\t\t\t */\n\t\t\t\tvideoConversionModulePromise = undefined;\n\t\t\t\tthrow error;\n\t\t\t} );\n\t}\n\treturn videoConversionModulePromise;\n}\n\n/**\n * Converts an animated GIF to a video file using the video conversion worker.\n *\n * @param id Queue item ID.\n * @param file GIF file object.\n * @param outputMimeType Output MIME type ('video/mp4' or 'video/webm').\n * @param maxDimensions Optional maximum dimension for downscaling.\n * @return Converted video file.\n */\nexport async function convertGifToVideo(\n\tid: QueueItemId,\n\tfile: File,\n\toutputMimeType: string,\n\tmaxDimensions?: number\n) {\n\tconst { convertGifToVideo: convert } = await loadVideoConversionModule();\n\t// Pass the File straight through: the worker reads its bytes once, off\n\t// the main thread, instead of materializing an ArrayBuffer here.\n\tconst buffer = await convert( id, file, outputMimeType, maxDimensions );\n\n\tconst ext = outputMimeType === 'video/webm' ? 'webm' : 'mp4';\n\tconst fileName = `${ getFileBasename( file.name ) }.${ ext }`;\n\treturn new File(\n\t\t[ new Blob( [ buffer as ArrayBuffer ], { type: outputMimeType } ) ],\n\t\tfileName,\n\t\t{ type: outputMimeType }\n\t);\n}\n\n/**\n * Cancels all ongoing GIF-to-video conversions for the given item.\n *\n * @param id Queue item ID to cancel operations for.\n * @return Whether any operation was cancelled.\n */\nexport async function cancelGifToVideoOperations( id: QueueItemId ) {\n\t/*\n\t * Resolve the worker even if it is still loading so a cancel issued during\n\t * the initial lazy-load window is not silently dropped (which would let the\n\t * conversion keep running for an item the caller already cancelled).\n\t */\n\tconst mod =\n\t\tvideoConversionModule ??\n\t\t( videoConversionModulePromise\n\t\t\t? await videoConversionModulePromise.catch( () => undefined )\n\t\t\t: undefined );\n\tif ( ! mod ) {\n\t\treturn false;\n\t}\n\treturn mod.cancelGifToVideoOperations( id );\n}\n\n/**\n * Terminates the video conversion worker if it has been loaded.\n */\nexport function terminateVideoConversionWorker(): void {\n\tif ( videoConversionModule ) {\n\t\tvideoConversionModule.terminateVideoConversionWorker();\n\t\treturn;\n\t}\n\t/*\n\t * The worker is still loading. Terminate it once the import resolves so a\n\t * teardown issued during the lazy-load window is honored rather than\n\t * leaving the worker resident.\n\t */\n\tif ( videoConversionModulePromise ) {\n\t\tvoid videoConversionModulePromise\n\t\t\t.then( ( mod ) => mod.terminateVideoConversionWorker() )\n\t\t\t.catch( () => {} );\n\t}\n}\n"], "mappings": ";AAGA,SAAS,uBAAuB;AAgBhC,IAAM,2BAA2B;AAU1B,SAAS,6BAA8B,OAA0B;AACvE,SACC,iBAAiB,SACjB,MAAM,QAAQ,WAAY,wBAAyB;AAErD;AAQA,IAAI;AAOJ,IAAI;AASJ,SAAS,4BAEP;AACD,MAAK,CAAE,8BAA+B;AACrC,mCAA+B,OAC9B,oCACD,EACE,KAAM,CAAE,QAAS;AACjB,8BAAwB;AACxB,aAAO;AAAA,IACR,CAAE,EACD,MAAO,CAAE,UAAW;AAMpB,qCAA+B;AAC/B,YAAM;AAAA,IACP,CAAE;AAAA,EACJ;AACA,SAAO;AACR;AAWA,eAAsB,kBACrB,IACA,MACA,gBACA,eACC;AACD,QAAM,EAAE,mBAAmB,QAAQ,IAAI,MAAM,0BAA0B;AAGvE,QAAM,SAAS,MAAM,QAAS,IAAI,MAAM,gBAAgB,aAAc;AAEtE,QAAM,MAAM,mBAAmB,eAAe,SAAS;AACvD,QAAM,WAAW,GAAI,gBAAiB,KAAK,IAAK,CAAE,IAAK,GAAI;AAC3D,SAAO,IAAI;AAAA,IACV,CAAE,IAAI,KAAM,CAAE,MAAsB,GAAG,EAAE,MAAM,eAAe,CAAE,CAAE;AAAA,IAClE;AAAA,IACA,EAAE,MAAM,eAAe;AAAA,EACxB;AACD;AAQA,eAAsB,2BAA4B,IAAkB;AAMnE,QAAM,MACL,0BACE,+BACC,MAAM,6BAA6B,MAAO,MAAM,MAAU,IAC1D;AACJ,MAAK,CAAE,KAAM;AACZ,WAAO;AAAA,EACR;AACA,SAAO,IAAI,2BAA4B,EAAG;AAC3C;AAKO,SAAS,iCAAuC;AACtD,MAAK,uBAAwB;AAC5B,0BAAsB,+BAA+B;AACrD;AAAA,EACD;AAMA,MAAK,8BAA+B;AACnC,SAAK,6BACH,KAAM,CAAE,QAAS,IAAI,+BAA+B,CAAE,EACtD,MAAO,MAAM;AAAA,IAAC,CAAE;AAAA,EACnB;AACD;", "names": [] }