Fix error handling in makeVideoScreenshot
This commit is contained in:
parent
76bf92dab4
commit
9d1be9228a
2 changed files with 36 additions and 38 deletions
|
@ -10,6 +10,7 @@ import type { LoggerType } from './Logging';
|
||||||
import { strictAssert } from '../util/assert';
|
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';
|
||||||
|
|
||||||
export { blobToArrayBuffer };
|
export { blobToArrayBuffer };
|
||||||
|
|
||||||
|
@ -208,47 +209,44 @@ export type MakeVideoScreenshotOptionsType = Readonly<{
|
||||||
logger: Pick<LoggerType, 'error'>;
|
logger: Pick<LoggerType, 'error'>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function makeVideoScreenshot({
|
async function loadVideo({
|
||||||
|
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({
|
||||||
objectUrl,
|
objectUrl,
|
||||||
contentType = IMAGE_PNG,
|
contentType = IMAGE_PNG,
|
||||||
logger,
|
logger,
|
||||||
}: MakeVideoScreenshotOptionsType): Promise<Blob> {
|
}: MakeVideoScreenshotOptionsType): Promise<Blob> {
|
||||||
return new Promise((resolve, reject) => {
|
const video = await loadVideo({ objectUrl, logger });
|
||||||
const video = document.createElement('video');
|
await new Promise<unknown>(res => {
|
||||||
|
video.currentTime = 1.0;
|
||||||
function seek() {
|
video.addEventListener('seeked', res, { once: true });
|
||||||
video.currentTime = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function capture() {
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
canvas.width = video.videoWidth;
|
|
||||||
canvas.height = video.videoHeight;
|
|
||||||
const context = canvas.getContext('2d');
|
|
||||||
strictAssert(context, 'Failed to get canvas context');
|
|
||||||
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
video.removeEventListener('loadeddata', seek);
|
|
||||||
video.removeEventListener('seeked', capture);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const image = canvasToBlob(canvas, contentType);
|
|
||||||
resolve(image);
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
video.addEventListener('loadeddata', seek);
|
|
||||||
video.addEventListener('seeked', capture);
|
|
||||||
|
|
||||||
video.addEventListener('error', error => {
|
|
||||||
logger.error('makeVideoScreenshot error', toLogFormat(error));
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
video.src = objectUrl;
|
|
||||||
});
|
});
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = video.videoWidth;
|
||||||
|
canvas.height = video.videoHeight;
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
strictAssert(context, 'Failed to get canvas context');
|
||||||
|
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||||
|
return canvasToBlob(canvas, contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeObjectUrl(
|
export function makeObjectUrl(
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
export type ExplodePromiseResultType<T> = Readonly<{
|
export type ExplodePromiseResultType<T> = Readonly<{
|
||||||
promise: Promise<T>;
|
promise: Promise<T>;
|
||||||
resolve: (value: T) => void;
|
resolve: (value: T) => void;
|
||||||
reject: (error: Error) => void;
|
reject: (error: unknown) => void;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function explodePromise<T>(): ExplodePromiseResultType<T> {
|
export function explodePromise<T>(): ExplodePromiseResultType<T> {
|
||||||
let resolve: (value: T) => void;
|
let resolve: (value: T) => void;
|
||||||
let reject: (error: Error) => void;
|
let reject: (error: unknown) => void;
|
||||||
|
|
||||||
const promise = new Promise<T>((innerResolve, innerReject) => {
|
const promise = new Promise<T>((innerResolve, innerReject) => {
|
||||||
resolve = innerResolve;
|
resolve = innerResolve;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue