问题描述
我正在用 Python 重写构建一个多工具 discord bot。
所以我决定也添加音乐。当我完成机器人并在我的本地电脑上尝试它时,它运行良好。但是,当我上传到 Heroku 时,我遇到了一些 ffmpeg 错误。
最初我认为 Heroku 是基于 Linux 的,所以我下载了适用于 Linux 的 ffmpeg。还是没用。然后我添加了一个 apt buildpack 并以这种方式下载。但是现在它起作用了,但出现了不同的错误。它找到了 ffmpeg 但歌曲没有播放,这显示在控制台中:
[ffmpeg:加载共享库时出错:libvpx.so.6:不能 打开共享对象文件:没有那个文件或目录]
我该如何解决这个问题?
完整代码如下:
import discord
from discord.ext import commands
import asyncio
import itertools
import sys
import traceback
from async_timeout import timeout
from functools import partial
from youtube_dl import YoutubedL
import ctypes
import ctypes.util
@commands.Cog.listener()
async def on_ready(self):
print("ctypes - Find opus:")
a = ctypes.util.find_library('opus')
print(a)
print("discord - Load Opus:")
b = discord.opus.load_opus(a)
print(b)
print("discord - Is loaded:")
c = discord.opus.is_loaded()
print(c)
ytdlopts = {
'format': 'bestaudio/best','outtmpl': 'downloads/%(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'
}
ffmpegopts = {
'before_options': '-nostdin','options': '-vn'
}
ytdl = YoutubedL(ytdlopts)
class VoiceConnectionError(commands.CommandError):
class InvalidVoiceChannel(VoiceConnectionError):
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self,source,*,data,requester):
super().__init__(source)
self.requester = requester
self.title = data.get('title')
self.web_url = data.get('webpage_url')
def __getitem__(self,item: str):
return self.__getattribute__(item)
@classmethod
async def create_source(cls,ctx,search: str,loop,download=False):
loop = loop or asyncio.get_event_loop()
to_run = partial(ytdl.extract_info,url=search,download=download)
data = await loop.run_in_executor(None,to_run)
if 'entries' in data:
data = data['entries'][0]
await ctx.send(f'```ini\n[Added {data["title"]} to the Queue.]\n```')
if download:
source = ytdl.prepare_filename(data)
else:
return {'webpage_url': data['webpage_url'],'requester': ctx.author,'title': data['title']}
return cls(discord.FFmpegPCMAudio(source),data=data,requester=ctx.author)
@classmethod
async def regather_stream(cls,loop):
"""Used for preparing a stream,instead of downloading.
Since Youtube Streaming links expire."""
loop = loop or asyncio.get_event_loop()
requester = data['requester']
to_run = partial(ytdl.extract_info,url=data['webpage_url'],download=False)
data = await loop.run_in_executor(None,to_run)
return cls(discord.FFmpegPCMAudio(data['url']),requester=requester)
class MusicPlayer(commands.Cog):
__slots__ = ('bot','_guild','_channel','_cog','queue','next','current','np','volume')
def __init__(self,ctx):
self.bot = ctx.bot
self._guild = ctx.guild
self._channel = ctx.channel
self._cog = ctx.cog
self.queue = asyncio.Queue()
self.next = asyncio.Event()
self.np = None
self.volume = .5
self.current = None
ctx.bot.loop.create_task(self.player_loop())
async def player_loop(self):
"""Our main player loop."""
await self.bot.wait_until_ready()
while not self.bot.is_closed():
self.next.clear()
try:
source = await self.queue.get()
except asyncio.TimeoutError:
return self.destroy(self._guild)
if not isinstance(source,YTDLSource):
try:
source = await YTDLSource.regather_stream(source,loop=self.bot.loop)
except Exception as e:
await self._channel.send(f'There was an error processing your song.\n'
f'```css\n[{e}]\n```')
continue
source.volume = self.volume
self.current = source
self._guild.voice_client.play(source,after=lambda _: self.bot.loop.call_soon_threadsafe(self.next.set))
self.np = await self._channel.send(f'**Now Playing:** `{source.title}` requested by '
f'`{source.requester}`')
await self.next.wait()
source.cleanup()
self.current = None
try:
await self.np.delete()
except discord.HTTPException:
pass
def destroy(self,guild):
"""disconnect and cleanup the player."""
return self.bot.loop.create_task(self._cog.cleanup(guild))
class Music(commands.Cog):
"""Music related commands."""
__slots__ = ('bot','players')
def __init__(self,bot):
self.bot = bot
self.players = {}
async def cleanup(self,guild):
try:
await guild.voice_client.disconnect()
except AttributeError:
pass
try:
del self.players[guild.id]
except KeyError:
pass
async def __local_check(self,ctx):
"""A local check which applies to all commands in this cog."""
if not ctx.guild:
raise commands.noprivateMessage
return True
async def __error(self,error):
"""A local error handler for all errors arising from commands in this cog."""
if isinstance(error,commands.noprivateMessage):
try:
return await ctx.send('This command can not be used in Private Messages.')
except discord.HTTPException:
pass
elif isinstance(error,InvalidVoiceChannel):
await ctx.send('Error connecting to Voice Channel. '
'Please make sure you are in a valid channel or provide me with one')
print('Ignoring exception in command {}:'.format(ctx.command),file=sys.stderr)
traceback.print_exception(type(error),error,error.__traceback__,file=sys.stderr)
def get_player(self,ctx):
"""Retrieve the guild player,or generate one."""
try:
player = self.players[ctx.guild.id]
except KeyError:
player = MusicPlayer(ctx)
self.players[ctx.guild.id] = player
return player
@commands.command(name='connect',aliases=['join'])
async def connect_(self,ctx):
try:
channel = ctx.author.voice.channel
except AttributeError:
raise InvalidVoiceChannel('No channel to join.')
vc = ctx.voice_client
if vc:
if vc.channel.id == channel.id:
return
try:
await vc.move_to(channel)
except asyncio.TimeoutError:
raise VoiceConnectionError(f'Moving to channel: <{channel}> timed out.')
else:
try:
await channel.connect()
except asyncio.TimeoutError:
raise VoiceConnectionError(f'Connecting to channel: <{channel}> timed out.')
await ctx.send(f'Connected to: **{channel}**',)
@commands.command(name='play',aliases=['sing'])
async def play_(self,search: str):
await ctx.trigger_typing()
vc = ctx.voice_client
if not vc:
await ctx.invoke(self.connect_)
player = self.get_player(ctx)
source = await YTDLSource.create_source(ctx,search,loop=self.bot.loop,download=False)
await player.queue.put(source)
@commands.command(name='pause')
async def pause_(self,ctx):
"""Pause the currently playing song."""
vc = ctx.voice_client
if not vc or not vc.is_playing():
return await ctx.send('I am not currently playing anything!')
elif vc.is_paused():
return
vc.pause()
await ctx.send(f'**`{ctx.author}`**: Paused the song!')
@commands.command(name='resume')
async def resume_(self,ctx):
"""Resume the currently paused song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently playing anything!',)
elif not vc.is_paused():
return
vc.resume()
await ctx.send(f'**`{ctx.author}`**: Resumed the song!')
@commands.command(name='skip')
async def skip_(self,ctx):
"""Skip the song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently playing anything!')
if vc.is_paused():
pass
elif not vc.is_playing():
return
vc.stop()
await ctx.send(f'**`{ctx.author}`**: Skipped the song!')
@commands.command(name='queue',aliases=['q','playlist'])
async def queue_info(self,ctx):
"""Retrieve a basic queue of upcoming songs."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently connected to voice!')
player = self.get_player(ctx)
if player.queue.empty():
return await ctx.send('There are currently no more queued songs.')
upcoming = list(itertools.islice(player.queue._queue,5))
fmt = '\n'.join(f'**`{_["title"]}`**' for _ in upcoming)
embed = discord.Embed(title=f'Upcoming - Next {len(upcoming)}',description=fmt)
await ctx.send(embed=embed)
@commands.command(name='Now_playing',aliases=['np','currentsong','playing'])
async def Now_playing_(self,ctx):
"""display information about the currently playing song."""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently connected to voice!',)
player = self.get_player(ctx)
if not player.current:
return await ctx.send('I am not currently playing anything!')
try:
await player.np.delete()
except discord.HTTPException:
pass
player.np = await ctx.send(f'**Now Playing:** `{vc.source.title}` '
f'requested by `{vc.source.requester}`')
@commands.command(name='volume',aliases=['vol'])
async def change_volume(self,vol: float):
"""Change the player volume.
Parameters
------------
volume: float or int [required]
The volume to set the player to in percentage. This must be between 1 and 100.
"""
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently connected to voice!',)
if not 0 < vol < 101:
return await ctx.send('Please enter a value between 1 and 100.')
player = self.get_player(ctx)
if vc.source:
vc.source.volume = vol / 100
player.volume = vol / 100
await ctx.send(f'**`{ctx.author}`**: Set the volume to **{vol}%**')
@commands.command(name='stop',aliases=['leave'])
async def stop_(self,ctx):
vc = ctx.voice_client
if not vc or not vc.is_connected():
return await ctx.send('I am not currently playing anything!')
await self.cleanup(ctx.guild)
def setup(client):
client.add_cog(Music(client))
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)