- Created `install_and_run.bat` for Windows installation and setup. - Created `install_and_run.sh` for Unix-based systems installation and setup. - Removed `main.py` as it is no longer needed. - Updated `requirements.txt` to specify package versions and added PyQt5. - Deleted `start.bat` as it is redundant. - Added unit tests for core functionality and scraping modes. - Implemented input validation utilities in `utils/validators.py`. - Added support for dual scraping modes in the scraper.
221 lines
7.0 KiB
Python
221 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
EBoek.info Scraper GUI Application
|
|
|
|
Main entry point for the PyQt5 GUI version of the EBoek.info scraper.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import traceback
|
|
import warnings
|
|
from pathlib import Path
|
|
|
|
# Suppress urllib3 OpenSSL warnings on macOS
|
|
warnings.filterwarnings("ignore", message="urllib3 v2 only supports OpenSSL 1.1.1+")
|
|
warnings.filterwarnings("ignore", category=UserWarning, module="urllib3")
|
|
|
|
# Ensure we can import PyQt5
|
|
try:
|
|
from PyQt5.QtWidgets import QApplication, QMessageBox
|
|
from PyQt5.QtCore import Qt
|
|
from PyQt5.QtGui import QIcon
|
|
except ImportError as e:
|
|
print("Error: PyQt5 is not installed.")
|
|
print("Please run: pip install PyQt5")
|
|
print(f"Import error: {e}")
|
|
sys.exit(1)
|
|
|
|
# Add the project root to Python path
|
|
project_root = Path(__file__).parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
# Import our GUI components
|
|
try:
|
|
from gui.main_window import MainWindow
|
|
from core.credentials import CredentialManager
|
|
except ImportError as e:
|
|
print(f"Error importing application modules: {e}")
|
|
print("Please ensure all required files are present.")
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
|
|
class EBoekScraperApp(QApplication):
|
|
"""
|
|
Main application class for the EBoek.info Scraper GUI.
|
|
"""
|
|
|
|
def __init__(self, argv):
|
|
super().__init__(argv)
|
|
|
|
self.setApplicationName("EBoek.info Scraper")
|
|
self.setApplicationVersion("2.0")
|
|
self.setOrganizationName("EBoek Scraper")
|
|
|
|
# Set application icon if available
|
|
self.set_application_icon()
|
|
|
|
# Handle exceptions
|
|
sys.excepthook = self.handle_exception
|
|
|
|
self.main_window = None
|
|
|
|
def set_application_icon(self):
|
|
"""Set the application icon if available."""
|
|
# You can add an icon file here if desired
|
|
# icon_path = project_root / "resources" / "icon.png"
|
|
# if icon_path.exists():
|
|
# self.setWindowIcon(QIcon(str(icon_path)))
|
|
pass
|
|
|
|
def handle_exception(self, exc_type, exc_value, exc_traceback):
|
|
"""Handle uncaught exceptions."""
|
|
if issubclass(exc_type, KeyboardInterrupt):
|
|
# Allow Ctrl+C to exit gracefully
|
|
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
return
|
|
|
|
# Log the exception
|
|
error_msg = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
|
|
print("Uncaught exception:")
|
|
print(error_msg)
|
|
|
|
# Show error dialog to user
|
|
if self.main_window:
|
|
try:
|
|
QMessageBox.critical(
|
|
self.main_window,
|
|
"Unexpected Error",
|
|
f"An unexpected error occurred:\n\n{str(exc_value)}\n\n"
|
|
f"The application may need to be restarted.\n\n"
|
|
f"Error type: {exc_type.__name__}"
|
|
)
|
|
except:
|
|
# If we can't show the dialog, just print
|
|
print("Could not display error dialog")
|
|
|
|
def initialize(self):
|
|
"""Initialize the application."""
|
|
try:
|
|
# Check system requirements
|
|
self.check_requirements()
|
|
|
|
# Create and show main window
|
|
self.main_window = MainWindow()
|
|
self.main_window.show()
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"Error initializing application: {e}")
|
|
traceback.print_exc()
|
|
|
|
# Try to show error dialog
|
|
try:
|
|
msg = QMessageBox()
|
|
msg.setIcon(QMessageBox.Critical)
|
|
msg.setWindowTitle("Initialization Error")
|
|
msg.setText(f"Could not initialize the application:\n\n{str(e)}")
|
|
msg.setDetailedText(traceback.format_exc())
|
|
msg.exec_()
|
|
except:
|
|
print("Could not display initialization error dialog")
|
|
|
|
return False
|
|
|
|
def check_requirements(self):
|
|
"""Check system requirements and dependencies."""
|
|
errors = []
|
|
|
|
# Check Python version
|
|
if sys.version_info < (3, 6):
|
|
errors.append(f"Python 3.6+ required (found {sys.version_info.major}.{sys.version_info.minor})")
|
|
|
|
# Check required modules
|
|
required_modules = ['selenium', 'urllib3']
|
|
missing_modules = []
|
|
|
|
for module in required_modules:
|
|
try:
|
|
__import__(module)
|
|
except ImportError:
|
|
missing_modules.append(module)
|
|
|
|
if missing_modules:
|
|
errors.append(f"Missing required modules: {', '.join(missing_modules)}")
|
|
|
|
# Check Chrome/Chromium availability (basic check)
|
|
chrome_paths = [
|
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", # macOS
|
|
"/usr/bin/google-chrome", # Linux
|
|
"/usr/bin/chromium-browser", # Linux (Chromium)
|
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", # Windows
|
|
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", # Windows 32-bit
|
|
]
|
|
|
|
chrome_found = any(Path(path).exists() for path in chrome_paths)
|
|
if not chrome_found:
|
|
# This is a warning, not an error
|
|
print("Warning: Chrome browser not detected in standard locations.")
|
|
print("Make sure Google Chrome is installed for the scraper to work.")
|
|
|
|
if errors:
|
|
error_text = "System requirement errors:\n\n" + "\n".join(f"• {error}" for error in errors)
|
|
error_text += "\n\nPlease install missing requirements and try again."
|
|
raise RuntimeError(error_text)
|
|
|
|
|
|
def show_startup_error(title, message):
|
|
"""Show a startup error dialog without a main window."""
|
|
app = QApplication.instance()
|
|
if not app:
|
|
app = QApplication(sys.argv)
|
|
|
|
msg = QMessageBox()
|
|
msg.setIcon(QMessageBox.Critical)
|
|
msg.setWindowTitle(title)
|
|
msg.setText(message)
|
|
msg.exec_()
|
|
|
|
|
|
def main():
|
|
"""Main entry point for the GUI application."""
|
|
# Set up high DPI support
|
|
if hasattr(Qt, 'AA_EnableHighDpiScaling'):
|
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
|
|
if hasattr(Qt, 'AA_UseHighDpiPixmaps'):
|
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
|
|
# Create the application
|
|
try:
|
|
app = EBoekScraperApp(sys.argv)
|
|
|
|
# Initialize and run
|
|
if app.initialize():
|
|
# Run the application
|
|
sys.exit(app.exec_())
|
|
else:
|
|
print("Application initialization failed")
|
|
sys.exit(1)
|
|
|
|
except Exception as e:
|
|
print(f"Fatal error starting application: {e}")
|
|
traceback.print_exc()
|
|
|
|
# Try to show error dialog
|
|
try:
|
|
show_startup_error(
|
|
"Startup Error",
|
|
f"Could not start the EBoek.info Scraper:\n\n{str(e)}\n\n"
|
|
f"Please check the installation and try again."
|
|
)
|
|
except:
|
|
print("Could not display startup error dialog")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |