A Discord music bot with an interactive NowPlaying panel, intelligent autoplay, and full Spotify integration. Runs entirely on Docker with zero local dependencies.
- Interactive NowPlaying — Persistent embed with live progress bar, album art, and control buttons (play/pause, skip, stop, loop, shuffle, reorder queue). No timeout.
- Smart Autoplay — When the queue runs out, Ghostify finds and enqueues related tracks from the same artist and similar artists via Spotify's co-listener discovery.
- Loop Modes — Choose between off, single-track, or full-queue looping.
- Queue Management — Reorder tracks interactively from Discord, shuffle on demand, or inspect the full queue.
- Spotify Integration — Search tracks, albums, and public playlists by URL or name. No user OAuth required.
- High-Quality Audio — Streams from YouTube via Lavalink with automatic Spotify-to-YouTube resolution.
- Docker-Only — No local Python, Java, or virtual environment needed. Single command to start.
cp .env.example .env
# Edit .env with your Discord bot token and Spotify API credentials
docker compose up --buildLavalink starts with an automatic healthcheck; the bot connects once audio is ready.
| Command | Usage |
|---|---|
/play <query> |
Search by name, YouTube URL, or Spotify URL |
/add <query> |
Add to queue without interrupting current playback |
/playlist <url> |
Play a public YouTube playlist |
/search <query> |
Browse and pick from multiple results |
| Command | Action |
|---|---|
/skip |
Skip to the next track |
/pause / /resume |
Pause or resume playback |
/stop |
Stop playback and disconnect the bot |
/loop |
Cycle between off, track, and queue modes |
/shuffle |
Randomize the current queue |
| Command | Action |
|---|---|
/nowplaying |
Display the current track with interactive controls |
/queue |
List all pending tracks |
/autoplay |
Toggle automatic queue refill |
When a track is playing, Ghostify sends a persistent embed featuring:
- Live progress bar with elapsed and total time
- Album art thumbnail
- Track details — artist, album, release date, genres
Control buttons (never expire):
| Button | Action |
|---|---|
| Pause/Play | Toggle playback |
| Skip | Next track |
| Stop | Clear queue and disconnect |
| Loop | Cycle loop mode |
| Shuffle | Randomize queue |
| Open | Open track on Spotify or YouTube |
| Queue | Show numbered queue with reorder support |
NowPlaying embed with live progress bar, album art, and persistent controls.
Ghostify uses the Spotify API (Client Credentials flow) to search for tracks, albums, and public playlists. Each Spotify URL is resolved to a YouTube stream via Lavalink's LavaSrc plugin.
- Single tracks, albums, and public playlists all work by URL or by name.
- No user OAuth tokens are required. Only a Client ID and Client Secret from the Spotify Developer Dashboard are needed.
When autoplay is enabled, Ghostify automatically fills the queue as tracks finish:
- Same-artist tracks — Spotify search returns the artist's most relevant tracks.
- Similar-artist tracks — Spotify's co-listener discovery identifies related artists, and their top tracks are fetched.
- YouTube fallback — If Spotify returns no results, a YouTube search by artist name is used.
The system deduplicates by URL and by artist-title pairs, and clears all seed history when the stop command is issued to prevent genre cross-contamination between sessions.
| Service | Base Image | Role |
|---|---|---|
bot |
Python 3.14 | Discord client (discord.py) |
lavalink |
Eclipse Temurin 21 + Lavalink 4.2.2 | Audio streaming server |
Key design decisions:
- Lavalink uses
network_mode: hostfor direct access to YouTube CDNs, avoiding common Docker audio issues. - A healthcheck ensures the bot only connects after Lavalink is ready.
- Every component runs in its own container with no local dependencies.
BOT_TOKEN= # Discord bot token
LAVALINK_PASSWORD= # Shared secret for Lavalink (generate a random one)
SPOTIFY_CLIENT_ID= # Spotify API Client ID
SPOTIFY_CLIENT_SECRET= # Spotify API Client Secret.
├── main.py # Bot entry point
├── config.py # Configuration from .env
├── docker-compose.yml # Docker orchestration
├── Dockerfile.bot # Bot image
├── Dockerfile.lavalink # Lavalink image
├── requirements.txt # Python dependencies
├── Img/ # Screenshots for documentation
├── lavalink/
│ ├── application.yml # Lavalink configuration (providers, plugins)
│ └── entrypoint.sh # Container entrypoint
├── cogs/
│ └── music.py # Core music logic and commands
└── utils/
├── embeds.py # Embed builders and progress bar
├── lavalink_voice.py # Lavalink voice bridge
├── search.py # Search resolution
└── spotify_resolver.py # Spotify API integration
# Follow live logs
docker compose logs -f
# Stop all services
docker compose down
# Rebuild after changes
docker compose up --build -d
# Restart only the bot (keeps Lavalink running)
docker compose restart botBot does not play audio — Lavalink requires access to googlevideo.com (YouTube CDN). network_mode: host resolves this automatically. If the problem persists, check your firewall or proxy settings.
Spotify tracks fail to load — Verify SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env. Ensure the application is correctly registered in the Spotify Developer Dashboard.
"Invalid response received" — Wait for Lavalink's healthcheck to complete, then restart the bot: docker compose restart bot.
Open an issue or submit a pull request. All contributions are welcome.
Instant playback with a single command.
.png)
