Discord.py音乐机器人报错AttributeError: 'NoneType'无play属性求助
Discord音乐机器人播放时报错
AttributeError: 'NoneType' object has no attribute 'play' 问题描述
我是编程新手,正在开发Discord音乐机器人,已经实现机器人加入、离开语音频道等功能,但播放音乐时出现错误AttributeError: 'NoneType' object has no attribute 'play'。我尝试查阅资料仍无法解决,此前已修复其他错误,现寻求帮助。相关代码如下:
@bot.command(name='playy', help='This command pauses the song') async def playyt(ctx, url): song_there = os.path.isfile("song.mp3") try: if song_there: os.remove("song.mp3") except PermissionError: em8 = discord.Embed(title = "Music Is Currently Playing", description = 'Please wait for the current playing music to end or use %leave <:_Paimon6:827074349450133524>.\nMusic provided by {ctx.author.mention} <:_Paimon6:827074349450133524>',color = ctx.author.color) await ctx.send(embed = em8) return voiceChannel = discord.utils.get(ctx.guild.voice_channels) await voiceChannel.connect() voice = discord.utils.get(client.voice_clients, guild=ctx.guild) em6 = discord.Embed(title = "Downloading Youtube Music", description = f'{url}\n\nPlease wait for paimon to setup the music you provide.\nMusic provided by {ctx.author.mention} <:_Paimon6:827074349450133524>',color = ctx.author.color) await ctx.send(embed = em6, delete_after = 2) await ctx.message.delete() ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '196', }], } with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) for file in os.listdir("./"): if file.endswith(".mp3"): os.rename(file, "song.mp3") voice.play(discord.FFmpegPCMAudio("song.mp3")) em1 = discord.Embed(title = "Now Listening Youtube Music", description = f'{url}\n\nPlease use %leave first to change music.\nMusic provided by {ctx.author.mention} <:_Paimon6:827074349450133524>',color = ctx.author.color) videoID = url.split("watch?v=")[1].split("&")[0] em1.set_thumbnail(url = f'https://img.youtube.com/vi/{videoID}/default.jpg'.format(videoID = videoID)) await ctx.send(embed = em1)
问题分析与修复
核心错误原因
报错的直接原因是voice变量为None,导致调用voice.play()时触发AttributeError。问题出在这一行:
voice = discord.utils.get(client.voice_clients, guild=ctx.guild)
你的命令是用@bot.command定义的,但这里却用了client来获取语音客户端,而不是bot。如果你的机器人实例是bot,那么应该用bot.voice_clients,否则找不到对应的语音客户端,voice就会是None。
优化修复方案
除了修正client为bot,还有几个潜在问题需要处理,让代码更稳定:
- 获取用户所在的语音频道:原代码
discord.utils.get(ctx.guild.voice_channels)会随机获取一个服务器语音频道,应该改为获取命令发起者所在的频道,同时处理用户不在频道的情况。 - 直接使用连接后的VoiceClient对象:调用
await voiceChannel.connect()会返回对应的VoiceClient,无需再用discord.utils.get获取,更可靠。 - 增加文件存在判断:下载并重命名文件后,要确保
song.mp3存在再播放。 - 处理FFmpeg路径问题:如果系统找不到FFmpeg可执行文件,需要指定其路径。
修复后的代码
import os import discord import youtube_dl @bot.command(name='playy', help='This command plays the song from YouTube') async def playyt(ctx, url): song_path = "song.mp3" song_there = os.path.isfile(song_path) # 处理已存在的音频文件 try: if song_there: os.remove(song_path) except PermissionError: em8 = discord.Embed( title="Music Is Currently Playing", description=f'Please wait for the current playing music to end or use %leave <:_Paimon6:827074349450133524>.\nMusic provided by {ctx.author.mention} <:_Paimon6:827074349450133524>', color=ctx.author.color ) await ctx.send(embed=em8) return # 检查用户是否在语音频道 if not ctx.author.voice: em_error = discord.Embed( title="Error", description="You need to be in a voice channel to use this command!", color=discord.Color.red() ) await ctx.send(embed=em_error) return # 连接到用户所在的语音频道 voice_channel = ctx.author.voice.channel voice = await voice_channel.connect() # 发送下载提示 em6 = discord.Embed( title="Downloading Youtube Music", description=f'{url}\n\nPlease wait for paimon to setup the music you provide.\nMusic provided by {ctx.author.mention} <:_Paimon6:827074349450133524>', color=ctx.author.color ) await ctx.send(embed=em6, delete_after=2) await ctx.message.delete() # 下载音频 ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '196', }], 'outtmpl': '%(title)s.%(ext)s', } with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) # 重命名下载的文件 downloaded_file = None for file in os.listdir("./"): if file.endswith(".mp3"): downloaded_file = file break if not downloaded_file: em_error = discord.Embed( title="Download Failed", description="Failed to download or convert the audio file!", color=discord.Color.red() ) await ctx.send(embed=em_error) await voice.disconnect() return os.rename(downloaded_file, song_path) # 播放音频(如果FFmpeg不在系统PATH,需要指定executable参数,比如executable="C:/ffmpeg/bin/ffmpeg.exe") voice.play(discord.FFmpegPCMAudio(song_path)) # 发送播放提示 em1 = discord.Embed( title="Now Listening Youtube Music", description=f'{url}\n\nPlease use %leave first to change music.\nMusic provided by {ctx.author.mention} <:_Paimon6:827074349450133524>', color=ctx.author.color ) # 提取视频ID并设置缩略图 try: if "watch?v=" in url: videoID = url.split("watch?v=")[1].split("&")[0] elif "/vi/" in url: videoID = url.split("/vi/")[1].split("/")[0] else: videoID = None if videoID: em1.set_thumbnail(url=f'https://img.youtube.com/vi/{videoID}/default.jpg') except IndexError: pass # 忽略视频ID提取失败的情况 await ctx.send(embed=em1)
关键修改说明
- 直接使用
voice_channel.connect()返回的voice对象,彻底避免None的问题。 - 增加了用户是否在语音频道的检查,避免机器人连接到错误的频道。
- 优化了文件下载和重命名的逻辑,增加了失败处理。
- 增强了视频ID的提取逻辑,避免因URL格式问题报错。
- 注释了FFmpeg路径的配置说明,解决可能的播放失败问题。
内容的提问来源于stack exchange,提问作者user21392318
相关产品推荐
相关产品推荐

