- Refactored index.html: Split into separate HTML (309 lines), CSS (908 lines), and JS (1,286 lines) files - Implemented media browser with folder configuration, recursive navigation, and thumbnail display - Added metadata extraction using mutagen library (title, artist, album, duration, bitrate, codec) - Implemented thumbnail generation and caching with SHA256 hash-based keys and LRU eviction - Added platform-specific file playback (os.startfile on Windows, xdg-open on Linux, open on macOS) - Implemented path validation security to prevent directory traversal attacks - Added smooth thumbnail loading with fade-in animation and loading spinner - Added i18n support for browser (English and Russian) - Updated dependencies: mutagen>=1.47.0, pillow>=10.0.0 - Added comprehensive media browser documentation to README Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
681 lines
18 KiB
Markdown
681 lines
18 KiB
Markdown
# 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
|
|
|
|
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.
|
|
|
|
### 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:
|
|
|
|
1. Copy `media_server/static/locales/en.json` to a new file named with your language code (e.g., `de.json` for German)
|
|
2. Translate all strings to your language, keeping the same JSON structure
|
|
3. Add your language to the `supportedLocales` object in `media_server/static/index.html`:
|
|
|
|
```javascript
|
|
const supportedLocales = {
|
|
'en': 'English',
|
|
'ru': 'Русский',
|
|
'de': 'Deutsch' // Add your language here
|
|
};
|
|
```
|
|
|
|
4. Test the translation by switching to your language in the Web UI
|
|
5. Submit a pull request with your changes
|
|
|
|
See [CLAUDE.md](CLAUDE.md#internationalization-i18n) for detailed translation guidelines.
|
|
|
|
## Media Browser
|
|
|
|
The Media Browser feature allows you to browse and play media files from configured folders directly through the Web UI.
|
|
|
|
### Browser Features
|
|
|
|
- **Folder Configuration** - Mount multiple media folders (music/video directories)
|
|
- **Recursive Navigation** - Browse through folder hierarchies with breadcrumb navigation
|
|
- **Thumbnail Display** - Automatically generated thumbnails from album art
|
|
- **Metadata Extraction** - View title, artist, album, duration, bitrate, and more
|
|
- **Remote Playback** - Play files on the PC running the media server (not in the browser)
|
|
- **Last Path Memory** - Automatically returns to your last browsed location
|
|
- **Lazy Loading** - Thumbnails load only when visible for better performance
|
|
|
|
### Configuration
|
|
|
|
Add media folders in your `config.yaml`:
|
|
|
|
```yaml
|
|
# Media folders for browser
|
|
media_folders:
|
|
music:
|
|
path: "C:\\Users\\YourUsername\\Music"
|
|
label: "My Music"
|
|
enabled: true
|
|
videos:
|
|
path: "C:\\Users\\YourUsername\\Videos"
|
|
label: "My Videos"
|
|
enabled: true
|
|
|
|
# Thumbnail size: "small" (150x150), "medium" (300x300), or "both"
|
|
thumbnail_size: "medium"
|
|
```
|
|
|
|
### How Playback Works
|
|
|
|
When you play a file from the Media Browser:
|
|
|
|
1. The file is opened using the **default system media player** on the PC running the media server
|
|
2. This is designed for **remote control scenarios** where you browse media from one device (e.g., Home Assistant dashboard, phone) but want audio to play on the PC
|
|
3. The media player must support the **Windows Media Session API** for playback tracking
|
|
|
|
### Media Player Compatibility
|
|
|
|
**⚠️ Important Limitation:** Not all media players expose their playback information to the Windows Media Session API. This means some players will open and play the file, but the Media Server UI won't show playback status, track information, or allow remote control.
|
|
|
|
**✅ Compatible Players** (work with playback tracking):
|
|
|
|
- **VLC Media Player** - Full support
|
|
- **Groove Music** (Windows 10/11 built-in) - Full support
|
|
- **Spotify** - Full support (if already running)
|
|
- **Chrome/Edge/Firefox** - Full support for web players
|
|
- **foobar2000** - Full support (with proper configuration/plugins)
|
|
|
|
**❌ Limited/No Support:**
|
|
|
|
- **Windows Media Player Classic** - Opens files but doesn't expose session info
|
|
- **Windows Media Player** (classic version) - Limited session support
|
|
|
|
**Recommendation:** Set **VLC Media Player** or **Groove Music** as your default audio player for the best experience with the Media Browser.
|
|
|
|
#### Changing Your Default Media Player (Windows)
|
|
|
|
1. Open Windows Settings → Apps → Default apps
|
|
2. Search for "Music player" or "Video player"
|
|
3. Select VLC Media Player or Groove Music
|
|
4. Files opened from Media Browser will now use the selected player
|
|
|
|
### API Endpoints
|
|
|
|
The Media Browser exposes several REST API endpoints:
|
|
|
|
| Endpoint | Method | Description |
|
|
|--------------------------|--------|-----------------------------------|
|
|
| `/api/browser/folders` | GET | List configured media folders |
|
|
| `/api/browser/browse` | GET | Browse directory contents |
|
|
| `/api/browser/metadata` | GET | Get media file metadata |
|
|
| `/api/browser/thumbnail` | GET | Get thumbnail image |
|
|
| `/api/browser/play` | POST | Open file with default player |
|
|
|
|
All endpoints require bearer token authentication.
|
|
|
|
### Security Notes
|
|
|
|
- **Path Traversal Protection** - All paths are validated to prevent directory traversal attacks
|
|
- **Folder Restrictions** - Only configured folders are accessible
|
|
- **Authentication Required** - All endpoints require a valid API token
|
|
|
|
## 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 <token>
|
|
```
|
|
|
|
**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 <token>
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
[
|
|
{
|
|
"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:**
|
|
```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
|