# 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: ```bash 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 ```bash pip install -r requirements.txt ``` Required packages: `winsdk`, `pywin32`, `pycaw`, `comtypes` ### Linux ```bash # Install system dependencies sudo apt-get install python3-dbus python3-gi libdbus-1-dev libglib2.0-dev pip install -r requirements.txt ``` ### macOS ```bash pip install -r requirements.txt ``` No additional dependencies - uses built-in `osascript`. ### Android (Termux) ```bash # 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: ```bash python -m media_server.main --generate-config ``` 2. View your API token: ```bash python -m media_server.main --show-token ``` 3. Start the server: ```bash 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: ```bash # 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 ```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:** ```bash 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_`): ```bash 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:** ```json { "status": "healthy", "platform": "Windows", "version": "1.0.0" } ``` ### Get Media Status ``` GET /api/media/status Authorization: Bearer ``` **Response:** ```json { "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 ``` **Response:** ```json [ { "name": "lock_screen", "label": "Lock Screen", "description": "Lock the workstation", "timeout": 5 } ] ``` #### Execute Script ``` POST /api/scripts/execute/{script_name} Authorization: Bearer Content-Type: application/json {"args": []} ``` **Response:** ```json { "success": true, "script": "lock_screen", "exit_code": 0, "stdout": "", "stderr": "" } ``` ### Configuring Scripts Add scripts in your `config.yaml`: ```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`: ```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: ```powershell .\media_server\service\install_task_windows.ps1 ``` To remove the scheduled task: ```powershell Unregister-ScheduledTask -TaskName "MediaServer" -Confirm:$false ``` ### Windows Service Install: ```bash python -m media_server.service.install_windows install ``` Start/Stop: ```bash python -m media_server.service.install_windows start python -m media_server.service.install_windows stop ``` Remove: ```bash python -m media_server.service.install_windows remove ``` ### Linux (systemd) Install: ```bash sudo ./service/install_linux.sh install ``` Enable and start for your user: ```bash sudo systemctl enable media-server@$USER sudo systemctl start media-server@$USER ``` View logs: ```bash 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