diff --git a/Music-bot/bot.py b/Music-bot/bot.py
index 8ee3768f126e1a29b2f7271502fa6077998abb00..b8dff791aff32dde93574ff3fe75989a2f27e43c 100644
--- a/Music-bot/bot.py
+++ b/Music-bot/bot.py
@@ -1,78 +1,42 @@
-import traceback
-import sys
+import configparser
 import datetime
-import discord
+import sys
+import traceback
 
+import discord
 from discord.ext import commands
 
 
 def get_prefix(bot, message):
-    prefixes = ['?']
-    return commands.when_mentioned_or(*prefixes)(bot, message)
-
+	prefixes = ['?']
+	return commands.when_mentioned_or(*prefixes)(bot, message)
 
-initial_extensions = ['cogs.meta', 'cogs.mod', 'cogs.misc', 'cogs.music', 'cogs.error_handler']
 
-help_attrs = dict(hidden=True)
-bot = commands.Bot(command_prefix=get_prefix, help_attrs=help_attrs, description="This is a multi-purpose bot")
+initial_extensions = ['cogs.meta', 'cogs.owner', 'cogs.music', 'cogs.error_handler']
 
+bot = commands.Bot(command_prefix=get_prefix, description="This is a multi-purpose bot")
 
-@bot.event
-async def on_ready():
-    bot.uptime = datetime.datetime.utcnow()
-    print(f'\n\nLogged in as: {bot.user.name} - {bot.user.id}\nAPI Version: {discord.__version__}\n')
+config = configparser.ConfigParser()
+config.read("tokens.ini")
+dev_token = config.get('token', 'dev')
+production_token = config.get('token', 'prod')
 
 
 @bot.event
-async def on_message(message):
-    # mod = bot.get_cog('Mod')
-
-    #if mod is not None and not cogs.utils.checks.is_owner_check(message):
-        # check if the user is bot banned
-    #    if message.author.id in mod.config.get('plonks', []):
-    #        return
-
-        # check if the channel is ignored
-        # but first, resolve their permissions
-
-    #    perms = message.channel.permissions_for(message.author)
-    #    bypass_ignore = perms.manage_roles
-
-        # if we don't have manage roles then we should
-        # check if it's the owner of the bot or they have Bot Admin role.
-
-    #    if not bypass_ignore:
-    #        if not message.channel.is_private:
-    #            bypass_ignore = discord.utils.get(message.author.roles, name='Bot Admin') is not None
-
-        # now we can finally realise if we can actually bypass the ignore.
-
-     #   if not bypass_ignore:
-     #       if message.channel.id in mod.config.get('ignored', []):
-     #           return
+async def on_ready():
+	bot.uptime = datetime.datetime.utcnow()
+	print(f'\n\nLogged in as: {bot.user.name} - {bot.user.id}\nAPI Version: {discord.__version__}\n')
 
-    # if someone private messages us with something that looks like a URL then
-    # we should try to see if it's an invite to a discord server and join it if so.
-    if message.channel.is_private and message.content.startswith('http'):
-        try:
-            invite = await bot.get_invite(message.content)
-            await bot.accept_invite(invite)
-            await bot.send_message(message.channel, 'Joined the server.')
-        except:
-            # if an error occurs at this point then ignore it and move on.
-            pass
-        finally:
-            return
-    await bot.process_commands(message)
 
 if __name__ == '__main__':
-    if any('debug' in arg.lower() for arg in sys.argv):
-        bot.command_prefix = '$'
-    for extension in initial_extensions:
-        try:
-            bot.load_extension(extension)
-        except:
-            print(f'Failed to load extension {extension}.', file=sys.stderr)
-            traceback.print_exc()
-
-bot.run('')
+	if any('debug' in arg.lower() for arg in sys.argv):
+		bot.command_prefix = '$'
+	for extension in initial_extensions:
+		try:
+			bot.load_extension(extension)
+		except:
+			print(f'Failed to load extension {extension}.', file=sys.stderr)
+			traceback.print_exc()
+
+bot.run(dev_token)
+#bot.run(production_token)
diff --git a/Music-bot/cogs/error_handler.py b/Music-bot/cogs/error_handler.py
index 0b33bc15e17a4b7e6a88aad00fecdbc450632393..9e2065c5d857628b6cc196e693037af48a3151d0 100644
--- a/Music-bot/cogs/error_handler.py
+++ b/Music-bot/cogs/error_handler.py
@@ -35,18 +35,21 @@ class CommandErrorHandler:
 				return await ctx.author.send(f'{ctx.command} can not be used in Private Messages.')
 			except:
 				pass
