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

@@ -1,10 +1,14 @@
export const CREATE = `
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
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,
thumbh INT NOT NULL,
thumbhash NVARCHAR(64) NOT NULL,
smallthumbw INT NOT NULL,
smallthumbh INT NOT NULL,
source NVARCHAR(MAX) NOT NULL,
fullbinary VARBINARY(MAX) NOT NULL,
thumbbinary VARBINARY(MAX) NOT NULL,
smallthumbbinary VARBINARY(MAX) NOT NULL,
CONSTRAINT UQ_pic_hash UNIQUE (hash)
);
END
@@ -1220,4 +1223,3 @@ export const SCHEMA_SCRIPTS = [
INIT_SHIPPING,
INIT_PACKAGE,
];

View File

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

View File

@@ -43,6 +43,9 @@ CREATE TABLE IF NOT EXISTS pic (
thumbw INTEGER NOT NULL,
thumbh INTEGER NOT NULL,
thumbhash TEXT NOT NULL,
smallthumbw INTEGER NOT NULL,
smallthumbh INTEGER NOT NULL,
smallthumbbinary BLOB 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;
/**
* 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 {string} sku - The article SKU.
* @param {Buffer} image - The source image buffer.
* @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
* 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) => {
const runAdd = async (runner) => {
@@ -71,6 +71,9 @@ export default function addImage(provider) {
thumbw: processed.thumb.w,
thumbh: processed.thumb.h,
thumbhash: processed.thumb.hash,
smallthumbw: processed.smallThumb.w,
smallthumbh: processed.smallThumb.h,
smallthumbbinary: processed.smallThumb.buffer,
source: '',
fullbinary: processed.full.buffer,
thumbbinary: processed.thumb.buffer,
@@ -107,6 +110,10 @@ export default function addImage(provider) {
w: processed.thumb.w,
h: processed.thumb.h,
},
smallThumb: {
w: processed.smallThumb.w,
h: processed.smallThumb.h,
},
fullHash,
thumbHash: processed.thumb.hash,
};
@@ -121,4 +128,3 @@ export default function addImage(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;
/**
* 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} brandId - The id of the brand.
* @param {Buffer} image - The source image buffer.
* @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) => {
const runAdd = async (runner) => {
@@ -69,6 +69,9 @@ export default function addImage(provider) {
thumbw: processed.thumb.w,
thumbh: processed.thumb.h,
thumbhash: processed.thumb.hash,
smallthumbw: processed.smallThumb.w,
smallthumbh: processed.smallThumb.h,
smallthumbbinary: processed.smallThumb.buffer,
source: '',
fullbinary: processed.full.buffer,
thumbbinary: processed.thumb.buffer,
@@ -88,6 +91,8 @@ export default function addImage(provider) {
thumbW: processed.thumb.w,
thumbH: processed.thumb.h,
thumbHash: processed.thumb.hash,
smallThumbW: processed.smallThumb.w,
smallThumbH: processed.smallThumb.h,
};
return {
@@ -96,6 +101,8 @@ export default function addImage(provider) {
thumbW: processed.thumb.w,
thumbH: processed.thumb.h,
thumbHash: processed.thumb.hash,
smallThumbW: processed.smallThumb.w,
smallThumbH: processed.smallThumb.h,
pics: [picObj],
};
};

View File

@@ -22,13 +22,13 @@ export default function addImage(provider) {
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} categoryId - The id of the category.
* @param {Buffer} image - The source image buffer.
* @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
* const result = await store.category.addImage(42, 5, imageBuffer);
@@ -71,6 +71,9 @@ export default function addImage(provider) {
thumbw: processed.thumb.w,
thumbh: processed.thumb.h,
thumbhash: processed.thumb.hash,
smallthumbw: processed.smallThumb.w,
smallthumbh: processed.smallThumb.h,
smallthumbbinary: processed.smallThumb.buffer,
source: '',
fullbinary: processed.full.buffer,
thumbbinary: processed.thumb.buffer,
@@ -98,6 +101,8 @@ export default function addImage(provider) {
thumbW: processed.thumb.w,
thumbH: processed.thumb.h,
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 THUMB_MAX = 200;
const SMALL_THUMB_MAX = 40;
export default async function processAvifImage(inputBuffer) {
const input = Buffer.isBuffer(inputBuffer)
@@ -26,8 +27,10 @@ export default async function processAvifImage(inputBuffer) {
const thumbPipeline = sharp(input)
.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
? Promise.resolve({
info: { width: w, height: h },
@@ -44,6 +47,9 @@ export default async function processAvifImage(inputBuffer) {
.ensureAlpha()
.raw()
.toBuffer({ resolveWithObject: true }),
smallThumbPipeline
.avif({ effort: 2 })
.toBuffer({ resolveWithObject: true }),
]);
const thumbHashBytes = rgbaToThumbHash(
@@ -66,5 +72,10 @@ export default async function processAvifImage(inputBuffer) {
buffer: thumb.data,
hash: Buffer.from(thumbHashBytes).toString('base64'),
},
smallThumb: {
w: smallThumb.info.width,
h: smallThumb.info.height,
buffer: smallThumb.data,
},
};
}