crunchyroll-toolkit
Version:
Toolkit Node.js complet pour extraire données d'animés, métadonnées et thumbnails depuis Crunchyroll avec techniques anti-détection 2024/2025
227 lines (226 loc) • 8.69 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SeleniumBrowserManager = void 0;
const selenium_webdriver_1 = require("selenium-webdriver");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const child_process = __importStar(require("child_process"));
class SeleniumBrowserManager {
constructor(options = {}) {
this.options = {
headless: true,
timeout: 30000,
maxRetries: 3,
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
locale: 'fr-FR',
...options
};
}
async initialize() {
if (!this.driver) {
console.log('🔧 Initialisation Selenium + undetected-chromedriver...');
// 1. Démarrer le script Python undetected-chromedriver
await this.startUndetectedDriver();
// 2. Attendre que le driver soit prêt
await this.waitForDriverReady();
// 3. Se connecter au driver via Selenium
await this.connectToDriver();
console.log('✅ Selenium + undetected-chromedriver initialisé avec succès');
}
}
async startUndetectedDriver() {
return new Promise((resolve, reject) => {
console.log('🐍 Démarrage du script Python undetected-chromedriver...');
const pythonArgs = ['undetected_driver.py'];
if (this.options.headless) {
pythonArgs.push('--headless');
}
// Utiliser l'environnement virtuel
const pythonPath = path.join(process.cwd(), 'venv', 'bin', 'python');
this.pythonProcess = child_process.spawn(pythonPath, pythonArgs, {
cwd: process.cwd(),
stdio: ['pipe', 'pipe', 'pipe']
});
this.pythonProcess.stdout?.on('data', (data) => {
const output = data.toString();
console.log(`🐍 Python: ${output.trim()}`);
if (output.includes('Driver prêt !')) {
resolve();
}
});
this.pythonProcess.stderr?.on('data', (data) => {
console.error(`🐍 Python Error: ${data.toString()}`);
});
this.pythonProcess.on('error', (error) => {
console.error('❌ Erreur Python process:', error);
reject(error);
});
this.pythonProcess.on('exit', (code) => {
console.log(`🐍 Python process terminé avec le code: ${code}`);
});
// Timeout de sécurité
setTimeout(() => {
if (!this.driver) {
reject(new Error('Timeout: Python driver non démarré après 30s'));
}
}, 30000);
});
}
async waitForDriverReady() {
console.log('⏳ Attente du driver connection info...');
// Attendre que le fichier driver_connection.json soit créé
for (let i = 0; i < 30; i++) {
if (fs.existsSync('driver_connection.json')) {
console.log('📁 Fichier de connexion trouvé');
return;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error('Timeout: fichier driver_connection.json non trouvé');
}
async connectToDriver() {
// Lire les infos de connexion
const connectionInfo = JSON.parse(fs.readFileSync('driver_connection.json', 'utf8'));
console.log('🔗 Connexion au driver undetected...');
console.log(`📡 URL: ${connectionInfo.command_executor_url}`);
console.log(`🆔 Session: ${connectionInfo.session_id}`);
try {
// Se connecter au driver existant
this.driver = await new selenium_webdriver_1.Builder()
.usingServer(connectionInfo.command_executor_url)
.build();
// Valider la connexion
await this.driver.getTitle();
console.log('✅ Connexion Selenium établie');
}
catch (error) {
console.error('❌ Erreur connexion Selenium:', error);
throw error;
}
}
async navigateTo(url) {
if (!this.driver) {
throw new Error('Driver not initialized');
}
console.log(`🌐 Navigation vers: ${url}`);
await this.driver.get(url);
}
async getDriver() {
if (!this.driver) {
throw new Error('Driver not initialized');
}
return this.driver;
}
async getPageSource() {
if (!this.driver) {
throw new Error('Driver not initialized');
}
return await this.driver.getPageSource();
}
async getTitle() {
if (!this.driver) {
throw new Error('Driver not initialized');
}
return await this.driver.getTitle();
}
async getCurrentUrl() {
if (!this.driver) {
throw new Error('Driver not initialized');
}
return await this.driver.getCurrentUrl();
}
async waitForElement(selector, timeout = 10000) {
if (!this.driver) {
throw new Error('Driver not initialized');
}
try {
return await this.driver.wait(selenium_webdriver_1.until.elementLocated(selenium_webdriver_1.By.css(selector)), timeout);
}
catch (error) {
console.log(`⚠️ Élément non trouvé: ${selector}`);
return null;
}
}
async executeScript(script) {
if (!this.driver) {
throw new Error('Driver not initialized');
}
return await this.driver.executeScript(script);
}
async close() {
console.log('🛑 Fermeture du browser manager...');
if (this.driver) {
try {
await this.driver.quit();
this.driver = undefined;
console.log('✅ Driver Selenium fermé');
}
catch (error) {
console.error('⚠️ Erreur fermeture driver:', error);
}
}
if (this.pythonProcess && !this.pythonProcess.killed) {
try {
this.pythonProcess.kill('SIGTERM');
console.log('✅ Process Python arrêté');
}
catch (error) {
console.error('⚠️ Erreur arrêt Python process:', error);
}
}
// Nettoyer le fichier de connexion
if (fs.existsSync('driver_connection.json')) {
fs.unlinkSync('driver_connection.json');
console.log('🧹 Fichier de connexion nettoyé');
}
}
async retry(fn) {
let lastError;
for (let i = 0; i < this.options.maxRetries; i++) {
try {
return await fn();
}
catch (error) {
lastError = error;
if (i < this.options.maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
throw lastError;
}
}
exports.SeleniumBrowserManager = SeleniumBrowserManager;