From Harmless Giraffe, 4 Weeks ago, written in Plain Text.
  1. # -*- coding: utf-8 -*-
  2.  
  3. from discord.ext import commands
  4. import youtube_dl
  5. import discord
  6.  
  7. # Suppress noise about console usage from errors
  8. youtube_dl.utils.bug_reports_message = lambda: ''
  9.  
  10.  
  11. ytdl_format_options = {
  12.         'format': 'bestaudio/best',
  13.         'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
  14.         'restrictfilenames': True,
  15.         'noplaylist': True,
  16.         'nocheckcertificate': True,
  17.         'ignoreerrors': False,
  18.         'logtostderr': False,
  19.         'quiet': True,
  20.         'no_warnings': True,
  21.         'default_search': 'auto',
  22.         'source_address': '0.0.0.0' # bind to ipv4 since ipv6 addresses cause issues sometimes
  23. }
  24.  
  25. ffmpeg_options = {
  26.         'options': '-vn'
  27. }
  28.  
  29. ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
  30.  
  31.  
  32. class YTDLSource(discord.PCMVolumeTransformer):
  33.         def __init__(self, source, *, data, volume=0.5):
  34.                 super().__init__(source, volume)
  35.  
  36.                 self.data = data
  37.  
  38.                 self.title = data.get('title')
  39.                 self.url = data.get('url')
  40.  
  41.         @classmethod
  42.         async def from_url(cls, url, *, loop=None, stream=False):
  43.                 loop = loop or asyncio.get_event_loop()
  44.                 data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))
  45.  
  46.                 if 'entries' in data:
  47.                         # take first item from a playlist
  48.                         data = data['entries'][0]
  49.  
  50.                 filename = data['url'] if stream else ytdl.prepare_filename(data)
  51.                 return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
  52.  
  53. class Boombox(commands.Cog):
  54.         """ Music controls for Lexi """
  55.  
  56.         def __init__(self, bot):
  57.                 self.bot = bot
  58.  
  59.         @commands.command()
  60.         async def join(self, ctx, *, channel: discord.VoiceChannel):
  61.                 """Joins a voice channel"""
  62.  
  63.                 if ctx.voice_client is not None:
  64.                         return await ctx.voice_client.move_to(channel)
  65.  
  66.                 await channel.connect()
  67.  
  68.         @commands.command()
  69.         async def joinme(self, ctx):
  70.                 """Joins on the requesting user"""
  71.                 vchannel = ctx.author.voice.channel
  72.                 await vchannel.connect()
  73.  
  74.         @commands.command()
  75.         async def play(self, ctx, *, query):
  76.                 """Plays a file from the local filesystem"""
  77.  
  78.                 source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(query))
  79.                 ctx.voice_client.play(source, after=lambda e: print('Player error: %s' % e) if e else None)
  80.  
  81.                 await ctx.send('Now playing: {}'.format(query))
  82.  
  83.         @commands.command()
  84.         async def yt(self, ctx, *, url):
  85.                 """Plays from a url (almost anything youtube_dl supports)"""
  86.  
  87.                 async with ctx.typing():
  88.                         player = await YTDLSource.from_url(url, loop=self.bot.loop)
  89.                         ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
  90.  
  91.                 await ctx.send('Now playing: {}'.format(player.title))
  92.  
  93.         @commands.command()
  94.         async def stream(self, ctx, *, url):
  95.                 """Streams from a url (same as yt, but doesn't predownload)"""
  96.  
  97.                 async with ctx.typing():
  98.                         player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
  99.                         ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
  100.  
  101.                 await ctx.send('Now playing: {}'.format(player.title))
  102.  
  103.         @commands.command()
  104.         async def volume(self, ctx, volume: int):
  105.                 """Changes the player's volume"""
  106.  
  107.                 if ctx.voice_client is None:
  108.                         return await ctx.send("Not connected to a voice channel.")
  109.  
  110.                 ctx.voice_client.source.volume = volume / 100
  111.                 await ctx.send("Changed volume to {}%".format(volume))
  112.  
  113.         @commands.command()
  114.         async def stop(self, ctx):
  115.                 """Stops and disconnects the bot from voice"""
  116.  
  117.                 await ctx.voice_client.disconnect()
  118.  
  119.         @play.before_invoke
  120.         @yt.before_invoke
  121.         @stream.before_invoke
  122.         async def ensure_voice(self, ctx):
  123.                 if ctx.voice_client is None:
  124.                         if ctx.author.voice:
  125.                                 await ctx.author.voice.channel.connect()
  126.                         else:
  127.                                 await ctx.send("You are not connected to a voice channel.")
  128.                                 raise commands.CommandError("Author not connected to a voice channel.")
  129.                 elif ctx.voice_client.is_playing():
  130.                         ctx.voice_client.stop()
  131.  
  132.  
  133.         @commands.command(hidden=True)
  134.         @commands.is_owner()
  135.         async def bbtest(self, ctx):
  136.                 await ctx.send("bb loaded!")
  137.  
  138.  
  139. def setup(bot):
  140.         bot.add_cog(Boombox(bot))
  141.  
captcha