diff --git a/app/static/public/js/video.js b/app/static/public/js/video.js index 7a53ccb..694fa12 100644 --- a/app/static/public/js/video.js +++ b/app/static/public/js/video.js @@ -690,8 +690,29 @@ ffmpegLoading = false; } + function toStableUint8(input) { + if (input instanceof Uint8Array) { + return new Uint8Array(input); + } + if (input instanceof ArrayBuffer) { + return new Uint8Array(input.slice(0)); + } + if (ArrayBuffer.isView(input)) { + const view = input; + const start = view.byteOffset || 0; + const end = start + (view.byteLength || 0); + return new Uint8Array(view.buffer.slice(start, end)); + } + throw new Error('binary_input_invalid'); + } + + function toStableArrayBuffer(input) { + return toStableUint8(input).buffer; + } + async function sha256Hex(bytes) { - const hashBuffer = await crypto.subtle.digest('SHA-256', bytes); + const stableBytes = toStableUint8(bytes); + const hashBuffer = await crypto.subtle.digest('SHA-256', stableBytes); return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, '0')).join(''); } @@ -700,15 +721,17 @@ if (!resp.ok) { throw new Error(`fetch_failed_${resp.status}`); } - return await resp.arrayBuffer(); + const raw = await resp.arrayBuffer(); + return raw.slice(0); } async function ffmpegWriteFile(ff, name, data) { + const stable = toStableUint8(data); if (typeof ff.writeFile === 'function') { - return await ff.writeFile(name, new Uint8Array(data)); + return await ff.writeFile(name, stable); } if (ff.FS) { - ff.FS('writeFile', name, new Uint8Array(data)); + ff.FS('writeFile', name, stable); return; } throw new Error('ffmpeg_writefile_unsupported'); @@ -717,13 +740,11 @@ async function ffmpegReadFile(ff, name) { if (typeof ff.readFile === 'function') { const out = await ff.readFile(name); - const view = out instanceof Uint8Array ? out : new Uint8Array(out); - return new Uint8Array(view); + return toStableUint8(out); } if (ff.FS) { const out = ff.FS('readFile', name); - const view = out instanceof Uint8Array ? out : new Uint8Array(out); - return new Uint8Array(view); + return toStableUint8(out); } throw new Error('ffmpeg_readfile_unsupported'); } @@ -1453,11 +1474,12 @@ dataUrl: frameResult.dataUrl, frameHash: frameResult.frameHash, sourceHash, - sourceBuffer: srcBuffer, + sourceBuffer: toStableArrayBuffer(srcBuffer), }; } async function concatVideosLocal(sourceBuffer, generatedVideoUrl) { + const sourceStable = toStableArrayBuffer(sourceBuffer); const generatedBuffer = await fetchArrayBuffer(generatedVideoUrl); const trimSeconds = (Math.max(0, lockedTimestampMs) / 1000).toFixed(3); @@ -1471,7 +1493,7 @@ const segB = `${prefix}_b.mp4`; const listFile = `${prefix}_list.txt`; const mergedFile = `${prefix}_merged.mp4`; - await ffmpegWriteFile(ff, segASource, sourceBuffer); + await ffmpegWriteFile(ff, segASource, sourceStable); await ffmpegWriteFile(ff, segBSource, generatedBuffer); if (Number(trimSeconds) > 0) { await ffmpegExec( @@ -1504,7 +1526,7 @@ await ffmpegDeleteFileSafe(ff, segB); await ffmpegDeleteFileSafe(ff, listFile); await ffmpegDeleteFileSafe(ff, mergedFile); - return new Blob([merged], { type: 'video/mp4' }); + return new Blob([toStableUint8(merged)], { type: 'video/mp4' }); }; try { diff --git a/app/static/public/pages/video.html b/app/static/public/pages/video.html index 88bbf59..c6fd054 100644 --- a/app/static/public/pages/video.html +++ b/app/static/public/pages/video.html @@ -274,7 +274,7 @@ - +