# Discord Music Bot Development — Tool Research Report > **Query:** What tools are needed to develop a Discord bot that streams YouTube audio to voice channels, with queue management via user commands — preferring JavaScript/TypeScript. --- ## Executive Summary Building a Discord music bot that streams YouTube audio to voice channels in 2025 requires a carefully chosen stack due to YouTube's frequently changing API restrictions and the deprecation of several once-popular libraries. The recommended modern stack centers on **Discord.js v14** (Discord API framework), **@discordjs/voice** (low-level voice layer), a high-level music framework (**discord-player** or **DisTube**), and a YouTube extraction backend using **youtubei.js** (via `discord-player-youtubei` extractor or DisTube's plugin system). For large-scale or production bots, **Lavalink v4** (a Java-based audio server) offers superior performance. TypeScript is natively supported by all recommended libraries and is strongly advisable. --- ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ Discord API (WebSocket + REST) │ └────────────────────────┬────────────────────────────────────────┘ │ ┌────────▼────────┐ │ discord.js │ ← Bot framework (JS/TS) │ v14 │ └────────┬────────┘ │ ┌──────────────┴───────────────┐ │ │ ┌────────▼──────────┐ ┌──────────▼──────────┐ │ Music Framework │ │ @discordjs/voice │ │ (discord-player │───────▶│ (low-level voice │ │ OR DisTube) │ │ connections) │ └────────┬──────────┘ └──────────┬──────────┘ │ │ ┌────────▼──────────┐ ┌──────────▼──────────┐ │ YouTube Extractor│ │ Audio Processing │ │ (youtubei.js / │ │ FFmpeg + Opus │ │ yt-dlp) │ │ (encoding/decoding)│ └───────────────────┘ └─────────────────────┘ ─── OR, for large bots ─── ┌──────────────────────────────────────────────────────────────────┐ │ Lavalink v4 (Java server) │ │ Lavaplayer + youtube-source plugin → streams audio via WS │ │ Node.js client: lavalink-client npm package │ └──────────────────────────────────────────────────────────────────┘ ``` --- ## Component 1: Discord Bot Framework — Discord.js v14 **Repository:** [discordjs/discord.js](https://github.com/discordjs/discord.js) **NPM:** `discord.js` **Language:** TypeScript (ships `.d.ts` declarations) Discord.js is the overwhelmingly dominant Discord API library for Node.js, offering complete coverage of the Discord REST API and Gateway WebSocket events.[^1] v14 introduced native TypeScript support, slash command builders, interaction handling, and the `GatewayIntentBits` enum. ### Required Intents for a Music Bot ```typescript import { Client, GatewayIntentBits } from 'discord.js'; const client = new Client({ intents: [ GatewayIntentBits.Guilds, // required for slash commands GatewayIntentBits.GuildVoiceStates, // required for voice channel management GatewayIntentBits.GuildMessages, // for prefix commands (if used) GatewayIntentBits.MessageContent, // PRIVILEGED: required to read message content ], }); ``` > **Note:** `MessageContent` is a **privileged intent** — it must be enabled manually in the [Discord Developer Portal](https://discord.com/developers/applications). For a slash-command-only bot, it's not needed. ### Slash Command Structure Modern bots use slash commands (`/play`, `/skip`, `/queue`, etc.) registered via the REST API. The recommended structure: ``` src/ commands/ play.ts skip.ts queue.ts stop.ts events/ ready.ts interactionCreate.ts deploy-commands.ts index.ts ``` --- ## Component 2: Voice Layer — @discordjs/voice **Repository:** [discordjs/voice](https://github.com/discordjs/voice) **NPM:** `@discordjs/voice` This is the **official, low-level Discord voice library** from the discord.js team.[^2] It handles: - Joining and leaving voice channels - Creating and managing audio players - Creating audio resources from streams - Subscribing voice connections to audio players It is the backbone used by all higher-level music frameworks (DisTube, discord-player). ### Key APIs ```typescript import { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, VoiceConnectionStatus, } from '@discordjs/voice'; ``` ### Required Peer Dependencies From the official README[^2], you need to install one option from each category: | Category | Recommended | Alternatives | Purpose | |---|---|---|---| | **Opus** | `@discordjs/opus` | `opusscript`, `mediaplex` | Audio encoding (Opus codec for Discord) | | **Encryption** | `sodium-native` | `sodium`, `libsodium-wrappers`, `tweetnacl` | Encrypting voice data | | **FFmpeg** | System FFmpeg | `ffmpeg-static` (npm) | Media transcoding | ```bash npm install @discordjs/voice @discordjs/opus sodium-native # Install FFmpeg: sudo apt install ffmpeg (Linux) or brew install ffmpeg (Mac) # OR: npm install ffmpeg-static ``` > **Important:** As of 2024, Node.js 16+ is required by `@discordjs/voice`; Node.js 20+ is recommended for modern discord.js projects.[^3] --- ## Component 3: Music Frameworks (High-Level Abstractions) Rather than building queue management, audio resource handling, and track searching from scratch, use one of these frameworks that sit on top of `@discordjs/voice`. ### Option A: discord-player (Recommended for TypeScript/Flexibility) **Repository:** [Androz2091/discord-player](https://github.com/Androz2091/discord-player) **NPM:** `discord-player` + `@discord-player/extractor` **Docs:** [discord-player.js.org](https://discord-player.js.org) discord-player is a robust, TypeScript-first music framework providing:[^4] - Built-in queue management with repeat, shuffle, volume control - 64+ built-in audio filter presets - Extensible Extractor API (pluggable source backends) - `useMainPlayer()` hooks for clean code patterns - Automatic voice state handling - IP rotation support - Object-oriented design with events system ```bash npm install discord-player @discord-player/extractor mediaplex ``` ```typescript import { Player } from 'discord-player'; import { DefaultExtractors } from '@discord-player/extractor'; import { YoutubeiExtractor } from 'discord-player-youtubei'; const player = new Player(client); await player.extractors.loadMulti(DefaultExtractors); // Register the YouTube extractor (see Component 4 below) await player.extractors.register(YoutubeiExtractor, {}); player.events.on('playerStart', (queue, track) => { queue.metadata.channel.send(`▶️ Now playing: **${track.cleanTitle}**`); }); ``` **Play command example:** ```typescript import { useMainPlayer } from 'discord-player'; const player = useMainPlayer(); const { track } = await player.play(voiceChannel, query, { nodeOptions: { metadata: interaction }, }); await interaction.followUp(`✅ Enqueued: **${track.cleanTitle}**`); ``` > **Note:** discord-player v6+ uses `discord-voip` (a fork of `@discordjs/voice`) as its internal voice layer.[^4] --- ### Option B: DisTube (Batteries-Included) **Repository:** [skick1234/DisTube](https://github.com/distubejs/distube) **NPM:** `distube` **Docs:** [distube.js.org](https://distube.js.org) DisTube is a high-level, all-in-one music bot library offering more out-of-the-box functionality:[^5] - Built-in queue, playlist, and voice connection management - 20+ built-in audio filters (bassboost, nightcore, echo, karaoke, etc.) - Plugin system supporting YouTube, Spotify, SoundCloud, Bandcamp, and 700+ other sites - Full TypeScript support - Requires `discord.js v14` and `@discordjs/voice` ```bash npm install distube @discordjs/voice @discordjs/opus ``` ```javascript import { DisTube } from 'distube'; const distube = new DisTube(client, { emitNewSongOnly: true }); distube.on('playSong', (queue, song) => queue.textChannel?.send(`▶️ Playing \`${song.name}\` - \`${song.formattedDuration}\``) ); // Play command handler distube.play(message.member!.voice.channel!, query, { message, textChannel: message.channel, member: message.member!, }); ``` ### Option A vs. B Comparison | Feature | discord-player | DisTube | |---|---|---| | Abstraction Level | Medium-High | High | | TypeScript Quality | Excellent | Good | | Queue Management | Built-in | Built-in | | Audio Filters | 64+ presets | 20+ built-in | | Plugin/Extractor System | Extractor API | Plugin system | | Customizability | High | Moderate | | Ease of Setup | Moderate | Simple | | Supports 700+ sites | Via extractors | Via plugins (yt-dlp plugin) | | Community Size | Large | Large | | Voice Library | `discord-voip` (fork) | `@discordjs/voice` | **Recommendation:** Use **discord-player** for TypeScript-first, flexible development. Use **DisTube** if you want maximum out-of-the-box features with minimal setup. --- ## Component 4: YouTube Audio Extraction — The Critical Piece YouTube is the most complex component due to frequent API changes and the **deprecation of ytdl-core** in August 2024.[^6] ### ⚠️ What NOT to Use Anymore | Library | Status | Reason | |---|---|---| | `ytdl-core` | **Deprecated (Aug 2024)** | No longer maintained; frequently broken | | `@distube/ytdl-core` | **Deprecated** | Maintenance stopped; recommends migration to youtubei.js | | `play-dl` | **Archived (Jun 2025)** | Repository archived; no new updates | ### ✅ Current Recommended Options #### Option 1: `discord-player-youtubei` (Best for discord-player) **Repository:** [retrouser955/discord-player-youtubei](https://github.com/retrouser955/discord-player-youtubei) **NPM:** `discord-player-youtubei` Built on top of `youtubei.js` (YouTube's InnerTube API), this extractor plugin is the current community standard for discord-player YouTube support.[^7] ```bash npm install discord-player-youtubei ``` ```typescript import { YoutubeiExtractor } from 'discord-player-youtubei'; // Register once at startup - not in every command await player.extractors.register(YoutubeiExtractor, { // Optional: authenticate with YouTube account for higher rate limits // authentication: 'OAUTH_TOKEN' }); ``` #### Option 2: `youtubei.js` (LuanRT) — Direct Usage **Repository:** [LuanRT/YouTube.js](https://github.com/LuanRT/YouTube.js) **NPM:** `youtubei.js` **Docs:** [ytjs.dev](https://ytjs.dev) A full JavaScript client for YouTube's private InnerTube API. Enables searching, metadata fetching, and stream URL extraction. Used under the hood by `discord-player-youtubei` and referenced as the recommended successor to `ytdl-core`.[^6] #### Option 3: `yt-dlp` (via subprocess — Most Robust) **Binary:** [yt-dlp/yt-dlp](https://github.com/yt-dlp/yt-dlp) (Python, installed separately) **Node wrapper:** `yt-dlp-wrap` npm package `yt-dlp` is a Python CLI tool that is the most actively maintained YouTube downloader. It handles YouTube's anti-bot measures most robustly.[^8] It can be called from Node.js as a subprocess: ```typescript import { spawn } from 'child_process'; import { createAudioResource, StreamType } from '@discordjs/voice'; function getYtDlpStream(url: string) { const proc = spawn('yt-dlp', [ '-f', 'bestaudio', '--no-playlist', '-o', '-', // output to stdout url, ], { stdio: ['ignore', 'pipe', 'ignore'] }); return createAudioResource(proc.stdout, { inputType: StreamType.Arbitrary, }); } ``` > **Note:** Requires `yt-dlp` binary to be installed on the host system (`pip install yt-dlp` or system package manager). Can also be used via DisTube's `@distube/yt-dlp` plugin. #### Option 4: DisTube YouTube Plugin For DisTube users, the official `@distube/youtube` plugin or the `@distube/yt-dlp` plugin handles YouTube extraction natively within the DisTube ecosystem.[^5] --- ## Component 5: Lavalink — For Production/Large-Scale Bots **Repository:** [lavalink-devs/Lavalink](https://github.com/lavalink-devs/Lavalink) **Docs:** [lavalink.dev](https://lavalink.dev) **Version:** v4 (stable) Lavalink is a **standalone Java audio server** that offloads all audio processing from your bot's Node.js process.[^9] It communicates with your bot over WebSocket and is used in production by major bots (FredBoat, Dyno, etc.). ### Why Lavalink? - **Performance**: Audio transcoding happens in a separate JVM process — your bot stays fast - **Scalability**: Supports multiple nodes for horizontal scaling - **Seeking**: Built-in seeking support - **Volume control**: Precise volume control - **Filters**: Equalizer, timescale, tremolo, vibrato, rotation, distortion filters - **YouTube support**: Via the official `youtube-source` plugin for Lavaplayer[^10] ### Requirements - **Java 17 LTS or newer** (runs as a separate process) - Your Node.js bot connects as a client via WebSocket ### Setup Overview 1. **Download** the Lavalink JAR from [GitHub Releases](https://github.com/lavalink-devs/Lavalink/releases) 2. **Configure** `application.yml`: ```yaml server: port: 2333 lavalink: server: password: "your-strong-password" plugins: youtube: enabled: true clients: ["MUSIC", "ANDROID_VR", "WEB", "WEBEMBEDDED"] ``` 3. **Run**: `java -jar Lavalink.jar` 4. **Connect** your Node.js bot using `lavalink-client`: ```bash npm install lavalink-client ``` ```typescript import { LavalinkManager } from 'lavalink-client'; client.lavalink = new LavalinkManager({ nodes: [{ authorization: 'your-strong-password', host: 'localhost', port: 2333, id: 'main-node', }], sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload), client: { id: process.env.CLIENT_ID!, username: 'MusicBot' }, }); client.on('raw', d => client.lavalink.sendRawData(d)); client.on('ready', () => client.lavalink.init({ ...client.user! })); ``` ### Node.js Lavalink Clients | Package | Notes | |---|---| | `lavalink-client` | Most actively maintained; full TypeScript support | | `shoukaku` | Performant, popular choice | | `lavacord` | Lightweight; [lavacord.js.org](https://lavacord.js.org) | | `moonlink.js` | Alternative with good TS support | ### Lavalink YouTube Source Plugin The official [`youtube-source`](https://github.com/lavalink-devs/youtube-source) plugin for Lavaplayer uses multiple InnerTube clients to maximize success rate against YouTube's anti-bot measures.[^10] Configure in `application.yml` as shown above. --- ## Component 6: Development Toolchain ### TypeScript Setup ```bash npm install -D typescript @types/node ts-node tsup ``` **Recommended `tsconfig.json`:** ```json { "compilerOptions": { "target": "ES2022", "module": "commonjs", "lib": ["ES2022"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` **Build tool:** Use **`tsup`** (fast, zero-config TypeScript bundler) or plain `tsc`. ### Environment Variables Use **`dotenv`** for managing secrets: ```bash npm install dotenv ``` ``` # .env DISCORD_TOKEN=your_bot_token CLIENT_ID=your_application_id GUILD_ID=optional_test_guild_id ``` ### Project Scaffolding Options | Template | Description | |---|---| | [KevinNovak/Discord-Bot-TypeScript-Template](https://github.com/KevinNovak/Discord-Bot-TypeScript-Template) | Production-ready TS template with PM2/Docker support | | [MericcaN41/discordjs-v14-template-ts](https://github.com/MericcaN41/discordjs-v14-template-ts) | Handlers, MongoDB, permissions, cooldowns | --- ## Component 7: Persistence & State Management For queue persistence across restarts or advanced features: | Need | Tool | |---|---| | Simple key-value / queue state | **Redis** (`ioredis` npm) | | Relational data (playlists, server settings) | **PostgreSQL** via `pg` or **SQLite** via `better-sqlite3` | | ORM | **Prisma** (excellent TypeScript support) | | In-memory only (simplest) | Native `Map` (no persistence) | For most bots, an in-memory `Map` per guild is sufficient for queue state. Persist only playlists/settings to a database. --- ## Component 8: Deployment & Hosting ### Minimum Hardware Requirements - **RAM:** 512 MB minimum; 1 GB recommended (2+ GB if running Lavalink on the same host) - **CPU:** 1 vCPU minimum - **OS:** Ubuntu 22.04+ LTS recommended - **Java:** Required only if running Lavalink on the same host (Java 17+) ### Process Management **PM2** (simpler, Node.js-native): ```bash npm install -g pm2 pm2 start dist/index.js --name music-bot pm2 save && pm2 startup ``` **Docker** (more portable, production-grade): ```dockerfile FROM node:20-alpine WORKDIR /app RUN apk add --no-cache ffmpeg python3 py3-pip && pip3 install yt-dlp COPY package*.json ./ RUN npm ci --only=production COPY dist/ ./dist/ CMD ["node", "dist/index.js"] ``` Use `docker-compose.yml` to run the bot + Lavalink together. ### Hosting Providers | Provider | Type | Notes | |---|---|---| | DigitalOcean Droplet | VPS | $6/mo for 1GB RAM; full control | | Linode/Akamai | VPS | Similar to DigitalOcean | | Hetzner Cloud | VPS | Cheapest per-GB in EU | | Railway.app | PaaS | Easy deploy, free tier available | | Fly.io | PaaS | Free tier with containers | | VPS self-hosted | VPS | Maximum control | > **Avoid serverless platforms** (Lambda, Vercel) — Discord bots require persistent WebSocket connections. --- ## Full Recommended Stack Summary ### For a Starter/Personal Bot (Simpler) ```bash npm install discord.js @discordjs/voice discord-player @discord-player/extractor \ discord-player-youtubei mediaplex ffmpeg-static sodium dotenv npm install -D typescript @types/node ts-node tsup ``` | Layer | Choice | |---|---| | Discord API | `discord.js` v14 | | Voice | `@discordjs/voice` (via discord-player internally) | | Music Framework | `discord-player` v6+ | | YouTube Backend | `discord-player-youtubei` (uses `youtubei.js`) | | Opus Encoding | `mediaplex` | | FFmpeg | `ffmpeg-static` or system install | | Encryption | `sodium` | | Language | TypeScript | | Deployment | PM2 on a VPS | ### For a Production Bot (Large Scale) ```bash npm install discord.js lavalink-client dotenv npm install -D typescript @types/node tsup # On server: Java 17+, then run Lavalink JAR ``` | Layer | Choice | |---|---| | Discord API | `discord.js` v14 | | Voice/Streaming | Lavalink v4 (Java server) + `lavalink-client` | | YouTube Backend | Lavalink `youtube-source` plugin | | Language | TypeScript | | Deployment | Docker Compose (bot + Lavalink) on VPS | --- ## Legal & ToS Considerations > ⚠️ **Important:** Streaming YouTube audio directly to Discord may violate [YouTube's Terms of Service](https://www.youtube.com/static?template=terms), specifically Section 5.B which prohibits downloading content except through YouTube's explicitly permitted mechanisms. Key considerations: - **Private bots** (in your own server) face significantly less legal risk than public bots - Two of the largest music bots — **Groovy** and **Rythm** — were shut down in 2021 following legal pressure from YouTube/Google - YouTube's Music API and official embeds are the ToS-compliant alternatives - Bots using Spotify typically use Spotify's search to find the song name, then stream the YouTube equivalent (since Spotify doesn't provide audio streams) - Consider using **SoundCloud** or other platforms with permissive APIs as alternatives or fallbacks --- ## Key Repositories Summary | Repository | Purpose | Language | |---|---|---| | [discordjs/discord.js](https://github.com/discordjs/discord.js) | Discord API client library | TypeScript | | [discordjs/voice](https://github.com/discordjs/voice) | Discord voice connections | TypeScript | | [Androz2091/discord-player](https://github.com/Androz2091/discord-player) | High-level music framework | TypeScript | | [skick1234/DisTube](https://github.com/distubejs/distube) | All-in-one music library | TypeScript | | [retrouser955/discord-player-youtubei](https://github.com/retrouser955/discord-player-youtubei) | YouTube extractor for discord-player | TypeScript | | [LuanRT/YouTube.js](https://github.com/LuanRT/YouTube.js) | InnerTube API client | TypeScript | | [lavalink-devs/Lavalink](https://github.com/lavalink-devs/Lavalink) | Audio server for large bots | Java | | [lavalink-devs/youtube-source](https://github.com/lavalink-devs/youtube-source) | YouTube plugin for Lavalink | Java | | [lavalink-devs/lavalink-client](https://github.com/lavalink-devs/lavalink-client) | Node.js Lavalink client | TypeScript | | [yt-dlp/yt-dlp](https://github.com/yt-dlp/yt-dlp) | YouTube downloader CLI | Python | --- ## Confidence Assessment | Finding | Confidence | Notes | |---|---|---| | discord.js v14 is the standard | **Very High** | Unambiguous community consensus | | `ytdl-core` is deprecated (Aug 2024) | **Very High** | Confirmed via npm README + GitHub | | `play-dl` is archived (Jun 2025) | **Very High** | Confirmed via GitHub repo status | | `discord-player-youtubei` is recommended | **High** | Multiple community sources + GitHub | | `discord-player` uses `discord-voip` fork | **High** | Confirmed via official README[^4] | | Lavalink v4 is stable | **Very High** | Confirmed via official README[^9] | | Lavalink youtube-source uses InnerTube | **Very High** | Confirmed via repo README[^10] | | `mediaplex` is recommended over `@discordjs/opus` for discord-player | **High** | Stated in discord-player README[^4] | | Legal risks around YouTube streaming | **High** | Based on precedent (Groovy/Rythm) + YouTube ToS | --- ## Footnotes [^1]: [discordjs/discord.js](https://github.com/discordjs/discord.js) — Official Discord.js repository; docs at [discord.js.org](https://discord.js.org) [^2]: [discordjs/voice README.md](https://github.com/discordjs/voice/blob/main/README.md) — Lists all required optional dependencies (encryption, Opus, FFmpeg) [^3]: [discordjs/voice installation docs](https://discordjs.guide/voice) — Node.js 16+ required; 20+ recommended [^4]: [Androz2091/discord-player README.md](https://github.com/Androz2091/discord-player/blob/master/README.md) — Full setup instructions, mediaplex recommendation, FFmpeg notes [^5]: [skick1234/DisTube README.md](https://github.com/distubejs/distube/blob/main/README.md) — Official README; plugin architecture; install `distube @discordjs/voice @discordjs/opus` [^6]: [ytdl-core deprecation discussion](https://stackoverflow.com/questions/78824388/ytdl-core-is-returning-a-stream-with-nothing-in-it) — Community discussion on ytdl-core being broken; `@distube/ytdl-core` points to `youtubei.js` as successor; [npm @distube/ytdl-core](https://www.npmjs.com/package/@distube/ytdl-core) [^7]: [retrouser955/discord-player-youtubei GitHub](https://github.com/retrouser955/discord-player-youtubei) — YouTube extractor built on youtubei.js for discord-player v6/v7 [^8]: [yt-dlp PyPI](https://pypi.org/project/yt-dlp/) — Most actively maintained YouTube downloader; regularly updated against YouTube's bot detection [^9]: [lavalink-devs/Lavalink README.md](https://github.com/lavalink-devs/Lavalink/blob/master/README.md) — v4 is stable; Java 17 required; used by FredBoat, Dyno in production [^10]: [lavalink-devs/youtube-source README.md](https://github.com/lavalink-devs/youtube-source) — YouTube plugin for Lavaplayer using multiple InnerTube clients for robustness