mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
Audio patch (#292)
* PEP8 * deal with duration problem * Limit output stream to 64k * stop duration checks over and over for _download_next * reset state correctly on stop and skip
This commit is contained in:
parent
a2e79e1c5a
commit
c6f0f1ee1c
116
cogs/audio.py
116
cogs/audio.py
@ -70,15 +70,19 @@ class AuthorNotConnected(NotConnected):
|
||||
class VoiceNotConnected(NotConnected):
|
||||
pass
|
||||
|
||||
|
||||
class UnauthorizedConnect(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnauthorizedSpeak(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnauthorizedSave(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ConnectTimeout(NotConnected):
|
||||
pass
|
||||
|
||||
@ -127,7 +131,7 @@ class Playlist:
|
||||
self.name = name
|
||||
self.author = author
|
||||
self.url = url
|
||||
self.main_class = main_class # reference to Audio
|
||||
self.main_class = main_class # reference to Audio
|
||||
self.path = path
|
||||
|
||||
if url is None and "link" in kwargs:
|
||||
@ -173,10 +177,11 @@ class Downloader(threading.Thread):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.url = url
|
||||
self.max_duration = max_duration
|
||||
self.done = False
|
||||
self.done = threading.Event()
|
||||
self.song = None
|
||||
self.failed = False
|
||||
self._download = download
|
||||
self.hit_max_length = threading.Event()
|
||||
self._yt = None
|
||||
|
||||
def run(self):
|
||||
@ -184,22 +189,27 @@ class Downloader(threading.Thread):
|
||||
self.get_info()
|
||||
if self._download:
|
||||
self.download()
|
||||
except MaximumLength:
|
||||
self.hit_max_length.set()
|
||||
except:
|
||||
self.done = True
|
||||
self.failed = True
|
||||
else:
|
||||
self.done = True
|
||||
self.done.set()
|
||||
|
||||
def download(self):
|
||||
if self.max_duration and self.song.duration > self.max_duration:
|
||||
log.debug("not downloading {} because of duration".format(
|
||||
self.song.id))
|
||||
return
|
||||
self.duration_check()
|
||||
|
||||
if not os.path.isfile('data/audio/cache' + self.song.id):
|
||||
video = self._yt.extract_info(self.url)
|
||||
self.song = Song(**video)
|
||||
|
||||
def duration_check(self):
|
||||
log.debug("duration {} for songid {}".format(self.song.duration,
|
||||
self.song.id))
|
||||
if self.max_duration and self.song.duration > self.max_duration:
|
||||
log.debug("songid {} too long".format(self.song.id))
|
||||
raise MaximumLength("songid {} has duration {} > {}".format(
|
||||
self.song.id, self.song.duration, self.max_duration))
|
||||
|
||||
def get_info(self):
|
||||
if self._yt is None:
|
||||
self._yt = youtube_dl.YoutubeDL(youtube_dl_options)
|
||||
@ -208,8 +218,9 @@ class Downloader(threading.Thread):
|
||||
process=False)
|
||||
else:
|
||||
self.url = self.url[9:]
|
||||
yt_id = self._yt.extract_info(self.url,
|
||||
download=False)["entries"][0]["id"] # Should handle errors here.
|
||||
yt_id = self._yt.extract_info(
|
||||
self.url, download=False)["entries"][0]["id"]
|
||||
# Should handle errors here ^
|
||||
self.url = "https://youtube.com/watch?v={}".format(yt_id)
|
||||
video = self._yt.extract_info(self.url, download=False,
|
||||
process=False)
|
||||
@ -325,7 +336,8 @@ class Audio:
|
||||
|
||||
use_avconv = self.settings["AVCONV"]
|
||||
volume = self.get_server_settings(server)["VOLUME"] / 100
|
||||
options = '-filter "volume=volume={}"'.format(volume)
|
||||
options = \
|
||||
'-filter "volume=volume={}" -b:a 64k -bufsize 64k'.format(volume)
|
||||
|
||||
try:
|
||||
voice_client.audio_player.process.kill()
|
||||
@ -384,6 +396,10 @@ class Audio:
|
||||
log.debug("downloader ID's mismatch on sid {}".format(server.id) +
|
||||
" gonna start dl-ing the next thing on the queue"
|
||||
" id {}".format(next_dl.song.id))
|
||||
try:
|
||||
next_dl.duration_check()
|
||||
except MaximumLength:
|
||||
return
|
||||
self.downloaders[server.id] = Downloader(next_dl.url, max_length,
|
||||
download=True)
|
||||
self.downloaders[server.id].start()
|
||||
@ -428,6 +444,18 @@ class Audio:
|
||||
|
||||
return self.queue[server.id]["NOW_PLAYING"]
|
||||
|
||||
def _get_queue_playlist(self, server):
|
||||
if server.id not in self.queue:
|
||||
return None
|
||||
|
||||
return self.queue[server.id]["PLAYLIST"]
|
||||
|
||||
def _get_queue_repeat(self, server):
|
||||
if server.id not in self.queue:
|
||||
return None
|
||||
|
||||
return self.queue[server.id]["REPEAT"]
|
||||
|
||||
async def _guarantee_downloaded(self, server, url):
|
||||
max_length = self.settings["MAX_LENGTH"]
|
||||
if server.id not in self.downloaders: # We don't have a downloader
|
||||
@ -449,10 +477,13 @@ class Audio:
|
||||
# Queue manager already started it for us, isn't that nice?
|
||||
pass
|
||||
|
||||
while self.downloaders[server.id].is_alive(): # Getting info w/o DL
|
||||
await asyncio.sleep(0.5)
|
||||
# Getting info w/o download
|
||||
self.downloaders[server.id].done.wait()
|
||||
|
||||
# This will throw a maxlength exception if required
|
||||
self.downloaders[server.id].duration_check()
|
||||
song = self.downloaders[server.id].song
|
||||
|
||||
log.debug("sid {} wants to play songid {}".format(server.id, song.id))
|
||||
|
||||
# Now we check to see if we have a cache hit
|
||||
@ -645,7 +676,13 @@ class Audio:
|
||||
log.debug('starting to play on "{}"'.format(server.name))
|
||||
|
||||
if self._valid_playable_url(url) or "[SEARCH:]" in url:
|
||||
song = await self._guarantee_downloaded(server, url)
|
||||
try:
|
||||
song = await self._guarantee_downloaded(server, url)
|
||||
except MaximumLength:
|
||||
log.warning("I can't play URL below because it is too long."
|
||||
" Use {}audioset maxlength to change this.\n\n"
|
||||
"{}".format(self.bot.command_prefix[0], url))
|
||||
raise
|
||||
local = False
|
||||
else: # Assume local
|
||||
try:
|
||||
@ -803,7 +840,7 @@ class Audio:
|
||||
"NOW_PLAYING": None}
|
||||
|
||||
def _stop(self, server):
|
||||
self._clear_queue(server)
|
||||
self._setup_queue(server)
|
||||
self._stop_player(server)
|
||||
self._stop_downloader(server)
|
||||
|
||||
@ -1000,7 +1037,7 @@ class Audio:
|
||||
|
||||
if not self.voice_connected(server):
|
||||
try:
|
||||
can_connect = self.has_connect_perm(author, server)
|
||||
self.has_connect_perm(author, server)
|
||||
except AuthorNotConnected:
|
||||
await self.bot.say("You must join a voice channel before I can"
|
||||
" play anything.")
|
||||
@ -1075,11 +1112,9 @@ class Audio:
|
||||
|
||||
# Checking already connected, will join if not
|
||||
|
||||
caller = inspect.currentframe().f_back.f_code.co_name
|
||||
|
||||
if not self.voice_connected(server):
|
||||
try:
|
||||
can_connect = self.has_connect_perm(author, server)
|
||||
self.has_connect_perm(author, server)
|
||||
except AuthorNotConnected:
|
||||
await self.bot.say("You must join a voice channel before I can"
|
||||
" play anything.")
|
||||
@ -1179,8 +1214,8 @@ class Audio:
|
||||
playlist.server = server
|
||||
|
||||
self._save_playlist(server, name, playlist)
|
||||
await self.bot.say("Playlist '{}' saved. Tracks: {}".format(name,
|
||||
len(songlist)))
|
||||
await self.bot.say("Playlist '{}' saved. Tracks: {}".format(
|
||||
name, len(songlist)))
|
||||
else:
|
||||
await self.bot.say("That URL is not a valid Soundcloud or YouTube"
|
||||
" playlist link. If you think this is in error"
|
||||
@ -1195,9 +1230,8 @@ class Audio:
|
||||
if name not in self._list_playlists(server):
|
||||
await self.bot.say("There is no playlist with that name.")
|
||||
return
|
||||
playlist = self._load_playlist(server, name,
|
||||
local=self._playlist_exists_local(
|
||||
server, name))
|
||||
playlist = self._load_playlist(
|
||||
server, name, local=self._playlist_exists_local(server, name))
|
||||
try:
|
||||
playlist.append_song(author, url)
|
||||
except UnauthorizedSave:
|
||||
@ -1213,7 +1247,6 @@ class Audio:
|
||||
# Need better wording ^
|
||||
await self.bot.say("Not implemented yet.")
|
||||
|
||||
|
||||
@playlist.command(pass_context=True, no_pm=True, name="list")
|
||||
async def playlist_list(self, ctx):
|
||||
"""Lists all available playlists"""
|
||||
@ -1279,18 +1312,18 @@ class Audio:
|
||||
if self._playlist_exists(server, name):
|
||||
if not self.voice_connected(server):
|
||||
try:
|
||||
can_connect = self.has_connect_perm(author, server)
|
||||
self.has_connect_perm(author, server)
|
||||
except AuthorNotConnected:
|
||||
await self.bot.say("You must join a voice channel before I can"
|
||||
" play anything.")
|
||||
await self.bot.say("You must join a voice channel before"
|
||||
" I can play anything.")
|
||||
return
|
||||
except UnauthorizedConnect:
|
||||
await self.bot.say("I don't have permissions to join your"
|
||||
" voice channel.")
|
||||
return
|
||||
except UnauthorizedSpeak:
|
||||
await self.bot.say("I don't have permissions to speak in your"
|
||||
" voice channel.")
|
||||
await self.bot.say("I don't have permissions to speak in"
|
||||
" your voice channel.")
|
||||
return
|
||||
else:
|
||||
await self._join_voice_channel(voice_channel)
|
||||
@ -1421,6 +1454,8 @@ class Audio:
|
||||
if self.is_playing(server):
|
||||
vc = self.voice_client(server)
|
||||
vc.audio_player.stop()
|
||||
if self._get_queue_repeat(server) is False:
|
||||
self._set_queue_nowplaying(server, None)
|
||||
await self.bot.say("Skipping...")
|
||||
else:
|
||||
await self.bot.say("Can't skip if I'm not playing.")
|
||||
@ -1441,8 +1476,9 @@ class Audio:
|
||||
song = self.queue[server.id]["NOW_PLAYING"]
|
||||
if song:
|
||||
msg = ("\n**Title:** {}\n**Author:** {}\n**Uploader:** {}\n"
|
||||
"**Views:** {}\n\n<{}>".format(song.title, song.creator,
|
||||
song.uploader, song.view_count, song.webpage_url))
|
||||
"**Views:** {}\n\n<{}>".format(
|
||||
song.title, song.creator, song.uploader,
|
||||
song.view_count, song.webpage_url))
|
||||
await self.bot.say(msg.replace("**Author:** None\n", ""))
|
||||
else:
|
||||
await self.bot.say("I don't know what this song is either.")
|
||||
@ -1582,13 +1618,18 @@ class Audio:
|
||||
if len(temp_queue) > 0:
|
||||
# Fake queue for irdumb's temp playlist songs
|
||||
log.debug("calling _play because temp_queue is non-empty")
|
||||
song = await self._play(sid, temp_queue.popleft())
|
||||
try:
|
||||
song = await self._play(sid, temp_queue.popleft())
|
||||
except MaximumLength:
|
||||
return
|
||||
elif len(queue) > 0: # We're in the normal queue
|
||||
url = queue.popleft()
|
||||
log.debug("calling _play on the normal queue")
|
||||
song = await self._play(sid, url)
|
||||
try:
|
||||
song = await self._play(sid, url)
|
||||
except MaximumLength:
|
||||
return
|
||||
if repeat and last_song:
|
||||
# TODO: stick NOW_PLAYING.url back into queue AFTER ending
|
||||
queue.append(last_song.webpage_url)
|
||||
else:
|
||||
song = None
|
||||
@ -1716,17 +1757,14 @@ def setup(bot):
|
||||
check_files()
|
||||
if youtube_dl is None:
|
||||
raise RuntimeError("You need to run `pip3 install youtube_dl`")
|
||||
return
|
||||
if opus is False:
|
||||
raise RuntimeError(
|
||||
"Your opus library's bitness must match your python installation's"
|
||||
" bitness. They both must be either 32bit or 64bit.")
|
||||
return
|
||||
elif opus is None:
|
||||
raise RuntimeError(
|
||||
"You need to install ffmpeg and opus. See \"https://github.com/"
|
||||
"Twentysix26/Red-DiscordBot/wiki/Requirements\"")
|
||||
return
|
||||
try:
|
||||
bot.voice_clients
|
||||
except AttributeError:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user