Private, restricted remote control for YouTube Music in your browser.
Let AI agents safely select, queue, and catalog the music you play.
Queuebird lets a local application or automation agent control YouTube Music without exposing Chrome DevTools Protocol, accepting arbitrary JavaScript, or navigating the browser between track URLs.
It consists of:
- Queuebird Server — a small authenticated Docker service that stores commands, playback state, sessions, and optional playback events in SQLite.
- Queuebird Extension — a Chrome Manifest V3 extension restricted to
music.youtube.com. It polls the server and performs a fixed allowlist of native player and queue operations.
CLI / agent / application
│ authenticated HTTP
▼
Queuebird Server
│ bounded commands
▼
Queuebird Extension
│ native YouTube Music UI
▼
YouTube Music
Once tracks have been placed in YouTube Music's native queue, playback no longer depends on the controlling application remaining online.
General browser automation is powerful, but it is an unnecessarily broad security boundary for changing a song. Queuebird exposes only these operations:
- play a specific track
- append a track or ordered micro-playlist
- pause or resume
- skip
- clear or replace the managed queue
- read bounded playback and queue state
There is no endpoint for executing code, choosing selectors, reading cookies,
or controlling other sites. Managed track changes use YouTube Music's SPA and
native queue rather than location.assign, avoiding page departures and
“Leave site?” prompts.
Queuebird is an early public release extracted from a working private system. The controller has extensive unit coverage, but YouTube Music's private DOM is not a stable API and may change without notice. Use it as experimental self-hosted software, not as a safety-critical playback service.
Queuebird is not affiliated with or endorsed by Google or YouTube.
Requirements: Docker with Compose.
git clone https://github.com/Empathos/queuebird.git
cd queuebird
cp .env.example .env
# Set QUEUEBIRD_API_KEY to the same long random secret used in the extension.
docker compose up --build -d
curl http://127.0.0.1:8080/healthzThe server listens only on 127.0.0.1:8080 by default and stores durable data
in the queuebird-data Docker volume.
Requirements: Node.js 20+ and Chromium/Chrome with Manifest V3 support.
cd extension
npm ci
npm run typecheck
npm run test:unit
npm run buildOpen chrome://extensions, enable Developer mode, choose Load unpacked,
and select extension/dist.
Open Queuebird's options page and configure:
- Collector URL:
http://127.0.0.1:8080 - Device ID:
queuebird-browser - API key: the value from
.env
Then open YouTube Music in the same browser profile, sign in, and leave the tab open. The tab need not remain focused, but the browser may require one manual play action before it permits unattended audio playback.
export QUEUEBIRD_API_KEY='your-secret'
curl -X POST http://127.0.0.1:8080/v1/music-control/commands \
-H "X-API-Key: $QUEUEBIRD_API_KEY" \
-H 'X-Device-ID: queuebird-browser' \
-H 'Content-Type: application/json' \
-d '{
"action": "replace_queue",
"items": [
{
"url": "https://music.youtube.com/watch?v=dQw4w9WgXcQ",
"title": "Never Gonna Give You Up",
"artist": "Rick Astley"
}
],
"session": {
"id": "demo-1",
"name": "First Queuebird session",
"mood": "upbeat",
"criteria": "demo"
}
}'The extension polls for the command, resolves tracks through native YouTube
Music controls, and acknowledges only after the page verifies the result.
The POST returns {"accepted":true,"command_id":1}; acceptance means the
server stored the command, not yet that the browser executed it.
Verify browser-reported state:
curl http://127.0.0.1:8080/v1/music-control/state \
-H "X-API-Key: $QUEUEBIRD_API_KEY" \
-H 'X-Device-ID: queuebird-browser'All /v1/* routes require X-API-Key. Command, state, and event routes use
X-Device-ID for routing; it is not an additional authentication factor.
| Method | Path | Purpose |
|---|---|---|
GET |
/healthz |
Liveness check |
POST |
/v1/music-control/commands |
Submit an allowlisted command |
GET |
/v1/music-control/commands?after=N |
Poll durable commands |
POST |
/v1/music-control/state |
Report bounded player state |
GET |
/v1/music-control/state |
Read latest player state |
GET |
/v1/music-sensor/time |
Clock synchronization |
POST |
/v1/music-events/batch |
Optional playback-event archive |
Interactive OpenAPI documentation is available at
http://127.0.0.1:8080/docs while the server is running.
{"action":"play","video_id":"…","title":"…","artist":"…"}{"action":"enqueue","video_id":"…","title":"…","artist":"…"}{"action":"pause"},{"action":"resume"}, or{"action":"skip"}{"action":"clear_queue"}replace_queueuses the full example above and accepts 1–20 items.
A device ID is created implicitly and must contain only letters, numbers, periods, underscores, or hyphens (maximum 64 characters). Each browser profile should use a distinct ID. Commands are durable in SQLite and monotonically numbered; the extension persists its last cursor and requests only newer commands. Queue operations are acknowledged after observed page state, retried within bounded limits, and aborted rather than silently appended out of order.
replace_queue manages Queuebird's requested session; it does not promise to
erase unrelated entries already present in YouTube Music's native queue.
Native-queue cleanup and deduplication remain roadmap items.
Queuebird's central design constraint is least authority:
- fixed command allowlist; no arbitrary browser operations
- extension host permission limited to YouTube Music and loopback collectors
- API credential retained in trusted extension storage
- content script cannot read the credential or choose a network destination
- no CDP, remote-debugging port,
eval, external scripts, or general page API - server bound to loopback in the supplied Compose configuration
See SECURITY.md before changing the network boundary.
The supplied extension intentionally accepts loopback collector URLs only. Running Chrome and Docker on different hosts therefore requires an audited extension policy change in addition to TLS and network authentication.
- Confirm
curl http://127.0.0.1:8080/healthzreturns{"ok":true}. - Open the extension popup and confirm the sensor is attached and delivery is healthy.
- Confirm the API key, collector URL, and device ID match the server command.
- Keep one signed-in YouTube Music tab open and manually start playback once.
- Inspect
docker compose logs queuebirdand the state endpoint above.
If Docker runs in a VM, container host, or another machine, its loopback is not the browser host's loopback; that topology is outside the secure default.
extension/ Manifest V3 extension, tests, and build scripts
server/ FastAPI + SQLite command broker
docker-compose.yml
SECURITY.md
cd extension
npm ci
npm run typecheck
npm run test:unit
npm run build
cd ../server
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
QUEUEBIRD_API_KEY=development-only QUEUEBIRD_DB=/tmp/queuebird.db \
uvicorn app.main:app --reloadThe source-level navigation guard prevents managed URL navigation from being
reintroduced. Browser integration tests require a Chromium build that supports
--load-extension; set CHROME_PATH if it is not discovered automatically.
- clean native-queue deduplication and removal
- WebSocket command delivery alongside polling
- signed extension releases and Chrome Web Store packaging
- CLI and OpenClaw/Home Assistant adapters
- additional music services through separate site-restricted controllers
Issues and focused pull requests are welcome. Please include tests for changes to the controller, command validation, or security boundary. Do not add generic browser-evaluation or selector-control endpoints.
MIT © 2026 Empathos