When I first started using Discord music bots, they felt like magic.
Paste a link, join a voice channel, and suddenly you’re listening to music with your friends. Simple.
Except… it wasn’t.
Over time, that “magic” turned into a gamble. You’d drop a YouTube link and wait:
- Maybe it plays
- Maybe it errors
- Maybe it just… does nothing
Eventually, it became a running joke in our server:
“Try it again. Maybe it’ll work this time.”
That’s when it clicked:
Why am I depending on something this unreliable?
So I built my own.
The Real Problem
At first glance, Discord music bots seem like a solved problem. There are tons of them—Jockie Music, Match, and countless clones.
But they all share the same weakness:
YouTube is a moving target.
Most bots rely on fragile or outdated methods to extract audio. When YouTube changes something (which it does often), those bots quietly break.
That’s why you get:
- Infinite loading
- Playback errors
- Random failures for perfectly valid links
It’s not your fault.
It’s the architecture.
A Different Approach
Instead of trying to patch around YouTube’s changes, I built BassKenobi around tools designed to handle them.
The stack is simple:
- discord.py — for interacting with Discord
- yt-dlp — for reliable media extraction
- PyNaCl — for voice support
- FFmpeg — for streaming audio
Nothing fancy. Just the right tools.
Why yt-dlp Is the Key
If there’s one decision that made this project work, it’s this one.
Most bots fail because they treat YouTube like a stable API.
It isn’t.
yt-dlp is actively maintained and built specifically to deal with that instability. When YouTube changes something, yt-dlp gets updated—fast.
It also supports more than just basic links:
- Full playlists
- Search queries (
!play never gonna give you up) - Different formats and sources
So instead of fighting breakage, you sidestep it entirely.
How the Bot Actually Works
Under the hood, BassKenobi is intentionally simple.
Each server gets its own music player:
class MusicPlayer:
def __init__(self):
self.queue = deque()
self.current = None
