generated from fahricansecer/boilerplate-be
@@ -237,4 +237,100 @@ IMPORTANT: Only output valid JSON, no markdown code blocks or other text.`;
|
||||
throw new Error('Failed to parse AI response as JSON');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Görsel Üretim (Gemini Image Generation) ─────────────────────────
|
||||
|
||||
/**
|
||||
* Gemini Image Generation API ile görsel üret.
|
||||
* Raspberry Pi 5 bellek koruması için buffer olarak döner.
|
||||
*
|
||||
* @param prompt - İngilizce görsel açıklaması
|
||||
* @param aspectRatio - Görsel en-boy oranı (16:9, 9:16, 1:1)
|
||||
* @returns Base64 decoded image buffer ve mime type
|
||||
*/
|
||||
async generateImage(
|
||||
prompt: string,
|
||||
aspectRatio: '16:9' | '9:16' | '1:1' = '16:9',
|
||||
): Promise<{ buffer: Buffer; mimeType: string } | null> {
|
||||
if (!this.isAvailable()) {
|
||||
throw new Error('Gemini AI is not available. Check your configuration.');
|
||||
}
|
||||
|
||||
const imageModel = this.configService.get<string>(
|
||||
'gemini.imageModel',
|
||||
'gemini-2.0-flash-preview-image-generation',
|
||||
);
|
||||
|
||||
try {
|
||||
this.logger.debug(`🎨 Görsel üretiliyor: "${prompt.substring(0, 80)}..." [${aspectRatio}]`);
|
||||
|
||||
const response = await this.client!.models.generateContent({
|
||||
model: imageModel,
|
||||
contents: [
|
||||
{
|
||||
role: 'user',
|
||||
parts: [
|
||||
{
|
||||
text: `Generate a high-quality image for this description: ${prompt}. Aspect ratio: ${aspectRatio}. Style: photorealistic, cinematic lighting, detailed.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
config: {
|
||||
responseModalities: ['TEXT', 'IMAGE'] as any,
|
||||
},
|
||||
});
|
||||
|
||||
// Gemini image response'dan image part'ı çıkar
|
||||
const parts = (response as any).candidates?.[0]?.content?.parts || [];
|
||||
for (const part of parts) {
|
||||
if (part.inlineData?.data) {
|
||||
const buffer = Buffer.from(part.inlineData.data, 'base64');
|
||||
const mimeType = part.inlineData.mimeType || 'image/png';
|
||||
|
||||
this.logger.log(`✅ Görsel üretildi: ${(buffer.length / 1024).toFixed(1)} KB`);
|
||||
return { buffer, mimeType };
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.warn('Gemini görsel üretemedi — boş yanıt');
|
||||
return null;
|
||||
} catch (error) {
|
||||
this.logger.error(`Gemini görsel üretim hatası: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sahne bazlı görsel üret — visualPrompt ve video stili kullanarak.
|
||||
*
|
||||
* @param visualPrompt - Sahnenin İngilizce görsel açıklaması
|
||||
* @param style - Video stili (cinematic, documentary, educational vb.)
|
||||
* @param aspectRatio - En-boy oranı
|
||||
* @returns Buffer ve mimeType
|
||||
*/
|
||||
async generateImageForScene(
|
||||
visualPrompt: string,
|
||||
style: string = 'cinematic',
|
||||
aspectRatio: '16:9' | '9:16' | '1:1' = '16:9',
|
||||
): Promise<{ buffer: Buffer; mimeType: string } | null> {
|
||||
const enhancedPrompt = `${visualPrompt}. Style: ${style}, professional production quality, volumetric lighting, sharp details, 8K resolution.`;
|
||||
return this.generateImage(enhancedPrompt, aspectRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Video için thumbnail görsel üret — proje başlığı ve açıklamasından.
|
||||
*
|
||||
* @param title - Video başlığı
|
||||
* @param description - Video açıklaması
|
||||
* @returns Buffer ve mimeType
|
||||
*/
|
||||
async generateThumbnail(
|
||||
title: string,
|
||||
description: string,
|
||||
): Promise<{ buffer: Buffer; mimeType: string } | null> {
|
||||
const prompt = `Create a compelling YouTube video thumbnail for a video titled "${title}". ${description}. Make it eye-catching with bold, dynamic composition. No text overlay needed.`;
|
||||
return this.generateImage(prompt, '16:9');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user