This commit is contained in:
seb
2026-10-09 18:26:01 +02:00
parent 07c4f0bff9
commit 178055f1d8
13 changed files with 80 additions and 25 deletions

View File

@@ -32,7 +32,7 @@ Adds a child article to a parent article (creates and attaches with auto-generat
*Source: `src/store/article/addImage.js`* *Source: `src/store/article/addImage.js`*
Adds an image to an article by its SKU, processing it into AVIF variants. Adds an image to an article by its SKU, processing full, thumb, and 40px-edge small-thumb AVIF variants.
**Parameters** **Parameters**
@@ -45,13 +45,13 @@ Adds an image to an article by its SKU, processing it into AVIF variants.
**Returns** **Returns**
`Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>` — The added image info. `Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, smallThumb: {w: number, h: number}, fullHash: string, thumbHash: string}>` — The added image info.
**Example** **Example**
```js ```js
const result = await store.article.addImage(42, 'SKU-1', imageBuffer); const result = await store.article.addImage(42, 'SKU-1', imageBuffer);
// => { picId, sort, full: { w, h, size }, thumb: { w, h }, fullHash, thumbHash } // => { picId, sort, full: { w, h, size }, thumb: { w, h }, smallThumb: { w, h }, fullHash, thumbHash }
``` ```
## `article.attachChild` ## `article.attachChild`

View File

@@ -6,7 +6,7 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
*Source: `src/store/brand/addImage.js`* *Source: `src/store/brand/addImage.js`*
Adds an image to a brand, processing it into AVIF full and thumb variants. Adds an image to a brand, processing full, thumb, and 40px-edge small-thumb AVIF variants.
**Parameters** **Parameters**
@@ -19,7 +19,7 @@ Adds an image to a brand, processing it into AVIF full and thumb variants.
**Returns** **Returns**
`Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, pics: Array<object>}>` `Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number, pics: Array<object>}>`
## `brand.create` ## `brand.create`

View File

@@ -6,7 +6,7 @@ This document is auto-generated from the JSDoc comments in `src/store` by runnin
*Source: `src/store/category/addImage.js`* *Source: `src/store/category/addImage.js`*
Adds an image to a category, processing it into AVIF full and thumb variants. Adds an image to a category, processing full, thumb, and 40px-edge small-thumb AVIF variants.
**Parameters** **Parameters**
@@ -19,7 +19,7 @@ Adds an image to a category, processing it into AVIF full and thumb variants.
**Returns** **Returns**
`Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>` — The added image info. `Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number}>` — The added image info.
**Example** **Example**

6
API.md
View File

@@ -74,7 +74,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `list(googleAuthId: number, tx?: object): Promise<Array<object>>` - `list(googleAuthId: number, tx?: object): Promise<Array<object>>`
- `update(id: number, input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number}, tx?: object): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number}>` - `update(id: number, input: {iso639: string, name: string, taxrate: number, reducedtaxrate: number}, tx?: object): Promise<{id: number, rootCategoryId: number, languageId: number, taxrateId: number, reducedtaxrateId: number, taxfreerateId: number}>`
- [category](API.category.md) - [category](API.category.md)
- `addImage(googleAuthId: number, categoryId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>` - `addImage(googleAuthId: number, categoryId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number}>`
- `createUnderParent(input: {googleAuthId: number, parentId: number, name: string}, tx?: object): Promise<{id: number, parentId: number, sort: number, name: string, slug: string}>` - `createUnderParent(input: {googleAuthId: number, parentId: number, name: string}, tx?: object): Promise<{id: number, parentId: number, sort: number, name: string, slug: string}>`
- `delete(input: {googleAuthId: number, id: number, recursive?: boolean}, tx?: object): Promise<void>` - `delete(input: {googleAuthId: number, id: number, recursive?: boolean}, tx?: object): Promise<void>`
- `deleteImage(googleAuthId: number, categoryId: number, tx?: object): Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>` - `deleteImage(googleAuthId: number, categoryId: number, tx?: object): Promise<{id: number, thumbW: null, thumbH: null, thumbHash: null}>`
@@ -86,7 +86,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `rename(input: {googleAuthId: number, id: number, name: string}, tx?: object): Promise<{id: number, name: string, slug: string}>` - `rename(input: {googleAuthId: number, id: number, name: string}, tx?: object): Promise<{id: number, name: string, slug: string}>`
- [article](API.article.md) - [article](API.article.md)
- `addChild(googleAuthId: number, parentSku: string, options: {attach?: boolean, attributeValueId?: number, value?: string, name?: string, unitamount?: number, weight?: number, track_stock?: boolean|number}, tx?: object): Promise<object>` - `addChild(googleAuthId: number, parentSku: string, options: {attach?: boolean, attributeValueId?: number, value?: string, name?: string, unitamount?: number, weight?: number, track_stock?: boolean|number}, tx?: object): Promise<object>`
- `addImage(googleAuthId: number, sku: string, image: Buffer, tx?: object): Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>` - `addImage(googleAuthId: number, sku: string, image: Buffer, tx?: object): Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, smallThumb: {w: number, h: number}, fullHash: string, thumbHash: string}>`
- `attachChild(googleAuthId: number, parentSku: string, options: {sku: string, childSku?: string, attributeValueId?: number, value?: string, reparent?: boolean, unitamount?: number, weight?: number, track_stock?: boolean|number}, tx?: object): Promise<{article: object, children: Array<object>}>` - `attachChild(googleAuthId: number, parentSku: string, options: {sku: string, childSku?: string, attributeValueId?: number, value?: string, reparent?: boolean, unitamount?: number, weight?: number, track_stock?: boolean|number}, tx?: object): Promise<{article: object, children: Array<object>}>`
- `createUnderParent(input: {googleAuthId: number, parentId: number, name: string, desc?: string, seo?: string, track_stock?: boolean|number}, tx?: object): Promise<{id: number, parentId: number, name: string, sku: string, track_stock: boolean}>` - `createUnderParent(input: {googleAuthId: number, parentId: number, name: string, desc?: string, seo?: string, track_stock?: boolean|number}, tx?: object): Promise<{id: number, parentId: number, name: string, sku: string, track_stock: boolean}>`
- `delete(googleAuthId: number, sku: string, optionsOrTx?: object|*, maybeTx?: object): Promise<void>` - `delete(googleAuthId: number, sku: string, optionsOrTx?: object|*, maybeTx?: object): Promise<void>`
@@ -129,7 +129,7 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `list(googleAuthId: number, options?: {frontendOnly?: boolean, maskWriteonly?: boolean}, tx?: object): Promise<Array<{key: string, value: string, frontend: boolean, writeonly: boolean}>>` - `list(googleAuthId: number, options?: {frontendOnly?: boolean, maskWriteonly?: boolean}, tx?: object): Promise<Array<{key: string, value: string, frontend: boolean, writeonly: boolean}>>`
- `set(googleAuthId: number, input: {key: string, value: string, frontend?: boolean, writeonly?: boolean}, tx?: object): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}>` - `set(googleAuthId: number, input: {key: string, value: string, frontend?: boolean, writeonly?: boolean}, tx?: object): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}>`
- [brand](API.brand.md) - [brand](API.brand.md)
- `addImage(googleAuthId: number, brandId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, pics: Array<object>}>` - `addImage(googleAuthId: number, brandId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number, pics: Array<object>}>`
- `create(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>` - `create(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>` - `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, opts?: {offset?: number, limit?: number}, tx?: object): Promise<{brands: Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>, total: number, offset: number, limit: number}>` - `list(googleAuthId: number, opts?: {offset?: number, limit?: number}, tx?: object): Promise<{brands: Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>, total: number, offset: number, limit: number}>`

View File

@@ -28,7 +28,7 @@ async function runTests() {
assert(existsSync(join(storageDir, 'fullbinary')), 'fullbinary dir should exist'); assert(existsSync(join(storageDir, 'fullbinary')), 'fullbinary dir should exist');
assert(existsSync(join(storageDir, 'thumbbinary')), 'thumbbinary dir should exist'); assert(existsSync(join(storageDir, 'thumbbinary')), 'thumbbinary dir should exist');
// Verify sqlite tables do NOT contain blob columns // Large file and image blobs stay on disk; the 40px small-thumb stays inline in pic.
const fileTableInfo = provider.db.pragma('table_info("file")'); const fileTableInfo = provider.db.pragma('table_info("file")');
const fileColNames = fileTableInfo.map((c) => c.name); const fileColNames = fileTableInfo.map((c) => c.name);
console.log('file table columns:', fileColNames); console.log('file table columns:', fileColNames);
@@ -40,6 +40,9 @@ async function runTests() {
console.log('pic table columns:', picColNames); console.log('pic table columns:', picColNames);
assert(!picColNames.includes('fullbinary'), 'pic table should NOT have fullbinary column'); assert(!picColNames.includes('fullbinary'), 'pic table should NOT have fullbinary column');
assert(!picColNames.includes('thumbbinary'), 'pic table should NOT have thumbbinary column'); assert(!picColNames.includes('thumbbinary'), 'pic table should NOT have thumbbinary column');
assert(picColNames.includes('smallthumbw'), 'pic table should have smallthumbw column');
assert(picColNames.includes('smallthumbh'), 'pic table should have smallthumbh column');
assert(picColNames.includes('smallthumbbinary'), 'pic table should have smallthumbbinary column');
// Setup base data: google_auth, company, unit, article, manifest // Setup base data: google_auth, company, unit, article, manifest
const googleAuthId = await store.google_auth.add('test@example.com'); const googleAuthId = await store.google_auth.add('test@example.com');
@@ -199,6 +202,18 @@ async function runTests() {
const picResult = await store.article.addImage(googleAuthId, article.sku, sampleImage); const picResult = await store.article.addImage(googleAuthId, article.sku, sampleImage);
const fullHash = picResult.fullHash; const fullHash = picResult.fullHash;
assert.equal(picResult.smallThumb.w, 40);
assert.equal(picResult.smallThumb.h, 27);
const smallThumbRow = provider.db.prepare(
'SELECT smallthumbw, smallthumbh, smallthumbbinary FROM pic WHERE hash = ?',
).get(fullHash);
assert.equal(smallThumbRow.smallthumbw, 40);
assert.equal(smallThumbRow.smallthumbh, 27);
assert(Buffer.isBuffer(smallThumbRow.smallthumbbinary), 'Small thumb AVIF must be stored in the pic row');
const smallThumbMetadata = await sharp(smallThumbRow.smallthumbbinary).metadata();
assert.equal(smallThumbMetadata.format, 'heif');
assert.equal(smallThumbMetadata.width, 40);
assert.equal(smallThumbMetadata.height, 27);
console.log('Article pic added with hash:', fullHash); console.log('Article pic added with hash:', fullHash);
const fullPath = join(storageDir, 'fullbinary', fullHash + '.avif'); const fullPath = join(storageDir, 'fullbinary', fullHash + '.avif');

View File

@@ -1,10 +1,14 @@
export const CREATE = ` export const CREATE = `
INSERT INTO pic ( INSERT INTO pic (
w, h, size, hash, thumbw, thumbh, thumbhash, source, fullbinary, thumbbinary w, h, size, hash, thumbw, thumbh, thumbhash,
smallthumbw, smallthumbh, smallthumbbinary,
source, fullbinary, thumbbinary
) )
OUTPUT INSERTED.id OUTPUT INSERTED.id
VALUES ( VALUES (
@w, @h, @size, @hash, @thumbw, @thumbh, @thumbhash, @source, @fullbinary, @thumbbinary @w, @h, @size, @hash, @thumbw, @thumbh, @thumbhash,
@smallthumbw, @smallthumbh, @smallthumbbinary,
@source, @fullbinary, @thumbbinary
) )
`; `;

View File

@@ -95,9 +95,12 @@ BEGIN
thumbw INT NOT NULL, thumbw INT NOT NULL,
thumbh INT NOT NULL, thumbh INT NOT NULL,
thumbhash NVARCHAR(64) NOT NULL, thumbhash NVARCHAR(64) NOT NULL,
smallthumbw INT NOT NULL,
smallthumbh INT NOT NULL,
source NVARCHAR(MAX) NOT NULL, source NVARCHAR(MAX) NOT NULL,
fullbinary VARBINARY(MAX) NOT NULL, fullbinary VARBINARY(MAX) NOT NULL,
thumbbinary VARBINARY(MAX) NOT NULL, thumbbinary VARBINARY(MAX) NOT NULL,
smallthumbbinary VARBINARY(MAX) NOT NULL,
CONSTRAINT UQ_pic_hash UNIQUE (hash) CONSTRAINT UQ_pic_hash UNIQUE (hash)
); );
END END
@@ -1220,4 +1223,3 @@ export const SCHEMA_SCRIPTS = [
INIT_SHIPPING, INIT_SHIPPING,
INIT_PACKAGE, INIT_PACKAGE,
]; ];

View File

@@ -8,10 +8,12 @@ export const {
export const CREATE = ` export const CREATE = `
INSERT INTO pic ( INSERT INTO pic (
w, h, size, hash, thumbw, thumbh, thumbhash, source w, h, size, hash, thumbw, thumbh, thumbhash,
smallthumbw, smallthumbh, smallthumbbinary, source
) )
VALUES ( VALUES (
@w, @h, @size, @hash, @thumbw, @thumbh, @thumbhash, @source @w, @h, @size, @hash, @thumbw, @thumbh, @thumbhash,
@smallthumbw, @smallthumbh, @smallthumbbinary, @source
) )
RETURNING id RETURNING id
`; `;

View File

@@ -43,6 +43,9 @@ CREATE TABLE IF NOT EXISTS pic (
thumbw INTEGER NOT NULL, thumbw INTEGER NOT NULL,
thumbh INTEGER NOT NULL, thumbh INTEGER NOT NULL,
thumbhash TEXT NOT NULL, thumbhash TEXT NOT NULL,
smallthumbw INTEGER NOT NULL,
smallthumbh INTEGER NOT NULL,
smallthumbbinary BLOB NOT NULL,
source TEXT NOT NULL source TEXT NOT NULL
); );
`; `;

View File

@@ -22,17 +22,17 @@ export default function addImage(provider) {
const addArticlePicSql = isSqlite ? SQLITE_ADD_ARTICLEPIC : MSSQL_ADD_ARTICLEPIC; const addArticlePicSql = isSqlite ? SQLITE_ADD_ARTICLEPIC : MSSQL_ADD_ARTICLEPIC;
/** /**
* Adds an image to an article by its SKU, processing it into AVIF variants. * Adds an image to an article by its SKU, processing full, thumb, and 40px-edge small-thumb AVIF variants.
* *
* @param {number} googleAuthId - The id of the Google auth user. * @param {number} googleAuthId - The id of the Google auth user.
* @param {string} sku - The article SKU. * @param {string} sku - The article SKU.
* @param {Buffer} image - The source image buffer. * @param {Buffer} image - The source image buffer.
* @param {object} [tx] - Optional transaction to run within. * @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, fullHash: string, thumbHash: string}>} The added image info. * @returns {Promise<{picId: number, sort: number, full: {w: number, h: number, size: number}, thumb: {w: number, h: number}, smallThumb: {w: number, h: number}, fullHash: string, thumbHash: string}>} The added image info.
* *
* @example * @example
* const result = await store.article.addImage(42, 'SKU-1', imageBuffer); * const result = await store.article.addImage(42, 'SKU-1', imageBuffer);
* // => { picId, sort, full: { w, h, size }, thumb: { w, h }, fullHash, thumbHash } * // => { picId, sort, full: { w, h, size }, thumb: { w, h }, smallThumb: { w, h }, fullHash, thumbHash }
*/ */
return async (googleAuthId, sku, image, tx) => { return async (googleAuthId, sku, image, tx) => {
const runAdd = async (runner) => { const runAdd = async (runner) => {
@@ -71,6 +71,9 @@ export default function addImage(provider) {
thumbw: processed.thumb.w, thumbw: processed.thumb.w,
thumbh: processed.thumb.h, thumbh: processed.thumb.h,
thumbhash: processed.thumb.hash, thumbhash: processed.thumb.hash,
smallthumbw: processed.smallThumb.w,
smallthumbh: processed.smallThumb.h,
smallthumbbinary: processed.smallThumb.buffer,
source: '', source: '',
fullbinary: processed.full.buffer, fullbinary: processed.full.buffer,
thumbbinary: processed.thumb.buffer, thumbbinary: processed.thumb.buffer,
@@ -107,6 +110,10 @@ export default function addImage(provider) {
w: processed.thumb.w, w: processed.thumb.w,
h: processed.thumb.h, h: processed.thumb.h,
}, },
smallThumb: {
w: processed.smallThumb.w,
h: processed.smallThumb.h,
},
fullHash, fullHash,
thumbHash: processed.thumb.hash, thumbHash: processed.thumb.hash,
}; };
@@ -121,4 +128,3 @@ export default function addImage(provider) {
return runAdd(makeRunner(provider)); return runAdd(makeRunner(provider));
}; };
} }

View File

@@ -22,13 +22,13 @@ export default function addImage(provider) {
const addBrandPicSql = isSqlite ? SQLITE_ADD_BRANDPIC : MSSQL_ADD_BRANDPIC; const addBrandPicSql = isSqlite ? SQLITE_ADD_BRANDPIC : MSSQL_ADD_BRANDPIC;
/** /**
* Adds an image to a brand, processing it into AVIF full and thumb variants. * Adds an image to a brand, processing full, thumb, and 40px-edge small-thumb AVIF variants.
* *
* @param {number} googleAuthId - The id of the Google auth user. * @param {number} googleAuthId - The id of the Google auth user.
* @param {number} brandId - The id of the brand. * @param {number} brandId - The id of the brand.
* @param {Buffer} image - The source image buffer. * @param {Buffer} image - The source image buffer.
* @param {object} [tx] - Optional transaction to run within. * @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, pics: Array<object>}>} * @returns {Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number, pics: Array<object>}>}
*/ */
return async (googleAuthId, brandId, image, tx) => { return async (googleAuthId, brandId, image, tx) => {
const runAdd = async (runner) => { const runAdd = async (runner) => {
@@ -69,6 +69,9 @@ export default function addImage(provider) {
thumbw: processed.thumb.w, thumbw: processed.thumb.w,
thumbh: processed.thumb.h, thumbh: processed.thumb.h,
thumbhash: processed.thumb.hash, thumbhash: processed.thumb.hash,
smallthumbw: processed.smallThumb.w,
smallthumbh: processed.smallThumb.h,
smallthumbbinary: processed.smallThumb.buffer,
source: '', source: '',
fullbinary: processed.full.buffer, fullbinary: processed.full.buffer,
thumbbinary: processed.thumb.buffer, thumbbinary: processed.thumb.buffer,
@@ -88,6 +91,8 @@ export default function addImage(provider) {
thumbW: processed.thumb.w, thumbW: processed.thumb.w,
thumbH: processed.thumb.h, thumbH: processed.thumb.h,
thumbHash: processed.thumb.hash, thumbHash: processed.thumb.hash,
smallThumbW: processed.smallThumb.w,
smallThumbH: processed.smallThumb.h,
}; };
return { return {
@@ -96,6 +101,8 @@ export default function addImage(provider) {
thumbW: processed.thumb.w, thumbW: processed.thumb.w,
thumbH: processed.thumb.h, thumbH: processed.thumb.h,
thumbHash: processed.thumb.hash, thumbHash: processed.thumb.hash,
smallThumbW: processed.smallThumb.w,
smallThumbH: processed.smallThumb.h,
pics: [picObj], pics: [picObj],
}; };
}; };

View File

@@ -22,13 +22,13 @@ export default function addImage(provider) {
const addCategoryPicSql = isSqlite ? SQLITE_ADD_CATEGORYPIC : MSSQL_ADD_CATEGORYPIC; const addCategoryPicSql = isSqlite ? SQLITE_ADD_CATEGORYPIC : MSSQL_ADD_CATEGORYPIC;
/** /**
* Adds an image to a category, processing it into AVIF full and thumb variants. * Adds an image to a category, processing full, thumb, and 40px-edge small-thumb AVIF variants.
* *
* @param {number} googleAuthId - The id of the Google auth user. * @param {number} googleAuthId - The id of the Google auth user.
* @param {number} categoryId - The id of the category. * @param {number} categoryId - The id of the category.
* @param {Buffer} image - The source image buffer. * @param {Buffer} image - The source image buffer.
* @param {object} [tx] - Optional transaction to run within. * @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>} The added image info. * @returns {Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, smallThumbW: number, smallThumbH: number}>} The added image info.
* *
* @example * @example
* const result = await store.category.addImage(42, 5, imageBuffer); * const result = await store.category.addImage(42, 5, imageBuffer);
@@ -71,6 +71,9 @@ export default function addImage(provider) {
thumbw: processed.thumb.w, thumbw: processed.thumb.w,
thumbh: processed.thumb.h, thumbh: processed.thumb.h,
thumbhash: processed.thumb.hash, thumbhash: processed.thumb.hash,
smallthumbw: processed.smallThumb.w,
smallthumbh: processed.smallThumb.h,
smallthumbbinary: processed.smallThumb.buffer,
source: '', source: '',
fullbinary: processed.full.buffer, fullbinary: processed.full.buffer,
thumbbinary: processed.thumb.buffer, thumbbinary: processed.thumb.buffer,
@@ -98,6 +101,8 @@ export default function addImage(provider) {
thumbW: processed.thumb.w, thumbW: processed.thumb.w,
thumbH: processed.thumb.h, thumbH: processed.thumb.h,
thumbHash: processed.thumb.hash, thumbHash: processed.thumb.hash,
smallThumbW: processed.smallThumb.w,
smallThumbH: processed.smallThumb.h,
}; };
}; };

View File

@@ -3,6 +3,7 @@ import { rgbaToThumbHash } from 'thumbhash';
const FULL_MAX = 1000; const FULL_MAX = 1000;
const THUMB_MAX = 200; const THUMB_MAX = 200;
const SMALL_THUMB_MAX = 40;
export default async function processAvifImage(inputBuffer) { export default async function processAvifImage(inputBuffer) {
const input = Buffer.isBuffer(inputBuffer) const input = Buffer.isBuffer(inputBuffer)
@@ -26,8 +27,10 @@ export default async function processAvifImage(inputBuffer) {
const thumbPipeline = sharp(input) const thumbPipeline = sharp(input)
.resize(THUMB_MAX, THUMB_MAX, { fit: 'inside', withoutEnlargement: true }); .resize(THUMB_MAX, THUMB_MAX, { fit: 'inside', withoutEnlargement: true });
const smallThumbPipeline = sharp(input)
.resize(SMALL_THUMB_MAX, SMALL_THUMB_MAX, { fit: 'inside', withoutEnlargement: true });
const [full, thumb, thumbRgba] = await Promise.all([ const [full, thumb, thumbRgba, smallThumb] = await Promise.all([
useOriginalFull useOriginalFull
? Promise.resolve({ ? Promise.resolve({
info: { width: w, height: h }, info: { width: w, height: h },
@@ -44,6 +47,9 @@ export default async function processAvifImage(inputBuffer) {
.ensureAlpha() .ensureAlpha()
.raw() .raw()
.toBuffer({ resolveWithObject: true }), .toBuffer({ resolveWithObject: true }),
smallThumbPipeline
.avif({ effort: 2 })
.toBuffer({ resolveWithObject: true }),
]); ]);
const thumbHashBytes = rgbaToThumbHash( const thumbHashBytes = rgbaToThumbHash(
@@ -66,5 +72,10 @@ export default async function processAvifImage(inputBuffer) {
buffer: thumb.data, buffer: thumb.data,
hash: Buffer.from(thumbHashBytes).toString('base64'), hash: Buffer.from(thumbHashBytes).toString('base64'),
}, },
smallThumb: {
w: smallThumb.info.width,
h: smallThumb.info.height,
buffer: smallThumb.data,
},
}; };
} }