Miles WardandClaude Fable 5.1 557eee9a06
Build and Test / Build and Test (push) Successful in 3m31s
publish script: find Docker CLI plugins under the private DOCKER_CONFIG
With an isolated DOCKER_CONFIG the CLI looks for buildx only in
$DOCKER_CONFIG/cli-plugins, fell back to the legacy builder, and rejected
--provenance. Symlink the user's plugin dir, honour Homebrew's, and fail
early if buildx is still missing.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-17 19:21:22 -04:00
2026-09-11 13:47:20 +09:30
2026-09-11 13:47:20 +09:30
2026-09-11 13:47:20 +09:30
2026-09-11 13:47:20 +09:30
2026-09-11 13:47:20 +09:30
2026-09-11 13:47:20 +09:30
2026-09-11 13:47:20 +09:30

$uicide Music Bot

$uicide Music Bot

A self-hosted Discord music bot with a clean interface — built from source and served from this Gitea, ready for Unraid.

release ci java license


Note

This is a fork of a fork of a fork. The bot is JMusicBot and the credit belongs to the people below. This repository exists so there is a copy of the working 0.7.1 line that cannot disappear, with a container image built here rather than pulled from someone else's registry. Nothing under src/ is changed.

Author Repository Role
jagrosh jagrosh/MusicBot Original author of JMusicBot
arif-banai arif-banai/MusicBot Revived and maintains it — dependency updates, Java 25, config migration
purnell569 purnell569/MusicBot Fixed YouTube playback (0.7.1). Direct parent of this repository.
mward4 this repository Reliable copy, image built and served from gitea.milesward.dev, Unraid template, Gitea CI

Licensed under Apache 2.0 — see LICENSE and NOTICE.

Contents

What this copy adds

  • The image is built here. gitea.milesward.dev/mward4/musicbot comes out of this repository's Dockerfile, linux/amd64, with OCI labels recording the commit and version it was built from. No dependency on ghcr.io or on anyone else's build.
  • An Unraid template, deploy/unraid/musicbot.xml: repository, appdata mapping, JVM flags, and a --user 99:100 default so a freshly created appdata folder works without a chown step.
  • CI on Gitea Actions. .gitea/workflows/build-and-test.yml runs the full Maven build — 676 unit and 48 integration tests — and a Docker build on every push to main. Upstream's GitHub workflows are kept in .github/ but do not run here.
  • Upstream tags preserved under upstream/<tag> (upstream/v0.7.1). This repository's own release tags are vX.Y.Z and match the version in pom.xml.
  • A first run that actually works headless. Upstream's image prompts for a token on a console that does not exist and exits without writing anything. This image's entrypoint runs the bot's own generate-config when config.txt is missing, tells you what to edit, and stops. It also checks the data directory is writable up front — the bot's own message for that case is a misleading "Another instance is already running".
  • A publishing runbook, docs/PUBLISHING.md, and a one-shot build-and-publish script for cutting the next release.

Quick start (Docker)

The container runs headless and writes a default config.txt on first run.

mkdir -p /path/to/musicbot

docker run --rm -it \
  --name musicbot \
  --user "$(id -u):$(id -g)" \
  -v "/path/to/musicbot:/musicbot" \
  gitea.milesward.dev/mward4/musicbot:latest
  1. The first run finds no config.txt, writes the default one into the mounted directory, and exits.
  2. Edit /path/to/musicbot/config.txt: in the discord { … } block set token to your Discord bot token and owner to your Discord user ID.
  3. Run the container again.

If the log says the directory is not writable, fix the ownership of the mounted directory (or change --user) and run again.

Already have a directory with config.txt, Playlists/ and serversettings.json from a JAR install? Mount that directory instead; nothing else changes.

Docker Compose:

services:
  musicbot:
    image: gitea.milesward.dev/mward4/musicbot:latest   # or :0.7.1 to pin a release
    container_name: musicbot
    user: "1000:1000"          # whoever owns the mounted directory
    volumes:
      - /path/to/musicbot:/musicbot
    restart: unless-stopped

See docker-compose.example.yml for the commented version.

Unraid

