Initial commit: Media Server for remote media control

FastAPI REST API server for controlling system-wide media playback
on Windows, Linux, macOS, and Android.

Features:
- Play/Pause/Stop/Next/Previous track controls
- Volume control and mute
- Seek within tracks
- Current track info (title, artist, album, artwork)
- WebSocket real-time status updates
- Script execution API
- Token-based authentication
- Cross-platform support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 14:41:00 +03:00
commit 83acf5f1ec
26 changed files with 3562 additions and 0 deletions

111
media_server/auth.py Normal file
View File

@@ -0,0 +1,111 @@
"""Authentication middleware and utilities."""
from typing import Optional
from fastapi import Depends, HTTPException, Query, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from .config import settings
security = HTTPBearer(auto_error=False)
async def verify_token(
request: Request,
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> str:
"""Verify the API token from the Authorization header.
Args:
request: The incoming request
credentials: The bearer token credentials
Returns:
The validated token
Raises:
HTTPException: If the token is missing or invalid
"""
if credentials is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing authentication token",
headers={"WWW-Authenticate": "Bearer"},
)
if credentials.credentials != settings.api_token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication token",
headers={"WWW-Authenticate": "Bearer"},
)
return credentials.credentials
class TokenAuth:
"""Dependency class for token authentication."""
def __init__(self, auto_error: bool = True):
self.auto_error = auto_error
async def __call__(
self,
request: Request,
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> str | None:
"""Verify the token and return it or raise an exception."""
if credentials is None:
if self.auto_error:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing authentication token",
headers={"WWW-Authenticate": "Bearer"},
)
return None
if credentials.credentials != settings.api_token:
if self.auto_error:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication token",
headers={"WWW-Authenticate": "Bearer"},
)
return None
return credentials.credentials
async def verify_token_or_query(
credentials: HTTPAuthorizationCredentials = Depends(security),
token: Optional[str] = Query(None, description="API token as query parameter"),
) -> str:
"""Verify the API token from header or query parameter.
Useful for endpoints that need to be accessed via URL (like images).
Args:
credentials: The bearer token credentials from header
token: Token from query parameter
Returns:
The validated token
Raises:
HTTPException: If the token is missing or invalid
"""
# Try header first
if credentials is not None:
if credentials.credentials == settings.api_token:
return credentials.credentials
# Try query parameter
if token is not None:
if token == settings.api_token:
return token
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing or invalid authentication token",
headers={"WWW-Authenticate": "Bearer"},
)