videotomp3transcriptor/src/services/cookiesManager.js
debian.StillHammer 9c3874d879 🎵 v2.0: Fresh start with Camoufox + yt-dlp
- Remove old backend (transcription, translation, summarization)
- Add Camoufox stealth cookie extraction
- Add automatic cookie refresh (14 days)
- Add cookie validation
- Simplified to focus on YouTube → MP3 downloads
- Auto-retry on bot detection
- Streaming support with range requests
- Clean architecture (services pattern)
- Full documentation
2026-01-31 07:40:22 +00:00

189 lines
5.1 KiB
JavaScript

const { exec } = require('child_process');
const { promisify } = require('util');
const fs = require('fs').promises;
const path = require('path');
const execAsync = promisify(exec);
/**
* Manages YouTube cookies lifecycle using Camoufox stealth extraction.
* Auto-refresh when expired, validates periodically.
*/
class CookiesManager {
constructor() {
this.cookiesPath = path.join(__dirname, '../../youtube-cookies.txt');
this.pythonPath = process.env.PYTHON_PATH || 'python3';
this.extractScript = path.join(__dirname, '../python/extract_cookies.py');
this.validateScript = path.join(__dirname, '../python/validate_cookies.py');
this.lastRefresh = null;
this.isValid = false;
// Refresh cookies every 14 days (YouTube cookies typically last 2-4 weeks)
this.refreshIntervalDays = 14;
// Check interval (every 12 hours)
this.checkIntervalMs = 12 * 60 * 60 * 1000;
}
/**
* Initialize cookies manager.
* Check if cookies exist, validate them, refresh if needed.
*/
async init() {
console.log('🔧 Initializing cookies manager...');
// Check if cookies file exists
try {
await fs.access(this.cookiesPath);
console.log('✅ Cookies file exists');
// Validate cookies
const valid = await this.validate();
if (!valid) {
console.log('⚠️ Cookies invalid, refreshing...');
await this.refresh();
} else {
console.log('✅ Cookies valid');
}
} catch (err) {
console.log('📝 No cookies found, generating fresh cookies...');
await this.refresh();
}
// Setup periodic validation (every 12 hours)
setInterval(() => {
this.checkAndRefresh().catch(err => {
console.error('Auto-check failed:', err.message);
});
}, this.checkIntervalMs);
console.log('✅ Cookies manager ready');
}
/**
* Validate cookies using Python script.
* @returns {Promise<boolean>} True if cookies are valid
*/
async validate() {
try {
const { stdout, stderr } = await execAsync(
`${this.pythonPath} ${this.validateScript} ${this.cookiesPath}`,
{ timeout: 60000 }
);
// Check for validation success in output
this.isValid = stdout.includes('Cookies are valid');
if (stderr && !stderr.includes('DeprecationWarning')) {
console.warn('Validation stderr:', stderr.trim());
}
return this.isValid;
} catch (err) {
console.error('Validation failed:', err.message);
this.isValid = false;
return false;
}
}
/**
* Refresh cookies using Camoufox extraction.
* @returns {Promise<boolean>} True if refresh succeeded
*/
async refresh() {
console.log('🔄 Refreshing YouTube cookies with Camoufox...');
try {
const { stdout, stderr } = await execAsync(
`${this.pythonPath} ${this.extractScript} ${this.cookiesPath}`,
{ timeout: 120000 } // 2 min timeout
);
console.log(stdout.trim());
if (stderr && !stderr.includes('DeprecationWarning')) {
console.warn('Camoufox stderr:', stderr.trim());
}
// Verify file was created
try {
await fs.access(this.cookiesPath);
this.lastRefresh = Date.now();
this.isValid = true;
console.log('✅ Cookies refreshed successfully');
return true;
} catch {
console.error('❌ Cookies file not created');
this.isValid = false;
return false;
}
} catch (err) {
console.error('❌ Failed to refresh cookies:', err.message);
this.isValid = false;
return false;
}
}
/**
* Check cookies age and validity, refresh if needed.
*/
async checkAndRefresh() {
console.log('🔍 Checking cookies status...');
// Check file age
try {
const stats = await fs.stat(this.cookiesPath);
const ageMs = Date.now() - stats.mtimeMs;
const ageDays = ageMs / (1000 * 60 * 60 * 24);
console.log(` Age: ${ageDays.toFixed(1)} days`);
// Refresh if too old
if (ageDays >= this.refreshIntervalDays) {
console.log(` Age threshold (${this.refreshIntervalDays} days) reached, refreshing...`);
await this.refresh();
return;
}
} catch {
// File doesn't exist
console.log(' Cookies file missing, refreshing...');
await this.refresh();
return;
}
// Validate cookies
const valid = await this.validate();
if (!valid) {
console.log(' Cookies invalid, refreshing...');
await this.refresh();
} else {
console.log(' Cookies OK ✅');
}
}
/**
* Get path to cookies file.
* @returns {string} Cookies file path
*/
getCookiesPath() {
return this.cookiesPath;
}
/**
* Get cookies status.
* @returns {object} Status object
*/
getStatus() {
return {
valid: this.isValid,
path: this.cookiesPath,
lastRefresh: this.lastRefresh,
refreshIntervalDays: this.refreshIntervalDays
};
}
}
// Export singleton
module.exports = new CookiesManager();