148 lines
5.2 KiB
JavaScript
148 lines
5.2 KiB
JavaScript
const { LogType } = require('loguix');
|
|
const clog = new LogType("Youtube-Stream");
|
|
const { spawn } = require('child_process');
|
|
const { __glob } = require('../../utils/GlobalVars');
|
|
// Variable globale pour stocker le processus actif
|
|
let currentYtProcess = null;
|
|
|
|
/**
|
|
* Tue le processus yt-dlp en cours proprement et attend sa fin réelle.
|
|
* Cela garantit qu'aucun flux ne se chevauche.
|
|
*/
|
|
function killCurrentProcess() {
|
|
return new Promise((resolve) => {
|
|
if (!currentYtProcess || currentYtProcess.exitCode !== null) {
|
|
currentYtProcess = null;
|
|
return resolve();
|
|
}
|
|
|
|
const pid = currentYtProcess.pid;
|
|
clog.log(`[YT-DLP] Nettoyage violent du processus PID: ${pid}`);
|
|
|
|
// Détection de l'OS pour utiliser la bonne commande de kill
|
|
const isWindows = process.platform === 'win32';
|
|
|
|
if (isWindows) {
|
|
// Sur Windows, taskkill /T (Tree) /F (Force) est nécessaire pour tuer les enfants (ffmpeg)
|
|
try {
|
|
exec(`taskkill /pid ${pid} /T /F`, (err) => {
|
|
// Peu importe l'erreur (ex: processus déjà mort), on considère que c'est fini
|
|
currentYtProcess = null;
|
|
resolve();
|
|
});
|
|
} catch (e) {
|
|
// Fallback si taskkill échoue
|
|
try { currentYtProcess.kill('SIGKILL'); } catch (e2) {}
|
|
currentYtProcess = null;
|
|
resolve();
|
|
}
|
|
} else {
|
|
// Sur Linux/Mac, on tente de tuer le groupe de processus
|
|
try {
|
|
process.kill(-pid, 'SIGKILL');
|
|
} catch (e) {
|
|
// Fallback si le group kill échoue
|
|
try { currentYtProcess.kill('SIGKILL'); } catch (e2) {}
|
|
}
|
|
currentYtProcess = null;
|
|
resolve();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {Object} song - L'objet contenant l'URL
|
|
* @param {number} seekTime - Le temps de démarrage en secondes (par défaut 0)
|
|
*/
|
|
async function getStream(song, seekTime = 0) {
|
|
// ÉTAPE 1 : On s'assure que l'ancien processus est BIEN mort avant de faire quoi que ce soit.
|
|
await killCurrentProcess();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
clog.log(`[YT-DLP] Lancement pour : ${song.url} (Début: ${seekTime}s)`);
|
|
|
|
const ytArgs = [
|
|
song.url,
|
|
'-o', '-',
|
|
|
|
// Sélecteur large : Audio pur OU Vidéo (ffmpeg se débrouillera)
|
|
'-f', 'bestaudio/best',
|
|
|
|
// IMPORTANT : On se fait passer pour Edge (Windows) pour correspondre aux cookies
|
|
'--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
|
|
'--force-ipv4',
|
|
'--no-warnings',
|
|
'--no-part',
|
|
'--no-keep-fragments',
|
|
'--buffer-size', '16K',
|
|
'--no-check-certificate',
|
|
'--ignore-config',
|
|
'--ignore-errors'
|
|
];
|
|
|
|
// --- GESTION DU TIMECODE (SEEK) ---
|
|
if (seekTime && seekTime > 0) {
|
|
ytArgs.push('--download-sections', `*${Math.round(seekTime)}-inf`);
|
|
}
|
|
|
|
// // --- GESTION DES COOKIES ---
|
|
// if (__glob.COOKIES) {
|
|
// // Utiliser le format Netscape pour les cookies est impératif
|
|
// ytArgs.push('--cookies', __glob.COOKIES);
|
|
// ytArgs.push('--no-cache-dir'); // Évite les conflits de cache avec les cookies
|
|
// }
|
|
|
|
// Lancement du nouveau processus
|
|
const yt = spawn('yt-dlp', ytArgs);
|
|
|
|
// On met à jour la variable globale tout de suite
|
|
currentYtProcess = yt;
|
|
|
|
let errorLogs = "";
|
|
|
|
yt.stderr.on('data', (data) => {
|
|
const msg = data.toString();
|
|
console.log(msg);
|
|
if (!msg.includes('[download]') && !msg.includes('[youtube]')) {
|
|
errorLogs += msg;
|
|
}
|
|
});
|
|
|
|
yt.on('error', (err) => {
|
|
clog.error("[YT-DLP] Erreur au lancement.", err);
|
|
// Si c'est ce processus qui est en cours, on le clean
|
|
if (currentYtProcess === yt) currentYtProcess = null;
|
|
reject(err);
|
|
});
|
|
|
|
yt.on('close', (code) => {
|
|
// Nettoyage de la référence globale si c'est bien nous
|
|
if (currentYtProcess === yt) currentYtProcess = null;
|
|
|
|
if (code !== 0 && code !== null && code !== 143 && code !== 137) { // 137 = SIGKILL
|
|
clog.warn(`[YT-DLP] Arrêt code ${code}. Logs : ${errorLogs}`);
|
|
}
|
|
});
|
|
|
|
if (yt.stdout) {
|
|
// --- SÉCURITÉ ANTI-OVERRIDE SUR LE FLUX ---
|
|
// Si le stream se ferme (le bot quitte le vocal ou skip), on tue yt-dlp
|
|
yt.stdout.on('close', () => {
|
|
if (!yt.killed && currentYtProcess === yt) {
|
|
yt.kill();
|
|
}
|
|
});
|
|
|
|
yt.stdout.on('error', () => {
|
|
if (!yt.killed && currentYtProcess === yt) yt.kill();
|
|
});
|
|
|
|
resolve(yt.stdout);
|
|
} else {
|
|
reject(new Error("Aucun flux stdout généré."));
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = { getStream }; |