darkmedia-x-api / backend /api /refresh_metadata.js
cybermedia's picture
Upload folder using huggingface_hub
343eed9 verified
// .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);