use source file for web video compression

Avoids the fetch(blob:) round trip in compressVideo, which copies the
bytes and throws a bare TypeError when the URL is revoked or the read
fails. Also adds errorMessage to the compressFailed metric.
This commit is contained in:
vineyardbovines
2026-07-07 15:35:12 -04:00
parent ea3d6827c7
commit 44d381aee6
4 changed files with 17 additions and 2 deletions
+2
View File
@@ -1404,6 +1404,8 @@ export type Events = {
uploadId: string uploadId: string
engine: string engine: string
errorClass: string errorClass: string
/** Truncated to 256 chars */
errorMessage: string
elapsedMs: number elapsedMs: number
} }
'video:upload:uploadStarted': { 'video:upload:uploadStarted': {
+6 -2
View File
@@ -44,8 +44,12 @@ export async function compressVideo(
hasWebCodecs: hasWebCodecs(), hasWebCodecs: hasWebCodecs(),
}) })
const response = await fetch(asset.uri) /*
const blob = await response.blob() * Prefer the original File over re-fetching the blob URL. A fetch round
* trip copies the bytes and fails with a bare TypeError if the URL was
* revoked or the read fails - the top web compressFailed error class.
*/
const blob = asset.file ?? (await (await fetch(asset.uri)).blob())
const isGif = blob.type === 'image/gif' const isGif = blob.type === 'image/gif'
const hasCodecs = hasWebCodecs() const hasCodecs = hasWebCodecs()
+6
View File
@@ -26,6 +26,11 @@ function errorClass(e: unknown): string {
return 'Unknown' return 'Unknown'
} }
function errorMessage(e: unknown): string {
const message = e instanceof Error ? e.message : String(e)
return message.slice(0, 256)
}
export type VideoTelemetry = { export type VideoTelemetry = {
readonly uploadId: string readonly uploadId: string
readonly engine: string readonly engine: string
@@ -208,6 +213,7 @@ export function createVideoTelemetry({
uploadId, uploadId,
engine, engine,
errorClass: errorClass(e), errorClass: errorClass(e),
errorMessage: errorMessage(e),
elapsedMs: Date.now() - phaseStartedAt, elapsedMs: Date.now() - phaseStartedAt,
}) })
endTxn('error') endTxn('error')
@@ -72,6 +72,7 @@ async function getMetadataWithWebCodecs(
return { return {
uri: blobUrl, uri: blobUrl,
file,
mimeType: file.type, mimeType: file.type,
width: videoTrack.displayWidth, width: videoTrack.displayWidth,
height: videoTrack.displayHeight, height: videoTrack.displayHeight,
@@ -92,6 +93,7 @@ async function getMetadataWithBrowserAPIs(
img.onload = () => { img.onload = () => {
resolve({ resolve({
uri: blobUrl, uri: blobUrl,
file,
mimeType: 'image/gif', mimeType: 'image/gif',
width: img.width, width: img.width,
height: img.height, height: img.height,
@@ -111,6 +113,7 @@ async function getMetadataWithBrowserAPIs(
video.onloadedmetadata = () => { video.onloadedmetadata = () => {
resolve({ resolve({
uri: blobUrl, uri: blobUrl,
file,
mimeType: file.type, mimeType: file.type,
width: video.videoWidth, width: video.videoWidth,
height: video.videoHeight, height: video.videoHeight,