- Add CSS to hide page content during translation load (opacity transition) - Hide dialogs explicitly until opened with showModal() - Hide auth overlay by default in HTML (shown only when needed) - Prevents flash of English text, dialogs, and auth overlay on page load - Smooth fade-in after translations are loaded Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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
- Multi-language support - English and Russian locales with automatic detection
Accessing the Web UI
-
Start the media server:
python -m media_server.main -
Open your browser and navigate to:
http://localhost:8765/ -
Enter your API token when prompted (get it with
media-server --show-token) -
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:
- Find your computer's IP address (e.g.,
192.168.1.100) - Navigate to
http://192.168.1.100:8765/from any device on the same network - Enter your API token
Security Note: For remote access over the internet, use a reverse proxy with HTTPS (nginx, Caddy) to encrypt traffic.
Localization
The Web UI supports multiple languages with automatic browser locale detection:
Available Languages:
- English (en) - Default
- Русский (ru) - Russian
The interface automatically detects your browser language on first visit. You can manually switch languages using the dropdown in the top-right corner of the Web UI.
Contributing New Locales:
We welcome translations for additional languages! To contribute a new locale:
-
Copy
media_server/static/locales/en.jsonto a new file named with your language code (e.g.,de.jsonfor German) -
Translate all strings to your language, keeping the same JSON structure
-
Add your language to the
supportedLocalesobject inmedia_server/static/index.html:const supportedLocales = { 'en': 'English', 'ru': 'Русский', 'de': 'Deutsch' // Add your language here }; -
Test the translation by switching to your language in the Web UI
-
Submit a pull request with your changes
See CLAUDE.md for detailed translation guidelines.
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
-
Generate configuration with API token:
python -m media_server.main --generate-config -
View your API token:
python -m media_server.main --show-token -
Start the server:
python -m media_server.main -
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!
- Navigate to
-
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 Tokens - Multiple tokens with labels for client identification
api_tokens:
home_assistant: "your-home-assistant-token-here"
mobile: "your-mobile-app-token-here"
web_ui: "your-web-ui-token-here"
poll_interval: 1.0
log_level: INFO
Authentication
The media server supports multiple API tokens with friendly labels. This allows you to:
- Issue different tokens for different clients (Home Assistant, mobile apps, web UI, etc.)
- Identify which client is making requests in the server logs
- Revoke individual tokens without affecting other clients
Token labels appear in all server logs, making it easy to track and debug client connections:
2026-02-06 03:36:20,806 - media_server.services.websocket_manager - [home_assistant] - INFO - WebSocket client connected
2026-02-06 03:28:24,258 - media_server.routes.scripts - [mobile] - INFO - Executing script: lock_screen
Viewing your tokens:
python -m media_server.main --show-token
Output:
Config directory: C:\Users\...\AppData\Roaming\media-server
API Tokens:
home_assistant B04zhGDjnxH6LIwxL3VOT0F4qORwaipD7LoDyeAG4EU
mobile xyz123...
web_ui abc456...
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_LOG_LEVEL=DEBUG
Note: For multi-token configuration, use the config.yaml file. Environment variables only support single-token mode.
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
Windows Task Scheduler (Recommended)
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
- Use HTTPS in production - Set up a reverse proxy (nginx, Caddy) with SSL
- Strong tokens - Default tokens are 32 random characters; don't use weak tokens
- Firewall - Only expose the port to trusted networks
- 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_ADDRESSmust be set correctly
Volume control not working
- Windows: Run as administrator if needed
- Linux: Ensure PulseAudio/PipeWire is running
License
MIT License