import os import sys import threading from datetime import datetime # Add the parent directory to sys.path to import our modules # This must be done before any local imports current_dir = os.path.dirname(__file__) parent_dir = os.path.join(current_dir, '..') sys.path.insert(0, os.path.abspath(parent_dir)) from flask import Flask, render_template, request, jsonify, redirect, url_for from flask_socketio import SocketIO, emit import logging import atexit from main import SeriesApp from server.core.entities.series import Serie from server.core.entities import SerieList from server.infrastructure.file_system import SerieScanner from server.infrastructure.providers.provider_factory import Loaders from web.controllers.auth_controller import session_manager, require_auth, optional_auth from config import config from application.services.queue_service import download_queue_bp # Import route blueprints from web.routes import ( auth_bp, auth_api_bp, api_bp, main_bp, static_bp, diagnostic_bp, config_bp ) from web.routes.websocket_handlers import register_socketio_handlers # Import API blueprints from their correct locations from web.controllers.api.v1.process import process_bp from web.controllers.api.v1.scheduler import scheduler_bp from web.controllers.api.v1.logging import logging_bp from web.controllers.api.v1.health import health_bp from application.services.scheduler_service import init_scheduler, get_scheduler from shared.utils.process_utils import (with_process_lock, RESCAN_LOCK, DOWNLOAD_LOCK, ProcessLockError, is_process_running, check_process_locks) # Import error handling and monitoring modules from web.middleware.error_handler import handle_api_errors # Performance optimization modules - not yet implemented # API integration and database modules - not yet implemented # User experience and accessibility modules - not yet implemented app = Flask(__name__, template_folder='web/templates/base', static_folder='web/static') app.config['SECRET_KEY'] = os.urandom(24) app.config['PERMANENT_SESSION_LIFETIME'] = 86400 # 24 hours socketio = SocketIO(app, cors_allowed_origins="*") # Error handler for API routes to return JSON instead of HTML @app.errorhandler(404) def handle_api_not_found(error): """Handle 404 errors for API routes by returning JSON instead of HTML.""" if request.path.startswith('/api/'): return jsonify({ 'success': False, 'error': 'API endpoint not found', 'path': request.path }), 404 # For non-API routes, let Flask handle it normally return error # Register all blueprints app.register_blueprint(download_queue_bp) app.register_blueprint(main_bp) app.register_blueprint(auth_bp) app.register_blueprint(auth_api_bp) app.register_blueprint(api_bp) app.register_blueprint(static_bp) app.register_blueprint(diagnostic_bp) app.register_blueprint(config_bp) # Register available API blueprints app.register_blueprint(process_bp) app.register_blueprint(scheduler_bp) app.register_blueprint(logging_bp) app.register_blueprint(health_bp) # Additional blueprints will be registered when features are implemented # Additional feature initialization will be added when features are implemented # Global variables are now managed in their respective route files # Keep only series_app for backward compatibility series_app = None def init_series_app(): """Initialize the SeriesApp with configuration directory.""" global series_app directory_to_search = config.anime_directory series_app = SeriesApp(directory_to_search) return series_app # Register WebSocket handlers register_socketio_handlers(socketio) # Pass socketio instance to API routes from web.routes.api_routes import set_socketio set_socketio(socketio) # Initialize scheduler scheduler = init_scheduler(config, socketio) def setup_scheduler_callbacks(): """Setup callbacks for scheduler operations.""" def rescan_callback(): """Callback for scheduled rescan operations.""" try: # Reinit and scan series_app.SerieScanner.Reinit() series_app.SerieScanner.Scan() # Refresh the series list series_app.List = SerieList.SerieList(series_app.directory_to_search) series_app.__InitList__() return {"status": "success", "message": "Scheduled rescan completed"} except Exception as e: raise Exception(f"Scheduled rescan failed: {e}") def download_callback(): """Callback for auto-download after scheduled rescan.""" try: if not series_app or not series_app.List: return {"status": "skipped", "message": "No series data available"} # Find series with missing episodes series_with_missing = [] for serie in series_app.List.GetList(): if serie.episodeDict: series_with_missing.append(serie) if not series_with_missing: return {"status": "skipped", "message": "No series with missing episodes found"} # Note: Actual download implementation would go here # For now, just return the count of series that would be downloaded return { "status": "started", "message": f"Auto-download initiated for {len(series_with_missing)} series", "series_count": len(series_with_missing) } except Exception as e: raise Exception(f"Auto-download failed: {e}") scheduler.set_rescan_callback(rescan_callback) scheduler.set_download_callback(download_callback) # Setup scheduler callbacks setup_scheduler_callbacks() # Advanced system initialization will be added when features are implemented # Register cleanup functions @atexit.register def cleanup_on_exit(): """Clean up resources on application exit.""" try: # Additional cleanup functions will be added when features are implemented logging.info("Application cleanup completed") except Exception as e: logging.error(f"Error during cleanup: {e}") if __name__ == '__main__': # Only run initialization and logging setup in the main process # This prevents duplicate initialization when Flask debug reloader starts # Initialize the series app init_series_app() # Configure enhanced logging system try: from server.infrastructure.logging.config import get_logger, logging_config logger = get_logger(__name__, 'webapp') logger.info("Enhanced logging system initialized") except ImportError: # Fallback to basic logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) logger.warning("Using fallback logging - enhanced logging not available") # Only display startup messages if we're not in the reloader child process if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': logger.info("Starting Aniworld Flask server...") logger.info(f"Anime directory: {config.anime_directory}") logger.info(f"Log level: {config.log_level}") # Start scheduler if enabled if hasattr(config, 'scheduled_rescan_enabled') and config.scheduled_rescan_enabled: logger.info(f"Starting scheduler - daily rescan at {getattr(config, 'scheduled_rescan_time', '03:00')}") scheduler.start_scheduler() else: logger.info("Scheduled operations disabled") logger.info("Server will be available at http://localhost:5000") try: # Run with SocketIO socketio.run(app, debug=True, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True) finally: # Clean shutdown if 'scheduler' in locals() and scheduler: scheduler.stop_scheduler() logger.info("Scheduler stopped") # Additional cleanup can be added here