feat(29-01): add dominant color extraction via Sharp
extractDominantColor() resizes image to 1x1 pixel for weighted average color. Integrated into fetchImageFromUrl to return dominantColor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import sharp from "sharp";
|
||||
import { uploadImage } from "./storage.service";
|
||||
|
||||
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp"];
|
||||
@@ -8,6 +9,7 @@ const FETCH_TIMEOUT = 10_000; // 10 seconds
|
||||
interface FetchImageResult {
|
||||
filename: string;
|
||||
sourceUrl: string;
|
||||
dominantColor: string | null;
|
||||
}
|
||||
|
||||
export async function fetchImageFromUrl(
|
||||
@@ -75,5 +77,29 @@ export async function fetchImageFromUrl(
|
||||
// Upload to object storage
|
||||
await uploadImage(Buffer.from(buffer), filename, contentType);
|
||||
|
||||
return { filename, sourceUrl: url };
|
||||
const dominantColor = await extractDominantColor(buffer);
|
||||
|
||||
return { filename, sourceUrl: url, dominantColor };
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the dominant color from an image buffer.
|
||||
* Resizes to 1x1 pixel for a perceptually weighted average.
|
||||
* Returns hex string like '#a3b2c1' or null on failure.
|
||||
*/
|
||||
export async function extractDominantColor(
|
||||
buffer: Buffer | ArrayBuffer,
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const { data } = await sharp(Buffer.from(buffer))
|
||||
.resize(1, 1)
|
||||
.raw()
|
||||
.toBuffer({ resolveWithObject: true });
|
||||
const r = data[0];
|
||||
const g = data[1];
|
||||
const b = data[2];
|
||||
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user