src / media / previewPayload.ts
src / media / previewPayload.ts
import {
drawthingsLimits,
encodeJpegPreviewFromBuffer,
getDefaultPreviewOptions,
getSize,
} from "../core-bundle.mjs";
export async function shouldUsePreviewPayload(
originalBytes: Uint8Array,
usePreviews: boolean,
): Promise<boolean> {
if (!usePreviews) return false;
const { width, height } = await getSize(Buffer.from(originalBytes));
return width > 0 && height > 0 && width + height > drawthingsLimits.previewMaxSum;
}
export async function previewPayloadBytes(
originalBytes: Uint8Array,
usePreviews: boolean,
): Promise<Uint8Array> {
if (!(await shouldUsePreviewPayload(originalBytes, usePreviews))) return originalBytes;
const { data } = await encodeJpegPreviewFromBuffer(Buffer.from(originalBytes), getDefaultPreviewOptions());
return new Uint8Array(data);
}
export interface GeneratedPreviewPayload {
bytes: Uint8Array;
sourceWidth: number;
sourceHeight: number;
payloadWidth: number;
payloadHeight: number;
}
export async function generatePreviewPayload(
originalBytes: Uint8Array,
): Promise<GeneratedPreviewPayload> {
const { width: sourceWidth, height: sourceHeight } = await getSize(Buffer.from(originalBytes));
const { data, width: payloadWidth, height: payloadHeight } = await encodeJpegPreviewFromBuffer(
Buffer.from(originalBytes),
getDefaultPreviewOptions(),
);
return {
bytes: new Uint8Array(data),
sourceWidth,
sourceHeight,
payloadWidth,
payloadHeight,
};
}
/**
* Safety limit for the no-preview path (usePreviewsForQueryEmbedding: false).
* Oversize originals (e.g. RAW 6000x4000) exceed the embedding context and
* fail hard on the server, so nothing lands in the store. Capping the sum at
* the configured limit keeps such images embeddable instead of lost.
* Returns the original bytes untouched when already within the limit.
*/
export async function capOriginalPayload(
originalBytes: Uint8Array,
maxSum: number,
): Promise<GeneratedPreviewPayload> {
const { width: sourceWidth, height: sourceHeight } = await getSize(Buffer.from(originalBytes));
if (sourceWidth <= 0 || sourceHeight <= 0 || sourceWidth + sourceHeight <= maxSum) {
return {
bytes: originalBytes,
sourceWidth,
sourceHeight,
payloadWidth: sourceWidth,
payloadHeight: sourceHeight,
};
}
const { data, width: payloadWidth, height: payloadHeight } = await encodeJpegPreviewFromBuffer(
Buffer.from(originalBytes),
{ maxDim: maxSum, maxSum, mode: "sum", quality: drawthingsLimits.previewQuality },
);
console.debug(
`[Index] Original exceeds embedding safety limit: ${sourceWidth}x${sourceHeight} (sum ${sourceWidth + sourceHeight}) -> capped to ${payloadWidth}x${payloadHeight} (sum ${payloadWidth + payloadHeight}/${maxSum})`,
);
return {
bytes: new Uint8Array(data),
sourceWidth,
sourceHeight,
payloadWidth,
payloadHeight,
};
}import {
drawthingsLimits,
encodeJpegPreviewFromBuffer,
getDefaultPreviewOptions,
getSize,
} from "../core-bundle.mjs";
export async function shouldUsePreviewPayload(
originalBytes: Uint8Array,
usePreviews: boolean,
): Promise<boolean> {
if (!usePreviews) return false;
const { width, height } = await getSize(Buffer.from(originalBytes));
return width > 0 && height > 0 && width + height > drawthingsLimits.previewMaxSum;
}
export async function previewPayloadBytes(
originalBytes: Uint8Array,
usePreviews: boolean,
): Promise<Uint8Array> {
if (!(await shouldUsePreviewPayload(originalBytes, usePreviews))) return originalBytes;
const { data } = await encodeJpegPreviewFromBuffer(Buffer.from(originalBytes), getDefaultPreviewOptions());
return new Uint8Array(data);
}
export interface GeneratedPreviewPayload {
bytes: Uint8Array;
sourceWidth: number;
sourceHeight: number;
payloadWidth: number;
payloadHeight: number;
}
export async function generatePreviewPayload(
originalBytes: Uint8Array,
): Promise<GeneratedPreviewPayload> {
const { width: sourceWidth, height: sourceHeight } = await getSize(Buffer.from(originalBytes));
const { data, width: payloadWidth, height: payloadHeight } = await encodeJpegPreviewFromBuffer(
Buffer.from(originalBytes),
getDefaultPreviewOptions(),
);
return {
bytes: new Uint8Array(data),
sourceWidth,
sourceHeight,
payloadWidth,
payloadHeight,
};
}
/**
* Safety limit for the no-preview path (usePreviewsForQueryEmbedding: false).
* Oversize originals (e.g. RAW 6000x4000) exceed the embedding context and
* fail hard on the server, so nothing lands in the store. Capping the sum at
* the configured limit keeps such images embeddable instead of lost.
* Returns the original bytes untouched when already within the limit.
*/
export async function capOriginalPayload(
originalBytes: Uint8Array,
maxSum: number,
): Promise<GeneratedPreviewPayload> {
const { width: sourceWidth, height: sourceHeight } = await getSize(Buffer.from(originalBytes));
if (sourceWidth <= 0 || sourceHeight <= 0 || sourceWidth + sourceHeight <= maxSum) {
return {
bytes: originalBytes,
sourceWidth,
sourceHeight,
payloadWidth: sourceWidth,
payloadHeight: sourceHeight,
};
}
const { data, width: payloadWidth, height: payloadHeight } = await encodeJpegPreviewFromBuffer(
Buffer.from(originalBytes),
{ maxDim: maxSum, maxSum, mode: "sum", quality: drawthingsLimits.previewQuality },
);
console.debug(
`[Index] Original exceeds embedding safety limit: ${sourceWidth}x${sourceHeight} (sum ${sourceWidth + sourceHeight}) -> capped to ${payloadWidth}x${payloadHeight} (sum ${payloadWidth + payloadHeight}/${maxSum})`,
);
return {
bytes: new Uint8Array(data),
sourceWidth,
sourceHeight,
payloadWidth,
payloadHeight,
};
}