如何编写命令中断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
相关产品推荐
相关产品推荐