+		elif isinstance(error, commands.NotOwner):
+			try:
+				return await ctx.send(f"The previously executed command is reserved for the bot owner only, "
+									  f"which you're not. \n **Permission denied.**")
+			except:
+				pass
+
+		elif isinstance(error, commands.CheckFailure):
+			pass
 
 		# For this error example we check to see where it came from...
 		elif isinstance(error, commands.BadArgument):
 			if ctx.command.qualified_name == 'tag list':  # Check if the command being invoked is 'tag list'
 				return await ctx.send('I could not find that member. Please try again.')
 
-		elif isinstance(error, commands.CheckFailure):
-			try:
-				return await ctx.send(f'{ctx.command} is reserved for the bot owner only. Sorry, but your permissions are not enough.')
-			except:
-				pass
-
 		# All other Errors not returned come here... And we can just print the default TraceBack.
 		print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
 		traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
diff --git a/Music-bot/cogs/meta.py b/Music-bot/cogs/meta.py
index e44c297a07425c5fdc10aaa54976b582c4e71002..9f7de8ab445fb8fd4b4ab38b3585ec2a0a3fcd3e 100644
--- a/Music-bot/cogs/meta.py
+++ b/Music-bot/cogs/meta.py
@@ -1,65 +1,104 @@
 import discord
 import datetime
+import os
 from discord.ext import commands
-from collections import Counter
 
 
 class Meta:
