alexei.dolgolyov a0d138bb93 Add built-in Web UI for media control and monitoring
- Add static file serving to FastAPI application
- Create responsive web interface with real-time updates
- Features:
  - Real-time status updates via WebSocket
  - Album artwork display with automatic updates
  - Playback controls (play, pause, next, previous)
  - Volume control with mute toggle
  - Seekable progress bar
  - Token authentication with localStorage persistence
  - Dark theme and responsive design
  - Auto-reconnect WebSocket support
- Update README with Web UI documentation
- Zero dependencies (vanilla HTML/CSS/JavaScript)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 03:25:08 +03:00

Media Server

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

Features

  • Built-in Web UI for real-time media control and monitoring
  • Control any media player via system-wide media transport controls
  • Play/Pause/Stop/Next/Previous track
  • Volume control and mute
  • Seek within tracks
  • Get current track info (title, artist, album, artwork)
  • WebSocket support for real-time updates
  • Token-based authentication
  • Cross-platform support

Web UI

The media server includes a built-in web interface for controlling and monitoring media playback.

Features

  • Real-time status updates via WebSocket connection
  • Album artwork display with automatic updates
  • Playback controls - Play, pause, next, previous
  • Volume control with mute toggle
  • Seekable progress bar - Click to jump to any position
  • Connection status indicator - Know when you're connected
  • Token authentication - Saved in browser localStorage
  • Responsive design - Works on desktop and mobile
  • Dark theme - Easy on the eyes

Accessing the Web UI

  1. Start the media server:

    python -m media_server.main
    
  2. Open your browser and navigate to:

    http://localhost:8765/
    
  3. Enter your API token when prompted (get it with media-server --show-token)

  4. Start playing media in any supported player and watch the UI update in real-time!

Remote Access

To access the Web UI from other devices on your network:

  1. Find your computer's IP address (e.g., 192.168.1.100)
  2. Navigate to http://192.168.1.100:8765/ from any device on the same network
  3. Enter your API token

Security Note: For remote access over the internet, use a reverse proxy with HTTPS (nginx, Caddy) to encrypt traffic.

Requirements

  • Python 3.10+
  • Platform-specific dependencies (see below)

Installation

Windows

pip install -r requirements.txt

Required packages: winsdk, pywin32, pycaw, comtypes

Linux

# Install system dependencies
sudo apt-get install python3-dbus python3-gi libdbus-1-dev libglib2.0-dev

pip install -r requirements.txt

macOS

pip install -r requirements.txt

No additional dependencies - uses built-in osascript.

Android (Termux)

# In Termux
pkg install python termux-api
pip install -r requirements.txt

Requires Termux and Termux:API apps from F-Droid.

Quick Start

  1. Generate configuration with API token:

    python -m media_server.main --generate-config
    
  2. View your API token:

    python -m media_server.main --show-token
    
  3. Start the server:

    python -m media_server.main
    
  4. Open the Web UI (recommended):

    • Navigate to http://localhost:8765/ in your browser
    • Enter your API token from step 2
    • Start playing media and control it from the web interface!
  5. Or test via API:

    # Health check (no auth required)
    curl http://localhost:8765/api/health
    
    # Get media status
    curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8765/api/media/status
    

Configuration

Configuration file locations:

  • Windows: %APPDATA%\media-server\config.yaml
  • Linux/macOS: ~/.config/media-server/config.yaml

config.yaml

host: 0.0.0.0
port: 8765
api_token: your-secret-token-here
poll_interval: 1.0
log_level: INFO

Environment Variables

All settings can be overridden with environment variables (prefix: MEDIA_SERVER_):

export MEDIA_SERVER_HOST=0.0.0.0
export MEDIA_SERVER_PORT=8765
export MEDIA_SERVER_API_TOKEN=your-token
export MEDIA_SERVER_LOG_LEVEL=DEBUG

API Reference

Health Check

GET /api/health

No authentication required. Returns server status and platform info.

Response:

{
  "status": "healthy",
  "platform": "Windows",
  "version": "1.0.0"
}

Get Media Status

GET /api/media/status
Authorization: Bearer <token>

Response:

{
  "state": "playing",
  "title": "Song Title",
  "artist": "Artist Name",
  "album": "Album Name",
  "album_art_url": "https://...",
  "duration": 240.5,
  "position": 120.3,
  "volume": 75,
  "muted": false,
  "source": "Spotify"
}

Media Controls

All control endpoints require authentication and return {"success": true} on success.

Endpoint Method Body Description
/api/media/play POST - Resume playback
/api/media/pause POST - Pause playback
/api/media/stop POST - Stop playback
/api/media/next POST - Next track
/api/media/previous POST - Previous track
/api/media/volume POST {"volume": 75} Set volume (0-100)
/api/media/mute POST - Toggle mute
/api/media/seek POST {"position": 60.0} Seek to position (seconds)
/api/media/turn_on POST - Execute on_turn_on callback
/api/media/turn_off POST - Execute on_turn_off callback
/api/media/toggle POST - Execute on_toggle callback

Script Execution

The server supports executing pre-defined scripts via API.

List Scripts

GET /api/scripts/list
Authorization: Bearer <token>

Response:

[
  {
    "name": "lock_screen",
    "label": "Lock Screen",
    "description": "Lock the workstation",
    "timeout": 5
  }
]

Execute Script

POST /api/scripts/execute/{script_name}
Authorization: Bearer <token>
Content-Type: application/json

{"args": []}

Response:

{
  "success": true,
  "script": "lock_screen",
  "exit_code": 0,
  "stdout": "",
  "stderr": ""
}

Configuring Scripts

Add scripts in your config.yaml:

