import { createHash } from 'node:crypto'; import { readFile } from 'node:fs/promises'; import { dirname, join, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; const repositoryRoot = join(dirname(fileURLToPath(import.meta.url)), '..'); const expectedVersion = '0.12.10'; const outputRoot = join( repositoryRoot, 'public', 'vendor', 'ffmpeg', expectedVersion ); const manifestPath = join(outputRoot, 'version.json'); const manifest = JSON.parse(await readFile(manifestPath, 'utf8')); if ( manifest.schemaVersion !== 1 || manifest.coreVersion !== expectedVersion || !Array.isArray(manifest.assets) || manifest.assets.length !== 5 ) { throw new Error(`Invalid FFmpeg asset manifest: ${manifestPath}`); } const expectedPaths = new Set([ 'mt/ffmpeg-core.js', 'mt/ffmpeg-core.wasm', 'mt/ffmpeg-core.worker.js', 'st/ffmpeg-core.js', 'st/ffmpeg-core.wasm', ]); const expectedHashes = new Map([ [ 'st/ffmpeg-core.js', '67a48f11645f85439f3fde4f2119042c16b374b910206b7a7a24f342e28dcae3', ], [ 'st/ffmpeg-core.wasm', '9f57947a5bd530d8f00c5b3f2cb2a3492faa7e5d823315342d6a8656d0a6b7b7', ], [ 'mt/ffmpeg-core.js', '270a2e6ff945e173238610669a3f7132df5f9c52698a9bf708cf5c2ab6bda0de', ], [ 'mt/ffmpeg-core.wasm', 'be2c97605366b78f3f13e21b52e81a55a79e1f29c133b03a68ec187b1a2ec41a', ], [ 'mt/ffmpeg-core.worker.js', 'f77898d631dc010b45c29c23cb4379c611a7d7b131bf591d08a656bb729a4ca3', ], ]); for (const asset of manifest.assets) { if (!expectedPaths.delete(asset.path)) { throw new Error(`Unexpected or duplicate FFmpeg asset: ${asset.path}`); } const bytes = await readFile(join(outputRoot, asset.path)); const digest = createHash('sha256').update(bytes).digest('hex'); if ( asset.bytes !== bytes.byteLength || asset.sha256 !== digest || expectedHashes.get(asset.path) !== digest ) { throw new Error(`FFmpeg asset verification failed: ${asset.path}`); } } if (expectedPaths.size > 0) { throw new Error( `FFmpeg assets are missing: ${[...expectedPaths].join(', ')}` ); } console.log( `Verified 5 FFmpeg ${expectedVersion} assets in ${relative(repositoryRoot, outputRoot)}` );