diff --git a/src/components/Image.astro b/src/components/Image.astro
index 7ad4658..fa4d2cf 100644
--- a/src/components/Image.astro
+++ b/src/components/Image.astro
@@ -1,8 +1,7 @@
---
import type { ImageMetadata } from "astro";
import { Picture as AstroImage } from "astro:assets";
-import { inferRemoteSize } from "astro/assets/utils";
-import { generateThumbHash, getExifData } from "@helpers/image";
+import { generateThumbHash, getImageBuffer, getExifData } from "@helpers/image";
interface Props {
src: ImageMetadata & { fsPath?: string; src?: string };
alt: string;
@@ -14,40 +13,18 @@ interface Props {
maxWidth?: number;
}
-async function checkImage(image: ImageMetadata) {
- const src = typeof image === "string" ? image : image.src;
- if (!src) return false;
- try {
- if (src.startsWith("/@fs") || src.startsWith("/_astro")) return true;
- const res = await inferRemoteSize(src);
- if (res.format) {
- image.format = res.format;
- return true;
- } else {
- console.log("Failed to load: ", src);
- }
- return false;
- } catch (err) {
- console.log("\n");
- console.log("Failed to fetch: ", src);
- return false;
- }
-}
-
const {
src: image,
loader = true,
- caption,
pictureClass = "",
hash = true,
alt,
maxWidth,
} = Astro.props;
-let thumbhash = hash && (await generateThumbHash(image));
-const imageOk = await checkImage(image);
-
-let exif = imageOk && (await getExifData(image));
+const imageBuffer = await getImageBuffer(image);
+let thumbhash = imageBuffer && (await generateThumbHash(imageBuffer));
+let exif = imageBuffer && (await getExifData(imageBuffer));
const sizes = [
{
@@ -68,23 +45,19 @@ const sizes = [
].filter((size) => !maxWidth || size.width <= maxWidth);
---
-{
- imageOk ? (
- size.width)}
- sizes={sizes
- .map((size) => `${size.media || "100vw"} ${size.width}px`)
- .join(", ")}>
-
-
- ) : undefined
-}
+ size.width)}
+ sizes={sizes
+ .map((size) => `${size.media || "100vw"} ${size.width}px`)
+ .join(", ")}>
+
+
diff --git a/src/helpers/image.ts b/src/helpers/image.ts
index fad82e3..8b01192 100644
--- a/src/helpers/image.ts
+++ b/src/helpers/image.ts
@@ -5,55 +5,26 @@ import { readFile } from "node:fs/promises";
import sharp from "sharp";
export async function generateThumbHash(
- image: ImageMetadata & { fsPath?: string },
-) {
- const scaleFactor = 100 / Math.max(image.width, image.height);
+ buffer: ArrayBuffer,
+): Promise {
+ if (!buffer) return;
- let smallWidth = Math.floor(image.width * scaleFactor);
- let smallHeight = Math.floor(image.height * scaleFactor);
+ const sp = sharp(buffer);
- try {
- const imagePath = (image as ImageMetadata & { fsPath: string }).fsPath ??
- image.src;
+ const meta = await sp.metadata();
+ const scaleFactor = 100 / Math.max(meta.width, meta.height);
+ const smallWidth = Math.floor(meta.width * scaleFactor);
+ const smallHeight = Math.floor(meta.height * scaleFactor);
- if (!imagePath) return;
+ const smallImg = await sp
+ .resize(smallWidth, smallHeight)
+ .withMetadata()
+ .raw()
+ .ensureAlpha()
+ .toBuffer();
- if (imagePath.endsWith(".svg")) return;
-
- let sp: ReturnType;
- if (imagePath.startsWith("https://") || imagePath.startsWith("http://")) {
- const res = await fetch(imagePath);
- if (!res.ok) {
- return;
- }
- const buffer = await res.arrayBuffer();
- sp = sharp(buffer);
- } 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}`,
- );
- return "";
- }
+ const hashBuffer = rgbaToThumbHash(smallWidth, smallHeight, smallImg);
+ return Buffer.from(hashBuffer).toString("base64");
}
const allowedExif = [
@@ -72,7 +43,9 @@ const allowedExif = [
"Model",
];
-export async function getExifData(image: ImageMetadata) {
+export async function getImageBuffer(
+ image: ImageMetadata,
+): Promise {
if (image.format === "svg") return undefined; // SVGs don't have EXIF data
const imagePath = (image as ImageMetadata & { fsPath: string }).fsPath ??
image.src;
@@ -80,31 +53,31 @@ export async function getExifData(image: ImageMetadata) {
if (!imagePath) return undefined;
try {
- let buffer: ArrayBufferLike;
if (imagePath.startsWith("https://") || imagePath.startsWith("http://")) {
- const res = await fetch(imagePath);
- buffer = await res.arrayBuffer();
+ const res = await fetch(imagePath, { signal: AbortSignal.timeout(5000) });
+ return await res.arrayBuffer();
} else {
const b = await readFile(imagePath);
- buffer = b.buffer;
+ return b.buffer as ArrayBuffer;
}
-
- const tags = await ExifReader.load(buffer, { async: true });
-
- if (!buffer) return undefined;
-
- const out: Record = {};
- 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);
+ } catch (err) {
return undefined;
}
}
+
+export async function getExifData(buffer: ArrayBuffer) {
+ if (!buffer) return undefined;
+
+ const tags = await ExifReader.load(buffer, { async: true });
+
+ const out: Record = {};
+ let hasExif = false;
+
+ for (const key of allowedExif) {
+ if (!tags[key]) continue;
+ hasExif = true;
+ out[key] = tags[key]?.description;
+ }
+
+ return hasExif ? out : undefined;
+}
diff --git a/src/helpers/memorium.ts b/src/helpers/memorium.ts
index 6bc28c1..2183ebf 100644
--- a/src/helpers/memorium.ts
+++ b/src/helpers/memorium.ts
@@ -28,7 +28,7 @@ export async function listResource(
): Promise {
const url = `${SERVER_URL}/resources/${id}`;
try {
- const response = await fetch(url);
+ const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
if (response.ok) {
const json = await response.json();
if (json.type == "dir") {