Heroku-22栈下Discord.py音乐机器人无法播放音乐的解决办法
解决Heroku-22栈上Discord音乐机器人播放异常问题
Heroku-22基于Ubuntu 22.04,和Heroku-20的Ubuntu 20.04相比,预装软件版本、依赖环境存在差异,导致原代码中的youtube-dl和FFmpeg配置无法适配新环境。以下是针对性修复方案:
核心问题分析
- youtube-dl停止维护:原代码使用的youtube-dl已停止更新,无法兼容新的流媒体协议,在Heroku-22环境下无法正确拉取音频流。
- FFmpeg参数不足:Heroku-22的FFmpeg版本更高,默认配置无法处理流媒体的重连需求,导致播放中断或无法启动。
修复步骤
1. 替换依赖库:用yt-dlp替代youtube-dl
yt-dlp是youtube-dl的活跃分支,兼容新平台和协议,可完全替代原库功能:
- 修改
requirements.txt,将youtube-dl替换为yt-dlp - 代码中同步替换导入和初始化逻辑
2. 调整FFmpeg配置
添加流媒体重连参数,确保在Heroku网络环境下稳定拉取音频流。
3. 确认Heroku环境的FFmpeg支持
Heroku-22默认预装FFmpeg,若缺失可在Heroku控制台的应用设置中添加buildpack:https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git
修改后的完整代码
import yt_dlp import discord from discord import app_commands, Interaction import asyncio import os class MyClient(discord.Client): async def on_ready(self): await self.wait_until_ready() await tree.sync() print(f"login at {self.user}") intents = discord.Intents.all() client = MyClient(intents=intents) tree = app_commands.CommandTree(client) yt_dlp.utils.bug_reports_message = lambda: '' ytdl_format_options = { 'format': 'bestaudio/best', 'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s', 'restrictfilenames': True, 'noplaylist': True, 'nocheckcertificate': True, 'ignoreerrors': False, 'logtostderr': False, 'quiet': True, 'no_warnings': True, 'default_search': 'auto', 'source_address': '0.0.0.0', # 绑定IPv4避免IPv6问题 } # 新增FFmpeg重连参数,适配Heroku-22环境 ffmpeg_options = { 'options': '-vn -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', } ytdl = yt_dlp.YoutubeDL(ytdl_format_options) class YTDLSource(discord.PCMVolumeTransformer): def __init__(self, source, *, data, volume=0.5): super().__init__(source, volume) self.data = data self.title = data.get('title') self.url = data.get('url') @classmethod async def from_url(cls, url, *, loop=None, stream=True): loop = loop or asyncio.get_event_loop() data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream)) if 'entries' in data: # 取播放列表第一个条目 data = data['entries'][0] filename = data['url'] if stream else ytdl.prepare_filename(data) return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data) queue = {} @tree.command(name="play", description="play music") async def playmusic(interaction: Interaction, url_title: str, playfirst: bool=False): await interaction.response.send_message("I'm looking for music!!") guild = str(interaction.guild.id) try: queue[guild] except KeyError: queue[guild] = [] if interaction.user.voice is None: await interaction.edit_original_response(content="you must join any voice channel") else: voice_client: discord.VoiceClient = discord.utils.get(client.voice_clients, guild=interaction.guild) if voice_client is None: await interaction.user.voice.channel.connect() voice_client: discord.VoiceClient = discord.utils.get(client.voice_clients, guild=interaction.guild) player = await YTDLSource.from_url(url_title, loop=None) if playfirst and len(queue[guild]) > 1: queue[guild].insert(1, player) else: queue[guild].append(player) if not voice_client.is_playing(): voice_client.play(player, after=None) await interaction.edit_original_response(content=f"{player.title} playing!!") else: await interaction.edit_original_response(content=f"{player.title} enqueued!") await asyncio.sleep(7) await interaction.delete_original_response() # 从环境变量获取token,避免硬编码 client.run(os.getenv('DISCORD_TOKEN'))
额外配置说明
- requirements.txt示例:
discord.py>=2.3.0 yt-dlp>=2023.10.13
- Heroku环境变量:将Discord机器人token添加到Heroku应用的环境变量中,键为
DISCORD_TOKEN,替代代码中的硬编码token。
内容的提问来源于stack exchange,提问作者bon ho
相关产品推荐
相关产品推荐

