"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isYtDlpAvailable = isYtDlpAvailable; exports.downloadVideo = downloadVideo; const child_process_1 = require("child_process"); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); function isYtDlpAvailable() { try { (0, child_process_1.execSync)('yt-dlp --version', { stdio: 'pipe' }); return true; } catch { return false; } } const PROGRESS_PREFIX = 'PROG '; function downloadVideo(url, outputDir, onProgress) { return new Promise((resolve, reject) => { if (!fs_1.default.existsSync(outputDir)) { fs_1.default.mkdirSync(outputDir, { recursive: true }); } const outputTemplate = path_1.default.join(outputDir, '%(title)s.%(ext)s'); // Pass arguments as an array — no shell, no quoting issues, no truncation // on URLs containing & | % ^ etc. (the original execSync bug on Windows). const args = [ '-f', 'best[ext=mp4]/best', '-o', outputTemplate, '--newline', '--progress-template', `${PROGRESS_PREFIX}%(progress._percent_str)s`, '--print', 'after_move:filepath', '--print', 'title', '--no-simulate', url, ]; const child = (0, child_process_1.spawn)('yt-dlp', args, { shell: false }); const stderrLines = []; const outputLines = []; let stdoutBuf = ''; let stderrBuf = ''; const handleStdoutLine = (line) => { if (!line) return; if (line.startsWith(PROGRESS_PREFIX)) { const m = line.slice(PROGRESS_PREFIX.length).match(/([\d.]+)\s*%/); if (m && onProgress) { const pct = parseFloat(m[1]); if (!isNaN(pct)) onProgress(pct); } return; } outputLines.push(line); }; child.stdout.on('data', (chunk) => { stdoutBuf += chunk.toString('utf-8'); let nl; while ((nl = stdoutBuf.indexOf('\n')) !== -1) { const line = stdoutBuf.slice(0, nl).replace(/\r$/, ''); stdoutBuf = stdoutBuf.slice(nl + 1); handleStdoutLine(line.trim()); } }); child.stderr.on('data', (chunk) => { stderrBuf += chunk.toString('utf-8'); let nl; while ((nl = stderrBuf.indexOf('\n')) !== -1) { const line = stderrBuf.slice(0, nl).replace(/\r$/, '').trim(); stderrBuf = stderrBuf.slice(nl + 1); if (line) stderrLines.push(line); } }); child.on('error', (err) => { reject(new Error(`yt-dlp failed to start: ${err.message}`)); }); const timeoutMs = 600000; const timer = setTimeout(() => { child.kill(); reject(new Error(`yt-dlp timed out after ${timeoutMs / 1000}s`)); }, timeoutMs); child.on('close', (code) => { clearTimeout(timer); if (stdoutBuf.trim()) handleStdoutLine(stdoutBuf.trim()); if (stderrBuf.trim()) stderrLines.push(stderrBuf.trim()); if (code !== 0) { const tail = stderrLines.slice(-3).join(' | ') || `exit code ${code}`; reject(new Error(tail)); return; } const filePath = outputLines[0]; const title = outputLines[1] || (filePath ? path_1.default.basename(filePath) : ''); if (!filePath) { reject(new Error('yt-dlp completed but did not report a filename')); return; } if (!fs_1.default.existsSync(filePath)) { reject(new Error(`yt-dlp reported success but file not found: ${filePath}`)); return; } resolve({ filePath: path_1.default.resolve(filePath), filename: path_1.default.basename(filePath), title, }); }); }); } //# sourceMappingURL=ytDlp.js.map