Files
max-richter.dev/src/helpers/image.ts
Max Richter 342f83c783
Some checks failed
Deploy to SFTP Server / build (push) Failing after 3m59s
feat: display articles
2025-10-22 17:43:04 +02:00

105 lines
2.7 KiB
TypeScript

import { rgbaToThumbHash } from "thumbhash";
import ExifReader from "exifreader";
import type { ImageMetadata } from "astro";
import sharp from "sharp";
export async function generateThumbHash(
image: ImageMetadata & { fsPath?: string },
) {
const scaleFactor = 100 / Math.max(image.width, image.height);
let smallWidth = Math.floor(image.width * scaleFactor);
let smallHeight = Math.floor(image.height * scaleFactor);
try {
const imagePath = (image as ImageMetadata & { fsPath: string }).fsPath ??
image.src;
if (!imagePath) return;
let sp: ReturnType<typeof sharp>;
if (imagePath.startsWith("https://") || imagePath.startsWith("http://")) {
const res = await fetch(imagePath);
if (!res.ok) {
return;
}
sp = sharp(await res.arrayBuffer());
} else {
sp = sharp(imagePath);
}
if (!smallWidth || !smallHeight) {
const meta = await sp.metadata();
const scaleFactor = 100 / Math.max(meta.width, meta.height);
smallWidth = Math.floor(meta.width * scaleFactor);
smallHeight = Math.floor(meta.height * scaleFactor);
}
const smallImg = await sp
.resize(smallWidth, smallHeight)
.withMetadata()
.raw()
.ensureAlpha()
.toBuffer();
const buffer = rgbaToThumbHash(smallWidth, smallHeight, smallImg);
return Buffer.from(buffer).toString("base64");
} catch (error) {
console.log(
`Could not generate thumbhash for ${image.fsPath ?? image.src}`,
error,
);
return "";
}
}
const allowedExif = [
"ApertureValue",
"DateTimeOriginal",
"ShutterSpeedValue",
"ExposureTime",
"ApertureValue",
"FNumber",
"FocalLength",
"GPSLatitude",
"GPSLongitude",
"GPSAltitude",
"IsoSpeedRatings",
"Make",
"Model",
];
export async function getExifData(image: ImageMetadata) {
if (image.format === "svg") return undefined; // SVGs don't have EXIF data")
const imagePath = (image as ImageMetadata & { fsPath: string }).fsPath ??
image.src;
if (!imagePath) return undefined;
try {
let buffer: ArrayBuffer;
if (imagePath.startsWith("https://") || imagePath.startsWith("http://")) {
const res = await fetch(imagePath);
buffer = await res.arrayBuffer();
} else {
buffer = await sharp(imagePath).toBuffer() as unknown as ArrayBuffer;
}
const tags = await ExifReader.load(buffer, { async: true });
const out: Record<string, any> = {};
let hasExif = false;
for (const key of allowedExif) {
if (!tags[key]) continue;
hasExif = true;
out[key] = tags[key]?.description;
}
return hasExif ? out : undefined;
} catch (error) {
console.log(`Error reading EXIF data from ${JSON.stringify(image)}`, error);
return undefined;
}
}