-	"""Commands for utilities related to Discord or the Bot itself."""
-
-	def __init__(self, bot):
-		self.bot = bot
-
-	def get_bot_uptime(self):
-		now = datetime.datetime.utcnow()
-		delta = now - self.bot.uptime
-		hours, remainder = divmod(int(delta.total_seconds()), 3600)
-		minutes, seconds = divmod(remainder, 60)
-		days, hours = divmod(hours, 24)
-		if days:
-			fmt = '{d} days, {h} hours, {m} minutes, and {s} seconds'
-		else:
-			fmt = '{h} hours, {m} minutes, and {s} seconds'
-		return fmt.format(d=days, h=hours, m=minutes, s=seconds)
-
-	@commands.command()
-	async def uptime(self):
-		"""Tells you how long the bot has been up for."""
-		await self.bot.say('Uptime: **{}**'.format(self.get_bot_uptime()))
-
-	@commands.command(name='quit', hidden=True)
-	@commands.is_owner()
-	async def _quit(self):
-		"""Quits the bot."""
-		await self.bot.logout()
-
-	@commands.command(name="about")
-	async def about_me(self):
-		"""Tells you information about the bot itself."""
-		result = ['**About Me:**']
-		result.append('- Author: samip537 (Discord ID: 157970669261422592, Github: samip5)')
-		result.append('- Bot ID: {} (Discord ID: {})'.format(self.bot.user.name, self.bot.user.id))
-		result.append('- Created on July 3rd, 2018')
-		result.append('- Library: discord.py (Python)')
-		result.append('- Running proudly on Kapsi.fi')
-		# changes = os.popen(
-		#	r'git show -s HEAD~3..HEAD --format="[%h](https://github.com/samip5/Discord-Bots/commit/%H) %s (%cr)"').read().strip()
-		# result.append('- Changes: {}'.format(changes))
-		result.append('- Uptime: {}'.format(self.get_bot_uptime()))
-		result.append('- Servers: {}'.format(len(self.bot.servers)))
-		# stats
-		total_members = sum(len(s.members) for s in self.bot.servers)
-		total_online = sum(1 for m in self.bot.get_all_members() if m.status != discord.Status.offline)
-		unique_members = set(self.bot.get_all_members())
-		unique_online = sum(1 for m in unique_members if m.status != discord.Status.offline)
-		channel_types = Counter(c.type for c in self.bot.get_all_channels())
-		voice = channel_types[discord.ChannelType.voice]
-		text = channel_types[discord.ChannelType.text]
-		result.append('- Total Members: {} ({} online)'.format(total_members, total_online))
-		result.append('- Unique Members: {} ({} online)'.format(len(unique_members), unique_online))
-		result.append('- {} text channels, {} voice channels'.format(text, voice))
-		await self.bot.say('\n'.join(result))
+    """Commands for utilities related to Discord or the Bot itself."""
+
+    def __init__(self, bot):
+        self.bot = bot
+
+    def get_bot_uptime(self):
+        now = datetime.datetime.utcnow()
+        delta = now - self.bot.uptime
+        hours, remainder = divmod(int(delta.total_seconds()), 3600)
+        minutes, seconds = divmod(remainder, 60)
+        days, hours = divmod(hours, 24)
+        if days:
+            fmt = '{d} days, {h} hours, {m} minutes, and {s} seconds'
+        else:
+            fmt = '{h} hours, {m} minutes, and {s} seconds'
+        return fmt.format(d=days, h=hours, m=minutes, s=seconds)
+
+    def get_system_uptime(self):
+        if os.name == 'nt':
+            return "Running on Windows, unable to figure out."
+
+        try:
+            f = open("/proc/uptime")
+            contents = f.read().split()
+            f.close()
+        except:
+            return "Cannot open uptime file: /proc/uptime"
+
+        total_seconds = float(contents[0])
+
+        # Helper vars:
+        MINUTE = 60
+        HOUR = MINUTE * 60
+        DAY = HOUR * 24
+
+        # Get the days, hours, etc:
+        days = int(total_seconds / DAY)
+        hours = int((total_seconds % DAY) / HOUR)
+        minutes = int((total_seconds % HOUR) / MINUTE)
+        seconds = int(total_seconds % MINUTE)
+
+        # Build up the pretty string (like this: "N days, N hours, N minutes, N seconds")
+        string = ""
+        if days > 0:
+            string += str(days) + " " + (days == 1 and "day" or "days") + ", "
+        if len(string) > 0 or hours > 0:
+            string += str(hours) + " " + (hours == 1 and "hour" or "hours") + ", "
+        if len(string) > 0 or minutes > 0:
+            string += str(minutes) + " " + (minutes == 1 and "minute" or "minutes") + ", "
+        string += str(seconds) + " " + (seconds == 1 and "second" or "seconds")
+
+        return string
+
+    @commands.command()
+    async def system_uptime(self, ctx):
+        """Tells you how long the system has been up for."""
+        await ctx.send('System uptime: **{}**'.format(self.get_system_uptime()))
+
+    @commands.command()
+    async def uptime(self, ctx):
+        """Tells you how long the bot has been up for."""
+        await ctx.send('Uptime: **{}**'.format(self.get_bot_uptime()))
+
+    @commands.command(name='quit', hidden=True)
+    @commands.is_owner()
+    async def _quit(self, ctx):
+        """Quits the bot."""
+        await self.bot.logout()
+
+    @commands.command(name="about")
+    async def about_me(self, ctx):
+        """Tells you information about the bot itself."""
+        result = ['**About Me:**']
+        result.append('- Author: samip537 (Discord ID: 157970669261422592, Github: samip5)')
+        result.append(f'- Bot ID: {self.bot.user.name} (Discord ID: {self.bot.user.id})')
+        result.append('- Created on July 3rd, 2018')
+        result.append('- Library: discord.py (Python)')
+        result.append('- Running proudly on Google Cloud Platform')
+        result.append("- Source: https://0xacab.org/samip537/discord-bots-private (Private)")
+        # changes = os.popen(
+        #	r'git show -s HEAD~3..HEAD --format="[%h](https://github.com/samip5/Discord-Bots/commit/%H) %s (%cr)"').read().strip()
+        # result.append('- Changes: {}'.format(changes))
+        result.append('- Bot uptime: {}'.format(self.get_bot_uptime()))
+        result.append('- System uptime: {}'.format(self.get_system_uptime()))
+        result.append('- Servers: {}'.format(len(self.bot.guilds)))
+        # stats
+        total_members = sum(len(s.members) for s in self.bot.guilds)
+        total_online = sum(1 for m in self.bot.get_all_members() if m.status != discord.Status.offline)
+        unique_members = set(self.bot.get_all_members())
+        unique_online = sum(1 for m in unique_members if m.status != discord.Status.offline)
+        result.append('- Total Members: {} ({} online)'.format(total_members, total_online))
+        result.append('- Unique Members: {} ({} online)'.format(len(unique_members), unique_online))
+        await ctx.send('\n'.join(result))
 
 
 def setup(bot):
-	bot.add_cog(Meta(bot))
+    bot.add_cog(Meta(bot))
diff --git a/Music-bot/cogs/misc.py b/Music-bot/cogs/misc.py
index 0f806191ce76d783a1bd3e2decc1a366c2a1d6ff..23bbbdf9e572a5a91bd7c0d461149cd5f5acfb0e 100644
--- a/Music-bot/cogs/misc.py
+++ b/Music-bot/cogs/misc.py
@@ -6,14 +6,14 @@ class Misc:
 	def __init__(self, bot):
 		self.bot = bot
 
-	@commands.command(name='top_role', aliases=['toprole'], pass_context=True)
+	@commands.command(name='top_role', aliases=['toprole'], pass_context=True, hidden=False)
 	async def show_toprole(self, ctx, *, member: discord.Member = None):
 		"""Simple command which shows the members Top Role."""
 
 		if member is None:
 			member = ctx.message.author
 
