Files
media-player-mixed/media_server/main.py
alexei.dolgolyov 5519e449cd Add WebSocket support for real-time media status updates
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>
2026-02-04 14:02:53 +03:00

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()