✨ Añadir detección automática de FFmpeg
- 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 <noreply@anthropic.com>
This commit is contained in:
86
app.py
86
app.py
@@ -4,6 +4,8 @@ import os
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import threading
|
import threading
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
import time
|
import time
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@@ -14,6 +16,49 @@ if not os.path.exists(app.config['DOWNLOAD_FOLDER']):
|
|||||||
|
|
||||||
download_status = {}
|
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:
|
class DownloadProgress:
|
||||||
def __init__(self, download_id):
|
def __init__(self, download_id):
|
||||||
self.download_id = download_id
|
self.download_id = download_id
|
||||||
@@ -48,6 +93,22 @@ def download_video(url, download_id, format_type='mp4'):
|
|||||||
progress = DownloadProgress(download_id)
|
progress = DownloadProgress(download_id)
|
||||||
download_status[download_id] = progress
|
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:
|
try:
|
||||||
ydl_opts = {
|
ydl_opts = {
|
||||||
'outtmpl': os.path.join(app.config['DOWNLOAD_FOLDER'], f'{download_id}.%(ext)s'),
|
'outtmpl': os.path.join(app.config['DOWNLOAD_FOLDER'], f'{download_id}.%(ext)s'),
|
||||||
@@ -180,6 +241,31 @@ def list_downloads():
|
|||||||
|
|
||||||
return jsonify(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'])
|
@app.route('/api/cleanup', methods=['POST'])
|
||||||
def cleanup_downloads():
|
def cleanup_downloads():
|
||||||
"""Limpia descargas fallidas y muy antiguas"""
|
"""Limpia descargas fallidas y muy antiguas"""
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ let progressInterval = null;
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
loadDownloads();
|
loadDownloads();
|
||||||
|
checkFFmpegStatus();
|
||||||
|
|
||||||
document.getElementById('downloadForm').addEventListener('submit', function(e) {
|
document.getElementById('downloadForm').addEventListener('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -265,5 +266,81 @@ function formatFileSize(bytes) {
|
|||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
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 = `
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
FFmpeg no está disponible. Las descargas de MP3 requieren FFmpeg.
|
||||||
|
<button class="btn btn-sm btn-outline-warning ms-2" onclick="installFFmpeg()">
|
||||||
|
<i class="fas fa-download"></i> Instalar FFmpeg
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
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 = '<i class="fas fa-spinner fa-spin"></i> 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
|
// Auto-refresh downloads list every 30 seconds
|
||||||
setInterval(loadDownloads, 30000);
|
setInterval(loadDownloads, 30000);
|
||||||
Reference in New Issue
Block a user