-		await self.bot.say(f'The top role for {member.display_name} is {member.top_role.name}')
+		await ctx.send(f'The top role for {member.display_name} is {member.top_role.name}')
 
 
 def setup(bot):
diff --git a/Music-bot/cogs/music.py b/Music-bot/cogs/music.py
index 3fbc47c1f37b68ebd3bbf40f5bb1dc71d5b8f6d4..efa0d5c69c2c2212db52e5c911a3b1ee5ff8f1da 100644
--- a/Music-bot/cogs/music.py
+++ b/Music-bot/cogs/music.py
@@ -1,248 +1,433 @@
-import discord
+"""
+Please understand Music bots are complex, and that even this basic example can be daunting to a beginner.
+For this reason it's highly advised you familiarize yourself with discord.py, python and asyncio, BEFORE
+you attempt to write a music bot.
+This example makes use of: Python 3.6
+For a more basic voice example please read:
+    https://github.com/Rapptz/discord.py/blob/rewrite/examples/basic_voice.py
+This is a very basic playlist example, which allows per guild playback of unique queues.
+The commands implement very basic logic for basic usage. But allow for expansion. It would be advisable to implement
+your own permissions and usage logic for commands.
+e.g You might like to implement a vote before skipping the song or only allow admins to stop the player.
+Music bots require lots of work, and tuning. Goodluck.
+If you find any bugs feel free to ping me on discord. @Eviee#0666
+"""
 import asyncio
+import itertools
+import sys
+import traceback
+from functools import partial
+
+import discord
+import youtube_dl
+from async_timeout import timeout
 from discord.ext import commands
