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