Replace HTTP polling with WebSocket push notifications for instant state change responses. Server broadcasts updates only when significant changes occur (state, track, volume, etc.) while letting Home Assistant interpolate position during playback. Includes seek detection for timeline updates and automatic fallback to HTTP polling if WebSocket disconnects. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
124 lines
3.2 KiB
Python
124 lines
3.2 KiB
Python
"""Media Server - FastAPI application entry point."""
|
|
|
|
import argparse
|
|
import logging
|
|
import sys
|
|
from contextlib import asynccontextmanager
|
|
|
|
import uvicorn
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from .config import settings, generate_default_config, get_config_dir
|
|
from .routes import 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="1.0.0",
|
|
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(health_router)
|
|
app.include_router(media_router)
|
|
app.include_router(scripts_router)
|
|
|
|
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()
|