import OpenAI from "openai"; // the newest OpenAI model is "gpt-5" which was released August 7, 2025. do not change this unless explicitly requested by the user const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); // Memory for generated descriptions to avoid repetition const generatedDescriptions = new Map>(); function getArtistFromTitle(title: string): string { // Extract artist name from title (everything before the first " - ") const match = title.match(/^([^-]+)/); return match ? match[1].trim() : ""; } function addToMemory(artist: string, description: string) { if (!generatedDescriptions.has(artist)) { generatedDescriptions.set(artist, new Set()); } generatedDescriptions.get(artist)!.add(description); } function getPreviousDescriptions(artist: string): string[] { return Array.from(generatedDescriptions.get(artist) || []); } export interface DescriptionGenerationOptions { maxCharacters?: number; language?: string; includeArtistInfo?: boolean; includeLabelInfo?: boolean; customInstructions?: string; contentType?: string; } export async function generateVideoDescription( title: string, options: DescriptionGenerationOptions = {} ): Promise { const { maxCharacters = 500, language = "slovenian", includeArtistInfo = true, includeLabelInfo = true, customInstructions = "", contentType = "music_video" } = options; try { // Extract artist name for memory checking const artist = getArtistFromTitle(title); const previousDescriptions = getPreviousDescriptions(artist); const avoidRepetition = previousDescriptions.length > 0 ? `\n\nWICHTIG: Für diesen Künstler wurden bereits folgende Beschreibungen erstellt:\n${previousDescriptions.map((desc, i) => `${i+1}. ${desc.substring(0, 100)}...`).join('\n')}\n\nErstelle eine VÖLLIG ANDERE Beschreibung mit anderen Worten, Fokus und Stil.` : ''; // Determine content type specific instructions const isShow = contentType === 'oddaja'; const contentTypeText = isShow ? 'Show/Sendung-Titel' : 'Musikvideo-Titel'; const contentDescription = isShow ? `WICHTIG: Dies ist eine TV-SENDUNG/SHOW (oddaja), NICHT ein Musikvideo! Analysiere den Show-Titel und beschreibe: - Die Show/Sendung und ihr Format - Die Gastgeber oder Moderatoren - Das Thema oder den Inhalt der Episode - Für wen die Show gedacht ist (Zielgruppe) - Art der Sendung (Talk-Show, Musiksendung, Unterhaltung, etc.)` : `Aus dem Titel extrahieren: - Name des Interpreten/Künstlers - Titel des Liedes/Stücks - Art des Inhalts (Lied, Instrumental, Live-Auftritt, etc.) Erstelle eine informative Beschreibung, die Folgendes beinhaltet: ${includeArtistInfo ? '- Informationen über den Interpreten (Musikstil, kurze Geschichte, bekannte Stücke)' : ''} - Beschreibung des Musikstils und Genres - Kurzer Hintergrund zum Stück, falls bekannt - Wofür es gedacht ist (Tanz, Zuhören, Konzert, etc.)`; const prompt = `Analysiere diesen ${contentTypeText}: "${title}" Schreibe auf DEUTSCH eine informative Beschreibung. ${contentDescription} NUR wenn du SICHERE Informationen hast, erwähne: ${includeLabelInfo ? '- Label/Plattenfirma (nur wenn du es sicher weißt)' : ''} Wenn du keine sicheren Informationen zu einem Punkt hast, lasse ihn WEG. Schreibe NIEMALS "Label unbekannt" oder ähnliches. Die Beschreibung soll maximal ${maxCharacters} Zeichen lang sein. Schreibe in einem freundlichen, informativen Ton. ${isShow ? 'Beschreibe die Show/Sendung, nicht die Musik.' : 'Verwende nicht die Wörter "Video" oder "Aufnahme" - schreibe über die Musik selbst.'} ${customInstructions ? `\nZUSÄTZLICHE ANWEISUNGEN: ${customInstructions}` : ''} Schreibe nur die Beschreibung, keine zusätzlichen Erklärungen.${avoidRepetition}";` console.log("Sending request to OpenAI with title:", title); // Debug log const response = await openai.chat.completions.create({ model: "gpt-4o", // Use gpt-4o instead of gpt-5 for better reliability messages: [ { role: "system", content: `Du bist ein ${isShow ? 'Medien- und TV-Show-Experte' : 'Musikexperte'} und hilfst bei der Erstellung hochwertiger Beschreibungen für ${isShow ? 'TV-Sendungen und Shows' : 'Musikinhalte'}. Du antwortest immer auf Deutsch und vermeidest Wiederholungen.` }, { role: "user", content: prompt } ], max_tokens: Math.ceil(maxCharacters / 2), // Use max_tokens for gpt-4o temperature: 0.7, // gpt-4o supports temperature }); console.log("OpenAI response received:", response.choices[0]?.message?.content); // Debug log const description = response.choices[0].message.content?.trim() || ""; // Add to memory for this artist if (artist && description) { addToMemory(artist, description); } console.log("Generated description:", description); // Debug log // Ensure we don't exceed character limit if (description.length > maxCharacters) { return description.substring(0, maxCharacters - 3) + "..."; } return description; } catch (error: any) { console.error("Error generating video description:", error); console.error("Error details:", error?.message || error); // More detailed error logging throw new Error("Fehler bei der KI-Beschreibungsgenerierung"); } } export async function generateBulkDescriptions( videos: Array<{ id: string; title: string }>, options: DescriptionGenerationOptions = {} ): Promise> { const results = []; for (const video of videos) { try { const description = await generateVideoDescription(video.title, options); results.push({ id: video.id, description }); // Add small delay to respect API rate limits await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { results.push({ id: video.id, description: "", error: error instanceof Error ? error.message : "Unknown error" }); } } return results; }