如何为Discord音乐机器人添加播放队列、切歌及清空队列功能
需求背景
Rhythm和Groovy两款Discord音乐机器人下架后,我决定开发适配自己所在Discord服务器的自用音乐机器人。参考了Youtuber的教程代码后,已经实现了加入语音频道、播放、暂停、恢复播放、断开连接的功能,现在需要添加播放队列、切歌、清空队列的功能,原有代码如下:
原有代码
main.py
import discord from discord.ext import commands import music cogs = [music] client = commands.Bot(command_prefix='!', intents = discord.Intents.all()) for i in range(len(cogs)): cogs[i].setup(client) client.run('TOKEN')
原有music.py
import discord from discord.ext import commands import youtube_dl class music(commands.Cog): def __init__(self, client): self.client = client @commands.command() async def join(self,ctx): if ctx.author.voice is None: await ctx.send("Get in a voice channel idiot!") voice_channel = ctx.author.voice.channel if ctx.voice_client is None: await voice_channel.connect() else: await ctx.voice_client.move_to(voice_channel) @commands.command() async def disconnect(self,ctx): await ctx.voice_client.disconnect() @commands.command() async def play(self,ctx,url): ctx.voice_client.stop() FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} YDL_OPTIONS = {'format':"bestaudio"} vc = ctx.voice_client self.music_queue = [] with youtube_dl.YoutubeDL(YDL_OPTIONS) as ydl: info = ydl.extract_info(url,download=False) url2 = info['formats'][0]['url'] source = await discord.FFmpegOpusAudio.from_probe(url2,**FFMPEG_OPTIONS) vc.play(source) @commands.command() async def pause(self, ctx): await ctx.voice_client.pause() await ctx.send("Paused ⏸️") @commands.command() async def resume(self, ctx): await ctx.voice_client.resume() await ctx.send("Resumed ⏯️ ") def setup(client): client.add_cog(music(client))
实现方案
直接替换原有music.py的内容即可,新增了队列管理、自动播放下一首、切歌、查看队列、清空队列功能,命令规则:
!play <URL>无播放任务时直接播放,已有播放任务时自动加入队列!skip切换到下一首歌曲!queue查看当前队列的所有待播放歌曲!clearqueue清空所有队列内容
修改后的music.py代码:
import discord from discord.ext import commands import youtube_dl import asyncio class music(commands.Cog): def __init__(self, client): self.client = client # 队列存储格式:(音频源对象, 歌曲标题) self.music_queue = [] self.is_playing = False self.vc = None self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} self.YDL_OPTIONS = {'format':"bestaudio", 'quiet': True} # 播放下一首的回调函数 def play_next(self, ctx): if len(self.music_queue) > 0: self.is_playing = True source, title = self.music_queue.pop(0) self.vc.play(source, after=lambda e: self.play_next(ctx)) asyncio.run_coroutine_threadsafe(ctx.send(f"现在播放: {title}"), self.client.loop) else: self.is_playing = False @commands.command() async def join(self,ctx): if ctx.author.voice is None: await ctx.send("你得先加入一个语音频道!") voice_channel = ctx.author.voice.channel if ctx.voice_client is None: self.vc = await voice_channel.connect() else: await ctx.voice_client.move_to(voice_channel) self.vc = ctx.voice_client @commands.command() async def disconnect(self,ctx): self.music_queue = [] self.is_playing = False await ctx.voice_client.disconnect() @commands.command() async def play(self,ctx,url): # 自动检查加入语音频道 if ctx.voice_client is None: if ctx.author.voice: self.vc = await ctx.author.voice.channel.connect() else: await ctx.send("你得先加入一个语音频道!") return # 解析音频链接 with youtube_dl.YoutubeDL(self.YDL_OPTIONS) as ydl: try: info = ydl.extract_info(url, download=False) except: await ctx.send("链接解析失败,请检查URL是否正确") return url2 = info['formats'][0]['url'] source = await discord.FFmpegOpusAudio.from_probe(url2, **self.FFMPEG_OPTIONS) title = info.get('title', '未知歌曲') # 判断是直接播放还是加入队列 if not self.is_playing: self.is_playing = True self.vc.play(source, after=lambda e: self.play_next(ctx)) await ctx.send(f"现在播放: {title}") else: self.music_queue.append((source, title)) await ctx.send(f"已加入队列: {title} | 当前队列长度: {len(self.music_queue)}") @commands.command() async def pause(self, ctx): if self.vc and self.vc.is_playing(): await self.vc.pause() await ctx.send("已暂停 ⏸️") else: await ctx.send("当前没有正在播放的内容") @commands.command() async def resume(self, ctx): if self.vc and self.vc.is_paused(): await self.vc.resume() await ctx.send("已恢复播放 ⏯️ ") else: await ctx.send("当前没有暂停的内容") @commands.command() async def skip(self, ctx): if self.vc and self.vc.is_playing(): self.vc.stop() await ctx.send("已切歌 ⏭️") else: await ctx.send("当前没有正在播放的内容") @commands.command() async def queue(self, ctx): if len(self.music_queue) == 0: await ctx.send("当前队列为空") return queue_str = "当前队列:\n" for i, (_, title) in enumerate(self.music_queue): queue_str += f"{i+1}. {title}\n" await ctx.send(queue_str) @commands.command() async def clearqueue(self, ctx): self.music_queue = [] await ctx.send("已清空队列 🗑️") def setup(client): client.add_cog(music(client))
注意事项
- 本地需要提前安装ffmpeg,否则音频无法正常解码播放
- 如果youtube_dl解析新版YouTube链接失效,可以换成yt-dlp,仅需把
import youtube_dl改成import yt_dlp as youtube_dl即可,其余代码无需修改 main.py无需修改,直接沿用原有代码即可
内容的提问来源于stack exchange,提问作者Jordan Amo
相关产品推荐
相关产品推荐

