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
163 lines
6.0 KiB
TypeScript
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;
|
|
} |