Nextcord Discord机器人MusicCog无法播放Spotify音乐求助
问题描述
我正在为Discord服务器开发机器人,使用Spotipy库添加音乐功能。机器人能正常加入语音频道,但调用!play ...命令时,只会显示“Now playing: ...”提示,没有任何声音输出。
已确认的信息:
- 机器人拥有包括Administrator在内的所有权限
- Spotify开发者密钥正确(已打码)
- 对第三方库使用经验不足,不确定是否是代码问题
附上代码:
main.py
import nextcord, os, config from nextcord.ext import commands intents = nextcord.Intents.all() intents.members = True bot = commands.Bot(command_prefix="!", intents=intents) @bot.event async def on_ready(): print("_______________________________________________________________") print(f"\nSuccessfully logged in as {bot.user.name} ({bot.user.id})\n") print("_______________________________________________________________") # LOAD COGS for filename in os.listdir('./cogs'): if filename.endswith('.py'): bot.load_extension(f"cogs.{filename[:-3]}") bot.run(config.TOKEN)
music.py(Cog)
import nextcord from nextcord.ext import commands import spotipy from spotipy.oauth2 import SpotifyClientCredentials class Music(commands.Cog): def __init__(self, bot): self.bot = bot self.voice = None self.sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id='//////', client_secret='/////')) @commands.command() async def join(self, ctx): if ctx.author.voice: self.voice = await ctx.author.voice.channel.connect() await ctx.send(f"Joined {ctx.author.voice.channel.name} voice channel.") else: await ctx.send("You need to be in a voice channel to use this command.") @commands.command() async def leave(self, ctx): if self.voice: await self.voice.disconnect() self.voice = None await ctx.send("Left the voice channel.") else: await ctx.send("I'm not connected to a voice channel.") @commands.command() async def play(self, ctx, *, query: str): if not self.voice or not self.voice.is_connected(): await ctx.send("I'm not connected to a voice channel. Use `!join` first.") return try: # Search for the track on Spotify track_info = self.sp.search(q=query, type='track', limit=1) # Check if any tracks were found if not track_info or not track_info['tracks']['items']: await ctx.send("Couldn't find any matching tracks.") return # Get the first track found track = track_info['tracks']['items'][0] track_url = track['external_urls']['spotify'] # Load and play the track source = await nextcord.FFmpegOpusAudio.from_probe(track_url) self.voice.play(source) # Send a message indicating the track is playing await ctx.send(f"Now playing: {track['name']} by {', '.join(artist['name'] for artist in track['artists'])}") except Exception as e: # Handle any errors that occur during track playback await ctx.send(f"An error occurred while playing the track: {e}") @commands.command() async def skip(self, ctx): if ctx.voice_client: ctx.voice_client.stop() await ctx.send("Skipped the current song.") @commands.command() async def stop(self, ctx): if ctx.voice_client: await ctx.voice_client.disconnect() self.voice = None await ctx.send("Stopped and disconnected from the voice channel.") def setup(bot): bot.add_cog(Music(bot)) print("MusicCog was loaded")
尝试过AI工具但没找到有效方案,求修复方法。
解决方案
问题核心在于你直接使用了Spotify的网页URL(track['external_urls']['spotify'])作为FFmpeg的输入源,而这个URL是网页链接,不是可直接解码的音频流,FFmpeg无法从中提取音频,所以没有声音输出。
Spotify的官方API不会提供完整的音频流(出于版权限制),你有两种可行的解决思路:
思路1:使用Spotify的预览音频
Spotify API会返回每个曲目30秒的预览音频URL,虽然时长有限,但可以快速验证播放功能:
修改play命令中获取音频源的部分:
# 替换原有的track_url和source部分 track = track_info['tracks']['items'][0] preview_url = track.get('preview_url') if not preview_url: await ctx.send("This track doesn't have a preview available.") return # 加载预览音频 source = await nextcord.FFmpegOpusAudio.from_probe(preview_url) self.voice.play(source)
思路2:使用第三方工具获取完整音频流(推荐)
如果需要播放完整曲目,需要借助yt-dlp这样的工具,通过曲目名称在YouTube上搜索对应的音频并获取流链接,再传给FFmpeg。
步骤如下:
- 安装依赖:
pip install yt-dlp
- 修改
music.py的play命令:
# 导入yt-dlp import yt_dlp # 在Music类的__init__中添加yt-dlp配置 def __init__(self, bot): self.bot = bot self.voice = None self.sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id='//////', client_secret='/////')) self.ydl_opts = { 'format': 'bestaudio/best', 'quiet': True, 'no_warnings': True, 'default_search': 'ytsearch', } # 修改play命令 @commands.command() async def play(self, ctx, *, query: str): if not self.voice or not self.voice.is_connected(): await ctx.send("I'm not connected to a voice channel. Use `!join` first.") return try: # 先通过Spotify获取准确的曲目名称和艺术家 track_info = self.sp.search(q=query, type='track', limit=1) if not track_info or not track_info['tracks']['items']: await ctx.send("Couldn't find any matching tracks on Spotify.") return track = track_info['tracks']['items'][0] full_query = f"{track['name']} {', '.join(artist['name'] for artist in track['artists'])}" # 使用yt-dlp获取音频流 with yt_dlp.YoutubeDL(self.ydl_opts) as ydl: info = ydl.extract_info(full_query, download=False) audio_url = info['entries'][0]['url'] # 加载音频流 source = await nextcord.FFmpegOpusAudio.from_probe(audio_url) self.voice.play(source) await ctx.send(f"Now playing: {track['name']} by {', '.join(artist['name'] for artist in track['artists'])}") except Exception as e: await ctx.send(f"An error occurred while playing the track: {str(e)}")
注意:这种方式依赖YouTube的内容,确保遵守相关版权规定。
此外,还需要确保你的服务器上已经安装了FFmpeg,Nextcord的音频功能依赖它。如果没有安装:
- Windows:从FFmpeg官网下载并添加到系统PATH
- Linux:用包管理器安装(如
apt install ffmpeg) - macOS:用Homebrew安装(
brew install ffmpeg)
内容的提问来源于stack exchange,提问作者fmDXP
相关产品推荐
相关产品推荐

