"""Media Server - FastAPI application entry point.""" import argparse import logging import sys from contextlib import asynccontextmanager from pathlib import Path import uvicorn from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from . import __version__ from .config import settings, generate_default_config, get_config_dir from .routes import audio_router, health_router, media_router, scripts_router from .services import get_media_controller from .services.websocket_manager import ws_manager def setup_logging(): """Configure application logging.""" logging.basicConfig( level=getattr(logging, settings.log_level.upper()), format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[logging.StreamHandler(sys.stdout)], ) @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan handler.""" setup_logging() logger = logging.getLogger(__name__) logger.info(f"Media Server starting on {settings.host}:{settings.port}") logger.info(f"API Token: {settings.api_token[:8]}...") # Start WebSocket status monitor controller = get_media_controller() await ws_manager.start_status_monitor(controller.get_status) logger.info("WebSocket status monitor started") yield # Stop WebSocket status monitor await ws_manager.stop_status_monitor() logger.info("Media Server shutting down") def create_app() -> FastAPI: """Create and configure the FastAPI application.""" app = FastAPI( title="Media Server", description="REST API for controlling system media playback", version=__version__, lifespan=lifespan, ) # Add CORS middleware for cross-origin requests app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Register routers app.include_router(audio_router) app.include_router(health_router) app.include_router(media_router) app.include_router(scripts_router) # Mount static files and serve UI at root static_dir = Path(__file__).parent / "static" if static_dir.exists(): app.mount("/static", StaticFiles(directory=str(static_dir)), name="static") @app.get("/", include_in_schema=False) async def serve_ui(): """Serve the Web UI.""" return FileResponse(static_dir / "index.html") return app app = create_app() def main(): """Main entry point for running the server.""" parser = argparse.ArgumentParser(description="Media Server") parser.add_argument( "--host", default=settings.host, help=f"Host to bind to (default: {settings.host})", ) parser.add_argument( "--port", type=int, default=settings.port, help=f"Port to bind to (default: {settings.port})", ) parser.add_argument( "--generate-config", action="store_true", help="Generate a default configuration file and exit", ) parser.add_argument( "--show-token", action="store_true", help="Show the current API token and exit", ) args = parser.parse_args() if args.generate_config: config_path = generate_default_config() print(f"Configuration file generated at: {config_path}") print(f"API Token has been saved to the config file.") return if args.show_token: print(f"API Token: {settings.api_token}") print(f"Config directory: {get_config_dir()}") return uvicorn.run( "media_server.main:app", host=args.host, port=args.port, reload=False, ) if __name__ == "__main__": main()