melodix

# Header

Go Reference Go Report Card Release License

Melodix — Self-hosted Discord music bot & CLI player

Presented by Señor Mega. Powered by Go, FFmpeg, and several questionable engineering decisions.

Self-hosted music player written in Go that can run either as:

Melodix supports YouTube, SoundCloud and internet radio, using multiple parsers with automatic fallback for resilience.


Table of contents


Features

Limitations


Try Melodix without installing

Use the official server

Try the bot in The Megabyte Order Discord server: enter a voice channel and use slash commands in #bot-music-spam.


Download a release

Download pre-built binaries:

https://github.com/keshon/melodix/releases

Each archive contains:


Commands

🕯️ Information

🎵 Music

⚙️ Settings

Example usage:

/music play Never Gonna Give You Up
/music play https://www.youtube.com/watch?v=dQw4w9WgXcQ
/music play http://stream-uk1.radioparadise.com/aac-320

You must be in a voice channel to use /music play.


How-to’s


Running Melodix yourself

You can run the Discord bot (voice channels) or the CLI player (local playback). Both use the same sources and queue logic.

What you need (both modes)

For the Discord bot you also need a Discord bot token from the Discord Developer Portal.

Discord bot — Step 1: Create the bot in Discord

  1. Open the Discord Developer Portal and create a new application. Note the Application ID.

  2. Go to the Bot section and create a bot. Copy the token (you will use it as DISCORD_TOKEN).

  3. Under Privileged Gateway Intents, enable:

    • Presence Intent
    • Server Members Intent
    • Message Content Intent
  4. Invite the bot to your server using this URL (replace YOUR_APPLICATION_ID with your Application ID from step 1):

    https://discord.com/oauth2/authorize?client_id=YOUR_APPLICATION_ID&scope=bot&permissions=3238912

  5. Open the URL in your browser, choose your server, and authorize. Grant the requested permissions when asked.

Discord bot — Step 2: Configure and run

Create a .env file in the folder where you run the bot (or set the same variables in your environment):

# Required for the Discord bot
DISCORD_TOKEN=your-discord-bot-token

Optional variables (you can add these to .env if needed):

Variable Description Default
STORAGE_PATH Path for bot data (e.g. command state). ./data/datastore.json
INIT_SLASH_COMMANDS Set to true to register slash commands on every startup. false
DEVELOPER_ID Your Discord user ID for developer-only commands. (none)
DISCORD_GUILD_BLACKLIST Comma-separated guild IDs the bot will leave. (none)

Run the Discord bot:

After the bot is running and invited to your server, use slash commands in any channel. For music, be in a voice channel and use /music play with a link or search term.

CLI player

The CLI player uses the same playback engine but runs in your terminal and plays through your speakers. No Discord token or server setup required.

Run the CLI player:

CLI commands (at the > prompt):

Command Description
play <url or query> [source] [parser] Add and play a track.
next Skip to the next track.
stop Stop playback and clear the queue.
queue Show now playing and the queue.
status Show current track and queue length.
quit Exit.

Example:

> play Never Gonna Give You Up
> queue
> next
> quit

How it works

High level flow:

flowchart TB
  User["User"]
  DiscordBot["Discord bot"]
  CLI["CLI"]
  MusicPlayer["Player (pkg/music)"]
  Resolver["Resolver"]
  Sink["Sink (Discord VC or speaker)"]
  User -->|"slash / play"| DiscordBot
  User -->|"play command"| CLI
  DiscordBot --> MusicPlayer
  CLI --> MusicPlayer
  MusicPlayer --> Resolver
  MusicPlayer --> Sink

Music package overview

The pkg/music package is the core of the bot. It implements the entire playback pipeline: resolving tracks, managing the queue, opening audio streams, and delivering PCM audio to different sinks (Discord voice or local playback).

The playback pipeline works as follows:

  1. Resolve — User input (URL or search) goes to the resolver. Sources (YouTube, SoundCloud, radio) match the input and return track metadata (URL, title, list of available parsers). No streaming yet.

  2. Enqueue — The player enqueues one or more tracks (metadata only). If nothing is playing, the caller typically calls PlayNext.

  3. PlayNext — The player pops the next track and calls startTrack: opens a RecoveryStream (which wraps TrackStream) and starts a playback goroutine that will obtain a sink and stream PCM.

  4. Get sink — The playback goroutine asks the sink provider for an AudioSink.
    • Discord: joins a voice channel and returns a sink that encodes PCM to Opus and sends it to the voice connection.
    • CLI / local: returns the speaker sink.
      If obtaining the sink fails, playback stops and the queue does not advance.
  5. Open streamRecoveryStream opens a TrackStream by trying the track’s parsers in order (ytdlp-link, kkdai-link, ffmpeg-link). Each parser produces PCM (48 kHz, stereo, 16-bit). If the stream dies early, RecoveryStream can retry with the same or next parser (up to a limit).

  6. Stream to sink — The player feeds the PCM ReadCloser into AudioSink.Stream. The sink runs until the stream ends or a stop signal is received.

  7. Next or stop — When the stream ends, the player can auto-advance to the next track (PlayNext) or stop. Calling Stop(true) clears the queue and releases the sink.

Subpackages: player, resolver, sink, sources, parsers, stream. See also pkg/music/README.md for using the library standalone.


Why Melodix?

I needed a self-hosted music bot that could reliably run for long DnD sessions without interruptions. I also wanted to achieve:


Support

For help or questions, use the The Megabyte Order — official Melodix Discord server.


FAQ


Contributing

Contributions are welcome. Open an issue or pull request on GitHub. For larger changes, discuss in the Melodix Discord server first.


License

Melodix is licensed under the MIT License.