WhatsApp group music player. Watches a WhatsApp group for shared songs, builds a weighted playlist, and plays it in the browser.
WhatsApp group
│
├── watcher (Node.js, runs on Mac or VPS)
│ detects music URLs (YouTube, Spotify, Apple Music)
│ POSTs to /api/radio/import-urls
│
└── admin UI
paste a WhatsApp chat export
same /api/radio/import-urls endpoint
│
▼
Cloudflare D1 (SQLite at edge)
song.link enrichment → YouTube + Spotify + Apple Music IDs
│
▼
Cloudflare Pages (static player)
weighted random queue, reactions, play history
Single database: Cloudflare D1. The watcher and the admin import UI both talk to the same API. No local SQLite.
cd app
npm install -g wrangler
wrangler login
wrangler pages deploy public # first deployOn first deploy, create the D1 database and KV namespace:
wrangler d1 create guts-radio
wrangler kv:namespace create RADIO_SECRETSCopy the IDs into app/wrangler.toml, then redeploy.
Environment variables (set in Cloudflare dashboard → Pages → Settings → Variables):
| Variable | Description |
|---|---|
YOUTUBE_PLAYLIST_ID |
YouTube playlist to sync to |
SPOTIFY_PLAYLIST_ID |
Spotify playlist to sync to |
SPOTIFY_CLIENT_ID |
Spotify app client ID |
APPLE_TEAM_ID |
Apple Developer team ID |
APPLE_MUSICKIT_KEY_ID |
MusicKit key ID |
Secrets (KV or dashboard secrets — not plain env):
SPOTIFY_CLIENT_SECRETAPPLE_MUSICKIT_PRIVATE_KEY
cd watcher
npm install
cp .env.example .env
# Edit .env: set RADIO_API_URL to your Cloudflare Pages URLFirst-time WhatsApp login:
node login.js # scan QR code onceSession is saved to .wwebjs_auth/ (gitignored). You only do this once.
Select which groups to watch:
node select-groups.js # interactive pickerGroups tagged music in groups.json will have music URLs forwarded to the radio.
Run the watcher:
node watcher.jsOr use the included launchd plist (launchd/com.whatsapp-playlist.watcher.plist) to run it as a macOS service.
For Linux/VPS, use the systemd unit or a process manager like PM2:
pm2 start watcher.js --name whatsapp-playlistEach group entry:
{
"id": "120363427268795660@g.us",
"name": "Music Fellowship",
"watch": true,
"tag": "music"
}watch: true— store all messages as JSONLtag: "music"— also forward music URLs to the radio API
RADIO_API_URL=https://your-deployment.pages.dev
If you don't want to run the watcher, you can import a WhatsApp chat export manually:
- In WhatsApp: open the group → ⋮ → More → Export chat → Without media
- Open
https://your-deployment.pages.dev/admin - Paste the export text → Import
Same enrichment pipeline runs either way.
/admin — import exports, trigger Spotify/Apple Music syncs, manage tracks.
app/ Cloudflare Pages app (static player + Workers API + D1 schema)
watcher/ Node.js WhatsApp watcher (Mac/VPS)