You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Discord机器人播放YouTube完整播放列表或单独添加链接?

Got it, let's fix this so your bot can handle full YouTube playlists and manage a proper song queue! Here's how to approach it step by step:

Step 1: Add a playlist parsing package

Ytdl-core doesn’t handle playlist parsing out of the box, so we’ll use ytpl—a lightweight package that fetches all videos from a YouTube playlist. Install it first via npm:

npm install ytpl

Step 2: Set up a server-specific queue system

We need to track separate queues for each Discord server (guild) so multiple servers can use the bot without interfering with each other. Add this at the top of your file, right after your other imports:

const ytpl = require('ytpl');
const queues = new Map(); // Key: Guild ID, Value: { connection, queue: [] }

Step 3: Rewrite the !play command to handle queues and playlists

Replace your existing client.on('message') code with this updated version. I’ll break down what each part does below:

client.on('message', async message => {
  if (!message.content.startsWith(prefix) || message.author.bot) return;
  
  const args = message.content.slice(prefix.length).trim().split(/ +/);
  const command = args.shift().toLowerCase();

  if (command === 'play') {
    if (message.channel.type !== 'text') return;
    const voiceChannel = message.member.voice.channel;
    
    if (!voiceChannel) {
      return message.reply('please join a voice channel first!');
    }
    if (!args.length) {
      return message.reply('you need to provide a YouTube video or playlist link!');
    }

    // Get or create a queue for this server
    const guildId = message.guild.id;
    let guildQueue = queues.get(guildId);
    if (!guildQueue) {
      guildQueue = { connection: null, queue: [] };
      queues.set(guildId, guildQueue);
    }

    // Check if input is a playlist or single video
    const inputUrl = args[0];
    if (ytdl.validatePlaylistURL(inputUrl)) {
      // Parse playlist and add all videos to queue
      try {
        const playlist = await ytpl(inputUrl, { limit: Infinity });
        playlist.items.forEach(item => {
          guildQueue.queue.push(`https://www.youtube.com/watch?v=${item.videoId}`);
        });
        message.reply(`Added **${playlist.items.length} videos** from playlist: *${playlist.title}*`);
      } catch (err) {
        console.error(err);
        return message.reply('Failed to parse the playlist—make sure it\'s a valid public playlist!');
      }
    } else if (ytdl.validateURL(inputUrl)) {
      // Add single video to queue
      guildQueue.queue.push(inputUrl);
      message.reply('Added video to the queue!');
    } else {
      return message.reply('That\'s not a valid YouTube video or playlist link!');
    }

    // Start playing if nothing is already playing
    if (!guildQueue.connection) {
      const connection = await voiceChannel.join();
      guildQueue.connection = connection;
      playNextSong(guildId, message.channel);
    }
  }
});

// Helper function to play the next song in the queue
async function playNextSong(guildId, textChannel) {
  const guildQueue = queues.get(guildId);
  if (!guildQueue || guildQueue.queue.length === 0) {
    // No more songs—leave voice channel and clear the queue
    guildQueue.connection.disconnect();
    queues.delete(guildId);
    return textChannel.send('Queue finished! Leaving voice channel.');
  }

  // Grab the next song from the queue
  const nextSongUrl = guildQueue.queue.shift();
  try {
    // Stream only audio (saves bandwidth)
    const stream = ytdl(nextSongUrl, { filter: 'audioonly' });
    const dispatcher = guildQueue.connection.play(stream);

    // Fetch video info to display in chat
    const videoInfo = await ytdl.getInfo(nextSongUrl);
    textChannel.send(`Now playing: **${videoInfo.videoDetails.title}**`);

    // When the song ends, play the next one
    dispatcher.on('finish', () => {
      playNextSong(guildId, textChannel);
    });

    // Handle playback errors gracefully
    dispatcher.on('error', err => {
      console.error('Playback error:', err);
      textChannel.send('Oops, there was an error playing that song—skipping to the next one.');
      playNextSong(guildId, textChannel);
    });
  } catch (err) {
    console.error('Stream error:', err);
    textChannel.send('Failed to load that song—skipping to the next one.');
    playNextSong(guildId, textChannel);
  }
}

What this fixes:

  • Playlist support: The bot now parses public playlists and adds every video to the queue.
  • Persistent queue: Songs are stored in a server-specific queue, so the bot will play through all added videos automatically.
  • Error handling: Gracefully skips songs that fail to load or play, and gives clear feedback to users.
  • Audio-only streaming: Added filter: 'audioonly' to reduce bandwidth usage (since we don’t need video for a music bot).

Quick notes:

  • Ensure your ffmpeg installation is accessible in your system PATH—ytdl-core relies on it to process audio streams.
  • Private playlists won’t work with ytpl, so make sure any playlist you use is set to public.
  • You can easily extend this with commands like !skip (to jump to the next song) or !queue (to show the current queue) if you want—just let me know if you need help with those!

内容的提问来源于stack exchange,提问作者Nick Jannini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 14:27:43