mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Sharp 0.35.1 moved FormatEnum to a namespace export and removed "avif" from FormatEnum (now a separate literal in toFormat). BullMQ 5.78.1 bundles ioredis 5.10.1 while we have 5.11.1, causing structural type mismatch. Also fixes new Biome 1.9 lint rules.
120 lines
4.1 KiB
TypeScript
120 lines
4.1 KiB
TypeScript
import sharp from "sharp";
|
|
import { brightness } from "./operations/brightness.js";
|
|
import { colorBlindness } from "./operations/color-blindness.js";
|
|
import { colorChannels } from "./operations/color-channels.js";
|
|
import { compress } from "./operations/compress.js";
|
|
import { contrast } from "./operations/contrast.js";
|
|
import { convert } from "./operations/convert.js";
|
|
import { crop } from "./operations/crop.js";
|
|
import { editMetadata } from "./operations/edit-metadata.js";
|
|
import { flip } from "./operations/flip.js";
|
|
import { grayscale } from "./operations/grayscale.js";
|
|
import { invert } from "./operations/invert.js";
|
|
import { resize } from "./operations/resize.js";
|
|
import { rotate } from "./operations/rotate.js";
|
|
import { saturation } from "./operations/saturation.js";
|
|
import { sepia } from "./operations/sepia.js";
|
|
import { sharpen, sharpenAdvanced } from "./operations/sharpen.js";
|
|
import { stripMetadata } from "./operations/strip-metadata.js";
|
|
import type {
|
|
BrightnessOptions,
|
|
ColorBlindnessOptions,
|
|
ColorChannelOptions,
|
|
CompressOptions,
|
|
ContrastOptions,
|
|
ConvertOptions,
|
|
CropOptions,
|
|
EditMetadataOptions,
|
|
FlipOptions,
|
|
OperationResult,
|
|
OutputFormat,
|
|
ResizeOptions,
|
|
RotateOptions,
|
|
SaturationOptions,
|
|
Sharp,
|
|
SharpenAdvancedOptions,
|
|
SharpenOptions,
|
|
StripMetadataOptions,
|
|
} from "./types.js";
|
|
import { getImageInfo } from "./utils/metadata.js";
|
|
|
|
export interface Operation {
|
|
type: string;
|
|
options: Record<string, unknown>;
|
|
}
|
|
|
|
const OPERATION_MAP: Record<
|
|
string,
|
|
(image: Sharp, options: Record<string, unknown>) => Promise<Sharp>
|
|
> = {
|
|
resize: (img, opts) => resize(img, opts as unknown as ResizeOptions),
|
|
crop: (img, opts) => crop(img, opts as unknown as CropOptions),
|
|
rotate: (img, opts) => rotate(img, opts as unknown as RotateOptions),
|
|
flip: (img, opts) => flip(img, opts as unknown as FlipOptions),
|
|
convert: (img, opts) => convert(img, opts as unknown as ConvertOptions),
|
|
compress: (img, opts) => compress(img, opts as unknown as CompressOptions),
|
|
"strip-metadata": (img, opts) => stripMetadata(img, opts as unknown as StripMetadataOptions),
|
|
brightness: (img, opts) => brightness(img, opts as unknown as BrightnessOptions),
|
|
contrast: (img, opts) => contrast(img, opts as unknown as ContrastOptions),
|
|
saturation: (img, opts) => saturation(img, opts as unknown as SaturationOptions),
|
|
"color-blindness": (img, opts) => colorBlindness(img, opts as unknown as ColorBlindnessOptions),
|
|
"color-channels": (img, opts) => colorChannels(img, opts as unknown as ColorChannelOptions),
|
|
grayscale: (img) => grayscale(img),
|
|
sepia: (img) => sepia(img),
|
|
sharpen: (img, opts) => sharpen(img, opts as unknown as SharpenOptions),
|
|
"sharpen-advanced": (img, opts) =>
|
|
sharpenAdvanced(img, opts as unknown as SharpenAdvancedOptions),
|
|
invert: (img) => invert(img),
|
|
"edit-metadata": (img, opts) => editMetadata(img, opts as unknown as EditMetadataOptions),
|
|
};
|
|
|
|
const FORMAT_MAP: Record<string, string> = {
|
|
jpg: "jpeg",
|
|
png: "png",
|
|
webp: "webp",
|
|
avif: "avif",
|
|
heif: "avif",
|
|
tiff: "tiff",
|
|
gif: "gif",
|
|
jxl: "jxl",
|
|
};
|
|
|
|
/**
|
|
* Process an image through a pipeline of operations.
|
|
*
|
|
* @param input - The raw image buffer
|
|
* @param operations - Array of operations to apply in sequence
|
|
* @param outputFormat - Optional output format (defaults to input format)
|
|
* @returns The processed image buffer and metadata
|
|
*/
|
|
export async function processImage(
|
|
input: Buffer,
|
|
operations: Operation[],
|
|
outputFormat?: OutputFormat,
|
|
): Promise<OperationResult> {
|
|
let image: Sharp = sharp(input);
|
|
|
|
// Apply each operation in sequence
|
|
for (const op of operations) {
|
|
const handler = OPERATION_MAP[op.type];
|
|
if (!handler) {
|
|
throw new Error(`Unknown operation: ${op.type}`);
|
|
}
|
|
image = await handler(image, op.options);
|
|
}
|
|
|
|
// Convert to output format if specified
|
|
if (outputFormat) {
|
|
const sharpFormat = FORMAT_MAP[outputFormat];
|
|
if (!sharpFormat) {
|
|
throw new Error(`Unsupported output format: ${outputFormat}`);
|
|
}
|
|
image = image.toFormat(sharpFormat as keyof sharp.FormatEnum);
|
|
}
|
|
|
|
const buffer = await image.toBuffer();
|
|
const info = await getImageInfo(buffer);
|
|
|
|
return { buffer, info };
|
|
}
|