ResumeVideo / server.js
j-r-b's picture
Upload 7 files
47c46ea verified
import express from 'express';
import { YoutubeTranscript } from 'youtube-transcript';
import path from 'path';
import { fileURLToPath } from 'url';
// Configuration pour __dirname avec ES Modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
// Hugging Face Spaces utilise le port 7860 par défaut
const port = process.env.PORT || 7860;
// Servir les fichiers statiques du dossier 'public'
app.use(express.static(path.join(__dirname, 'public')));
app.get('/get-transcript', async (req, res) => {
const videoUrl = req.query.url;
if (!videoUrl) {
return res.status(400).json({ error: 'URL de la vidéo manquante' });
}
try {
// Essayer d'extraire l'ID de différentes formes d'URL YouTube
let videoId = '';
if (videoUrl.includes('v=')) {
videoId = videoUrl.split('v=')[1].split('&')[0];
} else if (videoUrl.includes('youtu.be/')) {
videoId = videoUrl.split('youtu.be/')[1].split('?')[0];
} else {
// On pourrait ajouter d'autres formats d'URL ici (shorts, etc.)
// Pour l'instant, on assume que c'est un ID direct si ce n'est pas une URL connue
videoId = videoUrl;
}
if (!videoId) {
return res.status(400).json({ error: "Impossible d'extraire l'ID de la vidéo depuis l'URL." });
}
console.log(`Fetching transcript for video ID: ${videoId}`);
// Ajout d'un timeout pour éviter que la requête reste bloquée trop longtemps
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout dépassé lors de la récupération de la transcription')), 15000)
);
// Utilisation de Promise.race pour implémenter un timeout
const transcript = await Promise.race([
YoutubeTranscript.fetchTranscript(videoId),
timeoutPromise
]);
if (!transcript || transcript.length === 0) {
return res.status(404).json({ error: 'Transcription non trouvée ou vide pour cette vidéo.' });
}
// Concaténer les textes de la transcription
const fullText = transcript.map(item => item.text).join(' ');
res.json({ transcript: fullText });
} catch (error) {
// Log détaillé de l'erreur pour le débogage
console.error('Erreur détaillée lors de la récupération de la transcription:', {
message: error.message,
stack: error.stack,
name: error.name
});
// Gestion des cas d'erreur spécifiques
if (error.message && error.message.includes('Could not find transcripts')) {
return res.status(404).json({ error: "Aucune transcription disponible pour cette vidéo (elles sont peut-être désactivées ou n'existent pas en auto-généré)." });
}
if (error.message && error.message.includes('is not a valid video ID')) {
return res.status(400).json({ error: `L'ID vidéo extrait ('${error.message.split("'")[1]}') n'est pas valide. Vérifiez l'URL.` });
}
if (error.message && error.message.includes('Timeout dépassé')) {
return res.status(504).json({ error: 'Délai d\'attente dépassé lors de la récupération de la transcription. Veuillez réessayer.' });
}
// Nouvelle gestion spécifique pour les transcriptions désactivées
if (error.message && error.message.includes('Transcript is disabled')) {
const videoId = error.message.match(/\(([^)]+)\)/)?.[1] || "cette vidéo";
return res.status(404).json({
error: `Les sous-titres/transcriptions sont désactivés sur cette vidéo (ID: ${videoId}).`,
solution: "Essayez une autre vidéo qui possède des sous-titres activés."
});
}
// Message d'erreur plus détaillé pour les autres cas
res.status(500).json({
error: 'Erreur interne du serveur lors de la récupération de la transcription.',
details: error.message
});
}
});
// Route pour vérifier l'état du serveur (utile pour Hugging Face)
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok' });
});
// Route par défaut
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.listen(port, '0.0.0.0', () => {
console.log(`Serveur démarré sur http://0.0.0.0:${port}`);
});