Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 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 better
- from discord import opus
- OPUS_LIBS = ['libopus-0.x86.dll', 'libopus-0.x64.dll', 'libopus-0.dll', 'libopus.so.0', 'libopus.0.dylib', 'opus']
- def load_opus_lib(opus_libs):
- if opus.is_loaded():
- return True
- for opus_lib in opus_libs:
- try:
- opus.load_opus(opus_lib)
- return
- except OSError:
- pass
- load_opus_lib(OPUS_LIBS)
- 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' # ipv6 addresses cause issues sometimes
- }
- ffmpegopts = {
- 'before_options': '-nostdin',
- 'options': '-vn'
- }
- ytdl = YoutubeDL(ytdlopts)
- class VoiceConnectionError(commands.CommandError):
- """Custom Exception class for connection errors."""
- class InvalidVoiceChannel(VoiceConnectionError):
- """Exception for cases of invalid Voice Channels."""
- 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')
- self.thumbnail = data.get('thumbnail')
- # YTDL info dicts (data) have other useful information you might want
- # https://github.com/rg3/youtube-dl/blob/master/README.md
- def __getitem__(self, item: str):
- """Allows us to access attributes similar to a dict.
- This is only useful when you are NOT downloading.
- """
- 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:
- # take first item from a playlist
- data = data['entries'][0]
- embedi = discord.Embed(color=0x00D3FF, description=f"<:emoji_7:582981479232176139> **|** A música **{data['title']}** foi adicionada á fila de reprodução por **{better.bold(ctx.author.display_name)}**!\n``` ```")
- embedi.set_image(url=data['thumbnails'][0]['url'])
- await ctx.send(embed=embedi)
- 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, data, *, 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']), data=data, requester=requester)
- class MusicPlayer:
- """A class which is assigned to each guild using the bot for Music.
- This class implements a queue and loop, which allows for different guilds to listen to different playlists
- simultaneously.
- When the bot disconnects from the Voice it's instance will be destroyed.
- """
- __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 # Now playing message
- 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:
- # Wait for the next song. If we timeout cancel the player and disconnect...
- async with timeout(300): # 5 minutes...
- source = await self.queue.get()
- except asyncio.TimeoutError:
- return self.destroy(self._guild)
- if not isinstance(source, YTDLSource):
- # Source was probably a stream (not downloaded)
- # So we should regather to prevent stream expiration
- try:
- source = await YTDLSource.regather_stream(source, loop=self.bot.loop)
- except Exception as e:
- await self._channel.send(f'<:emoji_2:582981325607141407> **|** Não foi possível processar sua música!\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(embed=discord.Embed(color=0x00D3FF, description=f'<:emoji_18:582981824817659925> **|** Tocando agora: **{source.title}**\nAdicionada á playlist, por: '
- f'**{source.requester}**\n``` ```'))
- await self.next.wait()
- # Make sure the FFmpeg process is cleaned up.
- source.cleanup()
- self.current = None
- try:
- # We are no longer playing this song...
- 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, ctx, error):
- """A local error handler for all errors arising from commands in this cog."""
- if isinstance(error, commands.NoPrivateMessage):
- try:
- return await ctx.send('<:emoji_2:582981325607141407> **|** Ooops... este comando não pode ser usado em minha ``DM``')
- except discord.HTTPException:
- pass
- elif isinstance(error, InvalidVoiceChannel):
- await ctx.send('<:emoji_2:582981325607141407> **|** Eu não pude conectar-me á este canal!'
- 'Por favor, verifique se tenho permissão para entrar neste canal e tente novamente!')
- 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(aliases=['yt', 'tocar', 'musica', 'video'])
- async def play(self, ctx, *, string: str):
- try:
- erro1 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Não consegui me conectar ao canal!")
- erro2 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Você precisa estar em um canal de voz para usar este comando!")
- if ctx.author.voice:
- try: vc = ctx.voice_client if ctx.voice_client else (await ctx.author.voice.channel.connect())
- except:
- return await ctx.send(embed=erro1)
- else:
- return await ctx.send(embed=erro2)
- if not string: return await self.reproduzir(ctx)
- source = await YTDLSource.create_source(ctx, string, loop=self.bot.loop, download=True)
- await (self.get_player(ctx).queue.put(source))
- except Exception as e:
- print(f"{e}")
- @commands.command(aliases=['pmusic', 'pause'])
- async def pausar(self, ctx):
- voice = ctx.voice_client
- erro = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Você precisa estar em um canal de voz para usar este comando!")
- if not voice:
- return await ctx.send(embed=erro)
- await ctx.send(embed=discord.Embed(color=0x00D3FF, description="<:emoji_48:584767255607509049> **|** Á música foi pausada/despausada!"))
- return (voice.pause, voice.resume)[voice.is_paused()]()
- @commands.command(aliases=['skip', 'next'])
- async def pular(self, ctx):
- voice = ctx.voice_client
- erro = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Você precisa estar em um canal de voz para usar este comando!")
- erro1 = discord.Embed(color=0x00D3FF, description=f"<:emoji_50:584818472689598485> **|** A música foi pulada por {better.bold(ctx.author.display_name)}.")
- if not voice:
- return await ctx.send(embed=erro)
- if voice.is_playing():
- voice.stop()
- return await ctx.send(embed=erro1)
- return self
- @commands.command(name='volume', aliases=['vol'])
- async def volume(self, ctx, *, 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.
- """
- erro1 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Eu não estou conectado á nenhum canal de voz!")
- erro2 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Por favor selecione um número entre 1 e 100!")
- volu = discord.Embed(color=0x00D3FF, description=f"<:emoji_49:584767282811633764> **|** **`{ctx.author}`** Alterou o volume para: **{vol}%**")
- vc = ctx.voice_client
- if not vc or not vc.is_connected():
- return await ctx.send(embed=erro1)
- if not 0 < vol < 101:
- return await ctx.send(embed=erro2)
- player = self.get_player(ctx)
- if vc.source:
- vc.source.volume = vol / 100
- player.volume = vol / 100
- await ctx.send(embed=volu)
- @commands.command(name='parar')
- async def stop_(self, ctx):
- """Stop the currently playing song and destroy the player.
- !Warning!
- This will destroy the player assigned to your guild, also deleting any queued songs and settings.
- """
- erro = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Eu não estou tocando nada!")
- vc = ctx.voice_client
- if not vc or not vc.is_connected():
- return await ctx.send(embed=erro, delete_after=15)
- await self.cleanup(ctx.guild)
- def setup(client):
- client.add_cog(Music(client))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement