2025-09-29 14:53:25 +02:00

212 lines
7.9 KiB
Python

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