如何在discord.FFmpegPCMAudio播放完成后自动删除MP3文件?
解决discord.py播放完成后删除音频文件的问题
你的核心问题是未使用discord.py VoiceClient正确的播放结束回调机制,同时同步删除操作在异步环境中存在潜在风险,以下是修复方案:
关键问题分析
- 错误的回调设置:discord.py的
VoiceClient并没有on_audio_end属性,你设置的这个回调永远不会被触发,正确做法是使用play方法的after参数。 - 同步操作阻塞风险:
os.remove是同步IO操作,直接在异步任务中调用可能阻塞事件循环,需要用异步方式包装同步操作。 - 变量捕获问题:lambda表达式的延迟绑定会导致多次调用
play时,最终删除的是最后一次的audio_file而非当前播放的文件,需要固定参数绑定。
修复后的代码
import asyncio import os import codecs # 保留你原有的其他导入(如discord、functools等) @bot.command(name='play') async def play(ctx, url): global current_audio_file voice_client = ctx.guild.voice_client if voice_client.is_connected(): if ctx.author.voice and ctx.author.voice.channel == voice_client.channel: try: server = ctx.guild voice_channel = server.voice_client async with ctx.typing(): audio_file = None title = "" if "youtube" in url or "youtu" in url: result, title = await download_youtube_audio(url) result_b = bytes(result, "utf-8") audio_file_name = codecs.decode(result_b, 'charmap') audio_file, title2 = find_file(audio_file_name, 2) elif "spotify" in url: result = await download_spotify_audio(url) result_b = bytes(result, "utf-8") audio_file_name = codecs.decode(result_b, 'charmap') audio_file, title = find_file(audio_file_name, 1) if result: if "ERROR" not in result: await ctx.send(f'**Now playing:** {title}') if not voice_channel.is_playing(): # 定义播放结束后的回调函数 def after_playing(error): if error: print(f"播放出错: {error}") # 用asyncio.to_thread执行同步删除操作,避免阻塞事件循环 asyncio.create_task(asyncio.to_thread(delete_file, audio_file)) # 绑定回调到play方法的after参数 voice_channel.play( discord.FFmpegPCMAudio(executable="ffmpeg.exe", source=audio_file), after=after_playing ) current_audio_file = audio_file else: await ctx.send("当前正在播放其他歌曲!") else: print(f"下载音频出错: {result}") send_error(ctx, Exception(result)) else: await ctx.send("下载音频时发生错误。") except Exception as e: print(f"发生错误: {e}") send_error(ctx, e) await ctx.send("播放音频时发生错误。") else: await ctx.send("你不在机器人所在的语音频道中。") else: await ctx.send("机器人未连接到语音频道。") def delete_file(filename: str): if os.path.exists(filename): os.remove(filename) print(f"已删除文件: {filename}")
修改说明
- 改用官方
after回调:将播放结束逻辑绑定到play方法的after参数,这是discord.py官方支持的播放结束触发方式。 - 异步安全的删除操作:用
asyncio.to_thread把同步的delete_file包装成异步任务,避免阻塞事件循环导致机器人响应卡顿。 - 避免变量覆盖问题:直接定义
after_playing函数,确保每个回调都绑定当前播放的audio_file,不会被后续调用的变量覆盖。
内容的提问来源于stack exchange,提问作者Moni
相关产品推荐
相关产品推荐

