mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-12 16:55:37 +02:00
73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { Hono } from 'hono'
|
|
import { kvCaches } from "@nestri/cache"
|
|
import resize, { initResize } from '@jsquash/resize';
|
|
import decodeJpeg, { init as initDecodeJpeg } from '@jsquash/jpeg/decode';
|
|
import encodeAvif, { init as initEncodeAvif } from '@jsquash/avif/encode.js';
|
|
import JPEG_DEC_WASM from "@jsquash/jpeg/codec/dec/mozjpeg_dec.wasm";
|
|
import RESIZE_WASM from "@jsquash/resize/lib/resize/pkg/squoosh_resize_bg.wasm";
|
|
import AVIF_ENC_WASM from "@jsquash/avif/codec/enc/avif_enc.wasm";
|
|
|
|
const cacheOptions = {
|
|
key: "nexus",
|
|
namespace: "cover-cache"
|
|
};
|
|
|
|
const middleware = kvCaches(cacheOptions);
|
|
|
|
const decodeImage = async (buffer: ArrayBuffer) => {
|
|
await initDecodeJpeg(JPEG_DEC_WASM);
|
|
return decodeJpeg(buffer);
|
|
}
|
|
|
|
const resizeImage = async (image: { width: number; height: number }, width: number, height: number) => {
|
|
await initResize(RESIZE_WASM);
|
|
// Resize image with respect to aspect ratio
|
|
const aspectRatio = image.width / image.height;
|
|
const newWidth = width;
|
|
const newHeight = width / aspectRatio;
|
|
return resize(image, { width: newWidth, height: newHeight, fitMethod: "stretch" });
|
|
}
|
|
|
|
const encodeImage = async (image: { width: number; height: number }, format: string) => {
|
|
if (format === 'avif') {
|
|
await initEncodeAvif(AVIF_ENC_WASM);
|
|
return encodeAvif(image);
|
|
}
|
|
throw new Error(`Unsupported image format: ${format}`);
|
|
}
|
|
|
|
const app = new Hono()
|
|
|
|
app.notFound((c) => c.json({ message: 'Not Found', ok: false }, 404))
|
|
|
|
app.get('/:id', middleware, async (c) => {
|
|
const [gameId, imageType] = c.req.param("id").split('.');
|
|
const width = parseInt(c.req.query("width") || "460");
|
|
//We don't even use this, but let us keep it for future use
|
|
const height = parseInt(c.req.query("height") || "215");
|
|
if (!gameId || !imageType) {
|
|
return c.text("Invalid image parameters", 400)
|
|
}
|
|
//Support Avif only because of it's small size
|
|
const validImageTypes = ["avif"] //['jpg', 'png', 'webp', 'avif'];
|
|
if (!validImageTypes.includes(imageType)) {
|
|
return c.text('Invalid image type', 400);
|
|
}
|
|
|
|
const imageUrl = `https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/${gameId}/header.jpg`;
|
|
const image = await fetch(imageUrl);
|
|
if (!image.ok) {
|
|
return c.text('Image not found', 404);
|
|
}
|
|
const imageBuffer = await image.arrayBuffer();
|
|
const imageData = await decodeImage(imageBuffer);
|
|
const resizedImage = await resizeImage(imageData, width, height);
|
|
const resizedImageBuffer = await encodeImage(resizedImage, imageType);
|
|
return c.newResponse(resizedImageBuffer, 200, {
|
|
"Content-Type": `image/${imageType}`,
|
|
'Cache-Control': 'public, max-age=31536000, immutable'
|
|
})
|
|
})
|
|
|
|
export default app
|