Shen — a Discord music bot that doubles as a YouTube website

2026-09visit
discord.jsNode.jsExpressTypeScript

Share

Shen — a Discord music bot that doubles as a YouTube website

Shen is a personal Discord music bot with a full website bolted on. You search YouTube or YouTube Music from the site, hit play, and the track streams straight in your browser — or you add it to the queue and the bot plays it in your voice channel.

It is one Node project that does two jobs: an Express API that serves the site and streams audio, and a discord.js client that joins voice channels and plays tracks. The same queue powers both surfaces, so the website and the bot never get out of sync.

Two surfaces, one queue

The interesting part is the shared state. Search results from the web can be sent to the Discord queue, tracks played in Discord appear in the site's live "now playing" state, and a pending queue holds tracks until someone runs /join. Updates flow to the website over SSE (/api/events), so the player bar ticks live without polling.

architecture

Streaming that keeps up with YouTube

Audio extraction relies on yt-dlp (shipped as the bundled binary from @distube/yt-dlp) with play-dlas a fallback — YouTube's player/signing changes often, so two extraction paths give a decent buffer. ffmpeg-static supplies ffmpeg so any audio format can be transcoded: Opus for the Discord stream and MP3 for the browser player.

streaming flow
YouTube URL
    ↓ yt-dlp (primary) / play-dl (fallback)
direct audio URL
    ↓ ffmpeg
Discord: Opus 48kHz stream   Web: MP3 in <audio>

Full feature list

Why it was hard

The genuinely nasty part was YouTubedatacenter-IP blocking. On a cloud host like Render, YouTube frequently refuses or alters responses. The fix that works is routing extraction through a Cloudflare WARP proxy — running wireproxy so the bot tunnel exits from a residential-looking IP. Combined with the tv player client (tried before android/web) this dodges the sign-in bot wall most of the time.

There were also the classic single-process pitfalls: overnight crashes taking the whole service down (now caught by uncaughtException/unhandledRejection handlers), a double-enqueue bug where the web pending queue andthe bot both enqueued a track, and trusting client-supplied guild/channel IDs (fixed by deriving the play target from the caller's own Discord voice state).

Try it

The site is live at shen.zagan.space with the backend on Render. Source: the shen repository.