scripts:
  lock_screen:
    command: "rundll32.exe user32.dll,LockWorkStation"
    label: "Lock Screen"
    description: "Lock the workstation"
    timeout: 5
    shell: true

  shutdown:
    command: "shutdown /s /t 0"
    label: "Shutdown"
    description: "Shutdown the PC immediately"
    timeout: 10
    shell: true

  restart:
    command: "shutdown /r /t 0"
    label: "Restart"
    description: "Restart the PC"
    timeout: 10
    shell: true

  hibernate:
    command: "shutdown /h"
    label: "Hibernate"
    description: "Hibernate the PC"
    timeout: 10
    shell: true

  sleep:
    command: "rundll32.exe powrprof.dll,SetSuspendState 0,1,0"
    label: "Sleep"
    description: "Put PC to sleep"
    timeout: 10
    shell: true

Script configuration options:

Field Required Description
command Yes Command to execute
label No User-friendly display name (defaults to script name)
description No Description of what the script does
icon No Custom MDI icon (e.g., mdi:power)
timeout No Execution timeout in seconds (default: 30, max: 300)
working_dir No Working directory for the command
shell No Run in shell (default: true)

Configuring Callbacks

Callbacks are optional commands executed after media actions. Add them in your config.yaml:

callbacks:
  # Media control callbacks (run after successful action)
  on_play:
    command: "echo Play triggered"
    timeout: 10
    shell: true

  on_pause:
    command: "echo Pause triggered"
    timeout: 10
    shell: true

  on_stop:
    command: "echo Stop triggered"
    timeout: 10
    shell: true

  on_next:
    command: "echo Next track"
    timeout: 10
    shell: true

  on_previous:
    command: "echo Previous track"
    timeout: 10
    shell: true

  on_volume:
    command: "echo Volume changed"
    timeout: 10
    shell: true

  on_mute:
    command: "echo Mute toggled"
    timeout: 10
    shell: true

  on_seek:
    command: "echo Seek triggered"
    timeout: 10
    shell: true

  # Turn on/off/toggle (callback-only actions, no default behavior)
  on_turn_on:
    command: "echo PC turned on"
    timeout: 10
    shell: true

  on_turn_off:
    command: "rundll32.exe user32.dll,LockWorkStation"
    timeout: 5
    shell: true

  on_toggle:
    command: "echo Toggle triggered"
    timeout: 10
    shell: true

Available callbacks:

Callback Triggered by Description
on_play /api/media/play After play succeeds
on_pause /api/media/pause After pause succeeds
on_stop /api/media/stop After stop succeeds
on_next /api/media/next After next track succeeds
on_previous /api/media/previous After previous track succeeds
on_volume /api/media/volume After volume change succeeds
on_mute /api/media/mute After mute toggle
on_seek /api/media/seek After seek succeeds
on_turn_on /api/media/turn_on Callback-only action
on_turn_off /api/media/turn_off Callback-only action
on_toggle /api/media/toggle Callback-only action

Callback configuration options:

Field Required Description
command Yes Command to execute
timeout No Execution timeout in seconds (default: 30, max: 300)
working_dir No Working directory for the command
shell No Run in shell (default: true)

Running as a Service

Run in Administrator PowerShell from the project root:

.\media_server\service\install_task_windows.ps1

To remove the scheduled task:

Unregister-ScheduledTask -TaskName "MediaServer" -Confirm:$false

Windows Service

Install:

python -m media_server.service.install_windows install

Start/Stop:

python -m media_server.service.install_windows start
python -m media_server.service.install_windows stop

Remove:

python -m media_server.service.install_windows remove

Linux (systemd)

Install:

sudo ./service/install_linux.sh install

Enable and start for your user:

sudo systemctl enable media-server@$USER
sudo systemctl start media-server@$USER

View logs:

journalctl -u media-server@$USER -f

Command Line Options

python -m media_server.main [OPTIONS]

Options:
  --host TEXT          Host to bind to (default: 0.0.0.0)
  --port INTEGER       Port to bind to (default: 8765)
  --generate-config    Generate default config file and exit
  --show-token         Show current API token and exit

Security Recommendations

  1. Use HTTPS in production - Set up a reverse proxy (nginx, Caddy) with SSL
  2. Strong tokens - Default tokens are 32 random characters; don't use weak tokens
  3. Firewall - Only expose the port to trusted networks
  4. Secrets management - Don't commit tokens to version control

Supported Media Players

Windows

  • Spotify
  • Windows Media Player
  • VLC
  • Groove Music
  • Web browsers (Chrome, Edge, Firefox)
  • Any app using Windows Media Transport Controls

Linux

  • Any MPRIS-compliant player:
    • Spotify
    • VLC
    • Rhythmbox
    • Clementine
    • Web browsers
    • MPD (with MPRIS bridge)

macOS

  • Spotify
  • Apple Music
  • VLC (partial)
  • QuickTime Player

Android (via Termux)

  • System media controls
  • Limited seek support

Troubleshooting

"No active media session"

  • Ensure a media player is running and has played content
  • On Windows, check that the app supports media transport controls
  • On Linux, verify MPRIS with: dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep mpris

Permission errors on Linux

  • Ensure your user has access to the D-Bus session bus
  • For systemd service, the DBUS_SESSION_BUS_ADDRESS must be set correctly

Volume control not working

  • Windows: Run as administrator if needed
  • Linux: Ensure PulseAudio/PipeWire is running

License

MIT License

Description
No description provided
Readme 2.8 MiB
Languages
Python 46%
JavaScript 30.6%
CSS 13.3%
HTML 8.5%
Shell 0.8%
Other 0.8%