This commit is contained in:
seb
2026-07-31 20:10:19 +02:00
parent e8326e22ac
commit 243143e7eb
2 changed files with 32 additions and 11 deletions

View File

@@ -146,7 +146,7 @@ Backend mapping:
| `updateBySku` | `(googleAuthId, sku, { name })` | same shape as `getBySku` | | `updateBySku` | `(googleAuthId, sku, { name })` | same shape as `getBySku` |
| `addImage` | `(googleAuthId, sku, imageBuffer)` | image result (see below) | | `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: `addImage` returns:
@@ -154,7 +154,7 @@ Backend mapping:
{ {
picId, picId,
sort, 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) thumb: { w, h }, // scaled AVIF, max 200×200 (fit inside)
hash, // SHA-256 hex of full image bytes hash, // SHA-256 hex of full image bytes
thumbHash, // base64 thumbhash of thumb preview thumbHash, // base64 thumbhash of thumb preview

View File

@@ -10,16 +10,37 @@ export default async function processAvifImage(inputBuffer) {
? inputBuffer ? inputBuffer
: Buffer.from(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([ const [full, thumb, thumbRgba] = await Promise.all([
sharp(input) useOriginalFull
.resize(FULL_MAX, FULL_MAX, { fit: 'inside', withoutEnlargement: true }) ? Promise.resolve({
.avif({ effort: 4 }) info: { width: w, height: h },
.toBuffer({ resolveWithObject: true }), data: input,
sharp(input) })
.resize(THUMB_MAX, THUMB_MAX, { fit: 'inside', withoutEnlargement: true }) : sharp(input)
.avif({ effort: 4 }) .resize(FULL_MAX, FULL_MAX, { fit: 'inside', withoutEnlargement: true })
.toBuffer({ resolveWithObject: true }), .avif({ effort: 4 })
sharp(input) .toBuffer({ resolveWithObject: true }),
thumbPipeline.clone().avif({ effort: 4 }).toBuffer({ resolveWithObject: true }),
thumbPipeline
.clone()
.resize(100, 100, { fit: 'inside', withoutEnlargement: true }) .resize(100, 100, { fit: 'inside', withoutEnlargement: true })
.ensureAlpha() .ensureAlpha()
.raw() .raw()