[Audio] Song notifs / autodc, queue list improvements (#1232)

* [V2 Audio] Add features

Audioset notify for song change announcement, song and queue ehancements, timer toggle for enabling audio add-on cogs

* Update audioset timerdisconnect text

* Unneeded line from alternate version

* Update variables and display_now_playing

* Missing duration

* Update queue and np for local tracks

* Update np song.webpage_url for local on Linux

* Update queue and np for local songs

* Change emoji to Unicode names

Thanks to Tobotimus

* Queue displays np info always, draw_play icon changes

Removed duration next to title on currently playing song information in queue, was redundant with the draw_play duration displayed below. Implemented changes suggested by jorams, with modifications.
This commit is contained in:
aikaterna 2018-03-18 19:14:23 -07:00 committed by Kowlin
parent 0dafcfa083
commit 8b69042f08

View File

@ -19,6 +19,7 @@ import time
import inspect
import subprocess
import urllib.parse
import datetime
from enum import Enum
__author__ = "tekulvw"
@ -148,6 +149,10 @@ class Song:
self.duration = kwargs.pop('duration', 60)
self.start_time = kwargs.pop('start_time', None)
self.end_time = kwargs.pop('end_time', None)
self.thumbnail = kwargs.pop('thumbnail', None)
self.view_count = kwargs.pop('view_count', None)
self.rating = kwargs.pop('average_rating', None)
self.song_start_time = None
class QueuedSong:
def __init__(self, url, channel):
@ -310,8 +315,10 @@ class Audio:
self.queue = {} # add deque's, repeat
self.downloaders = {} # sid: object
self.settings = dataIO.load_json("data/audio/settings.json")
self.settings_path = "data/audio/settings.json"
self.server_specific_setting_keys = ["VOLUME", "VOTE_ENABLED",
"VOTE_THRESHOLD", "NOPPL_DISCONNECT"]
"VOTE_THRESHOLD", "NOPPL_DISCONNECT",
"NOTIFY", "NOTIFY_CHANNEL", "TIMER_DISCONNECT"]
self.cache_path = "data/audio/cache"
self.local_playlist_path = "data/audio/localtracks"
self._old_game = False
@ -892,6 +899,7 @@ class Audio:
except FileNotFoundError:
raise
song.song_start_time = datetime.datetime.now()
voice_client = await self._create_ffmpeg_player(server, song.id,
local=local,
start_time=song.start_time,
@ -1177,6 +1185,35 @@ class Audio:
await self.bot.say("Maximum length is now {} seconds.".format(length))
self.save_settings()
@checks.mod_or_permissions(manage_messages=True)
@audioset.command(name="notifychannel", pass_context=True)
async def audioset_notifychannel(self, ctx, channel: discord.Channel):
"""Sets the channel for the now playing announcement"""
server = ctx.message.server
if not server.me.permissions_in(channel).send_messages:
await self.bot.say("No permissions to speak in that channel.")
return
self.set_server_setting(server, "NOTIFY_CHANNEL", channel.id)
dataIO.save_json(self.settings_path, self.settings)
await self.bot.send_message(channel, "I will now announce new songs here.")
@audioset.command(name="notify", pass_context=True)
@checks.mod_or_permissions(manage_messages=True)
async def audioset_notify(self, ctx):
"""Sends a notification to the channel when the song changes"""
server = ctx.message.server
settings = self.get_server_settings(server.id)
notify = settings.get("NOTIFY", True)
self.set_server_setting(server, "NOTIFY", not notify)
if self.get_server_settings(server)["NOTIFY_CHANNEL"] is None:
self.set_server_setting(server, "NOTIFY_CHANNEL", ctx.message.channel.id)
dataIO.save_json(self.settings_path, self.settings)
if not notify:
await self.bot.say("Now notifying when a new track plays.")
else:
await self.bot.say("No longer notifying when a new track plays.")
self.save_settings()
@audioset.command(name="player")
@checks.is_owner()
async def audioset_player(self):
@ -1204,6 +1241,26 @@ class Audio:
" status")
self.save_settings()
@audioset.command(name="timerdisconnect", pass_context=True)
@checks.mod_or_permissions(manage_messages=True)
async def audioset_timerdisconnect(self, ctx):
"""Toggles the disconnect timer"""
server = ctx.message.server
settings = self.get_server_settings(server.id)
timer_disconnect = settings.get("TIMER_DISCONNECT", True)
self.set_server_setting(server, "TIMER_DISCONNECT",
not timer_disconnect)
if not timer_disconnect:
await self.bot.say("The bot will automatically disconnect after"
" playback is stopped and five minutes have"
" elapsed. Disable this setting to stop the"
" bot from disconnecting with other music cogs"
" playing.")
else:
await self.bot.say("The bot will no longer auto disconnect"
" while other music cogs are playing.")
self.save_settings()
@audioset.command(pass_context=True, name="volume", no_pm=True)
@checks.mod_or_permissions(manage_messages=True)
async def audioset_volume(self, ctx, percent: int=None):
@ -1771,22 +1828,27 @@ class Audio:
"""Not a command, use `queue` with no args to call this."""
server = ctx.message.server
channel = ctx.message.channel
if server.id not in self.queue:
now_playing = self._get_queue_nowplaying(server)
if server.id not in self.queue and now_playing is None:
await self.bot.say("Nothing playing on this server!")
return
elif len(self.queue[server.id][QueueKey.QUEUE]) == 0:
if len(self.queue[server.id][QueueKey.QUEUE]) == 0 and not self.is_playing(server):
await self.bot.say("Nothing queued on this server.")
return
colour = ''.join([choice('0123456789ABCDEF') for x in range(6)])
em = discord.Embed(description="", colour=int(colour, 16))
msg = ""
now_playing = self._get_queue_nowplaying(server)
if self.is_playing(server):
msg += "\n***Currently playing:***\n{}\n".format(now_playing.title)
msg += self._draw_play(now_playing, server) + "\n" # draw play thing
if now_playing.thumbnail is None:
now_playing.thumbnail = (self.bot.user.avatar_url).replace('webp', 'png')
em.set_thumbnail(url=now_playing.thumbnail)
if now_playing is not None:
msg += "\n***Now playing:***\n{}\n".format(now_playing.title)
queued_song_list = self._get_queue(server, 5)
tempqueued_song_list = self._get_queue_tempqueue(server, 5)
queued_song_list = self._get_queue(server, 10)
tempqueued_song_list = self._get_queue_tempqueue(server, 10)
await self.bot.say("Gathering information...")
@ -1795,21 +1857,65 @@ class Audio:
song_info = []
for num, song in enumerate(tempqueue_song_list, 1):
str_duration = str(datetime.timedelta(seconds=song.duration))
try:
song_info.append("{}. {.title}".format(num, song))
if song.title is None:
song_info.append("**[{}]** {.webpage_url} ({})".format(num, song, str_duration))
else:
song_info.append("**[{}]** {.title} ({})".format(num, song, str_duration))
except AttributeError:
song_info.append("{}. {.webpage_url}".format(num, song))
song_info.append("**[{}]** {.webpage_url} ({})".format(num, song, str_duration))
for num, song in enumerate(queue_song_list, len(song_info) + 1):
if num > 5:
str_duration = str(datetime.timedelta(seconds=song.duration))
if num > 10:
break
try:
song_info.append("{}. {.title}".format(num, song))
if song.title is None:
song_info.append("**[{}]** {.webpage_url} ({})".format(num, song, str_duration))
else:
song_info.append("**[{}]** {.title} ({})".format(num, song, str_duration))
except AttributeError:
song_info.append("{}. {.webpage_url}".format(num, song))
msg += "\n***Next up:***\n" + "\n".join(song_info)
song_info.append("**[{}]** {.webpage_url} ({})".format(num, song, str_duration))
await self.bot.say(msg)
if song_info:
msg += "\n***Next up:***\n" + "\n".join(song_info)
em.description = msg.replace('None', '-')
more_songs = len(self.queue[server.id][QueueKey.QUEUE]) - 10
if more_songs > 0:
em.set_footer(text="And {} more songs...".format(more_songs))
await self.bot.say(embed=em)
def _draw_play(self, song, server):
song_start_time = song.song_start_time
total_time = datetime.timedelta(seconds=song.duration)
current_time = datetime.datetime.now()
elapsed_time = current_time - song_start_time
sections = 12
loc_time = round((elapsed_time/total_time) * sections) # 10 sections
bar_char = '\N{BOX DRAWINGS HEAVY HORIZONTAL}'
seek_char = '\N{RADIO BUTTON}'
play_char = '\N{BLACK RIGHT-POINTING TRIANGLE}'
try:
if self.voice_client(server).audio_player.is_playing():
play_char = '\N{BLACK RIGHT-POINTING TRIANGLE}'
else:
play_char = '\N{DOUBLE VERTICAL BAR}'
except AttributeError:
pass
msg = "\n" + play_char + " "
for i in range(sections):
if i == loc_time:
msg += seek_char
else:
msg += bar_char
msg += " `{}`/`{}`".format(str(elapsed_time)[0:7],str(total_time))
return msg
@commands.group(pass_context=True, no_pm=True)
async def repeat(self, ctx):
@ -1950,7 +2056,7 @@ class Audio:
url = "https://www.youtube.com/watch?v={}".format(choice(ids))
await ctx.invoke(self.play, url_or_search_terms=url)
@commands.command(pass_context=True, no_pm=True)
@commands.command(pass_context=True, aliases=["np"], no_pm=True)
async def song(self, ctx):
"""Info about the current song."""
server = ctx.message.server
@ -1966,6 +2072,10 @@ class Audio:
song.view_count = None
if not hasattr(song, 'uploader'):
song.uploader = None
if song.rating is None:
song.rating = 0
if song.thumbnail is None:
song.thumbnail = (self.bot.user.avatar_url).replace('webp', 'png')
if hasattr(song, 'duration'):
m, s = divmod(song.duration, 60)
h, m = divmod(m, 60)
@ -1975,14 +2085,22 @@ class Audio:
dur = "{0}:{1:0>2}".format(m, s)
else:
dur = None
msg = ("\n**Title:** {}\n**Author:** {}\n**Uploader:** {}\n"
"**Views:** {}\n**Duration:** {}\n\n<{}>".format(
song.title, song.creator, song.uploader,
song.view_count, dur, song.webpage_url))
await self.bot.say(msg.replace("**Author:** None\n", "")
.replace("**Views:** None\n", "")
.replace("**Uploader:** None\n", "")
.replace("**Duration:** None\n", ""))
msg = ("**Author:** `{}`\n**Uploader:** `{}`\n"
"**Duration:** `{}`\n**Rating: **`{:.2f}`\n**Views:** `{}`".format(
song.creator, song.uploader, str(datetime.timedelta(seconds=song.duration)), song.rating,
song.view_count))
msg += self._draw_play(song, server) + "\n"
colour = ''.join([choice('0123456789ABCDEF') for x in range(6)])
em = discord.Embed(description="", colour=int(colour, 16))
if 'http' not in song.webpage_url:
em.set_author(name=song.title)
else:
em.set_author(name=song.title, url=song.webpage_url)
em.set_thumbnail(url=song.thumbnail)
em.description = msg.replace('None', '-')
await self.bot.say("**Currently Playing:**", embed=em)
else:
await self.bot.say("Darude - Sandstorm.")
@ -2072,6 +2190,9 @@ class Audio:
if stop_times[server] and \
int(time.time()) - stop_times[server] > 300:
# 5 min not playing to d/c
timer_disconnect = self.get_server_settings(server)
timer_disconnect = timer_disconnect.get("TIMER_DISCONNECT", True)
if timer_disconnect:
log.debug("dcing from sid {} after 300s".format(server.id))
self._clear_queue(server)
await self._stop_and_disconnect(server)
@ -2093,6 +2214,15 @@ class Audio:
if "NOPPL_DISCONNECT" not in ret:
ret["NOPPL_DISCONNECT"] = True
if "NOTIFY" not in ret:
ret["NOTIFY"] = False
if "NOTIFY_CHANNEL" not in ret:
ret["NOTIFY_CHANNEL"] = None
if "TIMER_DISCONNECT" not in ret:
ret["TIMER_DISCONNECT"] = True
for setting in self.server_specific_setting_keys:
if setting not in ret:
# Add the default
@ -2131,6 +2261,10 @@ class Audio:
"""This function assumes that there's something in the queue for us to
play"""
server = self.bot.get_server(sid)
if self.get_server_settings(server)["NOTIFY"] is True:
notify_channel = self.settings["SERVERS"][server.id]["NOTIFY_CHANNEL"]
if self.get_server_settings(server)["NOTIFY"] is False:
notify_channel = None
max_length = self.settings["MAX_LENGTH"]
# This is a reference, or should be at least
@ -2158,6 +2292,7 @@ class Audio:
url = queued_song.url
channel = queued_song.channel
song = await self._play(sid, url, channel)
await self.display_now_playing(server, song, notify_channel)
except MaximumLength:
return
elif len(queue) > 0: # We're in the normal queue
@ -2167,6 +2302,7 @@ class Audio:
log.debug("calling _play on the normal queue")
try:
song = await self._play(sid, url, channel)
await self.display_now_playing(server, song, notify_channel)
except MaximumLength:
return
if repeat and last_song:
@ -2210,6 +2346,47 @@ class Audio:
message = escape(message, mass_mentions=True)
await self.bot.send_message(next_channel, message)
async def display_now_playing(self, server, song, notify_channel:int):
channel = discord.utils.get(server.channels, id=notify_channel)
if channel is None:
return
if song.title is None:
return
def to_delete(m):
if "Now Playing" in m.content and m.author == self.bot.user:
return True
else:
return False
try:
await self.bot.purge_from(channel, limit=50, check=to_delete)
except discord.errors.Forbidden:
await self.bot.say("I need permissions to manage messages in this channel.")
if song:
if not hasattr(song, 'creator'):
song.creator = None
if not hasattr(song, 'uploader'):
song.uploader = None
if song.rating is None:
song.rating = 0
if song.thumbnail is None:
song.thumbnail = (self.bot.user.avatar_url).replace('webp', 'png')
msg = ("**Author:** `{}`\n**Uploader:** `{}`\n"
"**Duration:** `{}`\n**Rating: **`{:.2f}`\n**Views:** `{}`".format(
song.creator, song.uploader, str(datetime.timedelta(seconds=song.duration)), song.rating, song.view_count))
colour = ''.join([choice('0123456789ABCDEF') for x in range(6)])
em = discord.Embed(description="", colour=int(colour, 16))
if 'http' not in song.webpage_url:
em.set_author(name=song.title)
else:
em.set_author(name=song.title, url=song.webpage_url)
em.set_thumbnail(url=song.thumbnail)
em.description = msg.replace('None', '-')
await self.bot.send_message(channel, "**Now Playing:**", embed=em)
async def queue_scheduler(self):
while self == self.bot.get_cog('Audio'):
tasks = []