← back to Letsbegin
lib/gemini-image.ts
158 lines
/**
* Gemini Image Analysis Utility
* Uses Gemini 2.0 Flash for all image-related AI tasks
* FREE - no credits needed!
*/
const GEMINI_API_KEY = process.env.GEMINI_API_KEY
if (!GEMINI_API_KEY) {
throw new Error('GEMINI_API_KEY environment variable is required')
}
const GEMINI_ENDPOINT = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent'
export interface ImageAnalysisResult {
description: string
colors: string[]
styles: string[]
patterns: string[]
backgroundColor: string
tags: string[]
}
/**
* Analyze an image using Gemini Vision
* @param imageBase64 - Base64 encoded image data (without data URL prefix)
* @param mimeType - Image MIME type (image/jpeg, image/png, etc.)
* @param prompt - Custom prompt for analysis
*/
export async function analyzeImage(
imageBase64: string,
mimeType: string = 'image/jpeg',
prompt?: string
): Promise<string> {
const defaultPrompt = prompt || 'Describe this image in detail.'
const response = await fetch(`${GEMINI_ENDPOINT}?key=${GEMINI_API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{
parts: [
{ inlineData: { mimeType, data: imageBase64 } },
{ text: defaultPrompt }
]
}],
generationConfig: { temperature: 0.1, maxOutputTokens: 1000 }
})
})
if (!response.ok) {
const error = await response.text()
throw new Error(`Gemini Vision API error: ${error}`)
}
const data = await response.json()
return data.candidates?.[0]?.content?.parts?.[0]?.text || ''
}
/**
* Analyze wallcovering/wallpaper image for interior design tagging
* Returns structured analysis with colors, styles, patterns
*/
export async function analyzeWallcovering(
imageBase64: string,
productTitle: string,
mimeType: string = 'image/jpeg'
): Promise<ImageAnalysisResult> {
const prompt = `As an expert interior designer, analyze this wallcovering "${productTitle}".
Return ONLY valid JSON with this exact structure:
{
"backgroundColor": "single dominant background color",
"colors": ["max 3 most visible colors"],
"styles": ["1-2 design styles from: Traditional, Contemporary, Coastal, Tropical, Art Deco, Mid-Century Modern, Bohemian, Transitional, Victorian, Chinoiserie, Retro, Pop Art"],
"patterns": ["1-2 pattern types from: Botanical, Floral, Geometric, Damask, Palm, Trellis, Stripe, Abstract, Toile, Scenic, Solid, Textured"],
"tags": ["3-5 interior design tags for search"],
"description": "2-3 sentence professional interior designer description for commercial applications"
}`
const response = await fetch(`${GEMINI_ENDPOINT}?key=${GEMINI_API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{
parts: [
{ inlineData: { mimeType, data: imageBase64 } },
{ text: prompt }
]
}],
generationConfig: { temperature: 0.1, maxOutputTokens: 800 }
})
})
if (!response.ok) {
const error = await response.text()
throw new Error(`Gemini Vision API error: ${error}`)
}
const data = await response.json()
const text = data.candidates?.[0]?.content?.parts?.[0]?.text || ''
// Parse JSON from response
try {
const jsonMatch = text.match(/\{[\s\S]*\}/)
if (jsonMatch) {
return JSON.parse(jsonMatch[0])
}
} catch (e) {
console.error('Failed to parse Gemini response:', e)
}
// Return default structure if parsing fails
return {
description: text,
colors: [],
styles: [],
patterns: [],
backgroundColor: '',
tags: []
}
}
/**
* Get image from URL as base64
*/
export async function fetchImageAsBase64(imageUrl: string): Promise<{ base64: string; mimeType: string }> {
const response = await fetch(imageUrl)
if (!response.ok) {
throw new Error(`Failed to fetch image: ${response.status}`)
}
const buffer = await response.arrayBuffer()
const base64 = Buffer.from(buffer).toString('base64')
const mimeType = response.headers.get('content-type') || 'image/jpeg'
return { base64, mimeType }
}
/**
* Analyze image from URL
*/
export async function analyzeImageFromUrl(
imageUrl: string,
prompt?: string
): Promise<string> {
const { base64, mimeType } = await fetchImageAsBase64(imageUrl)
return analyzeImage(base64, mimeType, prompt)
}
/**
* Analyze wallcovering from URL
*/
export async function analyzeWallcoveringFromUrl(
imageUrl: string,
productTitle: string
): Promise<ImageAnalysisResult> {
const { base64, mimeType } = await fetchImageAsBase64(imageUrl)
return analyzeWallcovering(base64, productTitle, mimeType)
}