-from discord import Server
-
-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')
-
-
-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()
+
+from .utils import checks
+
+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 = youtube_dl.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')
+
+		# 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]
+
+		await ctx.send(f'```ini\n[Added {data["title"]} to the Queue.]\n```', delete_after=15)
+
+		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 = 0.1
+		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'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()
+
+			# 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:
-    """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.join_voice_channel(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.bot.say('Already in a voice channel...')
-        except discord.InvalidArgument:
-            await self.bot.say('This is not a voice channel...')
-        else:
-            await self.bot.say('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.bot.say('You are not in a voice channel.')
-            return False
-
-        state = self.get_voice_state(ctx.message.server)
-        if state.voice is None:
-            state.voice = await self.bot.join_voice_channel(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.
-
-        If there is a song currently in the queue, then it is
-        queued until the next song is done playing.
-
-        This command automatically searches as well from YouTube.
-        The list of supported sites can be found here:
-        https://rg3.github.io/youtube-dl/supportedsites.html
-        """
-        state = self.get_voice_state(ctx.message.server)
-        opts = {
-            'default_search': 'auto',
-            'quiet': True,
-        }
-
-        if state.voice is None:
-            success = await ctx.invoke(self.summon)
-            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.1
-            entry = VoiceEntry(ctx.message, player)
-            await self.bot.say('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 pause(self, ctx):
-        """Pauses the currently played song."""
-        state = self.get_voice_state(ctx.message.server)
-        if state.is_playing():
-            player = state.player
-            player.pause()
-
-    @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.server)
-        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.server
-        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()
-        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.server)
-        if not state.is_playing():
-            await self.bot.say('Not playing any music right now...')
-            return
-
-        voter = ctx.message.author
-        if voter == state.current.requester:
-            await self.bot.say('Requester requested skipping song...')
-            state.skip()
-        elif discord.utils.get(ctx.message.server.roles, name='DJ'):
-            await self.bot.say('User with DJ role 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.bot.say('Skip vote passed, skipping song...')
-                state.skip()
-            else:
-                await self.bot.say('Skip vote added, currently at [{}/3]'.format(total_votes))
-        else:
-            await self.bot.say('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.server)
-        if state.current is None:
-            await self.bot.say('Not playing anything.')
-        else:
-            skip_count = len(state.skip_votes)
-            await self.bot.say('Now playing {} [skips: {}/3]'.format(state.current, skip_count))
+	"""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('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', 'summon'])
+	async def connect_(self, ctx, *, channel: discord.VoiceChannel = None):
+		"""Connect to voice.
+		Parameters
+		------------
+		channel: discord.VoiceChannel [Optional]
+			The channel to connect to. If a channel is not specified, an attempt to join the voice channel you are in
+			will be made.
+		This command also handles moving the bot to different channels.
+		"""
+		if not channel:
+			try:
+				channel = ctx.author.voice.channel
+			except AttributeError:
+				raise InvalidVoiceChannel('No channel to join. Please either specify a valid channel or join one.')
+
+		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}**', delete_after=20)
+
+	@commands.command(name='play', aliases=['sing'])
+	async def play_(self, ctx, *, search: str):
+		"""Request a song and add it to the queue.
+		This command attempts to join a valid voice channel if the bot is not already in one.
+		Uses YTDL to automatically search and retrieve a song.
+		Parameters
+		------------
+		search: str [Required]
+			The song to search and retrieve using YTDL. This could be a simple search, an ID or URL.
+		"""
+		await ctx.trigger_typing()
+
+		vc = ctx.voice_client
+
+		if not vc:
+			await ctx.invoke(self.connect_)
+
+		player = self.get_player(ctx)
+
+		# If download is False, source will be a dict which will be used later to regather the stream.
+		# If download is True, source will be a discord.FFmpegPCMAudio with a VolumeTransformer.
+		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!', delete_after=20)
+		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!', delete_after=20)
+		elif not vc.is_paused():
+			return
+
+		vc.resume()
+		await ctx.send(f'**`{ctx.author}`**: Resumed the song!')
+
+	@commands.command(name='vote_skip')
+	async def vote_skip_(self, ctx):
+		"""Skips the song if the required votes have been acquired"""
+		vc = ctx.voice_client
+		votes = []
+
+		if not vc or not vc.is_connected():
+			return await ctx.send('I am not currently playing anything!', delete_after=20)
+
+		if vc.is_paused():
+			pass
+		elif not vc.is_playing():
+			return
+
+		if not {ctx.author} in votes:
+			votes.append(f'{ctx.author}')
+		elif {ctx.author} in votes:
+			await ctx.send(f"**`{ctx.author}`**: You have already voted")
+
+	@commands.command(name='skip')
+	@checks.song_requester_or_dj()
+	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!', delete_after=20)
+
+		if vc.is_paused():
+			pass
+		elif not vc.is_playing():
+			return
+
+		vc.stop()
+		if ctx.message.author.name == vc.source.requester.name:
+			await ctx.send(f'**`{ctx.author}`**: has skipped the song!')
+		elif discord.utils.get(ctx.message.author.roles, name="DJ"):
+			await ctx.send(f'**`{ctx.author}`**: has skipped the song, with the help of the DJ role!')
+
+	@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!', delete_after=20)
+
+		player = self.get_player(ctx)
+		if player.queue.empty():
+			return await ctx.send('There are currently no more queued songs.')
+
+		# Grab up to 5 entries from the queue...
+		upcoming = list(itertools.islice(player.queue._queue, 0, 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', 'current', '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!', delete_after=20)
+
+		player = self.get_player(ctx)
+		if not player.current:
+			return await ctx.send('I am not currently playing anything!')
+
+		try:
+			# Remove our previous now_playing message.
+			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, 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.
+		"""
+		vc = ctx.voice_client
+
+		if not vc or not vc.is_connected():
+			return await ctx.send('I am not currently connected to voice!', delete_after=20)
+
+		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')
+	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.
+		"""
+		vc = ctx.voice_client
+
+		if not vc or not vc.is_connected():
+			return await ctx.send('I am not currently playing anything!', delete_after=20)
+
+		await self.cleanup(ctx.guild)
 
 
 def setup(bot):
 	bot.add_cog(Music(bot))
+	print("Music cog loaded.")
diff --git a/Music-bot/cogs/music_basic.py b/Music-bot/cogs/music_basic.py
new file mode 100644
index 0000000000000000000000000000000000000000..3585ec99711f222eef3cfc842e53f0d2f1125fa9
--- /dev/null
+++ b/Music-bot/cogs/music_basic.py
@@ -0,0 +1,236 @@
+import asyncio
+
+import discord
+import youtube_dl
+from discord.ext import commands
+
+
+class VoiceConnectionError(commands.CommandError):
+    """Custom Exception class for connection errors."""
+
+
+class InvalidVoiceChannel(VoiceConnectionError):
+    """Exception for cases of invalid Voice Channels."""
+
+
+# Suppress noise about console usage from errors
+youtube_dl.utils.bug_reports_message = lambda: ''
+
+ytdl_format_options = {
+	'format': 'bestaudio/best',
+	'outtmpl': '%(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'  # bind to ipv4 since ipv6 addresses cause issues sometimes
+}
+
+ffmpeg_options = {
+	'before_options': '-nostdin',
+	'options': '-vn'
+}
+
+ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
+
+
+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'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()
+
+            # 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 YTDLSource(discord.PCMVolumeTransformer):
+	def __init__(self, source, *, data, volume=0.5):
+		super().__init__(source, volume)
+
+		self.data = data
+
+		self.title = data.get('title')
+		self.url = data.get('url')
+
+	@classmethod
+	async def from_url(cls, url, *, loop=None, stream=False):
+		loop = loop or asyncio.get_event_loop()
+		data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))
+
+		if 'entries' in data:
+			# take first item from a playlist
+			data = data['entries'][0]
+
+		filename = data['url'] if stream else ytdl.prepare_filename(data)
+		return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
+
+
+class Music:
+	def __init__(self, bot):
+		self.bot = bot
+
+	@commands.command(name='connect', aliases=['join', 'summon'])
+	async def connect_(self, ctx, *, channel: discord.VoiceChannel = None):
+		"""Connect to voice.
+		Parameters
+		------------
+		channel: discord.VoiceChannel [Optional]
+			The channel to connect to. If a channel is not specified, an attempt to join the voice channel you are in
+			will be made.
+		This command also handles moving the bot to different channels.
+		"""
+		if not channel:
+			try:
+				channel = ctx.author.voice.channel
+			except AttributeError:
+				raise InvalidVoiceChannel('No channel to join. Please either specify a valid channel or join one.')
+
+		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()
+	async def play(self, ctx, *, query):
+		"""Plays a file from the local filesystem"""
+
+		source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(query))
+		ctx.voice_client.play(source, after=lambda e: print('Player error: %s' % e) if e else None)
+
+		await ctx.send('Now playing: {}'.format(query))
+
+	@commands.command()
+	async def yt(self, ctx, *, url):
+		"""Plays from a url (almost anything youtube_dl supports)"""
+
+		async with ctx.typing():
+			player = await YTDLSource.from_url(url, loop=self.bot.loop)
+			ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
+
+		await ctx.send('Now playing: {}'.format(player.title))
+
+	@commands.command()
+	async def stream(self, ctx, *, url):
+		"""Streams from a url (same as yt, but doesn't predownload)"""
+
+		vc = ctx.voice_client
+
+		if not vc:
+			await ctx.invoke(self.connect_)
+
+		async with ctx.typing():
+			player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
+			ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
+
+		await ctx.send('Now playing: {}'.format(player.title))
+
+	@commands.command()
+	async def volume(self, ctx, volume: int):
+		"""Changes the player's volume"""
+
+		if ctx.voice_client is None:
+			return await ctx.send("Not connected to a voice channel.")
+
+		ctx.voice_client.source.volume = volume
+		await ctx.send("Changed volume to {}%".format(volume))
+
+	@commands.command()
+	async def stop(self, ctx):
+		"""Stops and disconnects the bot from voice"""
+
+		await ctx.voice_client.disconnect()
+
+	@play.before_invoke
+	@yt.before_invoke
+	@stream.before_invoke
+	async def ensure_voice(self, ctx):
+		if ctx.voice_client is None:
+			if ctx.author.voice:
+				await ctx.author.voice.channel.connect()
+			else:
+				await ctx.send("You are not connected to a voice channel.")
+				raise commands.CommandError("Author not connected to a voice channel.")
+		elif ctx.voice_client.is_playing():
+			ctx.voice_client.stop()
+
+
+def setup(bot):
+	bot.add_cog(Music(bot))
diff --git a/Music-bot/cogs/owner.py b/Music-bot/cogs/owner.py
index feee084ba93fb6648fd2566cbd2297a476dabf42..249e1708a7715c2a3c724ed3748c05c684e075df 100644
--- a/Music-bot/cogs/owner.py
+++ b/Music-bot/cogs/owner.py
@@ -3,50 +3,51 @@ from discord.ext import commands
 
 class OwnerCog:
 
-	def __init__(self, bot):
-		self.bot = bot
-
-	# Hidden means it won't show up on the default help.
-	@commands.command(name='load', hidden=True)
-	@commands.is_owner()
-	async def cog_load(self, ctx, *, cog: str):
-		"""Command which Loads a Module.
-		Remember to use dot path. e.g: cogs.owner"""
-
-		try:
-			self.bot.load_extension(cog)
-		except Exception as e:
-			await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}')
-		else:
-			await ctx.send('**`SUCCESS`**')
-
-	@commands.command(name='unload', hidden=True)
-	@commands.is_owner()
-	async def cog_unload(self, ctx, *, cog: str):
-		"""Command which Unloads a Module.
-		Remember to use dot path. e.g: cogs.owner"""
-
-		try:
-			self.bot.unload_extension(cog)
-		except Exception as e:
-			await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}')
-		else:
-			await ctx.send('**`SUCCESS`**')
-
-	@commands.command(name='reload', hidden=True)
-	@commands.is_owner()
-	async def cog_reload(self, ctx, *, cog: str):
-		"""Command which Reloads a Module.
-		Remember to use dot path. e.g: cogs.owner"""
-
-		try:
-			self.bot.unload_extension(cog)
-			self.bot.load_extension(cog)
-		except Exception as e:
-			await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}')
-		else:
-			await ctx.send('**`SUCCESS`**')
+    def __init__(self, bot):
+        self.bot = bot
+
+    # Hidden means it won't show up on the default help.
+    @commands.command(name='load', hidden=True)
+    @commands.is_owner()
+    async def cog_load(self, ctx, *, cog: str):
+        """Command which Loads a Module.
+        Remember to use dot path. e.g: cogs.owner"""
+
+        try:
+            self.bot.load_extension(cog)
+        except Exception as e:
+            await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}')
+        else:
+            await ctx.send('**`SUCCESS`**')
+
+    @commands.command(name='unload', hidden=True)
+    @commands.is_owner()
+    async def cog_unload(self, ctx, *, cog: str):
+        """Command which Unloads a Module.
+        Remember to use dot path. e.g: cogs.owner"""
+
+        try:
+            self.bot.unload_extension(cog)
+        except Exception as e:
+            await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}')
+        else:
+            await ctx.send('**`SUCCESS`**')
+
+    @commands.command(name='reload', hidden=True)
+    @commands.is_owner()
+    async def cog_reload(self, ctx, *, cog: str):
+        """Command which Reloads a Module.
+        Remember to use dot path. e.g: cogs.owner"""
+
+        try:
+            self.bot.unload_extension(cog)
+            self.bot.load_extension(cog)
+        except Exception as e:
+            await ctx.send(f'**`ERROR:`** {type(e).__name__} - {e}')
+        else:
+            await ctx.send('**`SUCCESS`**')
 
 
 def setup(bot):
