mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
import { encodeToBase64 } from "$lib/modules/crypto";
|
|
|
|
const scaleSize = (width: number, height: number, targetSize: number) => {
|
|
if (width <= targetSize || height <= targetSize) {
|
|
return { width, height };
|
|
}
|
|
|
|
const scale = targetSize / Math.min(width, height);
|
|
return {
|
|
width: Math.round(width * scale),
|
|
height: Math.round(height * scale),
|
|
};
|
|
};
|
|
|
|
const capture = (
|
|
width: number,
|
|
height: number,
|
|
drawer: (context: CanvasRenderingContext2D, width: number, height: number) => void,
|
|
targetSize = 250,
|
|
) => {
|
|
return new Promise<Blob>((resolve, reject) => {
|
|
const canvas = document.createElement("canvas");
|
|
const { width: scaledWidth, height: scaledHeight } = scaleSize(width, height, targetSize);
|
|
|
|
canvas.width = scaledWidth;
|
|
canvas.height = scaledHeight;
|
|
|
|
const context = canvas.getContext("2d");
|
|
if (!context) {
|
|
return reject(new Error("Failed to generate thumbnail"));
|
|
}
|
|
|
|
drawer(context, scaledWidth, scaledHeight);
|
|
canvas.toBlob((blob) => {
|
|
if (blob) {
|
|
resolve(blob);
|
|
} else {
|
|
reject(new Error("Failed to generate thumbnail"));
|
|
}
|
|
}, "image/webp");
|
|
});
|
|
};
|
|
|
|
const generateImageThumbnail = (imageUrl: string) => {
|
|
return new Promise<Blob>((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<Blob>((resolve, reject) => {
|
|
const video = document.createElement("video");
|
|
video.onloadedmetadata = () => {
|
|
video.currentTime = Math.min(time, video.duration);
|
|
video.requestVideoFrameCallback(() => {
|
|
captureVideoThumbnail(video).then(resolve).catch(reject);
|
|
});
|
|
};
|
|
video.onerror = reject;
|
|
|
|
video.muted = true;
|
|
video.playsInline = true;
|
|
video.src = videoUrl;
|
|
});
|
|
};
|
|
|
|
export const generateThumbnail = async (fileBuffer: ArrayBuffer, fileType: string) => {
|
|
let url;
|
|
try {
|
|
if (fileType === "image/heic") {
|
|
const { default: heic2any } = await import("heic2any");
|
|
url = URL.createObjectURL(
|
|
(await heic2any({
|
|
blob: new Blob([fileBuffer], { type: fileType }),
|
|
toType: "image/png",
|
|
})) as Blob,
|
|
);
|
|
return await generateImageThumbnail(url);
|
|
} else if (fileType.startsWith("image/")) {
|
|
url = URL.createObjectURL(new Blob([fileBuffer], { type: fileType }));
|
|
return await generateImageThumbnail(url);
|
|
} else if (fileType.startsWith("video/")) {
|
|
url = URL.createObjectURL(new Blob([fileBuffer], { type: fileType }));
|
|
return await generateVideoThumbnail(url);
|
|
}
|
|
return null;
|
|
} catch {
|
|
return null;
|
|
} finally {
|
|
if (url) {
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
}
|
|
};
|
|
|
|
export const getThumbnailUrl = (thumbnailBuffer: ArrayBuffer) => {
|
|
return `data:image/webp;base64,${encodeToBase64(thumbnailBuffer)}`;
|
|
};
|