#!/usr/bin/env python3 """ Cross-platform build script for EBoek.info Scraper Creates a standalone executable using PyInstaller """ import os import sys import subprocess import shutil from pathlib import Path def run_command(command, description): """Run a command and handle errors.""" print(f"→ {description}...") try: result = subprocess.run(command, shell=True, capture_output=True, text=True) if result.returncode != 0: print(f"✗ Error: {result.stderr}") return False if result.stdout.strip(): print(f" {result.stdout.strip()}") return True except Exception as e: print(f"✗ Exception: {e}") return False def main(): """Main build process.""" print("=" * 50) print("🚀 EBoek.info Scraper - Executable Builder") print("=" * 50) print() # Change to project root directory (parent of scripts/) script_dir = Path(__file__).parent project_root = script_dir.parent os.chdir(project_root) print(f"📂 Working from: {project_root}") print() # Check Python version if sys.version_info < (3, 8): print("✗ Error: Python 3.8+ required") sys.exit(1) # Check if PyInstaller is installed try: import PyInstaller print(f"✓ PyInstaller {PyInstaller.__version__} found") except ImportError: print("→ Installing PyInstaller...") if not run_command(f"{sys.executable} -m pip install pyinstaller", "Installing PyInstaller"): print("✗ Failed to install PyInstaller") sys.exit(1) # Clean previous builds print() for directory in ["dist", "build"]: if Path(directory).exists(): shutil.rmtree(directory) print(f"✓ Cleaned {directory}/") # Determine platform-specific settings is_windows = sys.platform.startswith('win') exe_name = "EBoek_Scraper.exe" if is_windows else "EBoek_Scraper" app_name = "EBoek_Scraper" # Base name without extension # Build command build_cmd = [ f"{sys.executable}", "-m", "PyInstaller", "--onefile", "--windowed", "--name", app_name, # Hidden imports for PyQt5 "--hidden-import", "PyQt5.QtCore", "--hidden-import", "PyQt5.QtGui", "--hidden-import", "PyQt5.QtWidgets", # Hidden imports for Selenium "--hidden-import", "selenium", "--hidden-import", "selenium.webdriver", "--hidden-import", "selenium.webdriver.chrome", "--hidden-import", "selenium.webdriver.chrome.options", "--hidden-import", "selenium.webdriver.common.by", # Hidden imports for our modules "--hidden-import", "core.scraper", "--hidden-import", "core.scraper_thread", "--hidden-import", "core.credentials", "--hidden-import", "gui.main_window", "--hidden-import", "gui.login_dialog", "--hidden-import", "gui.progress_dialog", "--hidden-import", "utils.validators", # Exclude unnecessary modules to reduce size "--exclude-module", "tkinter", "--exclude-module", "matplotlib", "--exclude-module", "numpy", "--exclude-module", "pandas", # Main script "gui_main.py" ] print() print("🔨 Building executable...") print("📝 This may take a few minutes...") print() # Run PyInstaller try: result = subprocess.run(build_cmd, capture_output=True, text=True) if result.returncode != 0: print("✗ Build failed!") print("Error output:") print(result.stderr) # Try fallback with spec file print() print("🔄 Trying alternative build with spec file...") spec_cmd = [f"{sys.executable}", "-m", "PyInstaller", "scripts/eboek_scraper.spec"] result = subprocess.run(spec_cmd, capture_output=True, text=True) if result.returncode != 0: print("✗ Alternative build also failed!") print(result.stderr) sys.exit(1) print("✓ Build completed successfully!") except Exception as e: print(f"✗ Build failed with exception: {e}") sys.exit(1) # Check if executable was created exe_path = Path("dist") / exe_name if not exe_path.exists(): print(f"✗ Error: Executable not found at {exe_path}") sys.exit(1) # Display results file_size = exe_path.stat().st_size / (1024 * 1024) # MB print() print("=" * 50) print("🎉 BUILD SUCCESSFUL!") print("=" * 50) print(f"📂 Executable location: {exe_path}") print(f"📊 File size: {file_size:.1f} MB") print() # Platform-specific instructions if is_windows: print("🪟 Windows Instructions:") print(f" • Run: {exe_path}") print(" • Windows may show security warning on first run") print(" • Click 'More Info' → 'Run Anyway' if prompted") else: print("🍎 macOS/Linux Instructions:") print(f" • Run: ./{exe_path}") print(" • macOS may show security warning on first run") print(" • Right-click and select 'Open' to bypass Gatekeeper") print() print("📦 Distribution:") print(f" • Share this single file: {exe_name}") print(" • No Python installation required on target machine") print(" • Includes all dependencies (PyQt5, Selenium, etc.)") print() print("✨ Ready for distribution!") if __name__ == "__main__": main()