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

如何编写命令中断Discord音乐机器人的time.sleep?

问题分析与解决方案

你猜的完全没错,time.sleep(video_length)就是核心问题——这是个同步阻塞函数,会直接卡住Discord.py的事件循环,导致整个机器人彻底停摆,没法处理任何其他命令、消息或事件,直到sleep时间结束。

解决步骤

1. 替换同步sleep为异步sleep

把time.sleep(video_length)换成await asyncio.sleep(video_length)。asyncio.sleep是异步非阻塞的,机器人在等待歌曲播放结束的间隙,还能正常响应其他命令。

2. 用异步任务实现可中断的播放流程

要让disconnect这类命令能中断播放,得把整个播放循环包装成asyncio的Task对象,这样就能通过取消任务来终止等待和播放。

修改后的代码示例

import asyncio
import urllib.parse
import urllib.request
import re
from pytube import YouTube
from discord.ext import commands

# 用全局变量管理播放任务和队列(大型项目建议用类封装,避免全局变量混乱)
song_queue = []
current_play_task = None

@bot.command()
async def play(ctx, *, search):  # 加*支持带空格的搜索关键词
    global current_play_task, song_queue
    
    # 处理YouTube搜索(注意:正则解析HTML不稳定,建议后续改用pytube.Search)
    query_string = urllib.parse.urlencode({'search_query': search})
    try:
        htm_content = urllib.request.urlopen(f'http://www.youtube.com/results?{query_string}')
        search_results = re.findall(r'/watch\?v=(.{11})', htm_content.read().decode())
    except Exception as e:
        await ctx.send("搜索出错了")
        return
    
    if not search_results:
        await ctx.send("没找到相关歌曲")
        return
    
    url = f'http://www.youtube.com/watch?v={search_results[0]}'
    song_queue.append(url)
    
    if len(song_queue) > 1:
        await ctx.send("已加入播放队列")
        return
    
    # 启动播放循环任务
    if current_play_task is None or current_play_task.done():
        async def play_loop():
            global song_queue
            while song_queue:
                current_url = song_queue[0]
                yt = YouTube(current_url)
                video_length = yt.length
                
                await play_song(ctx, current_url)
                await ctx.send(f"正在播放:{current_url}")
                
                try:
                    # 异步等待,不阻塞事件循环
                    await asyncio.sleep(video_length)
                except asyncio.CancelledError:
                    # 任务被取消时,清空队列并提示
                    song_queue.clear()
                    await ctx.send("播放已终止")
                    raise  # 必须重新抛出,标记任务为已取消状态
                
                del song_queue[0]
            
            global current_play_task
            current_play_task = None
        
        current_play_task = asyncio.create_task(play_loop())

@bot.command()
async def disconnect(ctx):
    global current_play_task
    # 如果有正在运行的播放任务,取消它
    if current_play_task and not current_play_task.done():
        current_play_task.cancel()
        # 断开语音频道(根据你的play_song逻辑补充)
        if ctx.voice_client:
            await ctx.voice_client.disconnect()
        await ctx.send("已停止播放并断开连接")
    else:
        await ctx.send("当前没有正在播放的内容")

额外建议

  • 你的搜索逻辑用正则解析YouTube HTML非常不稳定,YouTube页面结构一变就会失效,建议改用pytube.Search类:
    from pytube import Search
    s = Search(search)
    result = s.results[0]
    url = result.watch_url
    
  • 如果play_song里有同步阻塞操作(比如读取音频文件),要用asyncio.to_thread包装,避免再次阻塞事件循环:
    await asyncio.to_thread(sync_blocking_function, args)
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:20:38