Import deploy/unraid/musicbot.xml — Docker tab → Add Container → Template, or drop it into /boot/config/plugins/dockerMan/templates-user/ — and apply. The template maps /musicbot to /mnt/user/appdata/musicbot, publishes no ports (the bot only makes outbound connections), and runs as 99:100 so the appdata folder Unraid creates is writable straight away. Two things to know:

  • First start writes the config and stops. Open /mnt/user/appdata/musicbot/config.txt, set token and owner in the discord { … } block, start the container again. If the log instead says /musicbot is not writable by uid 99, the folder was created with different ownership: chown -R 99:100 /mnt/user/appdata/musicbot from the Unraid terminal and start again.
  • The Discord side needs one switch. In the Developer Portal, under your application → Bot → Privileged Gateway Intents, turn on Message Content Intent. Without it the bot never sees a command.

To run as the image's own non-root user (UID 10001) instead, clear Extra Parameters and chown -R 10001:10001 /mnt/user/appdata/musicbot once.

Versions and upgrades

Tag Meaning
gitea.milesward.dev/mward4/musicbot:latest The newest release.
gitea.milesward.dev/mward4/musicbot:0.7.1 That release, pinned. A version tag is never rebuilt with different code.

Each release is listed on this repository's Releases page with the pull command, what changed, and the JAR for non-Docker installs. To upgrade an Unraid container, change the tag and apply; config.txt, serversettings.json and Playlists/ live in the appdata mount and carry over. The config format is versioned and migrates itself on start.

What's different in 0.7.1

These are purnell569's notes for the release this copy is built from.

YouTube changed how it serves streams, which broke playback for everyone on the upstream 0.7.0 release. This version fixes that:

  • Signature cipher is no longer tied to OAuth. Deciphering and authentication are unrelated concerns, but the cipher was only applied when OAuth was on — so the default install fell back to the bundled extractor and died with Must find sig function from script: /s/player/…. It is now always applied and configurable via playback.youtube.remoteCipher.url.
  • Added the ANDROID client and reordered the client list by reliability. ANDROID is currently the only client YouTube reliably streams to anonymously; WEB now sits last because YouTube answers it with SABR-only responses that contain no playable format.
  • Sign-in failures are now reported usefully. The existing hint could never fire: it matched on the aggregate exception message rather than the per-client reasons, and compared against an ASCII apostrophe while YouTube sends ’ (U+2019).
  • youtube-source pinned past 1.18.2, which predates the fixes for the TV client User-Agent and for formats that omit contentLength.

Measured on the tracks that were failing: 5/5 playing, stable across repeat runs (previously 1/5). No OAuth or Google account required.

Requirements

Inside the container all of this is already handled. It matters when running the JAR directly.

  • Java 25 or newer.
  • glibc 2.38 or newer for the JDave/udpqueue native libraries. Ubuntu 24.04 "Noble" and Debian 13 "Trixie" qualify; check with ldd --version. On Debian/Ubuntu you may also need libopus0 and libsodium23.
  • Message Content Intent enabled in the Discord Developer Portal (see Unraid).

Setup

Features

  • Easy to run (just make sure Java is installed, and run!)
  • Fast loading of songs
  • No external keys needed (besides a Discord Bot token)
  • Smooth playback
  • Server-specific setup for the "DJ" role that can moderate the music
  • Clean and beautiful menus
  • Supports many sites, including YouTube, SoundCloud, and more
  • Supports many online radio/streams
  • Supports local files
  • Playlist support (both web/youtube, and local)

Supported sources and formats

Everything lavaplayer supports:

  • Sources: YouTube, SoundCloud, Bandcamp, Vimeo, Twitch streams, local files, HTTP URLs
  • Formats: MP3, FLAC, WAV, Matroska/WebM (AAC, Opus or Vorbis), MP4/M4A (AAC), OGG streams (Opus, Vorbis and FLAC), AAC streams, stream playlists (M3U and PLS)

Loading Example...

Running directly (without Docker)

The JAR for each version is attached to its release as JMusicBot-<version>.zip (Gitea does not allow .jar attachments; unzip it) with a SHA256SUMS.txt beside it. Then, with Java 25 on the path:

Linux / macOS / Windows (CMD):

java -Dfile.encoding=UTF-8 -Dnogui=true --enable-native-access=ALL-UNNAMED -jar JMusicBot-0.7.1.jar

Windows (PowerShell): PowerShell treats -D as its own parameter, so quote each option:

java "-Dfile.encoding=UTF-8" "-Dnogui=true" "--enable-native-access=ALL-UNNAMED" "-jar" ".\JMusicBot-0.7.1.jar"

-Dfile.encoding=UTF-8 keeps non-English characters (Cyrillic, Japanese, …) intact in Discord. See the Setup Page and Config reference for config.txt.

