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

如何在discord.FFmpegPCMAudio播放完成后自动删除MP3文件?

解决discord.py播放完成后删除音频文件的问题

你的核心问题是未使用discord.py VoiceClient正确的播放结束回调机制,同时同步删除操作在异步环境中存在潜在风险,以下是修复方案:

关键问题分析

  1. 错误的回调设置:discord.py的VoiceClient并没有on_audio_end属性,你设置的这个回调永远不会被触发,正确做法是使用play方法的after参数。
  2. 同步操作阻塞风险:os.remove是同步IO操作,直接在异步任务中调用可能阻塞事件循环,需要用异步方式包装同步操作。
  3. 变量捕获问题: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}")

修改说明

  1. 改用官方after回调:将播放结束逻辑绑定到play方法的after参数,这是discord.py官方支持的播放结束触发方式。
  2. 异步安全的删除操作:用asyncio.to_thread把同步的delete_file包装成异步任务,避免阻塞事件循环导致机器人响应卡顿。
  3. 避免变量覆盖问题:直接定义after_playing函数,确保每个回调都绑定当前播放的audio_file,不会被后续调用的变量覆盖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 21:10:05