From 95c8644dc4dd2c6bd1ac9c3d2769cf207f7f7ee7 Mon Sep 17 00:00:00 2001 From: Denis Emrulov Date: Thu, 29 Nov 2018 16:28:22 +0000 Subject: [PATCH] Music file --- musci.py | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 musci.py diff --git a/musci.py b/musci.py new file mode 100644 index 0000000..5e2367c --- /dev/null +++ b/musci.py @@ -0,0 +1,227 @@ +import asyncio +import discord +from discord.voice_client import VoiceClient +from discord.ext import commands +if not discord.opus.is_loaded(): + # the 'opus' library here is opus.dll on windows + # or libopus.so on linux in the current directory + # you should replace this with the location the + # opus library is located in and with the proper filename. + # note that on windows this DLL is automatically provided for you + discord.opus.load_opus('opus') +"""I took this code from https://mega.nz/#!eopkmZrD!6vNS6xitffXDp41JcO4-kjV5gxqrTuHcirgZiN4Vj2M +but I had to rewrite the commands from discord.py 0.16 version to discord.py 1.0.0a version . """ +def __init__(self, bot): + self.bot = bot + +class VoiceEntry: + def __init__(self, message, player): + self.requester = message.author + self.channel = message.channel + self.player = player + + def __str__(self): + fmt = ' {0.title} uploaded by {0.uploader} and requested by {1.display_name}' + duration = self.player.duration + if duration: + fmt = fmt + ' [length: {0[0]}m {0[1]}s]'.format(divmod(duration, 60)) + return fmt.format(self.player, self.requester) + +class VoiceState: + def __init__(self, bot): + self.current = None + self.voice = None + self.bot = bot + self.play_next_song = asyncio.Event() + self.songs = asyncio.Queue() + self.skip_votes = set() # a set of user_ids that voted + self.audio_player = self.bot.loop.create_task(self.audio_player_task()) + + def is_playing(self): + if self.voice is None or self.current is None: + return False + + player = self.current.player + return not player.is_done() + + @property + def player(self): + return self.current.player + + def skip(self): + self.skip_votes.clear() + if self.is_playing(): + self.player.stop() + + def toggle_next(self): + self.bot.loop.call_soon_threadsafe(self.play_next_song.set) + + async def audio_player_task(self): + while True: + self.play_next_song.clear() + self.current = await self.songs.get() + await self.bot.send_message(self.current.channel, 'Now playing' + str(self.current)) + self.current.player.start() + await self.play_next_song.wait() +class Music: + """Voice related commands. + Works in multiple servers at once. + """ + def __init__(self, bot): + self.bot = bot + self.voice_states = {} + + def get_voice_state(self, server): + state = self.voice_states.get(server.id) + if state is None: + state = VoiceState(self.bot) + self.voice_states[server.id] = state + + return state + + async def create_voice_client(self, channel): + voice = await self.bot.VoiceChannel.connect(channel) + state = self.get_voice_state(channel.server) + state.voice = voice + + def __unload(self): + for state in self.voice_states.values(): + try: + state.audio_player.cancel() + if state.voice: + self.bot.loop.create_task(state.voice.disconnect()) + except: + pass + + @commands.command(pass_context=True, no_pm=True) + async def join(self, ctx, *, channel : discord.channel): + """Joins a voice channel.""" + try: + await self.create_voice_client(channel) + except discord.ClientException: + await self.ctx.send('Already in a voice channel...') + except discord.InvalidArgument: + await self.ctx.send('This is not a voice channel...') + else: + await self.ctx.send('Ready to play audio in **' + channel.name) + + @commands.command(pass_context=True, no_pm=True) + async def summon(self, ctx): + """Summons the bot to join your voice channel.""" + summoned_channel = ctx.message.author.voice.channel + if summoned_channel is None: + await self.ctx.send('Are you sure your in a channel?') + return False + + state = self.get_voice_state(ctx.message.guild) + if state.voice is None: + state.voice = await self.bot.VoiceChannel.connect(summoned_channel) + else: + await state.voice.move_to(summoned_channel) + + return True + + @commands.command(pass_context=True, no_pm=True) + async def play(self, ctx, song : str): + """Plays a song. + """ + state = self.get_voice_state(ctx.channel.guild) + opts = { + 'default_search': 'auto', + 'quiet': True, + } + + if state.voice is None: + success = await ctx.invoke(self.summon) + await self.bot.say("Loading the song please be patient..") + if not success: + return + + try: + player = await state.voice.create_ytdl_player(song, ytdl_options=opts, after=state.toggle_next) + except Exception as e: + fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```' + await self.bot.send_message(ctx.message.channel, fmt.format(type(e).__name__, e)) + else: + player.volume = 0.6 + entry = VoiceEntry(ctx.message, player) + await self.ctx.send('Enqueued ' + str(entry)) + await state.songs.put(entry) + + @commands.command(pass_context=True, no_pm=True) + async def volume(self, ctx, value : int): + """Sets the volume of the currently playing song.""" + + state = self.get_voice_state(ctx.message.server) + if state.is_playing(): + player = state.player + player.volume = value / 100 + await self.bot.say('Set the volume to {:.0%}'.format(player.volume)) + @commands.command(pass_context=True, no_pm=True) + async def resume(self, ctx): + """Resumes the currently played song.""" + state = self.get_voice_state(ctx.message.guild) + if state.is_playing(): + player = state.player + player.resume() + + @commands.command(pass_context=True, no_pm=True) + async def stop(self, ctx): + """Stops playing audio and leaves the voice channel. + This also clears the queue. + """ + server = ctx.message.guild + state = self.get_voice_state(server) + + if state.is_playing(): + player = state.player + player.stop() + + try: + state.audio_player.cancel() + del self.voice_states[server.id] + await state.voice.disconnect() + await self.ctx.send("Cleared the queue and disconnected from voice channel ") + except: + pass + + @commands.command(pass_context=True, no_pm=True) + async def skip(self, ctx): + """Vote to skip a song. The song requester can automatically skip. + 3 skip votes are needed for the song to be skipped. + """ + + state = self.get_voice_state(ctx.message.guild) + if not state.is_playing(): + await self.ctx.send('Not playing any music right now...') + return + + voter = ctx.message.author + if voter == state.current.requester: + await self.ctx.send('Requester requested skipping song...') + state.skip() + elif voter.id not in state.skip_votes: + state.skip_votes.add(voter.id) + total_votes = len(state.skip_votes) + if total_votes >= 3: + await self.ctx.send('Skip vote passed, skipping song...') + state.skip() + else: + await self.ctx.send('Skip vote added, currently at [{}/3]'.format(total_votes)) + else: + await self.ctx.send('You have already voted to skip this song.') + + @commands.command(pass_context=True, no_pm=True) + async def playing(self, ctx): + """Shows info about the currently played song.""" + + state = self.get_voice_state(ctx.message.guild) + if state.current is None: + await self.ctx.send('Not playing anything.') + else: + skip_count = len(state.skip_votes) + await self.ctx.send('Now playing {} [skips: {}/3]'.format(state.current, skip_count)) + +def setup(bot): + bot.add_cog(Music(bot)) + print('Music is loaded')