Update error handling in makeVideoScreenshot

This commit is contained in:
trevor-signal 2024-03-21 18:54:14 -04:00 committed by GitHub
parent 1480c3f13a
commit d30da286dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,6 +11,7 @@ import { strictAssert } from '../util/assert';
import { canvasToBlob } from '../util/canvasToBlob'; import { canvasToBlob } from '../util/canvasToBlob';
import { KIBIBYTE } from './AttachmentSize'; import { KIBIBYTE } from './AttachmentSize';
import { explodePromise } from '../util/explodePromise'; import { explodePromise } from '../util/explodePromise';
import { SECOND } from '../util/durations';
export { blobToArrayBuffer }; export { blobToArrayBuffer };
@ -209,37 +210,12 @@ export type MakeVideoScreenshotOptionsType = Readonly<{
logger: Pick<LoggerType, 'error'>; logger: Pick<LoggerType, 'error'>;
}>; }>;
async function loadVideo({ const MAKE_VIDEO_SCREENSHOT_TIMEOUT = 30 * SECOND;
objectUrl,
logger,
}: MakeVideoScreenshotOptionsType): Promise<HTMLVideoElement> {
const video = document.createElement('video');
const { promise, resolve, reject } = explodePromise();
video.addEventListener('loadeddata', resolve);
video.addEventListener('error', reject);
video.src = objectUrl;
try {
await promise;
} catch (error) {
logger.error('loadVideo error', toLogFormat(video.error));
throw error;
} finally {
video.removeEventListener('loadeddata', resolve);
video.removeEventListener('error', reject);
}
return video;
}
export async function makeVideoScreenshot({ function captureScreenshot(
objectUrl, video: HTMLVideoElement,
contentType = IMAGE_PNG, contentType: MIMEType
logger, ): Promise<Blob> {
}: MakeVideoScreenshotOptionsType): Promise<Blob> {
const video = await loadVideo({ objectUrl, logger });
await new Promise<unknown>(res => {
video.currentTime = 1.0;
video.addEventListener('seeked', res, { once: true });
});
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
canvas.width = video.videoWidth; canvas.width = video.videoWidth;
canvas.height = video.videoHeight; canvas.height = video.videoHeight;
@ -249,6 +225,51 @@ export async function makeVideoScreenshot({
return canvasToBlob(canvas, contentType); return canvasToBlob(canvas, contentType);
} }
export async function makeVideoScreenshot({
objectUrl,
contentType = IMAGE_PNG,
logger,
}: MakeVideoScreenshotOptionsType): Promise<Blob> {
const signal = AbortSignal.timeout(MAKE_VIDEO_SCREENSHOT_TIMEOUT);
const video = document.createElement('video');
const { promise: videoLoadedAndSeeked, resolve, reject } = explodePromise();
function onLoaded() {
if (signal.aborted) {
return;
}
video.addEventListener('seeked', resolve);
video.currentTime = 1.0;
}
function onAborted() {
reject(signal.reason);
}
video.addEventListener('loadeddata', onLoaded);
video.addEventListener('error', reject);
signal.addEventListener('abort', onAborted);
try {
video.src = objectUrl;
await videoLoadedAndSeeked;
return await captureScreenshot(video, contentType);
} catch (error) {
logger.error('makeVideoScreenshot error:', toLogFormat(error));
throw error;
} finally {
// hard reset the video element so it doesn't keep loading
video.src = '';
video.load();
video.removeEventListener('loadeddata', onLoaded);
video.removeEventListener('error', reject);
video.removeEventListener('seeked', resolve);
signal.removeEventListener('abort', onAborted);
}
}
export function makeObjectUrl( export function makeObjectUrl(
data: Uint8Array | ArrayBuffer, data: Uint8Array | ArrayBuffer,
contentType: MIMEType contentType: MIMEType