# 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](https://thisweek.gnome.org/posts/2026/09/twig-267/) 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’”](https://www.techpolicy.press/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: ```bash sudo dnf install mpv ffmpeg ``` Debian/Ubuntu: ```bash sudo apt install mpv ffmpeg ``` Arch: ```bash sudo pacman -S mpv ffmpeg ``` ### 2. Clone and install ```bash git clone https://github.com/your-username/jam-session.git cd jam-session uv sync ``` This project uses [uv](https://docs.astral.sh/uv/) for package management. If you don't have uv: ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` --- ## Usage ### Start the server ```bash jam-session ``` Or with custom bind address and port: ```bash 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= ``` 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