← All work

Discord Music Bot

Developer2026shipped

A self-hosted Discord music bot whose audio path is built out of two external programs rather than a JavaScript extractor.

The audio path refuses to depend on anything that rots: yt-dlp resolves metadata only and ffmpeg reads the media URL itself, so when YouTube rotates its player, updating one pinned binary is the whole fix.

Facts

  • Fourteen slash commands, and no Lavalink, no Java, no Python and no native Opus or sodium package — Node 22's aes-256-gcm covers voice encryption and ffmpeg does the Opus encoding
  • The install script downloads the pinned binary and the SHA2-256SUMS file from the same release, verifies the checksum, and refuses to install a binary that does not match
  • Preflight checks ffmpeg, the opus muxer, yt-dlp and the environment before the bot logs in
  • Opus in-band FEC is on by default, and output is pinned to 48 kHz stereo with 20 ms frames
  • At most one ffmpeg child per guild, killed on stop, skip, seek, volume change, player destruction and process exit
  • An expired media URL is re-resolved exactly once and retried before an error is shown
  • 128 offline checks run with no network, no .env file and no yt-dlp binary; 13 live checks are opt-in

Stack

  • Node.js
  • discord.js
  • @discordjs/voice
  • yt-dlp
  • ffmpeg

There is no repository link here because there is no repository. The project was set up deliberately without git, and scripts/snapshot.sh is the rollback mechanism: a labelled tarball that excludes node_modules, the yt-dlp binary, the environment file and previous snapshots.

Two decisions carry the design. yt-dlp returns metadata and nothing else, so ffmpeg reads the media URL directly and -ss becomes a cheap HTTP range request — seeking does not re-download from zero. And volume and seek are the same operation, a restart of ffmpeg at the current position with new arguments, which is why the PCM path and a native Opus encoder are not in the dependency tree at all.

← All work