Advertisement
Guest User

Untitled

a guest
Jun 16th, 2019
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.06 KB | None | 0 0
  1. import discord
  2. from discord.ext import commands
  3.  
  4. import asyncio
  5. import itertools
  6. import sys
  7. import traceback
  8. from async_timeout import timeout
  9. from functools import partial
  10. from youtube_dl import YoutubeDL
  11.  
  12. import better
  13.  
  14. from discord import opus
  15. OPUS_LIBS = ['libopus-0.x86.dll', 'libopus-0.x64.dll', 'libopus-0.dll', 'libopus.so.0', 'libopus.0.dylib', 'opus']
  16.  
  17. def load_opus_lib(opus_libs):
  18. if opus.is_loaded():
  19. return True
  20.  
  21. for opus_lib in opus_libs:
  22. try:
  23. opus.load_opus(opus_lib)
  24. return
  25. except OSError:
  26. pass
  27. load_opus_lib(OPUS_LIBS)
  28.  
  29. ytdlopts = {
  30. 'format': 'bestaudio/best',
  31. 'outtmpl': 'downloads/%(extractor)s-%(id)s-%(title)s.%(ext)s',
  32. 'restrictfilenames': True,
  33. 'noplaylist': True,
  34. 'nocheckcertificate': True,
  35. 'ignoreerrors': False,
  36. 'logtostderr': False,
  37. 'quiet': True,
  38. 'no_warnings': True,
  39. 'default_search': 'auto',
  40. 'source_address': '0.0.0.0' # ipv6 addresses cause issues sometimes
  41. }
  42.  
  43. ffmpegopts = {
  44. 'before_options': '-nostdin',
  45. 'options': '-vn'
  46. }
  47.  
  48. ytdl = YoutubeDL(ytdlopts)
  49.  
  50.  
  51. class VoiceConnectionError(commands.CommandError):
  52. """Custom Exception class for connection errors."""
  53.  
  54.  
  55. class InvalidVoiceChannel(VoiceConnectionError):
  56. """Exception for cases of invalid Voice Channels."""
  57.  
  58.  
  59. class YTDLSource(discord.PCMVolumeTransformer):
  60.  
  61. def __init__(self, source, *, data, requester):
  62. super().__init__(source)
  63. self.requester = requester
  64.  
  65. self.title = data.get('title')
  66. self.web_url = data.get('webpage_url')
  67. self.thumbnail = data.get('thumbnail')
  68.  
  69. # YTDL info dicts (data) have other useful information you might want
  70. # https://github.com/rg3/youtube-dl/blob/master/README.md
  71.  
  72. def __getitem__(self, item: str):
  73. """Allows us to access attributes similar to a dict.
  74.  
  75. This is only useful when you are NOT downloading.
  76. """
  77. return self.__getattribute__(item)
  78.  
  79. @classmethod
  80. async def create_source(cls, ctx, search: str, *, loop, download=False):
  81. loop = loop or asyncio.get_event_loop()
  82.  
  83. to_run = partial(ytdl.extract_info, url=search, download=download)
  84. data = await loop.run_in_executor(None, to_run)
  85.  
  86. if 'entries' in data:
  87. # take first item from a playlist
  88. data = data['entries'][0]
  89.  
  90. 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``` ```")
  91. embedi.set_image(url=data['thumbnails'][0]['url'])
  92. await ctx.send(embed=embedi)
  93.  
  94. if download:
  95. source = ytdl.prepare_filename(data)
  96. else:
  97. return {'webpage_url': data['webpage_url'], 'requester': ctx.author, 'title': data['title']}
  98.  
  99. return cls(discord.FFmpegPCMAudio(source), data=data, requester=ctx.author)
  100.  
  101.  
  102. @classmethod
  103. async def regather_stream(cls, data, *, loop):
  104. """Used for preparing a stream, instead of downloading.
  105.  
  106. Since Youtube Streaming links expire."""
  107. loop = loop or asyncio.get_event_loop()
  108. requester = data['requester']
  109.  
  110. to_run = partial(ytdl.extract_info, url=data['webpage_url'], download=False)
  111. data = await loop.run_in_executor(None, to_run)
  112.  
  113. return cls(discord.FFmpegPCMAudio(data['url']), data=data, requester=requester)
  114.  
  115.  
  116. class MusicPlayer:
  117. """A class which is assigned to each guild using the bot for Music.
  118.  
  119. This class implements a queue and loop, which allows for different guilds to listen to different playlists
  120. simultaneously.
  121.  
  122. When the bot disconnects from the Voice it's instance will be destroyed.
  123. """
  124.  
  125. __slots__ = ('bot', '_guild', '_channel', '_cog', 'queue', 'next', 'current', 'np', 'volume')
  126.  
  127. def __init__(self, ctx):
  128. self.bot = ctx.bot
  129. self._guild = ctx.guild
  130. self._channel = ctx.channel
  131. self._cog = ctx.cog
  132.  
  133. self.queue = asyncio.Queue()
  134. self.next = asyncio.Event()
  135.  
  136. self.np = None # Now playing message
  137. self.volume = .5
  138. self.current = None
  139.  
  140. ctx.bot.loop.create_task(self.player_loop())
  141.  
  142. async def player_loop(self):
  143. """Our main player loop."""
  144. await self.bot.wait_until_ready()
  145.  
  146. while not self.bot.is_closed():
  147. self.next.clear()
  148.  
  149. try:
  150. # Wait for the next song. If we timeout cancel the player and disconnect...
  151. async with timeout(300): # 5 minutes...
  152. source = await self.queue.get()
  153. except asyncio.TimeoutError:
  154. return self.destroy(self._guild)
  155.  
  156. if not isinstance(source, YTDLSource):
  157. # Source was probably a stream (not downloaded)
  158. # So we should regather to prevent stream expiration
  159. try:
  160. source = await YTDLSource.regather_stream(source, loop=self.bot.loop)
  161. except Exception as e:
  162. await self._channel.send(f'<:emoji_2:582981325607141407> **|** Não foi possível processar sua música!\n'
  163. f'```css\n[{e}]\n```')
  164. continue
  165.  
  166. source.volume = self.volume
  167. self.current = source
  168.  
  169. self._guild.voice_client.play(source, after=lambda _: self.bot.loop.call_soon_threadsafe(self.next.set))
  170. self.np = await self._channel.send(embed=discord.Embed(color=0x00D3FF, description=f'<:emoji_18:582981824817659925> **|** Tocando agora: **{source.title}**\nAdicionada á playlist, por: '
  171. f'**{source.requester}**\n``` ```'))
  172. await self.next.wait()
  173.  
  174. # Make sure the FFmpeg process is cleaned up.
  175. source.cleanup()
  176. self.current = None
  177.  
  178. try:
  179. # We are no longer playing this song...
  180. await self.np.delete()
  181. except discord.HTTPException:
  182. pass
  183.  
  184. def destroy(self, guild):
  185. """Disconnect and cleanup the player."""
  186. return self.bot.loop.create_task(self._cog.cleanup(guild))
  187.  
  188.  
  189. class Music(commands.Cog):
  190. """Music related commands."""
  191.  
  192. __slots__ = ('bot', 'players')
  193.  
  194. def __init__(self, bot):
  195. self.bot = bot
  196. self.players = {}
  197.  
  198. async def cleanup(self, guild):
  199. try:
  200. await guild.voice_client.disconnect()
  201. except AttributeError:
  202. pass
  203.  
  204. try:
  205. del self.players[guild.id]
  206. except KeyError:
  207. pass
  208.  
  209. async def __local_check(self, ctx):
  210. """A local check which applies to all commands in this cog."""
  211. if not ctx.guild:
  212. raise commands.NoPrivateMessage
  213. return True
  214.  
  215. async def __error(self, ctx, error):
  216. """A local error handler for all errors arising from commands in this cog."""
  217. if isinstance(error, commands.NoPrivateMessage):
  218. try:
  219. return await ctx.send('<:emoji_2:582981325607141407> **|** Ooops... este comando não pode ser usado em minha ``DM``')
  220. except discord.HTTPException:
  221. pass
  222. elif isinstance(error, InvalidVoiceChannel):
  223. await ctx.send('<:emoji_2:582981325607141407> **|** Eu não pude conectar-me á este canal!'
  224. 'Por favor, verifique se tenho permissão para entrar neste canal e tente novamente!')
  225.  
  226. print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
  227. traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
  228.  
  229. def get_player(self, ctx):
  230. """Retrieve the guild player, or generate one."""
  231. try:
  232. player = self.players[ctx.guild.id]
  233. except KeyError:
  234. player = MusicPlayer(ctx)
  235. self.players[ctx.guild.id] = player
  236.  
  237. return player
  238.  
  239. @commands.command(aliases=['yt', 'tocar', 'musica', 'video'])
  240. async def play(self, ctx, *, string: str):
  241. try:
  242. erro1 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Não consegui me conectar ao canal!")
  243. erro2 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Você precisa estar em um canal de voz para usar este comando!")
  244.  
  245. if ctx.author.voice:
  246. try: vc = ctx.voice_client if ctx.voice_client else (await ctx.author.voice.channel.connect())
  247. except:
  248. return await ctx.send(embed=erro1)
  249. else:
  250. return await ctx.send(embed=erro2)
  251.  
  252. if not string: return await self.reproduzir(ctx)
  253. source = await YTDLSource.create_source(ctx, string, loop=self.bot.loop, download=True)
  254. await (self.get_player(ctx).queue.put(source))
  255. except Exception as e:
  256. print(f"{e}")
  257.  
  258. @commands.command(aliases=['pmusic', 'pause'])
  259. async def pausar(self, ctx):
  260. voice = ctx.voice_client
  261. erro = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Você precisa estar em um canal de voz para usar este comando!")
  262. if not voice:
  263. return await ctx.send(embed=erro)
  264. await ctx.send(embed=discord.Embed(color=0x00D3FF, description="<:emoji_48:584767255607509049> **|** Á música foi pausada/despausada!"))
  265. return (voice.pause, voice.resume)[voice.is_paused()]()
  266.  
  267. @commands.command(aliases=['skip', 'next'])
  268. async def pular(self, ctx):
  269. voice = ctx.voice_client
  270. erro = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Você precisa estar em um canal de voz para usar este comando!")
  271. erro1 = discord.Embed(color=0x00D3FF, description=f"<:emoji_50:584818472689598485> **|** A música foi pulada por {better.bold(ctx.author.display_name)}.")
  272. if not voice:
  273. return await ctx.send(embed=erro)
  274.  
  275. if voice.is_playing():
  276. voice.stop()
  277. return await ctx.send(embed=erro1)
  278. return self
  279.  
  280.  
  281.  
  282. @commands.command(name='volume', aliases=['vol'])
  283. async def volume(self, ctx, *, vol: float):
  284. """Change the player volume.
  285.  
  286. Parameters
  287. ------------
  288. volume: float or int [Required]
  289. The volume to set the player to in percentage. This must be between 1 and 100.
  290. """
  291. erro1 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Eu não estou conectado á nenhum canal de voz!")
  292. erro2 = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Por favor selecione um número entre 1 e 100!")
  293. volu = discord.Embed(color=0x00D3FF, description=f"<:emoji_49:584767282811633764> **|** **`{ctx.author}`** Alterou o volume para: **{vol}%**")
  294.  
  295. vc = ctx.voice_client
  296.  
  297. if not vc or not vc.is_connected():
  298. return await ctx.send(embed=erro1)
  299.  
  300. if not 0 < vol < 101:
  301. return await ctx.send(embed=erro2)
  302.  
  303. player = self.get_player(ctx)
  304.  
  305. if vc.source:
  306. vc.source.volume = vol / 100
  307.  
  308. player.volume = vol / 100
  309. await ctx.send(embed=volu)
  310.  
  311. @commands.command(name='parar')
  312. async def stop_(self, ctx):
  313. """Stop the currently playing song and destroy the player.
  314.  
  315. !Warning!
  316. This will destroy the player assigned to your guild, also deleting any queued songs and settings.
  317. """
  318. erro = discord.Embed(color=0x00D3FF, description="<:emoji_2:582981325607141407> **|** Eu não estou tocando nada!")
  319. vc = ctx.voice_client
  320.  
  321. if not vc or not vc.is_connected():
  322. return await ctx.send(embed=erro, delete_after=15)
  323.  
  324. await self.cleanup(ctx.guild)
  325.  
  326.  
  327. def setup(client):
  328. client.add_cog(Music(client))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement