resonance-server Svelte Themes

Resonance Server

A modern, Python-based reimplementation of Lyrion Music Server (LMS). Controls Squeezebox hardware, Squeezelite, and works with iPeng, Squeezer, and other LMS-compatible apps.

Resonance

Resonance

A modern, LMS-compatible music server written in Python

PyPI License: GPL-2.0 Python 3.11+ Svelte 5 Tailwind v4 Status: Alpha

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

pip install resonance-server
resonance

That's it. The server starts on ports 3483 (Slimproto), 9000 (HTTP/API), and 9090 (CLI). The package includes the Web UI and all core plugins — no extra build steps needed.

Open http://localhost:9000 in your browser to access the Web UI.

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

From Source

If you prefer to run from a git checkout (e.g. for development):

Linux / macOS:

git clone https://github.com/endegelaende/resonance-server.git
cd resonance-server
python3 -m venv .venv
.venv/bin/python -m pip install -e .
.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 -e .
.venv\Scripts\python.exe -m resonance

Note: When running from source, build the Web UI separately: cd web-ui && npm install && npm run build — see Web UI for details.


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: host so 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_uuid into 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 recommended way to install Resonance is via PyPI:

pip install resonance-server

This installs the server with all required dependencies, bundled core plugins, and the pre-built Web UI. No Node.js, no build steps — just install and run.

Tip: If python3 is 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

All dependencies are installed automatically via pip install resonance-server:

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 resonance-server[blurhash]   # BlurHash cover art placeholders (Pillow + blurhash-python)

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 system PATH.


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:

  1. Add your music — Open http://localhost:9000 in your browser (or http://localhost:5173 if running the dev server). Click Add Folder, enter the path to your music directory, and the library scan starts automatically.

  2. 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).

  3. 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.

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.

Top categories

Loading Svelte Themes