videofolxtv/server/aiService.ts
sebastjanartic 19e957153f Improve AI description generation to handle different content types
Update the AI service to allow specifying content type (e.g., 'oddaja' for shows) and adjust prompts accordingly to generate more relevant descriptions for various media formats.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 2cd2c0bc-434c-4bc9-ad3f-b99d3897a0d1
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2cd2c0bc-434c-4bc9-ad3f-b99d3897a0d1/n7jzC7R
2025-09-02 14:32:40 +00:00

163 lines
6.0 KiB
TypeScript

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<string, Set<string>>();
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<string> {
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("Napaka pri generiranju opisa s strani AI");
}
}
export async function generateBulkDescriptions(
videos: Array<{ id: string; title: string }>,
options: DescriptionGenerationOptions = {}
): Promise<Array<{ id: string; description: string; error?: string }>> {
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;
}