# Media Server A REST API server for controlling system media playback on Windows, Linux, macOS, and Android. ## Features - 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) - Token-based authentication - Cross-platform support ## 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. Test the connection: ```bash curl http://localhost:8765/api/health ``` 5. Test with authentication: ```bash 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_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_`): ```bash 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:** ```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