mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-07 03:38:53 -05:00
[Audio] Add error handling on failed song load (#887)
* Add error handling and user feedback to Audio cog * Remove verbose * Maybe? fix the OSError * Replace is with isinstance() * Attempt to force UTF-8 encoding * Fix unbound variable * Escape mass mentions on error output
This commit is contained in:
parent
cc596500a6
commit
78173bd68c
298
cogs/audio.py
298
cogs/audio.py
@ -5,7 +5,7 @@ import os
|
||||
from random import shuffle, choice
|
||||
from cogs.utils.dataIO import dataIO
|
||||
from cogs.utils import checks
|
||||
from cogs.utils.chat_formatting import pagify
|
||||
from cogs.utils.chat_formatting import pagify, escape
|
||||
from urllib.parse import urlparse
|
||||
from __main__ import send_cmd_help, settings
|
||||
from json import JSONDecodeError
|
||||
@ -18,6 +18,7 @@ import math
|
||||
import time
|
||||
import inspect
|
||||
import subprocess
|
||||
from enum import Enum
|
||||
|
||||
__author__ = "tekulvw"
|
||||
__version__ = "0.1.1"
|
||||
@ -45,11 +46,12 @@ youtube_dl_options = {
|
||||
'extractaudio': True,
|
||||
'audioformat': "mp3",
|
||||
'nocheckcertificate': True,
|
||||
'ignoreerrors': True,
|
||||
'ignoreerrors': False,
|
||||
'quiet': True,
|
||||
'no_warnings': True,
|
||||
'outtmpl': "data/audio/cache/%(id)s",
|
||||
'default_search': 'auto'
|
||||
'default_search': 'auto',
|
||||
'encoding': 'utf-8'
|
||||
}
|
||||
|
||||
|
||||
@ -59,7 +61,15 @@ class MaximumLength(Exception):
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
class YouTubeDlError(Exception):
|
||||
def __init__(self, m):
|
||||
self.message = m
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
|
||||
class NotConnected(Exception):
|
||||
pass
|
||||
@ -104,7 +114,6 @@ class InvalidSong(InvalidURL):
|
||||
class InvalidPlaylist(InvalidSong):
|
||||
pass
|
||||
|
||||
|
||||
class deque(collections.deque):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -119,6 +128,14 @@ class deque(collections.deque):
|
||||
self.appendleft(ret)
|
||||
return copy.deepcopy(ret)
|
||||
|
||||
class QueueKey(Enum):
|
||||
REPEAT = 1
|
||||
PLAYLIST = 2
|
||||
VOICE_CHANNEL_ID = 3
|
||||
QUEUE = 4
|
||||
TEMP_QUEUE = 5
|
||||
NOW_PLAYING = 6
|
||||
NOW_PLAYING_CHANNEL = 7
|
||||
|
||||
class Song:
|
||||
def __init__(self, **kwargs):
|
||||
@ -129,6 +146,10 @@ class Song:
|
||||
self.webpage_url = kwargs.pop('webpage_url', "")
|
||||
self.duration = kwargs.pop('duration', 60)
|
||||
|
||||
class QueuedSong:
|
||||
def __init__(self, url, channel):
|
||||
self.url = url
|
||||
self.channel = channel
|
||||
|
||||
class Playlist:
|
||||
def __init__(self, server=None, sid=None, name=None, author=None, url=None,
|
||||
@ -226,20 +247,22 @@ class Downloader(threading.Thread):
|
||||
self.max_duration = max_duration
|
||||
self.done = threading.Event()
|
||||
self.song = None
|
||||
self.failed = False
|
||||
self._download = download
|
||||
self.hit_max_length = threading.Event()
|
||||
self._yt = None
|
||||
self.error = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.get_info()
|
||||
if self._download:
|
||||
self.download()
|
||||
except youtube_dl.utils.DownloadError as e:
|
||||
self.error = str(e)
|
||||
except MaximumLength:
|
||||
self.hit_max_length.set()
|
||||
except:
|
||||
self.failed = True
|
||||
except OSError as e:
|
||||
log.warning("An operating system error occurred while downloading URL '{}':\n'{}'".format(self.url, str(e)))
|
||||
self.done.set()
|
||||
|
||||
def download(self):
|
||||
@ -272,7 +295,8 @@ class Downloader(threading.Thread):
|
||||
video = self._yt.extract_info(self.url, download=False,
|
||||
process=False)
|
||||
|
||||
self.song = Song(**video)
|
||||
if(video is not None):
|
||||
self.song = Song(**video)
|
||||
|
||||
|
||||
class Audio:
|
||||
@ -307,20 +331,23 @@ class Audio:
|
||||
await self.bot.change_presence(status=status, game=game)
|
||||
log.debug('Bot status changed to song title: ' + song.title)
|
||||
|
||||
def _add_to_queue(self, server, url):
|
||||
def _add_to_queue(self, server, url, channel):
|
||||
if server.id not in self.queue:
|
||||
self._setup_queue(server)
|
||||
self.queue[server.id]["QUEUE"].append(url)
|
||||
queued_song = QueuedSong(url, channel)
|
||||
self.queue[server.id][QueueKey.QUEUE].append(queued_song)
|
||||
|
||||
def _add_to_temp_queue(self, server, url):
|
||||
def _add_to_temp_queue(self, server, url, channel):
|
||||
if server.id not in self.queue:
|
||||
self._setup_queue(server)
|
||||
self.queue[server.id]["TEMP_QUEUE"].append(url)
|
||||
queued_song = QueuedSong(url, channel)
|
||||
self.queue[server.id][QueueKey.TEMP_QUEUE].append(queued_song)
|
||||
|
||||
def _addleft_to_queue(self, server, url):
|
||||
def _addleft_to_queue(self, server, url, channel):
|
||||
if server.id not in self.queue:
|
||||
self._setup_queue()
|
||||
self.queue[server.id]["QUEUE"].appendleft(url)
|
||||
queued_song = QueuedSong(url, channel)
|
||||
self.queue[server.id][QueueKey.QUEUE].appendleft(queued_song)
|
||||
|
||||
def _cache_desired_files(self):
|
||||
filelist = []
|
||||
@ -345,7 +372,7 @@ class Audio:
|
||||
queue = copy.deepcopy(self.queue)
|
||||
filelist = []
|
||||
for server in queue:
|
||||
now_playing = queue[server].get("NOW_PLAYING")
|
||||
now_playing = queue[server].get(QueueKey.NOW_PLAYING)
|
||||
try:
|
||||
filelist.append(now_playing.id)
|
||||
except AttributeError:
|
||||
@ -366,13 +393,13 @@ class Audio:
|
||||
def _clear_queue(self, server):
|
||||
if server.id not in self.queue:
|
||||
return
|
||||
self.queue[server.id]["QUEUE"] = deque()
|
||||
self.queue[server.id]["TEMP_QUEUE"] = deque()
|
||||
self.queue[server.id][QueueKey.QUEUE] = deque()
|
||||
self.queue[server.id][QueueKey.TEMP_QUEUE] = deque()
|
||||
|
||||
async def _create_ffmpeg_player(self, server, filename, local=False):
|
||||
"""This function will guarantee we have a valid voice client,
|
||||
even if one doesn't exist previously."""
|
||||
voice_channel_id = self.queue[server.id]["VOICE_CHANNEL_ID"]
|
||||
voice_channel_id = self.queue[server.id][QueueKey.VOICE_CHANNEL_ID]
|
||||
voice_client = self.voice_client(server)
|
||||
|
||||
if voice_client is None:
|
||||
@ -389,7 +416,7 @@ class Audio:
|
||||
await self._join_voice_channel(to_connect) # SHIT
|
||||
elif voice_client.channel.id != voice_channel_id:
|
||||
# This was decided at 3:45 EST in #advanced-testing by 26
|
||||
self.queue[server.id]["VOICE_CHANNEL_ID"] = voice_client.channel.id
|
||||
self.queue[server.id][QueueKey.VOICE_CHANNEL_ID] = voice_client.channel.id
|
||||
log.debug("reconnect chan id for sid {} is wrong, fixing".format(
|
||||
server.id))
|
||||
|
||||
@ -446,20 +473,27 @@ class Audio:
|
||||
|
||||
await voice_client.disconnect()
|
||||
|
||||
async def _download_all(self, url_list):
|
||||
async def _download_all(self, queued_song_list, channel):
|
||||
"""
|
||||
Doesn't actually download, just get's info for uses like queue_list
|
||||
"""
|
||||
downloaders = []
|
||||
for url in url_list:
|
||||
d = Downloader(url)
|
||||
for queued_song in queued_song_list:
|
||||
d = Downloader(queued_song.url)
|
||||
d.start()
|
||||
downloaders.append(d)
|
||||
|
||||
while any([d.is_alive() for d in downloaders]):
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
songs = [d.song for d in downloaders if d.song is not None and d.error is None]
|
||||
|
||||
invalid_downloads = [d for d in downloaders if d.error is not None]
|
||||
invalid_number = len(invalid_downloads)
|
||||
if(invalid_number > 0):
|
||||
await self.bot.send_message(channel, "The queue contains {} item(s)"
|
||||
" that can not be played.".format(invalid_number))
|
||||
|
||||
songs = [d.song for d in downloaders if d.song is not None]
|
||||
return songs
|
||||
|
||||
async def _download_next(self, server, curr_dl, next_dl):
|
||||
@ -475,6 +509,10 @@ class Audio:
|
||||
|
||||
while next_dl.is_alive():
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
error = next_dl.error
|
||||
if(error is not None):
|
||||
raise YouTubeDlError(error)
|
||||
|
||||
if curr_dl.song.id != next_dl.song.id:
|
||||
log.debug("downloader ID's mismatch on sid {}".format(server.id) +
|
||||
@ -539,7 +577,7 @@ class Audio:
|
||||
ret = []
|
||||
for i in range(limit):
|
||||
try:
|
||||
ret.append(self.queue[server.id]["QUEUE"][i])
|
||||
ret.append(self.queue[server.id][QueueKey.QUEUE][i])
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
@ -549,19 +587,25 @@ class Audio:
|
||||
if server.id not in self.queue:
|
||||
return None
|
||||
|
||||
return self.queue[server.id]["NOW_PLAYING"]
|
||||
return self.queue[server.id][QueueKey.NOW_PLAYING]
|
||||
|
||||
def _get_queue_nowplaying_channel(self, server):
|
||||
if server.id not in self.queue:
|
||||
return None
|
||||
|
||||
return self.queue[server.id][QueueKey.NOW_PLAYING_CHANNEL]
|
||||
|
||||
def _get_queue_playlist(self, server):
|
||||
if server.id not in self.queue:
|
||||
return None
|
||||
|
||||
return self.queue[server.id]["PLAYLIST"]
|
||||
return self.queue[server.id][QueueKey.PLAYLIST]
|
||||
|
||||
def _get_queue_repeat(self, server):
|
||||
if server.id not in self.queue:
|
||||
return None
|
||||
|
||||
return self.queue[server.id]["REPEAT"]
|
||||
return self.queue[server.id][QueueKey.REPEAT]
|
||||
|
||||
def _get_queue_tempqueue(self, server, limit):
|
||||
if server.id not in self.queue:
|
||||
@ -570,7 +614,7 @@ class Audio:
|
||||
ret = []
|
||||
for i in range(limit):
|
||||
try:
|
||||
ret.append(self.queue[server.id]["TEMP_QUEUE"][i])
|
||||
ret.append(self.queue[server.id][QueueKey.TEMP_QUEUE][i])
|
||||
except IndexError:
|
||||
pass
|
||||
return ret
|
||||
@ -598,6 +642,11 @@ class Audio:
|
||||
|
||||
# Getting info w/o download
|
||||
self.downloaders[server.id].done.wait()
|
||||
|
||||
# Youtube-DL threw an exception.
|
||||
error = self.downloaders[server.id].error
|
||||
if(error is not None):
|
||||
raise YouTubeDlError(error)
|
||||
|
||||
# This will throw a maxlength exception if required
|
||||
self.downloaders[server.id].duration_check()
|
||||
@ -626,7 +675,7 @@ class Audio:
|
||||
if server.id not in self.queue:
|
||||
return False
|
||||
|
||||
return self.queue[server.id]["PLAYLIST"]
|
||||
return self.queue[server.id][QueueKey.PLAYLIST]
|
||||
|
||||
async def _join_voice_channel(self, channel):
|
||||
server = channel.server
|
||||
@ -636,7 +685,7 @@ class Audio:
|
||||
raise ConnectTimeout("You are on connect cooldown for another {}"
|
||||
" seconds.".format(diff))
|
||||
if server.id in self.queue:
|
||||
self.queue[server.id]["VOICE_CHANNEL_ID"] = channel.id
|
||||
self.queue[server.id][QueueKey.VOICE_CHANNEL_ID] = channel.id
|
||||
try:
|
||||
await asyncio.wait_for(self.bot.join_voice_channel(channel),
|
||||
timeout=5, loop=self.bot.loop)
|
||||
@ -761,6 +810,10 @@ class Audio:
|
||||
while d.is_alive():
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
error = d.error
|
||||
if(error is not None):
|
||||
raise YouTubeDlError(error)
|
||||
|
||||
for entry in d.song.entries:
|
||||
if entry["url"][4] != "s":
|
||||
song_url = "https{}".format(entry["url"][4:])
|
||||
@ -778,6 +831,10 @@ class Audio:
|
||||
while d.is_alive():
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
error = d.error
|
||||
if(error is not None):
|
||||
raise YouTubeDlError(error)
|
||||
|
||||
for entry in d.song.entries:
|
||||
try:
|
||||
song_url = "https://www.youtube.com/watch?v={}".format(
|
||||
@ -792,7 +849,7 @@ class Audio:
|
||||
|
||||
return playlist
|
||||
|
||||
async def _play(self, sid, url):
|
||||
async def _play(self, sid, url, channel):
|
||||
"""Returns the song object of what's playing"""
|
||||
if type(sid) is not discord.Server:
|
||||
server = self.bot.get_server(sid)
|
||||
@ -803,13 +860,21 @@ class Audio:
|
||||
log.debug('starting to play on "{}"'.format(server.name))
|
||||
|
||||
if self._valid_playable_url(url) or "[SEARCH:]" in url:
|
||||
clean_url = self._clean_url(url)
|
||||
try:
|
||||
song = await self._guarantee_downloaded(server, url)
|
||||
except YouTubeDlError as e:
|
||||
message = ("I'm unable to play '{}' because of an error:\n"
|
||||
"'{}'".format(clean_url, str(e)))
|
||||
message = escape(message, mass_mentions=True)
|
||||
await self.bot.send_message(channel, message)
|
||||
return
|
||||
except MaximumLength:
|
||||
log.warning("I can't play URL below because it is too long."
|
||||
" Use [p]audioset maxlength to change this.\n\n"
|
||||
"{}".format(url))
|
||||
raise
|
||||
message = ("I'm unable to play '{}' because it exceeds the "
|
||||
"maximum audio length.".format(clean_url))
|
||||
message = escape(message, mass_mentions=True)
|
||||
await self.bot.send_message(channel, message)
|
||||
return
|
||||
local = False
|
||||
else: # Assume local
|
||||
try:
|
||||
@ -827,13 +892,15 @@ class Audio:
|
||||
|
||||
return song
|
||||
|
||||
def _play_playlist(self, server, playlist):
|
||||
def _play_playlist(self, server, playlist, channel):
|
||||
try:
|
||||
songlist = playlist.playlist
|
||||
name = playlist.name
|
||||
except AttributeError:
|
||||
songlist = playlist
|
||||
name = True
|
||||
|
||||
songlist = self._songlist_change_url_to_queued_song(songlist, channel)
|
||||
|
||||
log.debug("setting up playlist {} on sid {}".format(name, server.id))
|
||||
|
||||
@ -848,7 +915,7 @@ class Audio:
|
||||
self._set_queue_repeat(server, True)
|
||||
self._set_queue(server, songlist)
|
||||
|
||||
def _play_local_playlist(self, server, name):
|
||||
def _play_local_playlist(self, server, name, channel):
|
||||
songlist = self._local_playlist_songlist(name)
|
||||
|
||||
ret = []
|
||||
@ -856,7 +923,15 @@ class Audio:
|
||||
ret.append(os.path.join(name, song))
|
||||
|
||||
ret_playlist = Playlist(server=server, name=name, playlist=ret)
|
||||
self._play_playlist(server, ret_playlist)
|
||||
self._play_playlist(server, ret_playlist, channel)
|
||||
|
||||
def _songlist_change_url_to_queued_song(self, songlist, channel):
|
||||
queued_songlist = []
|
||||
for song in songlist:
|
||||
queued_song = QueuedSong(song, channel)
|
||||
queued_songlist.append(queued_song)
|
||||
|
||||
return queued_songlist
|
||||
|
||||
def _player_count(self):
|
||||
count = 0
|
||||
@ -924,10 +999,10 @@ class Audio:
|
||||
dataIO.save_json(f, playlist)
|
||||
|
||||
def _shuffle_queue(self, server):
|
||||
shuffle(self.queue[server.id]["QUEUE"])
|
||||
shuffle(self.queue[server.id][QueueKey.QUEUE])
|
||||
|
||||
def _shuffle_temp_queue(self, server):
|
||||
shuffle(self.queue[server.id]["TEMP_QUEUE"])
|
||||
shuffle(self.queue[server.id][QueueKey.TEMP_QUEUE])
|
||||
|
||||
def _server_count(self):
|
||||
return max([1, len(self.bot.servers)])
|
||||
@ -937,7 +1012,7 @@ class Audio:
|
||||
self._clear_queue(server)
|
||||
else:
|
||||
self._setup_queue(server)
|
||||
self.queue[server.id]["QUEUE"].extend(songlist)
|
||||
self.queue[server.id][QueueKey.QUEUE].extend(songlist)
|
||||
|
||||
def _set_queue_channel(self, server, channel):
|
||||
if server.id not in self.queue:
|
||||
@ -948,31 +1023,32 @@ class Audio:
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
self.queue[server.id]["VOICE_CHANNEL_ID"] = channel
|
||||
self.queue[server.id][QueueKey.VOICE_CHANNEL_ID] = channel
|
||||
|
||||
def _set_queue_nowplaying(self, server, song):
|
||||
def _set_queue_nowplaying(self, server, song, channel):
|
||||
if server.id not in self.queue:
|
||||
return
|
||||
|
||||
self.queue[server.id]["NOW_PLAYING"] = song
|
||||
self.queue[server.id][QueueKey.NOW_PLAYING] = song
|
||||
self.queue[server.id][QueueKey.NOW_PLAYING_CHANNEL] = channel
|
||||
|
||||
def _set_queue_playlist(self, server, name=True):
|
||||
if server.id not in self.queue:
|
||||
self._setup_queue(server)
|
||||
|
||||
self.queue[server.id]["PLAYLIST"] = name
|
||||
self.queue[server.id][QueueKey.PLAYLIST] = name
|
||||
|
||||
def _set_queue_repeat(self, server, value):
|
||||
if server.id not in self.queue:
|
||||
self._setup_queue(server)
|
||||
|
||||
self.queue[server.id]["REPEAT"] = value
|
||||
self.queue[server.id][QueueKey.REPEAT] = value
|
||||
|
||||
def _setup_queue(self, server):
|
||||
self.queue[server.id] = {"REPEAT": False, "PLAYLIST": False,
|
||||
"VOICE_CHANNEL_ID": None,
|
||||
"QUEUE": deque(), "TEMP_QUEUE": deque(),
|
||||
"NOW_PLAYING": None}
|
||||
self.queue[server.id] = {QueueKey.REPEAT: False, QueueKey.PLAYLIST: False,
|
||||
QueueKey.VOICE_CHANNEL_ID: None,
|
||||
QueueKey.QUEUE: deque(), QueueKey.TEMP_QUEUE: deque(),
|
||||
QueueKey.NOW_PLAYING: None, QueueKey.NOW_PLAYING_CHANNEL: None}
|
||||
|
||||
def _stop(self, server):
|
||||
self._setup_queue(server)
|
||||
@ -1011,7 +1087,7 @@ class Audio:
|
||||
return
|
||||
if len(active_servers) == 1:
|
||||
server = active_servers[0].server
|
||||
song = self.queue[server.id]["NOW_PLAYING"]
|
||||
song = self._get_queue_nowplaying(server)
|
||||
if song:
|
||||
await self._add_song_status(song)
|
||||
else:
|
||||
@ -1031,6 +1107,12 @@ class Audio:
|
||||
if yt or sc: # TODO: Add sc check
|
||||
return True
|
||||
return False
|
||||
|
||||
def _clean_url(self, url):
|
||||
if(self._valid_playable_url(url)):
|
||||
return "<{}>".format(url)
|
||||
|
||||
return url.replace("[SEARCH:]", "")
|
||||
|
||||
@commands.group(pass_context=True)
|
||||
async def audioset(self, ctx):
|
||||
@ -1245,6 +1327,7 @@ class Audio:
|
||||
server = ctx.message.server
|
||||
author = ctx.message.author
|
||||
voice_channel = author.voice_channel
|
||||
channel = ctx.message.channel
|
||||
|
||||
# Checking already connected, will join if not
|
||||
|
||||
@ -1291,7 +1374,7 @@ class Audio:
|
||||
await self.bot.say("Local playlist not found.")
|
||||
return
|
||||
|
||||
self._play_local_playlist(server, name)
|
||||
self._play_local_playlist(server, name, channel)
|
||||
|
||||
@local.command(name="list", no_pm=True)
|
||||
async def list_local(self):
|
||||
@ -1330,6 +1413,7 @@ class Audio:
|
||||
server = ctx.message.server
|
||||
author = ctx.message.author
|
||||
voice_channel = author.voice_channel
|
||||
channel = ctx.message.channel
|
||||
|
||||
# Checking if playing in current server
|
||||
|
||||
@ -1386,13 +1470,14 @@ class Audio:
|
||||
|
||||
self._stop_player(server)
|
||||
self._clear_queue(server)
|
||||
self._add_to_queue(server, url)
|
||||
self._add_to_queue(server, url, channel)
|
||||
|
||||
@commands.command(pass_context=True, no_pm=True)
|
||||
async def prev(self, ctx):
|
||||
"""Goes back to the last song."""
|
||||
# Current song is in NOW_PLAYING
|
||||
server = ctx.message.server
|
||||
channel = ctx.message.channel
|
||||
|
||||
if self.is_playing(server):
|
||||
curr_url = self._get_queue_nowplaying(server).webpage_url
|
||||
@ -1400,17 +1485,17 @@ class Audio:
|
||||
if self._is_queue_playlist(server):
|
||||
# need to reorder queue
|
||||
try:
|
||||
last_url = self.queue[server.id]["QUEUE"].pop()
|
||||
last_url = self.queue[server.id][QueueKey.QUEUE].pop()
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
log.debug("prev on sid {}, curr_url {}".format(server.id,
|
||||
curr_url))
|
||||
|
||||
self._addleft_to_queue(server, curr_url)
|
||||
self._addleft_to_queue(server, curr_url, channel)
|
||||
if last_url:
|
||||
self._addleft_to_queue(server, last_url)
|
||||
self._set_queue_nowplaying(server, None)
|
||||
self._addleft_to_queue(server, last_url, channel)
|
||||
self._set_queue_nowplaying(server, None, None)
|
||||
|
||||
self.voice_client(server).audio_player.stop()
|
||||
|
||||
@ -1463,7 +1548,11 @@ class Audio:
|
||||
except InvalidPlaylist:
|
||||
await self.bot.say("That playlist URL is invalid.")
|
||||
return
|
||||
|
||||
except YouTubeDlError as e:
|
||||
await self.bot.say("An error occurred while enumerating the playlist:\n"
|
||||
"'{}'".format(str(e)))
|
||||
return
|
||||
|
||||
playlist = self._make_playlist(author, url, songlist)
|
||||
# Returns a Playlist object
|
||||
|
||||
@ -1522,6 +1611,7 @@ class Audio:
|
||||
|
||||
Does NOT write to disk."""
|
||||
server = ctx.message.server
|
||||
channel = ctx.message.channel
|
||||
if not self.voice_connected(server):
|
||||
await self.bot.say("Not voice connected in this server.")
|
||||
return
|
||||
@ -1535,7 +1625,7 @@ class Audio:
|
||||
" happen.")
|
||||
|
||||
# We have a queue to modify
|
||||
self._add_to_queue(server, url)
|
||||
self._add_to_queue(server, url, channel)
|
||||
|
||||
await self.bot.say("Queued.")
|
||||
|
||||
@ -1570,6 +1660,7 @@ class Audio:
|
||||
server = ctx.message.server
|
||||
author = ctx.message.author
|
||||
voice_channel = ctx.message.author.voice_channel
|
||||
channel = ctx.message.channel
|
||||
|
||||
caller = inspect.currentframe().f_back.f_code.co_name
|
||||
|
||||
@ -1606,7 +1697,7 @@ class Audio:
|
||||
if caller == "playlist_start_mix":
|
||||
shuffle(playlist.playlist)
|
||||
|
||||
self._play_playlist(server, playlist)
|
||||
self._play_playlist(server, playlist, channel)
|
||||
await self.bot.say("Playlist queued.")
|
||||
else:
|
||||
await self.bot.say("That playlist does not exist.")
|
||||
@ -1627,6 +1718,7 @@ class Audio:
|
||||
if url is None:
|
||||
return await self._queue_list(ctx)
|
||||
server = ctx.message.server
|
||||
channel = ctx.message.channel
|
||||
if not self.voice_connected(server):
|
||||
await ctx.invoke(self.play, url_or_search_terms=url)
|
||||
return
|
||||
@ -1652,23 +1744,24 @@ class Audio:
|
||||
url = url.split("&")[0] # Temp fix for the &list issue
|
||||
|
||||
# We have a queue to modify
|
||||
if self.queue[server.id]["PLAYLIST"]:
|
||||
if self.queue[server.id][QueueKey.PLAYLIST]:
|
||||
log.debug("queueing to the temp_queue for sid {}".format(
|
||||
server.id))
|
||||
self._add_to_temp_queue(server, url)
|
||||
self._add_to_temp_queue(server, url, channel)
|
||||
else:
|
||||
log.debug("queueing to the actual queue for sid {}".format(
|
||||
server.id))
|
||||
self._add_to_queue(server, url)
|
||||
self._add_to_queue(server, url, channel)
|
||||
await self.bot.say("Queued.")
|
||||
|
||||
async def _queue_list(self, ctx):
|
||||
"""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:
|
||||
await self.bot.say("Nothing playing on this server!")
|
||||
return
|
||||
elif len(self.queue[server.id]["QUEUE"]) == 0:
|
||||
elif len(self.queue[server.id][QueueKey.QUEUE]) == 0:
|
||||
await self.bot.say("Nothing queued on this server.")
|
||||
return
|
||||
|
||||
@ -1679,13 +1772,13 @@ class Audio:
|
||||
if now_playing is not None:
|
||||
msg += "\n***Now playing:***\n{}\n".format(now_playing.title)
|
||||
|
||||
queue_url_list = self._get_queue(server, 5)
|
||||
tempqueue_url_list = self._get_queue_tempqueue(server, 5)
|
||||
queued_song_list = self._get_queue(server, 5)
|
||||
tempqueued_song_list = self._get_queue_tempqueue(server, 5)
|
||||
|
||||
await self.bot.say("Gathering information...")
|
||||
|
||||
queue_song_list = await self._download_all(queue_url_list)
|
||||
tempqueue_song_list = await self._download_all(tempqueue_url_list)
|
||||
queue_song_list = await self._download_all(queued_song_list, channel)
|
||||
tempqueue_song_list = await self._download_all(tempqueued_song_list, channel)
|
||||
|
||||
song_info = []
|
||||
for num, song in enumerate(tempqueue_song_list, 1):
|
||||
@ -1711,7 +1804,7 @@ class Audio:
|
||||
server = ctx.message.server
|
||||
if ctx.invoked_subcommand is None:
|
||||
if self.is_playing(server):
|
||||
if self.queue[server.id]["REPEAT"]:
|
||||
if self.queue[server.id][QueueKey.REPEAT]:
|
||||
msg = "The queue is currently looping."
|
||||
else:
|
||||
msg = "The queue is currently not looping."
|
||||
@ -1730,8 +1823,8 @@ class Audio:
|
||||
" Try playing something first.")
|
||||
return
|
||||
|
||||
self._set_queue_repeat(server, not self.queue[server.id]["REPEAT"])
|
||||
repeat = self.queue[server.id]["REPEAT"]
|
||||
self._set_queue_repeat(server, not self.queue[server.id][QueueKey.REPEAT])
|
||||
repeat = self.queue[server.id][QueueKey.REPEAT]
|
||||
if repeat:
|
||||
await self.bot.say("Repeat toggled on.")
|
||||
else:
|
||||
@ -1784,7 +1877,7 @@ class Audio:
|
||||
if self.can_instaskip(msg.author):
|
||||
vc.audio_player.stop()
|
||||
if self._get_queue_repeat(server) is False:
|
||||
self._set_queue_nowplaying(server, None)
|
||||
self._set_queue_nowplaying(server, None, None)
|
||||
await self.bot.say("Skipping...")
|
||||
else:
|
||||
if msg.author.id in self.skip_votes[server.id]:
|
||||
@ -1804,7 +1897,7 @@ class Audio:
|
||||
if vote >= thresh:
|
||||
vc.audio_player.stop()
|
||||
if self._get_queue_repeat(server) is False:
|
||||
self._set_queue_nowplaying(server, None)
|
||||
self._set_queue_nowplaying(server, None, None)
|
||||
self.skip_votes[server.id] = []
|
||||
await self.bot.say("Vote threshold met. Skipping...")
|
||||
return
|
||||
@ -2028,13 +2121,14 @@ class Audio:
|
||||
max_length = self.settings["MAX_LENGTH"]
|
||||
|
||||
# This is a reference, or should be at least
|
||||
temp_queue = self.queue[server.id]["TEMP_QUEUE"]
|
||||
queue = self.queue[server.id]["QUEUE"]
|
||||
repeat = self.queue[server.id]["REPEAT"]
|
||||
last_song = self.queue[server.id]["NOW_PLAYING"]
|
||||
temp_queue = self.queue[server.id][QueueKey.TEMP_QUEUE]
|
||||
queue = self.queue[server.id][QueueKey.QUEUE]
|
||||
repeat = self.queue[server.id][QueueKey.REPEAT]
|
||||
last_song = self.queue[server.id][QueueKey.NOW_PLAYING]
|
||||
last_song_channel = self.queue[server.id][QueueKey.NOW_PLAYING_CHANNEL]
|
||||
|
||||
assert temp_queue is self.queue[server.id]["TEMP_QUEUE"]
|
||||
assert queue is self.queue[server.id]["QUEUE"]
|
||||
assert temp_queue is self.queue[server.id][QueueKey.TEMP_QUEUE]
|
||||
assert queue is self.queue[server.id][QueueKey.QUEUE]
|
||||
|
||||
# _play handles creating the voice_client and player for us
|
||||
|
||||
@ -2047,21 +2141,27 @@ class Audio:
|
||||
# Fake queue for irdumb's temp playlist songs
|
||||
log.debug("calling _play because temp_queue is non-empty")
|
||||
try:
|
||||
song = await self._play(sid, temp_queue.popleft())
|
||||
queued_song = temp_queue.popleft()
|
||||
url = queued_song.url
|
||||
channel = queued_song.channel
|
||||
song = await self._play(sid, url, channel)
|
||||
except MaximumLength:
|
||||
return
|
||||
elif len(queue) > 0: # We're in the normal queue
|
||||
url = queue.popleft()
|
||||
queued_song = queue.popleft()
|
||||
url = queued_song.url
|
||||
channel = queued_song.channel
|
||||
log.debug("calling _play on the normal queue")
|
||||
try:
|
||||
song = await self._play(sid, url)
|
||||
song = await self._play(sid, url, channel)
|
||||
except MaximumLength:
|
||||
return
|
||||
if repeat and last_song:
|
||||
queued_last_song = QueuedSong(last_song.webpage_url, last_song_channel)
|
||||
queue.append(last_song.webpage_url)
|
||||
else:
|
||||
song = None
|
||||
self.queue[server.id]["NOW_PLAYING"] = song
|
||||
self._set_queue_nowplaying(server, song, channel)
|
||||
log.debug("set now_playing for sid {}".format(server.id))
|
||||
self.bot.loop.create_task(self._update_bot_status())
|
||||
|
||||
@ -2069,25 +2169,41 @@ class Audio:
|
||||
# We're playing but we might be able to download a new song
|
||||
curr_dl = self.downloaders.get(server.id)
|
||||
if len(temp_queue) > 0:
|
||||
next_dl = Downloader(temp_queue.peekleft(),
|
||||
max_length)
|
||||
queued_next_song = temp_queue.peekleft()
|
||||
next_url = queued_next_song.url
|
||||
next_channel = queued_next_song.channel
|
||||
next_dl = Downloader(next_url, max_length)
|
||||
elif len(queue) > 0:
|
||||
next_dl = Downloader(queue.peekleft(), max_length)
|
||||
queued_next_song = queue.peekleft()
|
||||
next_url = queued_next_song.url
|
||||
next_channel = queued_next_song.channel
|
||||
next_dl = Downloader(next_url, max_length)
|
||||
else:
|
||||
next_dl = None
|
||||
|
||||
if next_dl is not None:
|
||||
# Download next song
|
||||
next_dl.start()
|
||||
await self._download_next(server, curr_dl, next_dl)
|
||||
try:
|
||||
# Download next song
|
||||
next_dl.start()
|
||||
await self._download_next(server, curr_dl, next_dl)
|
||||
except YouTubeDlError as e:
|
||||
if len(temp_queue) > 0:
|
||||
temp_queue.popleft()
|
||||
elif len(queue) > 0:
|
||||
queue.popleft()
|
||||
clean_url = self._clean_url(next_url)
|
||||
message = ("I'm unable to play '{}' because of an "
|
||||
"error:\n'{}'".format(clean_url, str(e)))
|
||||
message = escape(message, mass_mentions=True)
|
||||
await self.bot.send_message(next_channel, message)
|
||||
|
||||
async def queue_scheduler(self):
|
||||
while self == self.bot.get_cog('Audio'):
|
||||
tasks = []
|
||||
queue = copy.deepcopy(self.queue)
|
||||
for sid in queue:
|
||||
if len(queue[sid]["QUEUE"]) == 0 and \
|
||||
len(queue[sid]["TEMP_QUEUE"]) == 0:
|
||||
if len(queue[sid][QueueKey.QUEUE]) == 0 and \
|
||||
len(queue[sid][QueueKey.TEMP_QUEUE]) == 0:
|
||||
continue
|
||||
# log.debug("scheduler found a non-empty queue"
|
||||
# " for sid: {}".format(sid))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user