mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
[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:
parent
0dafcfa083
commit
8b69042f08
235
cogs/audio.py
235
cogs/audio.py
@ -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,10 +2190,13 @@ class Audio:
|
||||
if stop_times[server] and \
|
||||
int(time.time()) - stop_times[server] > 300:
|
||||
# 5 min not playing to d/c
|
||||
log.debug("dcing from sid {} after 300s".format(server.id))
|
||||
self._clear_queue(server)
|
||||
await self._stop_and_disconnect(server)
|
||||
stop_times[server] = None
|
||||
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)
|
||||
stop_times[server] = None
|
||||
await asyncio.sleep(5)
|
||||
|
||||
def get_server_settings(self, 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 = []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user