// .youtube/scripts/refresh_metadata.js // Recrée les titres, descriptions et tags via Groq pour TOUTES les vidéos traitées (_published). // Basé sur le style DarkMedia-X : Horreur Analogique / Found Footage / IA / Shorts. const fs = require("fs"); const path = require("path"); const Groq = require("groq-sdk"); const Anthropic = require("@anthropic-ai/sdk"); // --- CONFIGURATION --- const SCRIPT_DIR = __dirname; const YOUTUBE_DIR = path.dirname(SCRIPT_DIR); const VIDEOS_DIR = path.join(YOUTUBE_DIR, "videos"); const PUBLISHED_DIR = path.join(VIDEOS_DIR, "_published"); // Charger les clés depuis .env (à la racine) require('dotenv').config({ path: path.join(__dirname, '../../.env'), override: true }); const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, }); async function generateMetadata(oldTitle, slug) { console.log(` [*] Appel Claude 3.5 (Anthropic) pour: ${slug}...`); const response = await anthropic.messages.create({ model: "claude-3-5-sonnet-20241022", max_tokens: 1024, system: `You are a YouTube viral expert specializing in Analog Horror (Found Footage) for the channel "DarkMedia-X". Your style is mysterious, disturbing, and high-retention. The goal is to hook viewers in the first 3 seconds and make them want to know the lore. Always include "#Shorts" in both title and description. All responses in French (primary) with English keywords (#Shorts, #AnalogHorror).`, messages: [ { role: "user", content: `Create metadata for a horror video with: - Current Title / Context: ${oldTitle} - Slug: ${slug} Output format (STRICT): TITLE: [A scary, clickbaity mystery title in French, including #Shorts] DESCRIPTION: [A short, lore-heavy description that builds atmosphere. Use 2-3 lines of text, then hashtags: #Shorts #Horreur #DarkMedia-X #AnalogHorror #FoundFootage] TAGS: [15-20 comma-separated tags]` } ], }); const raw = response.content[0].text; const titleMatch = raw.match(/TITLE:\s*(.+)/); const descMatch = raw.match(/DESCRIPTION:\s*([\s\S]+?)(?=TAGS:|$)/); const tagsMatch = raw.match(/TAGS:\s*([\s\S]+)/); return { title: titleMatch ? titleMatch[1].trim() : `${oldTitle} #Shorts`, description: descMatch ? descMatch[1].trim() : `${oldTitle}\n\n#Shorts #Horreur #DarkMedia-X`, tags: tagsMatch ? tagsMatch[1].trim() : "Shorts, Horreur, DarkMedia-X" }; } async function processFolder(folderName) { const folderPath = path.join(PUBLISHED_DIR, folderName); const titleFile = path.join(folderPath, "title.txt"); const descFile = path.join(folderPath, "description.txt"); const tagsFile = path.join(folderPath, "tags.txt"); const metaFile = path.join(folderPath, "metadata.json"); let oldTitle = folderName.replace(/_/g, " "); if (fs.existsSync(titleFile)) { oldTitle = fs.readFileSync(titleFile, "utf8").trim(); } try { const updated = await generateMetadata(oldTitle, folderName); // Backup current title into metadata let metadata = {}; if (fs.existsSync(metaFile)) { metadata = JSON.parse(fs.readFileSync(metaFile, "utf8")); } metadata.backup_old_title = metadata.title || oldTitle; metadata.title = updated.title; metadata.refined_title = updated.title; metadata.description_detailed = updated.description; metadata.tags = updated.tags.split(",").map(s => s.trim()); metadata.status = "refreshed_with_ai"; metadata.updated_at = new Date().toISOString(); fs.writeFileSync(titleFile, updated.title, "utf8"); fs.writeFileSync(descFile, updated.description, "utf8"); fs.writeFileSync(tagsFile, updated.tags, "utf8"); fs.writeFileSync(metaFile, JSON.stringify(metadata, null, 4), "utf8"); console.log(` [+] Folder "${folderName}" OK.`); } catch (e) { console.error(` [X] Failed for ${folderName}:`, e.message); } } async function main() { console.log("\n--- DarkMedia-X : METADATA REFRESHER (AI) ---"); if (!fs.existsSync(PUBLISHED_DIR)) { console.log(`Aborting: ${PUBLISHED_DIR} is missing.`); return; } const folders = fs.readdirSync(PUBLISHED_DIR).filter(f => { return fs.statSync(path.join(PUBLISHED_DIR, f)).isDirectory() && !f.startsWith("_"); }); console.log(`Scanning ${folders.length} folders in _published...`); for (const folder of folders) { console.log(`- Traitement de ${folder}...`); await processFolder(folder); // Add small delay to avoid rate limiting await new Promise(r => setTimeout(r, 1000)); } console.log("\n✅ Tous les titres et descriptions ont été recréés !"); } main().catch(console.error);