A local, collaborative music player (main-branch-only mirror of forge.ejuarezg.com/ejuarezg/jam-session)
Python 60%
JavaScript 25%
CSS 11%
HTML 4%

README.md

Jam Session #

A locally-hosted web application for collaborative music queuing at gatherings. People connected to the home network can add YouTube songs to a shared queue, vote on what plays next, and skip songs democratically — all without accounts or logins, using ephemeral nicknames valid for the session. The host machine plays the music through mpv + yt-dlp (ad-free), and playback survives server restarts because mpv runs as a separate process.

The music keeps playing no matter what — server crashes, disconnects, browser restarts, nothing interrupts audio playback on the host machine.

Development disclosure #

This project has been developed with substantial assistance from large language models (LLMs), a form of probabilistic automation. Such tools can produce plausible but incorrect code or explanations; their output does not establish that behavior is correct. The human maintainer is responsible for reviewing changes and validating them with tests.

This wording follows GNOME's “Probabilistically Automated” label and its focus on describing how work was produced without treating a model as a human-like agent. For more on precise, non-anthropomorphic language, see “We Need to Talk About How We Talk About ‘AI’”.


How It Works #

One person runs the server on a Linux machine connected to speakers. Everyone else on the same Wi-Fi opens the web app on their phone. That's it.

  • Add songs — paste any YouTube link, it resolves and queues instantly
  • Vote — upvote songs to move them higher in the queue
  • Skip — vote-to-skip the current track (threshold: 50%+1 of guests, configurable)
  • Host controls — the host can pause, skip, reorder, save/load sessions, and change settings

No apps to install on phones. No accounts. No ads.


System Requirements #

  • Linux host machine (tested on Fedora)
  • Python 3.12+
  • mpv — audio playback engine
  • ffmpeg — audio format conversion (used by yt-dlp internally)
  • Network access to YouTube (for yt-dlp resolution)

Installation #

1. Install system dependencies #

Fedora:

sudo dnf install mpv ffmpeg

Debian/Ubuntu:

sudo apt install mpv ffmpeg

Arch:

sudo pacman -S mpv ffmpeg

2. Clone and install #

git clone https://github.com/your-username/jam-session.git
cd jam-session
uv sync

This project uses uv for package management. If you don't have uv:

curl -LsSf https://astral.sh/uv/install.sh | sh

Usage #

Start the server #

jam-session

Or with custom bind address and port:

jam-session --bind 192.168.1.100 --port 8080

On startup the server:

  1. Prints the LAN URL and a terminal QR code
  2. Registers on the network via mDNS (Bonjour)
  3. Starts mpv for audio playback

Connect from a phone #

Guests open the URL shown in the terminal or scan the QR code. The web app works on any modern mobile browser — no app install needed.

Host access #

The server prints a host URL with a secret token on startup:

Host URL: http://192.168.1.100:8090/?host=<token>

Opening this URL grants full host controls: pause, skip, reorder queue, change settings, and manage sessions.


Configuration #

Settings are stored at ~/.config/jam-session/settings.json and can be changed at runtime by the host through the settings panel in the web UI.

Setting Default Description
skip_threshold_pct 50 Percentage of guests needed to trigger a skip (10–100)
auto_persist false Auto-save queue on every change, survive unexpected exits
skip_cooldown_seconds 3 Cooldown before skip votes can be cast after track change (0–10)

Data locations #

Path Purpose
~/.config/jam-session/settings.json Server settings
~/.local/share/jam-session/sessions/ Saved session files
~/.local/share/jam-session/last-session.json Auto-backup on exit
~/.cache/jam-session/ Audio file cache (1 GB max, LRU eviction)
/tmp/jam-session-mpv.sock mpv IPC socket

Architecture #

Browser (phone) ──WebSocket──▶ FastAPI Server ──IPC socket──▶ mpv Process
                      │                                         │
                      │  ◀─── REST API (/api/queue, /api/qrcode)
                      │                                         │
                  In-memory                                    Audio
                  queue state                                  output
  • mpv runs as a separate OS process — kill the web server, the music doesn't stop
  • WebSocket is the primary real-time transport for queue updates, votes, and time sync
  • State is in-memory with optional JSON file persistence for sessions
  • No database, no Docker, no build step — just run it

Project structure #

jam-session/
├── jam_session/           # Python backend
│   ├── server.py          # FastAPI app, REST/WS endpoints, CLI entry point
│   ├── session.py         # Queue logic, voting, nicknames, skip votes, host auth
│   ├── music.py           # mpv subprocess management, YouTube resolution
│   ├── ws.py              # WebSocket connection manager, broadcast utilities
│   ├── models.py          # Pydantic data models
│   └── cache.py           # Audio file cache with LRU eviction
├── static/                # Frontend (vanilla HTML/CSS/JS)
│   ├── index.html         # Main page
│   ├── app.js             # WebSocket client, DOM manipulation, drag-and-drop
│   └── style.css          # Dark theme, responsive at 480px breakpoint
├── tests/                 # Test suite
│   └── test_session.py    # 40+ tests for session logic
├── pyproject.toml         # Dependencies and entry point
└── uv.lock                # Locked dependency versions

Tech Stack #

Technology Purpose
Python 3.12+ Backend language
FastAPI Web framework with WebSocket support
uvicorn ASGI server
yt-dlp YouTube URL resolution, audio extraction
mpv Audio playback (separate process, JSON IPC)
zeroconf mDNS/Bonjour service advertisement
Vanilla JS Frontend (no framework, no build step)
Jinja2 HTML templating

License #

MIT