PWA remote control for cmus
Project description
cmus-web
A self-hosted PWA remote control for cmus. Control your player from any device on your network.
Table of contents
- Features
- A note on PWA and HTTPS
- Prerequisites
- Installation
- Usage
- Docker
- Keyboard shortcuts
- Tech stack
- License
Features
- Real-time player state via SSE (track, position, volume, shuffle/repeat)
- Embedded album art extraction (MP3, FLAC, M4A, etc.)
- Installable PWA with offline caching
- Keyboard shortcuts for all playback controls
- Customizable accent color (persisted in localStorage)
- Zero-build frontend (vanilla HTML/CSS/JS + Alpine.js)
A note on PWA and HTTPS
The install/Add to Home Screen prompt only works over HTTPS. Browsers require a secure context for service workers and PWA installation. If you're only accessing cmus-web over LAN on http://192.168.x.x:8000, the app still works fine as a regular web page, you just won't get the install prompt.
If you want full PWA support, you need a domain name with a TLS certificate. This is straightforward with a reverse proxy like Traefik, which handles automatic HTTPS via Let's Encrypt. Put cmus-web behind Traefik and you're done.
Prerequisites
- Python 3.13+
- cmus running with a socket (default:
$XDG_RUNTIME_DIR/cmus-socketor/tmp/cmus-socket) cmus-remoteon yourPATH
Installation
From PyPI
pip install cmus-web
Or with pipx (isolated, recommended for CLI tools):
pipx install cmus-web
From source
git clone https://github.com/FawkesOficial/cmus-web.git
cd cmus-web
uv sync
Usage
Make sure cmus is running, then:
cmus-web
Open http://127.0.0.1:8000 in your browser.
CLI options
cmus-web [OPTIONS]
Options:
--host TEXT Bind address (default: 127.0.0.1)
--port INTEGER Port (default: 8000)
--socket TEXT cmus socket path (overrides CMUS_SOCKET env)
--music-dir TEXT Path to music directory for album art in Docker
--prefix TEXT Host path prefix from cmus-remote to strip (for Docker)
--help Show this message and exit.
Examples
# Bind to all interfaces (accessible from other devices)
cmus-web --host 0.0.0.0
# Use a custom socket path
cmus-web --socket /run/user/1000/cmus-socket
# Run on a different port
cmus-web --port 9090
Docker
Important: cmus socket
cmus-web communicates with cmus through a Unix domain socket. If the socket path you mount does not exist on the host, Docker will silently create a directory in its place, which will break the connection and can be confusing to debug.
Use this helper script to verify the socket exists before starting the container:
#!/usr/bin/env bash
# start-cmus-web.sh - verify cmus socket exists, then start the container
SOCKET="${CMUS_SOCKET:-$XDG_RUNTIME_DIR/cmus-socket}"
if [ ! -S "$SOCKET" ]; then
echo "Error: cmus socket not found at $SOCKET"
echo "Make sure cmus is running and the socket path is correct."
exit 1
fi
docker run -d \
-v "$SOCKET":/tmp/cmus-socket \
-v ~/Music:/music \
-e CMUS_WEB_PREFIX="$HOME/Music" \
-p 8000:8000 \
cmus-web
docker run
docker build -t cmus-web .
docker run -d \
-v /run/user/1000/cmus-socket:/tmp/cmus-socket \
-v ~/Music:/music \
-e CMUS_WEB_PREFIX=/home/user/Music \
-p 8000:8000 \
cmus-web
The music volume mount enables album art extraction. Use CMUS_WEB_PREFIX to tell cmus-web what host path to strip from cmus-remote file paths so they resolve correctly inside the container.
docker compose
services:
cmus-web:
build: .
container_name: cmus-web
ports:
- "8000:8000"
volumes:
- /run/user/1000/cmus-socket:/tmp/cmus-socket
- ~/Music:/music
environment:
- CMUS_WEB_PREFIX=/home/user/Music
restart: unless-stopped
docker compose up -d
Keyboard shortcuts
| Key | Action |
|---|---|
Space / C |
Play / Pause |
B |
Next track |
Z |
Previous track |
S |
Toggle shuffle |
R |
Toggle repeat |
L |
Search lyrics online |
+ / - |
Volume up / down |
Arrow Left / Right |
Seek -5s / +5s |
Arrow Up / Down |
Volume up / down |
Tech stack
- Backend: Python, FastAPI, uvicorn, Mutagen, SSE-Starlette
- Frontend: Vanilla HTML/CSS/JS, Alpine.js
- Packaging: uv, Hatchling
- Container: Debian slim, uv
License
cmus-web is distributed under the terms of the GPL-3.0 license.
Project details
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 cmus_web-0.1.2.tar.gz.
File metadata
- Download URL: cmus_web-0.1.2.tar.gz
- Upload date:
- Size: 132.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ba8abd8a273a2ebdc69c4559348cd8360284c10a400d721c11be83477643728
|
|
| MD5 |
e6f12cfa3c78b47360e49293284c6b0a
|
|
| BLAKE2b-256 |
4d8bfeac3328833312c1a4d2bd5d3170c722ba0d681ee4fa292da4ed9842d252
|
Provenance
The following attestation bundles were made for cmus_web-0.1.2.tar.gz:
Publisher:
python-publish.yml on FawkesOficial/cmus-web
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cmus_web-0.1.2.tar.gz -
Subject digest:
4ba8abd8a273a2ebdc69c4559348cd8360284c10a400d721c11be83477643728 - Sigstore transparency entry: 1512475367
- Sigstore integration time:
-
Permalink:
FawkesOficial/cmus-web@f74f3d23e68b91092b07d70273910bd004e2ff23 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/FawkesOficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@f74f3d23e68b91092b07d70273910bd004e2ff23 -
Trigger Event:
release
-
Statement type:
File details
Details for the file cmus_web-0.1.2-py3-none-any.whl.
File metadata
- Download URL: cmus_web-0.1.2-py3-none-any.whl
- Upload date:
- Size: 103.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e746e0cadb002ba249a80293af84a1c8da1ddde870306ce2517605e5777330f0
|
|
| MD5 |
a853b45e596cb893538f8d875b115f95
|
|
| BLAKE2b-256 |
5f173fee3769ca6dbc21c89c6e195b9d2214584b10129e16352862f3ae1eedb4
|
Provenance
The following attestation bundles were made for cmus_web-0.1.2-py3-none-any.whl:
Publisher:
python-publish.yml on FawkesOficial/cmus-web
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cmus_web-0.1.2-py3-none-any.whl -
Subject digest:
e746e0cadb002ba249a80293af84a1c8da1ddde870306ce2517605e5777330f0 - Sigstore transparency entry: 1512475470
- Sigstore integration time:
-
Permalink:
FawkesOficial/cmus-web@f74f3d23e68b91092b07d70273910bd004e2ff23 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/FawkesOficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@f74f3d23e68b91092b07d70273910bd004e2ff23 -
Trigger Event:
release
-
Statement type: