import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { PrismaService } from '../../../database/prisma.service'; import { GeminiService } from '../../gemini/gemini.service'; import { NeuroAnalysisResult, YoutubeAudit } from '../types/skriptai.types'; /** * AnalysisService * * Service for AI-powered content analysis including: * - Neuro Marketing Analysis * - YouTube Audit * - Commercial Brief Generation * * TR: AI destekli içerik analizi servisi (Nöro Pazarlama, YouTube Denetimi, Ticari Brief). * EN: Service for AI-powered content analysis. */ @Injectable() export class AnalysisService { private readonly logger = new Logger(AnalysisService.name); constructor( private readonly prisma: PrismaService, private readonly gemini: GeminiService, ) {} /** * Perform Neuro Marketing Analysis on a script * * @param projectId - Project ID * @returns Neuro analysis result */ async analyzeNeuroMarketing(projectId: string): Promise { const project = await this.prisma.scriptProject.findUnique({ where: { id: projectId }, include: { segments: { orderBy: { sortOrder: 'asc' } } }, }); if (!project) { throw new NotFoundException(`Project with ID ${projectId} not found`); } const fullScript = project.segments .map((s) => s.narratorScript) .join('\n\n'); const prompt = `Analyze this script using Consumer Neuroscience and Cialdini's 6 Principles of Persuasion. Script: ${fullScript.substring(0, 10000)} Provide: 1. Engagement Score (0-100): How well does it capture attention? 2. Dopamine Score (0-100): Does it create anticipation & reward loops? 3. Clarity Score (0-100): Is the message clear and memorable? 4. Cialdini's Persuasion Metrics (0-100 each): - Reciprocity: Does it give value first? - Scarcity: Does it create urgency? - Authority: Does it establish credibility? - Consistency: Does it align with viewer beliefs? - Liking: Is the tone likeable/relatable? - Social Proof: Does it reference others' actions? 5. Neuro Metrics: - Attention Hooks: Moments that grab attention - Emotional Triggers: Points that evoke emotion - Memory Anchors: Unique/memorable elements - Action Drivers: CTAs or challenges 6. Suggestions: 3-5 specific improvements Return JSON: { "engagementScore": number, "dopamineScore": number, "clarityScore": number, "persuasionMetrics": { "reciprocity": number, "scarcity": number, "authority": number, "consistency": number, "liking": number, "socialProof": number }, "neuroMetrics": { "attentionHooks": ["..."], "emotionalTriggers": ["..."], "memoryAnchors": ["..."], "actionDrivers": ["..."] }, "suggestions": ["..."] }`; const resp = await this.gemini.generateJSON( prompt, '{ engagementScore, dopamineScore, clarityScore, persuasionMetrics, neuroMetrics, suggestions }', ); // Save to project await this.prisma.scriptProject.update({ where: { id: projectId }, data: { neuroAnalysis: resp.data as any }, }); return resp.data; } /** * Perform YouTube Audit * * @param projectId - Project ID * @returns YouTube audit result */ async performYoutubeAudit(projectId: string): Promise { const project = await this.prisma.scriptProject.findUnique({ where: { id: projectId }, include: { segments: { orderBy: { sortOrder: 'asc' } } }, }); if (!project) { throw new NotFoundException(`Project with ID ${projectId} not found`); } const fullScript = project.segments .map((s) => s.narratorScript) .join('\n\n'); const prompt = `Perform a YouTube Algorithm Audit on this script for topic "${project.topic}". Script: ${fullScript.substring(0, 10000)} Analyze and provide: 1. Hook Score (0-100): First 10 seconds effectiveness 2. Pacing Score (0-100): Does it maintain momentum? 3. Viral Potential (0-100): Shareability factor 4. Retention Analysis: 3-5 potential drop-off points with time, issue, suggestion, severity (High/Medium/Low) 5. Thumbnail Concepts: 3 high-CTR thumbnail ideas with: - Concept name - Visual description - Text overlay - Color psychology - Emotion target - AI generation prompt 6. Title Options: 5 clickable titles (curiosity gap, numbers, power words) 7. Community Post: Engaging post to tease the video 8. Pinned Comment: Engagement-driving first comment 9. SEO Description: Optimized video description with keywords 10. Keywords: 10 relevant search keywords Return JSON: { "hookScore": number, "pacingScore": number, "viralPotential": number, "retentionAnalysis": [{ "time": "0:30", "issue": "...", "suggestion": "...", "severity": "High" }], "thumbnails": [{ "conceptName": "...", "visualDescription": "...", "textOverlay": "...", "colorPsychology": "...", "emotionTarget": "...", "aiPrompt": "..." }], "titles": ["..."], "communityPost": "...", "pinnedComment": "...", "description": "...", "keywords": ["..."] }`; const resp = await this.gemini.generateJSON( prompt, '{ hookScore, pacingScore, viralPotential, retentionAnalysis, thumbnails, titles, communityPost, pinnedComment, description, keywords }', ); // Save to project await this.prisma.scriptProject.update({ where: { id: projectId }, data: { youtubeAudit: resp.data as any }, }); return resp.data; } /** * Generate Commercial Brief (Sponsorship Analysis) * * @param projectId - Project ID * @returns Commercial brief with sponsor suggestions */ async generateCommercialBrief(projectId: string) { const project = await this.prisma.scriptProject.findUnique({ where: { id: projectId }, include: { segments: { orderBy: { sortOrder: 'asc' } } }, }); if (!project) { throw new NotFoundException(`Project with ID ${projectId} not found`); } const fullScript = project.segments .map((s) => s.narratorScript) .join('\n\n'); const prompt = `Analyze this content for commercial viability and sponsorship opportunities. Topic: "${project.topic}" Audience: ${project.targetAudience.join(', ')} Content Type: ${project.contentType} Script excerpt: ${fullScript.substring(0, 5000)} Provide: 1. Viability Score (1-10 scale as string): "8/10" 2. Viability Reason: Why this content is commercially viable 3. Sponsor Suggestions (3-5 potential sponsors): - Company name - Industry - Match reason (why this sponsor fits) - Email draft (outreach template) Return JSON: { "viabilityScore": "8/10", "viabilityReason": "...", "sponsors": [ { "name": "Company Name", "industry": "Tech/Finance/etc", "matchReason": "...", "emailDraft": "..." } ] }`; const resp = await this.gemini.generateJSON<{ viabilityScore: string; viabilityReason: string; sponsors: { name: string; industry: string; matchReason: string; emailDraft: string; }[]; }>(prompt, '{ viabilityScore, viabilityReason, sponsors }'); // Save to project await this.prisma.scriptProject.update({ where: { id: projectId }, data: { commercialBrief: resp.data as any }, }); return resp.data; } /** * Generate thumbnails using external image service * * @param prompt - Image generation prompt * @returns Generated image URL */ /** * Generate thumbnails using external image service * Applies "Nano Banana" prompt enrichment for high-quality results. * * @param prompt - Image generation prompt * @returns Generated image URL */ async generateThumbnailImage(prompt: string): Promise { // Quality boosters (Nano Banana style) const QUALITY_BOOSTERS = [ 'highly detailed', '8k resolution', 'professional photography', 'studio lighting', 'sharp focus', 'cinematic composition', 'vibrant colors', 'masterpiece', ]; // Enrich prompt with Nano Banana logic const enrichedPrompt = `${prompt}, ${QUALITY_BOOSTERS.join(', ')}. CRITICAL OBJECTIVE: The result MUST achieve a perfect 10/10 score. Clarity: 10/10. Professionalism: 10/10.`; // Use Real Nano Banana (Gemini Imagen) return await this.gemini.generateImage(enrichedPrompt); } /** * Generate visual assets for a project * * @param projectId - Project ID * @param count - Number of assets to generate * @returns Generated visual assets */ async generateVisualAssets(projectId: string, count: number = 5) { const project = await this.prisma.scriptProject.findUnique({ where: { id: projectId }, }); if (!project) { throw new NotFoundException(`Project with ID ${projectId} not found`); } const prompt = `Generate ${count} specific, simple visual keywords for an image generator about "${project.topic}". Format: "subject action context style". Keep it English, concise, no special chars. Return JSON array of strings.`; const resp = await this.gemini.generateJSON( prompt, '["keyword1", "keyword2", ...]', ); // Generate image URLs and save to database const assets = await Promise.all( resp.data.map(async (keyword) => { const url = await this.generateThumbnailImage(keyword); return this.prisma.visualAsset.create({ data: { projectId, url, desc: keyword, selected: true, }, }); }), ); return assets; } }