-	bot.add_cog(OwnerCog(bot))
\ No newline at end of file
+    bot.add_cog(OwnerCog(bot))
+    print("Owner cog loaded.")
diff --git a/Music-bot/cogs/utils/checks.py b/Music-bot/cogs/utils/checks.py
new file mode 100644
index 0000000000000000000000000000000000000000..bfb23d67ef954c94d871244f79f7272f527c63f7
--- /dev/null
+++ b/Music-bot/cogs/utils/checks.py
@@ -0,0 +1,123 @@
+import discord
+from discord.ext import commands
+
+
+async def check_permissions(ctx, perms, *, check=all):
+    is_owner = await ctx.bot.is_owner(ctx.author)
+    if is_owner:
+        return True
+
+    resolved = ctx.channel.permissions_for(ctx.author)
+    return check(getattr(resolved, name, None) == value for name, value in perms.items())
+
+
+def has_permissions(*, check=all, **perms):
+    async def pred(ctx):
+        return await check_permissions(ctx, perms, check=check)
+    return commands.check(pred)
+
+
+async def check_guild_permissions(ctx, perms, *, check=all):
+    is_owner = await ctx.bot.is_owner(ctx.author)
+    if is_owner:
+        return True
+
+    if ctx.guild is None:
+        return False
+
+    resolved = ctx.author.guild_permissions
+    return check(getattr(resolved, name, None) == value for name, value in perms.items())
+
+
+def has_guild_permissions(*, check=all, **perms):
+    async def pred(ctx):
+        return await check_guild_permissions(ctx, perms, check=check)
+    return commands.check(pred)
+
+
+def is_in_guilds(*guild_ids):
+    def predicate(ctx):
+        guild = ctx.guild
+        if guild is None:
+            return False
+        return guild.id in guild_ids
+    return commands.check(predicate)
+
+
+def am_i_owner():
+    async def predicate(ctx):
+        is_owner = await ctx.bot.is_owner(ctx.author)
+        if is_owner:
+            return True
+        else:
+            return False
+    return commands.check(predicate)
+
+
+def is_mod():
+    async def pred(ctx):
+        return await check_guild_permissions(ctx, {'manage_guild': True})
+    return commands.check(pred)
+
+
+def is_admin():
+    async def pred(ctx):
+        return await check_guild_permissions(ctx, {'administrator': True})
+    return commands.check(pred)
+
+
+def mod_or_permissions(**perms):
+    perms['manage_guild'] = True
+
+    async def predicate(ctx):
+        return await check_guild_permissions(ctx, perms, check=any)
+    return commands.check(predicate)
+
+
+def admin_or_permissions(**perms):
+    perms['administrator'] = True
+
+    async def predicate(ctx):
+        return await check_guild_permissions(ctx, perms, check=any)
+    return commands.check(predicate)
+
+
+def song_requester_or_dj():
+    async def predicate(ctx):
+        vc = ctx.voice_client
+
+        if not vc or not vc.is_connected():
+            await ctx.send('I am not currently connected to voice!')
+
+        if ctx.message.author.name == vc.source.requester.name:
+            return True
+        elif discord.utils.get(ctx.message.author.roles, name="DJ"):
+            return True
+        elif ctx.message.author != vc.source.requester or not discord.utils.get(ctx.message.author.roles, name="DJ"):
+            await ctx.send(f"The bot owner has been naughty and has failed to add a voting system, so sorry but your "
+                           f"permissions are not enough to use this command.")
+            return False
+
+    return commands.check(predicate)
+
+
+def song_requester_or_owner_or_dj():
+    async def predicate(ctx):
+        is_owner = await ctx.bot.is_owner(ctx.author)
+        vc = ctx.voice_client
+
+        if not vc or not vc.is_connected():
+            await ctx.send('I am not currently connected to voice!')
+
+        if ctx.message.author.name == vc.source.requester.name:
+            return True
+        elif is_owner:
+            return True
+        elif discord.utils.get(ctx.message.author.roles, name="DJ"):
+            return True
+        elif ctx.message.author != vc.source.requester or not discord.utils.get(ctx.message.author.roles, name="DJ"):
+            await ctx.send(f"The bot owner has been naughty and has failed to add a voting system, so sorry but your "
+                           f"permissions are not enough to use this command.")
+            return False
+
+    return commands.check(predicate)
\ No newline at end of file
diff --git a/Music-bot/cogs/utils/config.py b/Music-bot/cogs/utils/config.py
index 84e73a823f09ee27ad72734a6013424478922b6d..0aecf938045475cd0c4779565475eb0fa5b0d2b5 100644
--- a/Music-bot/cogs/utils/config.py
+++ b/Music-bot/cogs/utils/config.py
@@ -5,54 +5,54 @@ import asyncio
 
 
 class Config:
