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

Discord.Py机器人播放多首歌曲时提示已连接语音频道错误问题

错误触发原因
  • 核心报错原因:你每次调用play命令都会直接执行await voiceChannel.connect(),没有先判断机器人是否已经接入当前服务器的语音频道,重复发起连接请求就会抛出ClientException: Already connected to a voice channel异常。
  • 其他影响功能的逻辑缺陷:
    • 使用了阻塞式的time.sleep(5),会卡住Discord.py的异步事件循环,导致其他命令无法响应,同时你没有导入time模块,运行到这里本身就会触发变量未定义错误
    • 重复编写了两次文件重命名、调用voice.play的代码,属于冗余逻辑,会导致播放逻辑异常
    • 预先定义的songs异步队列、play_next_song事件完全没有被使用,反而代码里直接引用了未定义的song_queue、song变量,运行到对应分支时会触发报错
    • 用单文件song.mp3覆盖存储歌曲的方式,本身不支持队列播放,每次下载都会覆盖上一首的音频文件
修复后的实现代码
import discord
from discord.ext import commands
import youtube_dl
import os
import asyncio

# 注意现在Discord.py需要手动开启Intents才能正常获取成员、语音状态等信息,需要到Discord开发者后台开启对应权限
client = commands.Bot(command_prefix="^", intents=discord.Intents.all())

@client.command()
async def play(ctx, url: str):
    song_there = os.path.isfile("song.mp3")
    try:
        if song_there:
            os.remove("song.mp3")
    except PermissionError:
        await ctx.send("请等待当前播放的音乐结束,或者使用stop命令")
        return

    # 先判断是否已经连接语音频道,避免重复连接
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    if not voice or not voice.is_connected():
        if not ctx.author.voice:
            await ctx.send("你需要先加入一个语音频道")
            return
        voiceChannel = ctx.author.voice.channel
        voice = await voiceChannel.connect()

    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
        'quiet': True
    }
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])
    for file in os.listdir("./"):
        if file.endswith(".mp3") and file != "song.mp3":
            os.rename(file, "song.mp3")
    
    # 播放前先停止当前正在播放的音频
    if voice.is_playing():
        voice.stop()
    
    voice.play(discord.FFmpegPCMAudio("song.mp3"))

    # 异步等待播放结束,避免阻塞事件循环
    while voice.is_playing() or voice.is_paused():
        await asyncio.sleep(2)
    
    # 播放结束后清理文件
    song_there = os.path.isfile("song.mp3")
    if song_there:
        os.remove("song.mp3")


@client.command()
async def leave(ctx):
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    if voice and voice.is_connected():
        await voice.disconnect()
        # 离开后清理残留文件
        if os.path.isfile("song.mp3"):
            os.remove("song.mp3")
    else:
        await ctx.send("机器人当前没有连接到任何语音频道")


@client.command()
async def pause(ctx):
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    if voice and voice.is_playing():
        voice.pause()
    else:
        await ctx.send("当前没有正在播放的音频")


@client.command()
async def resume(ctx):
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    if voice and voice.is_paused():
        voice.resume()
    else:
        await ctx.send("当前没有暂停的音频")


@client.command()
async def stop(ctx):
    voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
    if voice and (voice.is_playing() or voice.is_paused()):
        voice.stop()
        # 停止后清理文件
        if os.path.isfile("song.mp3"):
            os.remove("song.mp3")
    else:
        await ctx.send("当前没有正在播放的音频")


client.run('你的机器人Token')
额外说明

如果需要支持队列播放,你可以把预先定义的songs异步队列利用起来,每次调用play命令时先把歌曲URL加入队列,单独起一个异步任务循环消费队列里的歌曲即可,不需要每次都在play命令里处理下载播放逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 04:36:02