Docker notes

  • Config persistence. /musicbot is the container's working directory and must be mounted; config.txt, serversettings.json and Playlists/ are read and written there.
  • User. The image's own user is jmusicbot (UID 10001). Either make the mounted directory writable by 10001 or pass --user (the Unraid template does the latter with 99:100).
  • JAVA_OPTS. The entrypoint starts the JVM with -XX:+UseZGC -XX:+AlwaysPreTouch. Set JAVA_OPTS to replace that, e.g. -Xms256m -Xmx512m -XX:+UseZGC -XX:+AlwaysPreTouch to cap the heap.
  • Image contents. A jlink-trimmed Java 25 runtime on bitnami/minideb:trixie (glibc 2.41) and the shaded JAR built with the docker Maven profile, which drops the Windows and macOS natives. Labels: org.opencontainers.image.revision (commit) and .version.

Published images: https://gitea.milesward.dev/mward4/-/packages/container/musicbot

Performance tuning

For optimal audio quality with minimal stuttering, the following JVM and configuration options are recommended.

JVM flags

The bot works best with ZGC (Z Garbage Collector), which provides sub-millisecond pause times:

java -Dfile.encoding=UTF-8 \
     -XX:+UseZGC \
     -XX:+AlwaysPreTouch \
     -Dnogui=true \
     --enable-native-access=ALL-UNNAMED \
     -jar JMusicBot-0.7.1.jar
  • -XX:+UseZGC: sub-millisecond GC pauses (generational mode is default in JDK 24+)
  • -XX:+AlwaysPreTouch: pre-allocates memory at startup to avoid page faults
  • -Xms / -Xmx: optional; fix or cap the heap (e.g. -Xms256m -Xmx512m)

The Docker image uses ZGC and AlwaysPreTouch by default; JAVA_OPTS overrides.

Audio buffers

Configurable buffers in config.txt protect against GC pauses. Higher values add latency; the defaults suit most setups.

performance {
  # NAS buffer duration in ms (protects against JVM pauses up to this duration)
  nasBufferMs = 800

  # Lavaplayer frame buffer duration in ms (amount of decoded audio to buffer)
  frameBufferMs = 2000
}

Proxy configuration

Specific components can be routed through an HTTP proxy while others connect directly — useful for proxying audio traffic without touching Discord API traffic. Add to config.txt:

proxy {
  host = "127.0.0.1"
  port = 8080

  lavaplayer = true   # Audio source requests (YouTube, SoundCloud, etc.)
  jda = false         # Discord API traffic
  github = false      # Version check requests
}

Leave host empty or port at 0 to disable. SOCKS proxies are not supported.

Development and publishing

There is no Java toolchain requirement on the workstation; the build runs in a container:

docker run --rm -v "$PWD":/build -w /build -v musicbot-m2:/root/.m2 maven:3.9.12-eclipse-temurin-25-alpine mvn -B -ntp verify

That is what CI runs on every push to main. Cutting a release — tag, image build, registry push, Gitea release with the JAR attached — is documented step by step in docs/PUBLISHING.md; scripts/build-and-publish-unraid.sh does the image part in one pass.

Keeping up with upstream:

git fetch upstream --tags
git merge upstream/main

upstream is purnell569/MusicBot on GitHub. Their DEVELOPMENT_WORKFLOW.md and ARCHITECTURE.md are kept as-is for reference; the GitHub Actions and ghcr.io specifics in the former do not apply to this repository.

Questions, bug reports, credits

Problems with the bot itself — playback, commands, config — are almost always upstream's to fix and are best raised at arif-banai/MusicBot or purnell569/MusicBot. Anything specific to this copy (the image, the Unraid template, CI, publishing) belongs in this repository's issues.

The libraries that make the bot possible deserve the stars:

Editing

This bot (and the source code here) might not be easy to edit for inexperienced programmers. The main purpose of having the source public is to show the capabilities of the libraries, to allow others to understand how the bot works, and to allow those knowledgeable about Java, JDA, and Discord bot development to contribute. If you choose to make edits, please do so in accordance with the Apache 2.0 License.

S
Description
$uicide Music Bot — Discord music bot (JMusicBot 0.7.1 fork with working YouTube playback). Reliable copy of purnell569/MusicBot; container image built and served from this Gitea for Unraid.
Readme Apache-2.0
5 MiB
0 Stars 1 Watchers 0 Forks
2026-09-17 19:27:05 -04:00
Languages
Java 99.2%
Shell 0.6%
Dockerfile 0.2%