Spaces:
Sleeping
Sleeping
| // .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); | |