-	"""The "database" object. Internally based on ``json``."""
-
-	def __init__(self, name, **options):
-		self.name = name
-		self.object_hook = options.pop('object_hook', None)
-		self.encoder = options.pop('encoder', None)
-		self.loop = options.pop('loop', asyncio.get_event_loop())
-		if options.pop('load_later', False):
-			self.loop.create_task(self.load())
-		else:
-			self.load_from_file()
-
-	def load_from_file(self):
-		try:
-			with open(self.name, 'r') as f:
-				self._db = json.load(f, object_hook=self.object_hook)
-		except FileNotFoundError:
-			self._db = {}
-
-	async def load(self):
-		await self.loop.run_in_executor(None, self.load_from_file)
-
-	def _dump(self):
-		with open(self.name, 'w') as f:
-			json.dump(self._db, f, ensure_ascii=True, cls=self.encoder)
-
-	async def save(self):
-		await self.loop.run_in_executor(None, self._dump)
-
-	def get(self, key, *args):
-		"""Retrieves a config entry."""
-		return self._db.get(key, *args)
-
-	async def put(self, key, value, *args):
-		"""Edits a config entry."""
-		self._db[key] = value
-		await self.save()
-
-	async def remove(self, key):
-		"""Removes a config entry."""
-		del self._db[key]
-		await self.save()
-
-	def __contains__(self, item):
-		return self._db.__contains__(item)
-
-	def __len__(self):
-		return self._db.__len__()
-
-	def all(self):
-		return self._db
+    """The "database" object. Internally based on ``json``."""
+
+    def __init__(self, name, **options):
+        self.name = name
+        self.object_hook = options.pop('object_hook', None)
+        self.encoder = options.pop('encoder', None)
+        self.loop = options.pop('loop', asyncio.get_event_loop())
+        if options.pop('load_later', False):
+            self.loop.create_task(self.load())
+        else:
+            self.load_from_file()
+
+    def load_from_file(self):
+        try:
+            with open(self.name, 'r') as f:
+                self._db = json.load(f, object_hook=self.object_hook)
+        except FileNotFoundError:
+            self._db = {}
+
+    async def load(self):
+        await self.loop.run_in_executor(None, self.load_from_file)
+
+    def _dump(self):
+        with open(self.name, 'w') as f:
+            json.dump(self._db, f, ensure_ascii=True, cls=self.encoder)
+
+    async def save(self):
+        await self.loop.run_in_executor(None, self._dump)
+
+    def get(self, key, *args):
+        """Retrieves a config entry."""
+        return self._db.get(key, *args)
+
+    async def put(self, key, value, *args):
+        """Edits a config entry."""
+        self._db[key] = value
+        await self.save()
+
+    async def remove(self, key):
+        """Removes a config entry."""
+        del self._db[key]
+        await self.save()
+
+    def __contains__(self, item):
+        return self._db.__contains__(item)
+
+    def __len__(self):
+        return self._db.__len__()
+
+    def all(self):
+        return self._db