From a5332b2d382fef49c822d4a8072446dd2151c9ce Mon Sep 17 00:00:00 2001 From: renato97 Date: Mon, 10 Nov 2025 15:15:28 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20A=C3=B1adir=20detecci=C3=B3n=20auto?= =?UTF-8?q?m=C3=A1tica=20de=20FFmpeg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar funciones check_ffmpeg() y install_ffmpeg() para detectar e instalar FFmpeg - Implementar verificación previa a descargas MP3 para asegurar disponibilidad de FFmpeg - Crear endpoints /api/ffmpeg/status y /api/ffmpeg/install para gestión de FFmpeg - Mejorar frontend con detección de estado de FFmpeg y opción de instalación automática - Deshabilitar opción MP3 si FFmpeg no está disponible - Añadir mensajes de error específicos para problemas de FFmpeg 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ static/js/app.js | 77 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/app.py b/app.py index e776241..361b2ed 100644 --- a/app.py +++ b/app.py @@ -4,6 +4,8 @@ import os import uuid from datetime import datetime import threading +import subprocess +import shutil import time app = Flask(__name__) @@ -14,6 +16,49 @@ if not os.path.exists(app.config['DOWNLOAD_FOLDER']): download_status = {} +def check_ffmpeg(): + """Verificar si FFmpeg está disponible en el sistema""" + try: + result = subprocess.run(['ffmpeg', '-version'], + capture_output=True, text=True, timeout=10) + return result.returncode == 0 + except (subprocess.TimeoutExpired, FileNotFoundError): + return False + +def install_ffmpeg(): + """Intentar instalar FFmpeg automáticamente""" + try: + # Detectar el sistema operativo + import platform + system = platform.system().lower() + + if system == 'linux': + # Para sistemas basados en Debian/Ubuntu + commands = [ + ['sudo', 'apt-get', 'update'], + ['sudo', 'apt-get', 'install', '-y', 'ffmpeg'] + ] + elif system == 'darwin': + # Para macOS usando Homebrew + commands = [ + ['brew', 'update'], + ['brew', 'install', 'ffmpeg'] + ] + elif system == 'windows': + # Para Windows (simplificado - requeriría manual) + return False + else: + return False + + for cmd in commands: + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + return False + + return True + except Exception: + return False + class DownloadProgress: def __init__(self, download_id): self.download_id = download_id @@ -48,6 +93,22 @@ def download_video(url, download_id, format_type='mp4'): progress = DownloadProgress(download_id) download_status[download_id] = progress + # Verificar FFmpeg para formatos que lo requieren + if format_type == 'mp3': + if not check_ffmpeg(): + progress.status = 'error' + progress.error = "FFmpeg no está disponible. La conversión a MP3 requiere FFmpeg. Por favor, instala FFmpeg o contacta al administrador." + return + + # Opcional: intentar instalar FFmpeg automáticamente si no está disponible + # if install_ffmpeg(): + # progress.status = 'info' + # progress.error = "FFmpeg ha sido instalado automáticamente. Intenta la descarga nuevamente." + # else: + # progress.status = 'error' + # progress.error = "No se pudo instalar FFmpeg automáticamente. La conversión a MP3 requiere FFmpeg." + # return + try: ydl_opts = { 'outtmpl': os.path.join(app.config['DOWNLOAD_FOLDER'], f'{download_id}.%(ext)s'), @@ -180,6 +241,31 @@ def list_downloads(): return jsonify(downloads) +@app.route('/api/ffmpeg/status') +def ffmpeg_status(): + """Verificar el estado de FFmpeg""" + ffmpeg_available = check_ffmpeg() + return jsonify({ + 'available': ffmpeg_available, + 'can_install': True if not ffmpeg_available else False + }) + +@app.route('/api/ffmpeg/install', methods=['POST']) +def install_ffmpeg_api(): + """Intentar instalar FFmpeg automáticamente""" + try: + if check_ffmpeg(): + return jsonify({'success': True, 'message': 'FFmpeg ya está disponible.'}) + + # Intentar instalar FFmpeg + success = install_ffmpeg() + if success: + return jsonify({'success': True, 'message': 'FFmpeg instalado correctamente.'}) + else: + return jsonify({'success': False, 'message': 'No se pudo instalar FFmpeg automáticamente. Debes instalarlo manualmente.'}) + except Exception as e: + return jsonify({'success': False, 'message': f'Error durante la instalación: {str(e)}'}), 500 + @app.route('/api/cleanup', methods=['POST']) def cleanup_downloads(): """Limpia descargas fallidas y muy antiguas""" diff --git a/static/js/app.js b/static/js/app.js index a0d02ab..28e2f6d 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -3,6 +3,7 @@ let progressInterval = null; document.addEventListener('DOMContentLoaded', function() { loadDownloads(); + checkFFmpegStatus(); document.getElementById('downloadForm').addEventListener('submit', function(e) { e.preventDefault(); @@ -265,5 +266,81 @@ function formatFileSize(bytes) { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } +function checkFFmpegStatus() { + fetch('/api/ffmpeg/status') + .then(response => response.json()) + .then(data => { + const mp3Option = document.querySelector('input[value="mp3"]'); + const mp3Label = document.querySelector('label[for="mp3"]'); + + if (!data.available) { + // MP3 option is not available + mp3Option.disabled = true; + mp3Label.style.opacity = '0.5'; + mp3Label.style.cursor = 'not-allowed'; + + // Add warning indicator + if (!document.getElementById('ffmpegWarning')) { + const warning = document.createElement('div'); + warning.id = 'ffmpegWarning'; + warning.className = 'alert alert-warning alert-sm mt-2'; + warning.innerHTML = ` + + FFmpeg no está disponible. Las descargas de MP3 requieren FFmpeg. + + `; + mp3Label.parentNode.insertBefore(warning, mp3Label.parentNode.nextSibling); + + // Switch to MP4 by default + document.querySelector('input[value="mp4"]').checked = true; + } + } else { + // FFmpeg is available + if (document.getElementById('ffmpegWarning')) { + document.getElementById('ffmpegWarning').remove(); + } + mp3Option.disabled = false; + mp3Label.style.opacity = '1'; + mp3Label.style.cursor = 'pointer'; + } + }) + .catch(error => { + console.error('Error checking FFmpeg status:', error); + }); +} + +function installFFmpeg() { + const btn = document.querySelector('#ffmpegWarning button'); + const originalContent = btn.innerHTML; + btn.disabled = true; + btn.innerHTML = ' Instalando...'; + + fetch('/api/ffmpeg/install', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + } + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + alert('✅ ' + data.message); + checkFFmpegStatus(); // Recheck status + } else { + alert('❌ ' + data.message); + btn.disabled = false; + btn.innerHTML = originalContent; + } + }) + .catch(error => { + console.error('Error installing FFmpeg:', error); + alert('Error al intentar instalar FFmpeg: ' + error.message); + btn.disabled = false; + btn.innerHTML = originalContent; + }); +} + // Auto-refresh downloads list every 30 seconds setInterval(loadDownloads, 30000); \ No newline at end of file