discord music bot · self-hosted · go · open source

Playback should survive.

Melodix streams YouTube, SoundCloud and internet radio into your Discord server. A flaky stream, a dead voice connection, a gateway reconnect — these are expected, and it recovers from them instead of dropping the queue. Run it yourself; your server stays independent.

$ go build -o melodix-discord ./cmd/discord
Melodix BOT Today at 12:32
▶️ Now Playing
youtube ytnative-link 3:32 Rick Astley
melodix · logs

why it survives

Every track carries its own fallback chain.

Sources break. That is assumed from the start, not handled as a special case.

When someone pastes a link, Melodix has to turn that web page into an actual audio stream it can pipe into a voice channel. The component that does this is called a parser, and YouTube fights it constantly — reshuffling its internals every few weeks. So Melodix doesn't bet everything on one parser. It keeps a chain of them, ordered fastest-first, and works down the list until one returns a playable stream. The panel above shows that chain running live: the embed your users would see, and the log underneath it. Click a parser below to see what it does.

ytnative built in-house · zero deps

Melodix's own parser, written specifically for this project. It speaks YouTube's private InnerTube API — the same protocol the official phone and TV apps use — pretending to be the YouTube app on an Apple Vision Pro, which hands back direct audio links with nothing to decrypt. No external programs and no embedded JavaScript engine, just plain HTTP calls from Go. With nothing to decrypt it is the cheapest route, so it is the default. When YouTube changes something, which it will, ytnative tends to fail instantly instead of hanging, and past breakages have been fixed by bumping one client version string.

SoundCloud has its own parser, scnative, built on SoundCloud's api-v2. Internet-radio URLs skip straight to ffmpeg, since there is nothing to extract. The pattern repeats: try the fast native path, keep sturdier fallbacks in reserve.

survives discord too

A silent gateway or a dead voice connection is detected and recovered automatically. Queues survive restarts.

keeps a memory

/history shows what was played; /play 42 replays entry 42. No link hunting.

stays small

One binary. State is an embedded store in a directory you pick — no database server to run. Also runs as a terminal player, straight to your speakers.

when things break

A dropped connection should not end the song.

A fallback chain decides which parser plays a track. It does nothing for the far more common problem: the track was already playing fine, and then the network moved. Melodix treats that as a separate job, handled at three levels.

read ahead

Playback runs from a buffer, not straight off the socket, so a stall underneath is absorbed instead of heard. The buffer does not pre-fill: playback still starts immediately, and it fills as the track plays.

repair in place

When a connection is cut mid-track, Melodix asks the server for the next byte it had not yet received and carries on. The player above it never notices, and nothing already downloaded is fetched twice.

reopen, then switch

If the source is genuinely gone, not briefly interrupted, the same parser reopens at the position it had reached. Only when that fails too does the chain move on.

Discord itself is covered separately: a voice reconnect or a gateway drop rejoins and resumes without losing the queue, and the buffer keeps playing while it happens.

self-hosted

The bot you invited can be taken away.

Public music bots are a single point of failure you do not control. The two largest, Groovy and Rythm, were shut down in 2021 after a request from YouTube, taking every server's setup with them. Others have drifted in the same direction since: listener caps, queue limits, a premium tier for what used to be free, and an outage that takes out every server at once, because there is only one instance.

Self-hosting removes the middleman instead of betting on a kinder one. Melodix runs under your Discord application, on a machine you already have, with no account to sign up for and nothing phoning home. Nobody can revoke it, meter it, or decide that skipping tracks is now a paid feature. If a source breaks, you can read the log, see which parser failed and why, and update on your own schedule — not wait for someone else's.

The trade is real and worth stating plainly: you are the one keeping it online. That means a machine that stays on, a token you keep secret, and an occasional update when YouTube changes something. In exchange the bot is yours, and it does not disappear.

quick start

Two commands, once you have a token.

Discord bot

# token from the Discord Developer Portal
$ go build -o melodix-discord ./cmd/discord
$ DISCORD_TOKEN=your-token ./melodix-discord

Terminal player

# no Discord account required
$ go build -o melodix-cli ./cmd/cli
$ ./melodix-cli

FFmpeg is only needed for SoundCloud and internet radio; the common YouTube path does not touch it. yt-dlp is the last-resort fallback, and the one thing that plays YouTube live broadcasts. Full setup — bot creation, invite link, every config knob, Docker — lives in docs/running.md.

requirements

One binary, one token, one machine.

There is no database server to provision and no Redis. State lives in an embedded file-backed store inside a directory you pick, so a backup is a copy of that folder. Everything in the cards below is optional except the first.

required

A Discord bot token, and a machine that stays online. Prebuilt binaries cover Linux, Windows and macOS on both amd64 and arm64 — a small VPS or a Raspberry Pi is plenty, since YouTube playback does no transcoding.

ffmpeg

Needed for SoundCloud and internet radio, which have to be decoded. Melodix prefers YouTube's Opus formats, which Discord takes unchanged, so the common YouTube path never reaches it.

yt-dlp + a js runtime

The last parser in the chain, and the one that plays live broadcasts. Keep Node, Deno or Bun on PATH beside it — without one, yt-dlp cannot answer YouTube's challenges and live streams fail.

Two settings are worth knowing about on a constrained connection: BUFFER_AHEAD_MS sets how long an interruption can last before anyone hears it, and MAX_AUDIO_BITRATE caps the audio format — roughly halving bandwidth with little audible difference, because Discord re-encodes to 64 kbps anyway unless the server is boosted. Both are documented in docs/running.md.

commands

Few commands. Easy to remember.

/playlink, search query, radio URL, or history id
/searchpick from five results instead of the top hit
/queuewhat is playing and what is waiting
/nextskip to the next track
/stopstop playback and clear the queue
/historyrecently played tracks, replayable by id
/helpcommands by category, group, or flat
/settingsenable or disable command groups per server
/maintenanceping, stats, state export
/aboutwhere this bot came from

Try it live in the Ctrl+Z server — join a voice channel and play something in #music-spam. Or browse the full command reference on GitHub.

downloads

Prebuilt binaries.

Each bundle ships both binaries, a ready-to-edit .env, and the docs. Or run it in Docker.

faq

Questions worth asking first.

Is Melodix free?

Yes. Melodix is open source under the MIT license, with no paid tier, no premium commands and no usage limits. You run it on your own machine with your own Discord token, so the only cost is whatever you already pay for that machine.

Can Melodix play YouTube playlists and mixes?

Yes. Pasting a playlist or mix link expands the whole list into the queue instead of playing only the first video. If the link names a video inside a playlist, that video plays first and the rest of the list follows it.

Does Melodix support Spotify?

No. Melodix plays YouTube, SoundCloud and internet radio. Spotify does not offer a way for a third-party bot to stream its catalogue, so bots that appear to support it are really searching for the same track on YouTube. If that is what you want, paste the track name into /play and you get the same result without the indirection.

Can one instance serve several Discord servers?

Yes. Each server gets its own queue and its own player, so playback in one is unaffected by playback in another. One process handles them all; the limit is the bandwidth and CPU of the machine you run it on.

Where does Melodix store data, and how do I back it up?

In one directory, set by STORAGE_PATH and defaulting to ./data/store. It holds guild settings, the command log and playback history in an embedded write-ahead-logged datastore. Stop the bot and copy the folder; that is the whole backup. The track cache is separate, set by CACHE_DIR, and is safe to delete at any time.

Something not covered here? Ask in the Ctrl+Z server or open an issue on GitHub.