A modern Python implementation of the Logitech Media Server (Squeezebox Server)
Project description
Resonance
A modern, LMS-compatible music server written in Python
An independent reimplementation of the
Lyrion Music Server
(formerly Logitech Media Server / SlimServer), built from scratch in Python with asyncio.
Controls Squeezebox hardware, Squeezelite, and works with iPeng, Squeezer, and other LMS-compatible apps.
Disclaimer — Resonance is a hobby project, not affiliated with or endorsed by the Lyrion / LMS project. It is under active development, not finished, and will contain bugs. When protocol behavior is unclear, the LMS source code is the reference. LLMs are used extensively as a coding partner throughout development. The developer only owns a single Squeezebox Radio — other hardware (Touch, Boom, Transporter, Classic, Controller) has not been tested. Feedback and bug reports are very welcome!
Table of Contents
- Quick Start
- Docker
- Architecture
- Features
- Installation Details
- Transcoding Tools (optional)
- Web UI
- First Steps
- Project Structure
- Running the Tests
- Contributing
- License
- Acknowledgments
Quick Start
Linux / macOS:
git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
python3 -m venv .venv
.venv/bin/python -m pip install mutagen aiosqlite fastapi uvicorn httpx
.venv/bin/python -m resonance
Windows (PowerShell or cmd.exe):
git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
python -m venv .venv
.venv\Scripts\python.exe -m pip install mutagen aiosqlite fastapi uvicorn httpx
.venv\Scripts\python.exe -m resonance
The server starts on ports 3483 (Slimproto), 9000 (HTTP/API), and 9090 (CLI).
Players on the same subnet may discover the server automatically via UDP broadcast.
If not, point your player to the server IP manually (e.g. squeezelite -s <server-ip>).
→ See First Steps for what to do next.
Command-line options
Options:
-v, --verbose Enable debug logging
-p, --port PORT Slimproto port (default: 3483)
--host HOST Bind address (default: 0.0.0.0)
--web-port PORT HTTP port (default: 9000)
--cli-port PORT Telnet CLI port (default: 9090, 0 to disable)
--version Show version
Docker
Resonance ships with a multi-stage Dockerfile and a docker-compose.yml for easy deployment.
The image includes all audio transcoding tools (faad, flac, lame, sox, ffmpeg) and the
pre-built Svelte Web-UI — no extra setup required.
Note: The Compose file uses
network_mode: hostso that Squeezebox UDP discovery broadcasts on port 3483 reach the container. Docker bridge mode does not forward LAN broadcasts, which prevents players from finding the server automatically.
Quick start with Docker Compose:
# 1. Clone the repository
git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
# 2. Configure your environment
cp .env.example .env
# Edit .env — at minimum set MUSIC_DIR to your music library path
# 3. Build and start
docker compose up -d
# 4. Open the Web UI
# http://localhost:9000
# 5. View logs
docker compose logs -f resonance
Standalone Docker run (host network):
docker build -t resonance-server .
docker run -d \
--name resonance-server \
--network host \
--restart unless-stopped \
-v /path/to/music:/music:ro \
-v resonance-data:/app/data \
-v resonance-cache:/app/cache \
resonance-server
If you have been running Resonance natively before, mount your existing
cache/server_uuidinto the container (-v ./cache/server_uuid:/app/cache/server_uuid:ro) so the server keeps the same identity. Squeezebox Radio blacklists UUIDs of servers it previously failed to connect to, so a consistent UUID is essential.
Environment variables
| Variable | Default | Description |
|---|---|---|
MUSIC_DIR |
./music |
Host path to your music library (mounted read-only) |
LOG_LEVEL |
INFO |
Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL |
RESONANCE_DISPLAY |
0 |
Enable bitmap display rendering for SB2/3/Classic/Boom (set to 1 after hardware verification) |
See .env.example for the full reference.
Architecture
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Web-UI / │ │ │ │ Squeezebox │
│ iPeng / │◄────►│ Resonance │◄────►│ Radio/Touch │──► ))
│ Squeezer │ HTTP │ Server │Slim- │ Squeezelite │
│ │ │ (Python) │proto │ │
└─────────────┘ └──────┬──────┘ └─────────────┘
│
┌──────┴──────┐
│ SQLite │
│ Music DB │
└─────────────┘
Resonance speaks the same protocols as LMS. The server gives commands, players execute.
| Port | Protocol | Purpose |
|---|---|---|
| 3483 | Slimproto (TCP) | Binary player control |
| 9000 | HTTP | Streaming + JSON-RPC + Web UI |
| 9090 | Telnet CLI | Text-based command interface |
Features
Protocols & Compatibility
| Feature | Status |
|---|---|
| Slimproto (binary player control) | Yes |
| JSON-RPC API (LMS-compatible) | Yes |
| Cometd/Bayeux (real-time push) | Yes |
| Telnet CLI (Port 9090) | Yes |
| UDP Discovery (auto-detect) | Yes |
| Jive Menu System (Radio, Touch, Boom, Controller) | Yes |
Audio
| Feature | Status |
|---|---|
| HTTP Streaming (MP3, FLAC, OGG, WAV) | Yes |
| On-the-fly Transcoding (M4A, M4B, AAC, ALAC) | Yes |
| Internet Radio (radio-browser.info via plugin) | Yes |
| Podcasts (RSS + PodcastIndex via plugin) | Yes |
| Remote URL Proxy (HTTPS → HTTP for hardware) | Yes |
| Gapless Playback | Yes |
| Crossfade (configurable overlap) | Yes |
| ReplayGain (track & album mode) | Yes |
| Seeking (byte-accurate & time-based) | Yes |
| DSD/DoP (DSF/DFF, native + transcode) | Yes |
Library & Playback
| Feature | Status |
|---|---|
| Music Library (scanner, SQLite, full-text search) | Yes |
| Cover Art (extraction, caching, BlurHash placeholders) | Yes |
| Playlist / Queue (shuffle, repeat, insert, move) | Yes |
| Favorites (hierarchical folders, LMS-compatible) | Yes |
| Alarm Scheduling (per-player) | Yes |
| Device Capabilities (volume curves, hardware flags) | Yes |
| Plugin System (commands, menus, content providers) | Yes |
| Server-Driven UI (plugins build web pages in Python) | Yes |
| Security Headers (CSP, X-Frame-Options, etc.) | Yes |
| Community Plugin Repository (one-click install) | Yes |
Frontends
| Frontend | Status |
|---|---|
| Web UI — Svelte 5 + Tailwind v4 (see below) | Yes |
| iPeng (iOS) | Verified |
| Squeezer (Android) | Verified |
Installation Details
The Quick Start above covers cloning and installing. Below are additional details for reference.
Alternative — Download ZIP instead of git clone:
Go to https://github.com/endegelaende/resonance-server → green Code button → Download ZIP.
Tip: If
python3is not found or too old, install Python 3.11+ via your package manager:
- Debian/Ubuntu:
sudo apt install python3 python3-venv python3-pip- Fedora:
sudo dnf install python3- macOS:
brew install python@3
Python Dependencies
| Package | Purpose |
|---|---|
mutagen |
Read audio file metadata (tags, duration, cover art) |
aiosqlite |
Async SQLite for the music library database |
fastapi |
Web framework for JSON-RPC, REST API, streaming |
uvicorn |
ASGI server that runs FastAPI |
httpx |
Fully featured HTTP client for Python 3 |
Optional Dependencies
pip install blurhash-python pillow # BlurHash cover art placeholders
Transcoding Tools (optional)
Whether a format is streamed directly (passthrough) or transcoded depends on
both the audio format and the player/device type. The rules are defined
in resonance/config/devices.toml (device tiers) and
resonance/config/legacy.conf (transcoding pipelines).
Common cases that need no extra tools: MP3, FLAC, OGG, and WAV are passthrough for most players (Squeezelite, SB2+, Radio, Touch, Boom).
Cases that require external tools:
| Tool | When needed |
|---|---|
| faad | M4A, M4B, ALAC, AAC-in-MP4 — decodes audio from MP4 containers |
| lame | Used together with faad — encodes the decoded stream to MP3 |
| flac | FLAC → PCM conversion (devices requesting raw PCM), server-side crossfade |
| sox | Opus support, OGG → PCM fallback, server-side crossfade |
Example: MP4-container formats (M4A, M4B, ALAC) always need transcoding because no Squeezebox hardware or Squeezelite can reliably stream MP4 over HTTP. WMA works on SB2+ but needs transcoding on SLIMP3. Opus always needs
sox.
Windows
Binaries are included in third_party/bin/ — no extra installation needed.
Linux
# Standard tools (Debian/Ubuntu):
sudo apt install -y flac lame sox
macOS
# Using Homebrew:
brew install flac lame sox
For faad you need the LMS-patched version from
ralph-irving/faad2, which adds
seeking support (-j/-e flags) and ALAC decoding.
How to get the LMS-patched faad binary
Option A — Extract from LMS package (easiest):
Download the LMS package from lms-community.github.io,
extract it, and copy the faad binary for your architecture:
# Example for x86_64:
cp /path/to/lms/Bin/x86_64-linux/faad third_party/bin/faad
chmod +x third_party/bin/faad
Available architectures: x86_64-linux, aarch64-linux, arm-linux,
armhf-linux, i386-linux, powerpc-linux, sparc-linux, darwin,
i386-freebsd-64int, i86pc-solaris-thread-multi-64int.
Option B — Build from source:
git clone https://github.com/ralph-irving/faad2.git
cd ~/faad2
autoreconf -i
./configure
make
sudo make install
sudo ldconfig
Verify:
ldconfig -p | grep libfaad
which faad
faad -h
Note: If you place the binaries in
third_party/bin/, Resonance finds them automatically. Otherwise, make sure they are on your systemPATH.
Web UI
Resonance ships with a web interface built with Svelte 5, SvelteKit, and Tailwind CSS v4.
Running the Web UI
cd web-ui
npm install # one-time
npm run dev # dev server → http://localhost:5173
npm run build # production build → web-ui/build/
The dev server proxies API requests to the Python backend on port 9000. Make sure the backend is running first.
API Integration
The frontend communicates with Resonance via:
| Protocol | Endpoint | Purpose |
|---|---|---|
| JSON-RPC | /jsonrpc.js |
LMS-compatible API (player control, library queries) |
| REST | /api/* |
Modern endpoints (folders, scan, artwork, plugins) |
| Cometd | /cometd |
Real-time updates (currently uses polling) |
| SSE | /api/plugins/{id}/events |
Server-Sent Events for plugin UI live updates |
Project Structure
web-ui/
├── src/
│ ├── app.css # Global styles + Tailwind v4 theme
│ ├── app.html # HTML shell
│ ├── lib/
│ │ ├── api.ts # API client (JSON-RPC + REST)
│ │ ├── components/
│ │ │ ├── AddFolderModal.svelte # Add music folder dialog
│ │ │ ├── AlarmSettings.svelte # Per-player alarm management
│ │ │ ├── BlurHashPlaceholder.svelte# Blurred artwork placeholder
│ │ │ ├── CoverArt.svelte # Album art with BlurHash + glow
│ │ │ ├── FavoritesView.svelte # Favorites browse + manage
│ │ │ ├── NowPlaying.svelte # Playback controls + progress
│ │ │ ├── PlayerSelector.svelte # Multi-player dropdown
│ │ │ ├── PlaylistsView.svelte # Saved playlists manage
│ │ │ ├── PluginsView.svelte # Plugin management UI
│ │ │ ├── PodcastView.svelte # Podcast browse + subscribe
│ │ │ ├── QualityBadge.svelte # Lossless / Hi-Res indicators
│ │ │ ├── Queue.svelte # Playlist sidebar
│ │ │ ├── RadioView.svelte # Internet radio browse + search
│ │ │ ├── ResizeHandle.svelte # Drag-to-resize panels
│ │ │ ├── SearchBar.svelte # Search with debounce + Ctrl+K
│ │ │ ├── SettingsPanel.svelte # Player settings
│ │ │ ├── Sidebar.svelte # Navigation sidebar
│ │ │ ├── ToastContainer.svelte # Toast notification renderer
│ │ │ └── TrackList.svelte # Track list with play/add actions
│ │ ├── plugin-ui/ # Server-Driven UI (SDUI) system
│ │ │ ├── registry.ts # Widget type → Svelte component map
│ │ │ ├── PluginRenderer.svelte # Recursive component renderer
│ │ │ ├── PluginPageView.svelte # Page container (SSE + polling)
│ │ │ ├── actions.svelte.ts # Generic action dispatcher
│ │ │ └── widgets/ # 20 widget components
│ │ │ ├── ActionButton.svelte, Alert.svelte, Card.svelte, ...
│ │ │ └── Toggle.svelte, Textarea.svelte, Modal.svelte, ...
│ │ └── stores/
│ │ ├── color.svelte.ts # Dynamic accent colors (Vibrant)
│ │ ├── player.svelte.ts # Player state + polling
│ │ ├── toast.svelte.ts # Toast notification store
│ │ └── ui.svelte.ts # Navigation + layout state
│ └── routes/
│ ├── +layout.svelte
│ └── +page.svelte
├── static/ # Fonts, favicon, brand assets
├── package.json
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts
First Steps
Once the server is running:
-
Add your music — Open
http://localhost:9000in your browser (orhttp://localhost:5173if running the dev server). Click Add Folder, enter the path to your music directory, and the library scan starts automatically. -
Connect a player — Start Squeezelite or power on your Squeezebox hardware. Players on the same subnet may discover the server automatically via UDP broadcast. If discovery doesn't work, specify the server IP explicitly (e.g.
squeezelite -s <server-ip>or enter the IP in your hardware player's network settings). -
Play music — Browse your library in the Web UI, select a track or album, and hit play. LMS-compatible apps like iPeng (iOS) or Squeezer (Android) should work as well, but this has not been fully verified yet.
Project Structure
resonance-server/
├── resonance/ # Main Python package
│ ├── __main__.py # Entry point (python -m resonance)
│ ├── server.py # Main server, starts all components
│ ├── content_provider.py # ContentProvider ABC + Registry
│ ├── plugin.py # PluginContext (DI for plugins)
│ ├── plugin_manager.py # Plugin discovery, loading, lifecycle
│ ├── config/ # Configuration
│ │ ├── devices.toml # Device tiers (Modern/Legacy)
│ │ └── legacy.conf # Transcoding rules (LMS-style)
│ ├── core/ # Business logic
│ │ ├── library.py # MusicLibrary facade
│ │ ├── library_db.py # SQLite database layer
│ │ ├── scanner.py # Audio file scanner (mutagen)
│ │ ├── playlist.py # Playlist management
│ │ ├── artwork.py # Cover art extraction + BlurHash
│ │ └── events.py # Event bus (pub/sub)
│ ├── player/ # Player management
│ │ ├── client.py # PlayerClient (status, commands)
│ │ ├── capabilities.py # Device capabilities + volume curves
│ │ └── registry.py # PlayerRegistry (all players)
│ ├── protocol/ # Network protocols
│ │ ├── slimproto.py # Slimproto server (Port 3483)
│ │ ├── cli.py # Telnet CLI (Port 9090)
│ │ ├── discovery.py # UDP discovery
│ │ └── commands.py # Binary command builder
│ ├── streaming/ # Audio streaming
│ │ ├── server.py # Streaming server + URL proxy
│ │ ├── transcoder.py # Transcoding pipeline
│ │ ├── crossfade.py # Server-side crossfade (SoX)
│ │ ├── seek_coordinator.py # Latest-wins seek coordination
│ │ └── policy.py # Format decision logic
│ └── web/ # HTTP layer
│ ├── server.py # FastAPI app (Port 9000)
│ ├── jsonrpc.py # JSON-RPC handler
│ ├── cometd.py # Bayeux long-polling
│ ├── handlers/ # Command handlers
│ │ ├── status.py # Player status
│ │ ├── playback.py # Play/Pause/Stop
│ │ ├── playlist.py # Queue commands
│ │ ├── seeking.py # Seek (non-blocking)
│ │ └── library.py # Library queries
│ └── routes/ # FastAPI routes
│ ├── api.py # REST API
│ ├── streaming.py # /stream.mp3 (local + remote proxy)
│ ├── artwork.py # Cover art endpoints
│ └── cometd.py # /cometd
├── plugins/ # Plugin directory (auto-discovered)
│ ├── example/ # Hello World template
│ ├── favorites/ # Favorites (LMS-compatible)
│ ├── nowplaying/ # Now Playing tutorial plugin
│ ├── radio/ # Internet Radio (radio-browser.info)
│ └── podcast/ # Podcast (RSS + PodcastIndex)
├── web-ui/ # Svelte 5 frontend
├── tests/ # pytest suite (2853 tests)
├── scripts/ # Dev & test scripts
├── third_party/ # External binaries
│ ├── bin/ # faad, flac, lame, sox (Windows)
│ └── squeezelite/ # Squeezelite binary
├── docs/ # Documentation
│ ├── ARCHITECTURE.md # System architecture
│ ├── CHANGELOG.md # Change log
│ ├── PLUGINS.md # Plugin system overview
│ ├── PLUGIN_API.md # Plugin API reference (incl. SDUI §19)
│ ├── PLUGIN_TUTORIAL.md # Plugin tutorial (step by step)
│ └── PLUGIN_CASESTUDY.md # Case study: raopbridge plugin deep dive
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Reference deployment config
├── .dockerignore # Docker build context exclusions
├── .env.example # Environment variable reference
├── pyproject.toml # Python project config
└── LICENSE # GPL-2.0
Running the Tests
# Install dev dependencies (one-time)
pip install -e ".[dev]"
# Install ALL optional dependencies (Pillow, blurhash, dev tools)
pip install -e ".[all]"
# Run the full test suite
pytest
# Run with coverage report
pytest --cov
# Run a specific test module
pytest tests/test_radio_plugin.py -v
Optional Dependency Markers
Some tests require optional dependencies (Pillow, external audio tools like
flac, lame, sox). These tests are tagged with pytest markers and will
be auto-skipped when the dependency is missing — no failures, just skips.
| Marker | Requires | Install |
|---|---|---|
@pytest.mark.requires_pil |
Pillow (PIL) | pip install -e ".[blurhash]" |
@pytest.mark.requires_tools |
flac, lame, sox on PATH |
See Transcoding Tools |
# Run only tests that need no optional dependencies
pytest -m "not requires_pil and not requires_tools"
# Run only PIL-dependent tests
pytest -m requires_pil
# Run everything (missing deps auto-skip with clear message)
pytest
Contributing
Resonance is a hobby project and contributions are welcome! Here's how you can help:
- Bug reports — Open an issue with steps to reproduce.
- Hardware testing — If you own Squeezebox hardware other than Radio, your test results are especially valuable.
- Pull requests — Fork the repo, create a branch, make your changes, and open a PR.
License
GPL-2.0 — same as the original Lyrion Music Server.
See THIRD_PARTY_NOTICES.md for licenses of all dependencies and shipped binaries.
Acknowledgments
A huge thank-you to the Squeezebox community — you keep this wonderful platform alive.
- Lyrion Music Server (GitHub) — the original that inspired this project
- LMS Community Forums — for keeping Squeezebox alive
- Squeezelite by Ralph Irving — excellent software player
- ralph-irving/faad2 — patched faad binary with seeking and ALAC support
- Community Plugins — community-contributed plugins (installable via the Plugin Manager)
If you have feedback, ideas, or run into bugs — please open an issue or start a discussion. Community input is what makes this project better.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file resonance_server-0.1.0.tar.gz.
File metadata
- Download URL: resonance_server-0.1.0.tar.gz
- Upload date:
- Size: 998.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0fe015ee1230626711f2247088902a14b1088d1fc9871854c02ec384a73c39f6
|
|
| MD5 |
2df1e80f54f6a8130d01a2f7275da582
|
|
| BLAKE2b-256 |
ce2147ba6b213fbb6d37078ea44f4bc7f981bd2904b6923a25d597a89770eaca
|
Provenance
The following attestation bundles were made for resonance_server-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on endegelaende/resonance-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
resonance_server-0.1.0.tar.gz -
Subject digest:
0fe015ee1230626711f2247088902a14b1088d1fc9871854c02ec384a73c39f6 - Sigstore transparency entry: 1050046077
- Sigstore integration time:
-
Permalink:
endegelaende/resonance-server@1a1c0afc0844ca92a8f119e14604061437224e82 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/endegelaende
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@1a1c0afc0844ca92a8f119e14604061437224e82 -
Trigger Event:
push
-
Statement type:
File details
Details for the file resonance_server-0.1.0-py3-none-any.whl.
File metadata
- Download URL: resonance_server-0.1.0-py3-none-any.whl
- Upload date:
- Size: 687.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b20639a5e5c9431c0df531679cf03dd96468f040ab75898aefcb83096100f0f
|
|
| MD5 |
9fff4c72363d56367ac7ecb0784b50ed
|
|
| BLAKE2b-256 |
05e2067f4c01cdb1448bfe9a403bed8c6a1327fc77f1ae028fa392a60b931a38
|
Provenance
The following attestation bundles were made for resonance_server-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on endegelaende/resonance-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
resonance_server-0.1.0-py3-none-any.whl -
Subject digest:
9b20639a5e5c9431c0df531679cf03dd96468f040ab75898aefcb83096100f0f - Sigstore transparency entry: 1050046081
- Sigstore integration time:
-
Permalink:
endegelaende/resonance-server@1a1c0afc0844ca92a8f119e14604061437224e82 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/endegelaende
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@1a1c0afc0844ca92a8f119e14604061437224e82 -
Trigger Event:
push
-
Statement type: