diff --git a/cogs/audio.py b/cogs/audio.py index a79b7a01f..4a44e2c45 100644 --- a/cogs/audio.py +++ b/cogs/audio.py @@ -49,6 +49,7 @@ class Audio: def __init__(self, bot): self.bot = bot self.music_player = EmptyPlayer() + self.sfx_player = EmptyPlayer() self.settings = fileIO("data/audio/settings.json", "load") self.queue_mode = False self.queue = [] @@ -86,7 +87,8 @@ class Audio: self.playlist = [] self.queue.append(link) self.music_player.paused = False - if self.music_player.is_playing(): self.music_player.stop() + if not self.sfx_player.is_done(): self.sfx_player.stop() + if not self.music_player.is_done(): self.music_player.stop() await self.bot.say("Playing requested link...") else: self.playlist = [] @@ -147,6 +149,8 @@ class Audio: self.music_player.stop() else: await self.vote_skip(msg) + elif self.sfx_player.is_playing(): + self.sfx_player.stop() async def vote_skip(self, msg): v_channel = msg.server.me.voice_channel @@ -209,6 +213,70 @@ class Audio: else: await self.bot.say("There are no valid playlists in the localtracks folder.\nIf you're the owner, see {}".format(help_link)) + @commands.command(pass_context=True, no_pm=True) + async def sfx(self, ctx, *, name : str): + """Plays a local sound file + + For bot's owner: + audio files must be placed in the data/audio/sfx folder. + Supported files are mp3, flac, wav""" + #sound effects default enabled + server = ctx.message.server + if server.id not in self.settings["SERVER_SFX_ON"]: + self.settings["SERVER_SFX_ON"][server.id] = True + if not self.settings["SERVER_SFX_ON"][server.id]: + await self.bot.say("Sound effects are not enabled on this server") + return + + msg = ctx.message + localsfx = self.get_local_sfx() + if localsfx and ("data/audio/sfx/" not in name and "\\" not in name): + if name in localsfx.keys(): + file = "data/audio/sfx/{}{}".format(name,localsfx[name]) + + if await self.check_voice(msg.author, ctx.message): + try: + if self.music_player.is_playing(): + self.music_player.paused = True + self.music_player.pause() + + if self.sfx_player.is_playing(): + self.sfx_player.stop() + + self.sfx_player = self.bot.voice.create_ffmpeg_player(file, options='''-filter:a "volume={}"'''.format(self.settings["VOLUME"])) + self.sfx_player.start() + while self.sfx_player.is_playing(): + await asyncio.sleep(.5) + + if not self.music_player.is_playing(): + self.music_player.paused = False + self.music_player.resume() + except AttributeError: + #music_player not used yet. Still an EmptyPlayer. + #better to ask for forgiveness? + pass + except Exception as e: + print(e) + + else: + await self.bot.say("There is no sound effect with that name.") + else: + await self.bot.say("There are no valid sound effects in the `data/audio/sfx` folder.") + + def get_local_sfx(self): + filenames = {} + files = os.listdir("data/audio/sfx/") + #only reason why these are the only supported ext is cause I haven't tried any others + supported = [".mp3",".flac",".wav"] + for f in files: + item = os.path.splitext(f) + if item[1] in supported: + filenames[item[0]] = item[1] + if filenames != {}: + return filenames + else: + return False + @commands.command(pass_context=True, no_pm=True) async def loop(self, ctx): """Loops single song @@ -260,7 +328,7 @@ class Audio: """Stops audio activity """ msg = ctx.message - if self.music_player.is_playing(): + if self.music_player.is_playing() or self.sfx_player.is_playing(): if await self.is_alone_or_admin(msg): await self.close_audio() else: @@ -272,7 +340,8 @@ class Audio: self.queue = [] self.playlist = [] self.current = -1 - if self.music_player.is_playing(): self.music_player.stop() + if not self.music_player.is_done(): self.music_player.stop() + if not self.sfx_player.is_done(): self.sfx_player.stop() await asyncio.sleep(1) if self.bot.voice: await self.bot.voice.disconnect() @@ -355,7 +424,9 @@ class Audio: @commands.command() async def resume(self): """Resumes paused song.""" - if not self.music_player.is_playing(): + if self.sfx_player.is_playing(): + self.sfx_player.stop() + elif not self.music_player.is_playing(): self.music_player.paused = False self.music_player.resume() await self.bot.say("Resuming song.") @@ -397,6 +468,30 @@ class Audio: else: await self.bot.say("There are no local playlists.") + @_list.command(name="sfx", pass_context=True) + async def list_sfx(self, ctx): + msgs = ["Available local sound effects: \n\n```\n"] + files = self.get_local_sfx() + m = 0 + maxm = 1980 + if files: + for i, d in enumerate(files.keys()): + if len(d) < maxm: #how did you get a filename this large? + if len(msgs[m]) + len(d) > maxm: + msgs[m] += "```" + m += 1 + msgs.append("```\n") + if i % 4 == 0 and i != 0: + msgs[m] += d + "\n" + else: + msgs[m] += d + "\t" + msgs[m] += "```" + for msg in msgs: + await self.bot.send_message(ctx.message.author, msg) + await asyncio.sleep(1) + else: + await self.bot.say("There are no local sound effects.") + @_list.command(name="queue", pass_context=True) async def list_queue(self, ctx): queue_list = await self.queue_titles() @@ -427,10 +522,14 @@ class Audio: async def audioset(self, ctx): """Changes audio module settings""" if ctx.invoked_subcommand is None: + server = ctx.message.server await send_cmd_help(ctx) msg = "```" for k, v in self.settings.items(): - msg += str(k) + ": " + str(v) + "\n" + if type(v) is dict and server.id in v: + msg += str(k) + ": " + str(v[server.id]) + "\n" + else: + msg += str(k) + ": " + str(v) + "\n" msg += "```" await self.bot.say(msg) @@ -471,6 +570,22 @@ class Audio: else: await self.bot.say("Volume must be between 0 and 1. Example: 0.40") + @audioset.command(name="sfx", pass_context=True, no_pm=True) + async def _sfx(self, ctx): + """Enables/Disables sound effects usage in the server""" + #default on. + server = ctx.message.server + if server.id not in self.settings["SERVER_SFX_ON"]: + self.settings["SERVER_SFX_ON"][server.id] = True + else: + self.settings["SERVER_SFX_ON"][server.id] = not self.settings["SERVER_SFX_ON"][server.id] + #for a toggle, settings should save here in case bot fails to send message + fileIO("data/audio/settings.json", "save", self.settings) + if self.settings["SERVER_SFX_ON"][server.id]: + await self.bot.say("Sound effects are now enabled on this server.") + else: + await self.bot.say("Sound effects are now disabled on this server.") + @audioset.command() @checks.is_owner() async def maxcache(self, size : int): @@ -537,7 +652,12 @@ class Audio: await asyncio.sleep(1) if self.downloader["ID"]: try: - if self.music_player.is_playing(): self.music_player.stop() + # sfx_player should only ever get stuck as much as music_player does + # when it happens to music_player, the reponsibility is put on the user to !stop + # so we'll do the same with sfx_player. a timeout could be placed here though. + while self.sfx_player.is_playing(): + await asyncio.sleep(.5) + if not self.music_player.is_done(): self.music_player.stop() self.music_player = self.bot.voice.create_ffmpeg_player(path + self.downloader["ID"], options='''-filter:a "volume={}"'''.format(self.settings["VOLUME"])) self.music_player.paused = False self.music_player.start() @@ -815,6 +935,9 @@ class EmptyPlayer(): #dummy player def is_playing(self): return False + def is_done(self): + return True + class MaximumLength(Exception): def __init__(self, m): self.message = m @@ -822,7 +945,7 @@ class MaximumLength(Exception): return self.message def check_folders(): - folders = ("data/audio", "data/audio/cache", "data/audio/playlists", "data/audio/localtracks") + folders = ("data/audio", "data/audio/cache", "data/audio/playlists", "data/audio/localtracks", "data/audio/sfx") for folder in folders: if not os.path.exists(folder): print("Creating " + folder + " folder...") @@ -830,7 +953,7 @@ def check_folders(): def check_files(): - default = {"VOLUME" : 0.5, "MAX_LENGTH" : 3700, "QUEUE_MODE" : True, "MAX_CACHE" : 0, "SOUNDCLOUD_CLIENT_ID": None, "TITLE_STATUS" : True} + default = {"VOLUME" : 0.5, "MAX_LENGTH" : 3700, "QUEUE_MODE" : True, "MAX_CACHE" : 0, "SOUNDCLOUD_CLIENT_ID": None, "TITLE_STATUS" : True, "SERVER_SFX_ON" : {}} settings_path = "data/audio/settings.json" if not os.path.isfile(settings_path):