diff --git a/src/lib/modules/file/cache.ts b/src/lib/modules/file/cache.ts index 31eac28..ccb187e 100644 --- a/src/lib/modules/file/cache.ts +++ b/src/lib/modules/file/cache.ts @@ -54,9 +54,7 @@ export const deleteFileCache = async (fileId: number) => { export const getFileThumbnailCache = async (fileId: number) => { const thumbnail = loadedThumbnails.get(fileId); - if (thumbnail) { - return thumbnail; - } + if (thumbnail) return thumbnail; const thumbnailBuffer = await readFile(`/thumbnail/file/${fileId}`); if (!thumbnailBuffer) return null; diff --git a/src/lib/modules/thumbnail.ts b/src/lib/modules/thumbnail.ts index 873772e..9c009bd 100644 --- a/src/lib/modules/thumbnail.ts +++ b/src/lib/modules/thumbnail.ts @@ -12,50 +12,25 @@ const scaleSize = (width: number, height: number, targetSize: number) => { }; }; -const generateImageThumbnail = (imageUrl: string) => { - return new Promise((resolve, reject) => { - const image = new Image(); - image.onload = () => { - const canvas = document.createElement("canvas"); - const { width, height } = scaleSize(image.width, image.height, 250); - - canvas.width = width; - canvas.height = height; - - const context = canvas.getContext("2d"); - if (!context) { - return reject(new Error("Failed to generate thumbnail")); - } - - context.drawImage(image, 0, 0, width, height); - canvas.toBlob((blob) => { - if (blob) { - resolve(blob); - } else { - reject(new Error("Failed to generate thumbnail")); - } - }, "image/webp"); - }; - image.onerror = reject; - - image.src = imageUrl; - }); -}; - -export const captureVideoThumbnail = (video: HTMLVideoElement) => { +const capture = ( + width: number, + height: number, + drawer: (context: CanvasRenderingContext2D, width: number, height: number) => void, + targetSize = 250, +) => { return new Promise((resolve, reject) => { const canvas = document.createElement("canvas"); - const { width, height } = scaleSize(video.videoWidth, video.videoHeight, 250); + const { width: scaledWidth, height: scaledHeight } = scaleSize(width, height, targetSize); - canvas.width = width; - canvas.height = height; + canvas.width = scaledWidth; + canvas.height = scaledHeight; const context = canvas.getContext("2d"); if (!context) { return reject(new Error("Failed to generate thumbnail")); } - context.drawImage(video, 0, 0, width, height); + drawer(context, scaledWidth, scaledHeight); canvas.toBlob((blob) => { if (blob) { resolve(blob); @@ -66,14 +41,36 @@ export const captureVideoThumbnail = (video: HTMLVideoElement) => { }); }; +const generateImageThumbnail = (imageUrl: string) => { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => { + capture(image.width, image.height, (context, width, height) => { + context.drawImage(image, 0, 0, width, height); + }) + .then(resolve) + .catch(reject); + }; + image.onerror = reject; + + image.src = imageUrl; + }); +}; + +export const captureVideoThumbnail = (video: HTMLVideoElement) => { + return capture(video.videoWidth, video.videoHeight, (context, width, height) => { + context.drawImage(video, 0, 0, width, height); + }); +}; + const generateVideoThumbnail = (videoUrl: string, time = 0) => { return new Promise((resolve, reject) => { const video = document.createElement("video"); - video.onloadeddata = () => { - video.currentTime = time; - }; - video.onseeked = () => { - captureVideoThumbnail(video).then(resolve).catch(reject); + video.onloadedmetadata = () => { + video.currentTime = Math.min(time, video.duration); + video.requestVideoFrameCallback(() => { + captureVideoThumbnail(video).then(resolve).catch(reject); + }); }; video.onerror = reject;