224 lines
8.7 KiB
Python
224 lines
8.7 KiB
Python
import os
|
|
import sys
|
|
import subprocess
|
|
import requests
|
|
import shutil
|
|
import tempfile
|
|
from pathlib import Path
|
|
from PySide6.QtGui import QIcon
|
|
|
|
def check_7zip_installed():
|
|
"""Check if 7-Zip is installed on Windows."""
|
|
try:
|
|
subprocess.run(['7z', '--help'],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0)
|
|
return True
|
|
except (subprocess.SubprocessError, FileNotFoundError):
|
|
return False
|
|
|
|
def download_file(url, dest_path, progress_callback=None):
|
|
"""Download a file from URL to destination path with progress indication."""
|
|
try:
|
|
response = requests.get(url, stream=True, timeout=30) # Added timeout
|
|
response.raise_for_status() # Check for HTTP errors
|
|
total_size = int(response.headers.get('content-length', 0))
|
|
|
|
with open(dest_path, 'wb') as f:
|
|
if total_size == 0:
|
|
f.write(response.content)
|
|
else:
|
|
downloaded = 0
|
|
for data in response.iter_content(chunk_size=8192):
|
|
downloaded += len(data)
|
|
f.write(data)
|
|
if progress_callback:
|
|
progress = int((downloaded / total_size) * 100)
|
|
progress_callback(f"⚡ Downloading FFmpeg components... {progress}%")
|
|
return True
|
|
except requests.RequestException as e:
|
|
print(f"Download error: {str(e)}")
|
|
return False
|
|
|
|
def get_ffmpeg_install_path():
|
|
"""Get the FFmpeg installation path."""
|
|
if sys.platform == 'win32':
|
|
return os.path.join(os.getenv('LOCALAPPDATA'), 'ffmpeg', 'ffmpeg-7.1-full_build', 'bin')
|
|
elif sys.platform == 'darwin':
|
|
paths = ['/usr/local/bin', '/opt/homebrew/bin', '/usr/bin']
|
|
for path in paths:
|
|
if os.path.exists(os.path.join(path, 'ffmpeg')):
|
|
return path
|
|
return '/usr/local/bin' # Default Homebrew path
|
|
else:
|
|
return '/usr/bin' # Standard Linux path
|
|
|
|
def check_ffmpeg_installed():
|
|
"""Check if FFmpeg is installed and accessible."""
|
|
try:
|
|
# First try the PATH
|
|
result = subprocess.run(['ffmpeg', '-version'],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
check=True,
|
|
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0,
|
|
timeout=5) # Added timeout
|
|
return True
|
|
except (subprocess.SubprocessError, FileNotFoundError):
|
|
# If not in PATH, check the installation directory
|
|
ffmpeg_path = get_ffmpeg_install_path()
|
|
if sys.platform == 'win32':
|
|
ffmpeg_exe = os.path.join(ffmpeg_path, 'ffmpeg.exe')
|
|
else:
|
|
ffmpeg_exe = os.path.join(ffmpeg_path, 'ffmpeg')
|
|
|
|
if os.path.exists(ffmpeg_exe):
|
|
# Add to PATH if found
|
|
os.environ['PATH'] = f"{ffmpeg_path}{os.pathsep}{os.environ.get('PATH', '')}"
|
|
return True
|
|
return False
|
|
except Exception as e:
|
|
print(f"FFmpeg check error: {str(e)}")
|
|
return False
|
|
|
|
def install_ffmpeg_windows():
|
|
"""Install FFmpeg on Windows using either 7z or zip method."""
|
|
ffmpeg_path = get_ffmpeg_install_path()
|
|
|
|
# Check if already installed
|
|
if check_ffmpeg_installed():
|
|
print("✨ FFmpeg is already installed!")
|
|
return True
|
|
|
|
try:
|
|
# Define variables
|
|
ffmpeg_url = "https://github.com/GyanD/codexffmpeg/releases/download/7.1/ffmpeg-7.1-full_build.7z"
|
|
zip_url = "https://github.com/GyanD/codexffmpeg/releases/download/7.1/ffmpeg-7.1-full_build.zip"
|
|
extract_dir = os.path.join(os.getenv('LOCALAPPDATA'), 'ffmpeg')
|
|
full_build_dir = os.path.join(extract_dir, 'ffmpeg-7.1-full_build')
|
|
bin_dir = os.path.join(full_build_dir, 'bin')
|
|
|
|
# Create extraction directory if it doesn't exist
|
|
os.makedirs(extract_dir, exist_ok=True)
|
|
|
|
# Choose installation method based on 7-Zip availability
|
|
use_7zip = check_7zip_installed()
|
|
temp_file = tempfile.NamedTemporaryFile(delete=False,
|
|
suffix='.7z' if use_7zip else '.zip').name
|
|
|
|
# Download with progress callback
|
|
if not download_file(ffmpeg_url if use_7zip else zip_url, temp_file,
|
|
progress_callback=lambda msg: print(msg)):
|
|
raise Exception("Failed to download FFmpeg")
|
|
|
|
print("🔧 Extracting FFmpeg components...")
|
|
try:
|
|
if use_7zip:
|
|
# Extract using 7-Zip
|
|
subprocess.run(['7z', 'x', temp_file, f'-o{extract_dir}', '-y'],
|
|
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0,
|
|
timeout=300) # 5-minute timeout
|
|
else:
|
|
# Extract using built-in Windows tools
|
|
import zipfile
|
|
with zipfile.ZipFile(temp_file, 'r') as zip_ref:
|
|
zip_ref.extractall(extract_dir)
|
|
except Exception as e:
|
|
raise Exception(f"Extraction failed: {str(e)}")
|
|
|
|
print("⚙️ Configuring system paths...")
|
|
# Add to System Path
|
|
user_path = os.environ.get('PATH', '')
|
|
if bin_dir not in user_path:
|
|
subprocess.run(['setx', 'PATH', f"{user_path};{bin_dir}"],
|
|
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0)
|
|
os.environ['PATH'] = f"{user_path};{bin_dir}"
|
|
|
|
# Clean up
|
|
try:
|
|
os.unlink(temp_file)
|
|
except Exception:
|
|
pass # Ignore cleanup errors
|
|
|
|
# Verify installation
|
|
if not check_ffmpeg_installed():
|
|
raise Exception("FFmpeg installation verification failed")
|
|
|
|
print("✨ FFmpeg installation completed successfully!")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error installing FFmpeg: {str(e)}")
|
|
return False
|
|
|
|
def install_ffmpeg_macos():
|
|
"""Install FFmpeg on macOS using Homebrew."""
|
|
try:
|
|
# Check if Homebrew is installed
|
|
try:
|
|
subprocess.run(['brew', '--version'],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
check=True,
|
|
timeout=5)
|
|
except (subprocess.SubprocessError, FileNotFoundError):
|
|
print("Installing Homebrew...")
|
|
brew_install_cmd = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
|
subprocess.run(brew_install_cmd, shell=True, check=True, timeout=300)
|
|
|
|
# Install FFmpeg
|
|
print("Installing FFmpeg...")
|
|
subprocess.run(['brew', 'install', 'ffmpeg'], check=True, timeout=300)
|
|
|
|
# Verify installation
|
|
if not check_ffmpeg_installed():
|
|
raise Exception("FFmpeg installation verification failed")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"Error installing FFmpeg: {str(e)}")
|
|
return False
|
|
|
|
def install_ffmpeg_linux():
|
|
"""Install FFmpeg on Linux using appropriate package manager."""
|
|
try:
|
|
# Detect the package manager
|
|
if shutil.which('apt'):
|
|
# Debian/Ubuntu
|
|
subprocess.run(['sudo', 'apt', 'update'], check=True, timeout=60)
|
|
subprocess.run(['sudo', 'apt', 'install', '-y', 'ffmpeg'], check=True, timeout=300)
|
|
elif shutil.which('dnf'):
|
|
# Fedora
|
|
subprocess.run(['sudo', 'dnf', 'install', '-y', 'ffmpeg'], check=True, timeout=300)
|
|
elif shutil.which('pacman'):
|
|
# Arch Linux
|
|
subprocess.run(['sudo', 'pacman', '-S', '--noconfirm', 'ffmpeg'], check=True, timeout=300)
|
|
elif shutil.which('snap'):
|
|
# Universal snap package
|
|
subprocess.run(['sudo', 'snap', 'install', 'ffmpeg'], check=True, timeout=300)
|
|
else:
|
|
raise Exception("No supported package manager found")
|
|
|
|
# Verify installation
|
|
if not check_ffmpeg_installed():
|
|
raise Exception("FFmpeg installation verification failed")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"Error installing FFmpeg: {str(e)}")
|
|
return False
|
|
|
|
def auto_install_ffmpeg():
|
|
"""Automatically install FFmpeg based on the operating system."""
|
|
if sys.platform == 'win32':
|
|
return install_ffmpeg_windows()
|
|
elif sys.platform == 'darwin':
|
|
return install_ffmpeg_macos()
|
|
elif sys.platform.startswith('linux'):
|
|
return install_ffmpeg_linux()
|
|
else:
|
|
print(f"Unsupported operating system: {sys.platform}")
|
|
return False |