From 60eebc670553fc37f126d61e782f37502ba2d3fa Mon Sep 17 00:00:00 2001 From: aikaterna Date: Tue, 23 Jan 2018 20:03:22 -0800 Subject: [PATCH] Add radio.py --- README.md | 2 + radio/info.json | 7 +++ radio/radio.py | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 radio/info.json create mode 100644 radio/radio.py diff --git a/README.md b/README.md index 03b1ae3..410a67a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ otherbot - Have multiple Red instances and want to know when one goes offline? E pug - Warcraft pug checker. A port of PugBot's module: https://github.com/reznok/PugBot +radio - A hidden unpublished gem from Paddo, with a couple edits. Plays http audio streams like icecast and mp3 streams. + riot - an old Fredboat command, requested by Mewleficent. seen - By Paddolicious#8880. Check when the user was last active on a server. diff --git a/radio/info.json b/radio/info.json new file mode 100644 index 0000000..9acd0a0 --- /dev/null +++ b/radio/info.json @@ -0,0 +1,7 @@ +{ + "AUTHOR" : "Paddo, with edits by aikaterna", + "NAME" : "radio", + "SHORT" : "Plays http streams like icecast or mp3 streams.", + "DESCRIPTION" : "This cog can only be used with audio unloaded. To play m3u or pls files, extract the http stream url they contain and use that - this cog can remember urls for later playback.", + "TAGS": ["radio", "stream", "icecast"] +} diff --git a/radio/radio.py b/radio/radio.py new file mode 100644 index 0000000..64fd054 --- /dev/null +++ b/radio/radio.py @@ -0,0 +1,161 @@ +import os +import discord +import asyncio +from discord.ext import commands +from __main__ import send_cmd_help +from cogs.utils.dataIO import dataIO + + +class Radio: + def __init__(self, bot): + self.bot = bot + self.players = {} + self.memory_path = 'data/radio/memory.json' + self.memory = dataIO.load_json(self.memory_path) + + @commands.group(pass_context=True, no_pm=True, name='radio') + async def _radio(self, ctx): + """Streaming audio commands.""" + audio_cog = self.bot.get_cog('Audio') + if audio_cog: + return await self.bot.say("Please unload the audio cog before using this cog.") + if ctx.invoked_subcommand is None: + await send_cmd_help(ctx) + + @_radio.command(pass_context=True, no_pm=True, name='stop') + async def _leave(self, ctx): + """Stops playback.""" + server = ctx.message.server + voice_client = await self.voice_client(server) + await self.stop_playing(server) + if voice_client: + await voice_client.disconnect() + + @_radio.command(no_pm=True, pass_context=True, name='play') + async def _play(self, ctx, url: str): + """Play a http stream.""" + server = ctx.message.server + if server.id in self.players: + await self.stop_playing(server) + await self.play_stream(ctx, url) + await self.bot.say("Now playing: <{}>".format(url)) + + @_radio.command(no_pm=True, pass_context=True, name='list') + async def _list(self, ctx): + """List saved stream URLs.""" + server = ctx.message.server + message = '```\n' + message += '{:<30}{}\n\n'.format('NAME', 'URL') + if server.id in self.memory: + for stream in self.memory[server.id]: + message += '{:<30}{}\n'.format(stream, self.memory[server.id][stream]) + message += '```' + await self.bot.say(message) + + @_radio.command(no_pm=True, pass_context=True, name='add') + async def _add(self, ctx, name: str, url: str): + """Add a url to save for radio playback.""" + server = ctx.message.server + await self.add_to_memory(server, name, url) + await self.bot.say('Added to memory') + + @_radio.command(no_pm=True, pass_context=True, name='load') + async def _load(self, ctx, name: str): + """Load a saved url for radio playback.""" + server = ctx.message.server + if server.id in self.memory: + if name.lower() in self.memory[server.id]: + url = self.memory[server.id][name.lower()] + if server.id in self.players: + await self.stop_playing(server) + await self.play_stream(ctx, url) + await self.bot.say("Now playing: <{}>".format(url)) + else: + await self.bot.say('"{}" is not in memory.'.format(name.lower())) + else: + await self.bot.say('Nothing in memory yet') + + async def save_memory(self): + dataIO.save_json(self.memory_path, self.memory) + + async def add_to_memory(self, server, name, url): + if server.id not in self.memory: + self.memory[server.id] = {} + self.memory[server.id][name.lower()] = url + await self.save_memory() + + async def join_voice_channel(self, channel): + try: + await self.bot.join_voice_channel(channel) + return True + except discord.InvalidArgument: + await self.bot.say('You need to be in a voice channel yourself.') + except discord.Forbidden: + await self.bot.say('I don\'t have permissions to join this channel.') + return False + + async def leave_voice_channel(self, server): + voice_client = await self.voice_client(server) + if server.id in self.players: + self.players[server.id].stop() + del self.players[server.id] + await self.stop_playing(server) + await voice_client.disconnect() + + async def voice_connected(self, server): + return self.bot.is_voice_connected(server) + + async def voice_client(self, server): + return self.bot.voice_client_in(server) + + async def stop_playing(self, server): + if server.id in self.players: + self.players[server.id].stop() + del self.players[server.id] + + async def start_playing(self, server, url): + if server.id not in self.players: + voice_client = await self.voice_client(server) + audio_player = voice_client.create_ffmpeg_player(url) + self.players[server.id] = audio_player + self.players[server.id].start() + + async def play_stream(self, ctx, url): + server = ctx.message.server + channel = ctx.message.author.voice_channel + if not ctx.message.channel.is_private: + check = True + if not await self.voice_connected(server): + check = await self.join_voice_channel(channel) + if check: + await self.start_playing(server, url) + + async def _playing_check(self): + while self == self.bot.get_cog('Radio'): + for player in self.players: + if not self.players[player].is_playing(): + server = self.bot.get_server(player) + await self.leave_voice_channel(server) + break + await asyncio.sleep(30) + + +def check_folder(): + if not os.path.exists('data/radio'): + print('Creating data/radio folder...') + os.makedirs('data/radio') + + +def check_file(): + if not dataIO.is_valid_json('data/radio/memory.json'): + print('Creating memory.json...') + dataIO.save_json('data/radio/memory.json', {}) + + +def setup(bot): + check_folder() + check_file() + cog = Radio(bot) + loop = asyncio.get_event_loop() + loop.create_task(cog._playing_check()) + bot.add_cog(cog)