173 lines
5.5 KiB
Python
173 lines
5.5 KiB
Python
#!/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() |