From 243143e7eb52d2af2b7108561a0b15b1cf7c7fac Mon Sep 17 00:00:00 2001 From: seb Date: Fri, 31 Jul 2026 20:10:19 +0200 Subject: [PATCH] u --- README.md | 4 ++-- src/util/processAvifImage.js | 39 +++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6b28809..129be7d 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Backend mapping: | `updateBySku` | `(googleAuthId, sku, { name })` | same shape as `getBySku` | | `addImage` | `(googleAuthId, sku, imageBuffer)` | image result (see below) | -`addImage` accepts a full-size AVIF `Buffer`, scales to max 1000×1000 (`pic.binary`) and 200×200 (`pic.thumbbinary`) via `sharp`, computes `hash` / `thumbhash`, links `articlepic`. Auth like `getBySku`. Errors: `Article not found`, `Not authorized`. +`addImage` accepts a full-size AVIF `Buffer`. Full image: if already AVIF and ≤1000×1000, stored as-is; otherwise scaled to max 1000×1000 (`pic.binary`). Thumb always scaled to max 200×200 (`pic.thumbbinary`) via `sharp`. Computes `hash` / `thumbhash`, links `articlepic`. Auth like `getBySku`. Errors: `Article not found`, `Not authorized`. `addImage` returns: @@ -154,7 +154,7 @@ Backend mapping: { picId, sort, - full: { w, h, size }, // scaled AVIF, max 1000×1000 (fit inside) + full: { w, h, size }, // AVIF, max 1000×1000 (stored as-is when already AVIF and within bounds) thumb: { w, h }, // scaled AVIF, max 200×200 (fit inside) hash, // SHA-256 hex of full image bytes thumbHash, // base64 thumbhash of thumb preview diff --git a/src/util/processAvifImage.js b/src/util/processAvifImage.js index 0b9dd0b..1b9462f 100644 --- a/src/util/processAvifImage.js +++ b/src/util/processAvifImage.js @@ -10,16 +10,37 @@ export default async function processAvifImage(inputBuffer) { ? inputBuffer : Buffer.from(inputBuffer); + const meta = await sharp(input).metadata(); + const w = meta.width ?? 0; + const h = meta.height ?? 0; + const isAvif = + meta.format === 'avif' || + (meta.format === 'heif' && meta.compression === 'av1'); + const useOriginalFull = + isAvif && w > 0 && h > 0 && w <= FULL_MAX && h <= FULL_MAX; + + if (!useOriginalFull) { + console.log( + `processAvifImage: converting full image (format=${meta.format ?? 'unknown'}, ${w}×${h}, avif=${isAvif})`, + ); + } + + const thumbPipeline = sharp(input) + .resize(THUMB_MAX, THUMB_MAX, { fit: 'inside', withoutEnlargement: true }); + const [full, thumb, thumbRgba] = await Promise.all([ - sharp(input) - .resize(FULL_MAX, FULL_MAX, { fit: 'inside', withoutEnlargement: true }) - .avif({ effort: 4 }) - .toBuffer({ resolveWithObject: true }), - sharp(input) - .resize(THUMB_MAX, THUMB_MAX, { fit: 'inside', withoutEnlargement: true }) - .avif({ effort: 4 }) - .toBuffer({ resolveWithObject: true }), - sharp(input) + useOriginalFull + ? Promise.resolve({ + info: { width: w, height: h }, + data: input, + }) + : sharp(input) + .resize(FULL_MAX, FULL_MAX, { fit: 'inside', withoutEnlargement: true }) + .avif({ effort: 4 }) + .toBuffer({ resolveWithObject: true }), + thumbPipeline.clone().avif({ effort: 4 }).toBuffer({ resolveWithObject: true }), + thumbPipeline + .clone() .resize(100, 100, { fit: 'inside', withoutEnlargement: true }) .ensureAlpha() .raw()