From d421c1c240e16994deaa18c1fe870ed52c5e052a Mon Sep 17 00:00:00 2001 From: Draper <27962761+Drapersniper@users.noreply.github.com> Date: Tue, 27 Oct 2020 16:16:19 +0000 Subject: [PATCH] [Audio] New stuff from RLL 0.7.0 (#4529) * New stuff from RLL 0.7.0 * discard here * formatting * do this properly * make it more unique * bump RLL * nuke `[p]llset restport`, only `[p]llset wsport` matters * Update setup.cfg * properly deprecate Rest port and Ensure Nodes are properly closed upon running LLSET commands * restore player on a attempt reconnect * restore player as a task * ensure we send the signal only if not playing. * register events a little earlier * hmmm * ffs * update application.yml * fix permissions edge case --- redbot/cogs/audio/core/__init__.py | 4 +++ redbot/cogs/audio/core/abc.py | 13 +++++++- redbot/cogs/audio/core/commands/audioset.py | 4 +-- redbot/cogs/audio/core/commands/controller.py | 1 + redbot/cogs/audio/core/commands/llset.py | 30 ++----------------- redbot/cogs/audio/core/commands/player.py | 2 +- redbot/cogs/audio/core/events/dpy.py | 4 +++ redbot/cogs/audio/core/events/lavalink.py | 9 ++++++ redbot/cogs/audio/core/tasks/lavalink.py | 17 +++++++++-- redbot/cogs/audio/core/tasks/startup.py | 13 +++++--- redbot/cogs/audio/data/application.yml | 4 +-- setup.cfg | 2 +- 12 files changed, 61 insertions(+), 42 deletions(-) diff --git a/redbot/cogs/audio/core/__init__.py b/redbot/cogs/audio/core/__init__.py index eed486c69..1ecfc3a56 100644 --- a/redbot/cogs/audio/core/__init__.py +++ b/redbot/cogs/audio/core/__init__.py @@ -1,4 +1,5 @@ import asyncio +import datetime import json from collections import Counter @@ -62,6 +63,7 @@ class Audio( self.play_lock = {} self.lavalink_connect_task = None + self._restore_task = None self.player_automated_timer_task = None self.cog_cleaned_up = False self.lavalink_connection_aborted = False @@ -82,6 +84,8 @@ class Audio( "can_post": False, "can_delete": False, } + self._ll_guild_updates = set() + self._last_ll_update = datetime.datetime.now(datetime.timezone.utc) default_global = dict( schema_version=1, diff --git a/redbot/cogs/audio/core/abc.py b/redbot/cogs/audio/core/abc.py index e5cb70f2e..065b30056 100644 --- a/redbot/cogs/audio/core/abc.py +++ b/redbot/cogs/audio/core/abc.py @@ -1,11 +1,12 @@ from __future__ import annotations import asyncio +import datetime from abc import ABC, abstractmethod from collections import Counter from pathlib import Path -from typing import TYPE_CHECKING, Any, List, Mapping, MutableMapping, Optional, Tuple, Union +from typing import Set, TYPE_CHECKING, Any, List, Mapping, MutableMapping, Optional, Tuple, Union import aiohttp import discord @@ -57,6 +58,7 @@ class MixinMeta(ABC): _error_counter: Counter lavalink_connect_task: Optional[asyncio.Task] + _restore_task: Optional[asyncio.Task] player_automated_timer_task: Optional[asyncio.Task] cog_init_task: Optional[asyncio.Task] cog_ready_event: asyncio.Event @@ -64,6 +66,9 @@ class MixinMeta(ABC): _default_lavalink_settings: Mapping permission_cache = discord.Permissions + _last_ll_update: datetime.datetime + _ll_guild_updates: Set[int] + @abstractmethod async def command_llsetup(self, ctx: commands.Context): raise NotImplementedError() @@ -122,6 +127,12 @@ class MixinMeta(ABC): ) -> None: raise NotImplementedError() + @abstractmethod + async def lavalink_update_handler( + self, player: lavalink.Player, event_type: lavalink.enums.PlayerState, extra + ) -> None: + raise NotImplementedError() + @abstractmethod async def _clear_react( self, message: discord.Message, emoji: MutableMapping = None diff --git a/redbot/cogs/audio/core/commands/audioset.py b/redbot/cogs/audio/core/commands/audioset.py index 5990c63d6..830f83b94 100644 --- a/redbot/cogs/audio/core/commands/audioset.py +++ b/redbot/cogs/audio/core/commands/audioset.py @@ -1472,14 +1472,12 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass): async def command_audioset_restart(self, ctx: commands.Context): """Restarts the lavalink connection.""" async with ctx.typing(): - lavalink.unregister_event_listener(self.lavalink_event_handler) await lavalink.close() if self.player_manager is not None: await self.player_manager.shutdown() self.lavalink_restart_connect() - lavalink.register_event_listener(self.lavalink_event_handler) - await self.restore_players() + await self.send_embed_msg( ctx, title=_("Restarting Lavalink"), diff --git a/redbot/cogs/audio/core/commands/controller.py b/redbot/cogs/audio/core/commands/controller.py index 03ad1f68b..e0a98608d 100644 --- a/redbot/cogs/audio/core/commands/controller.py +++ b/redbot/cogs/audio/core/commands/controller.py @@ -72,6 +72,7 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass): await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands) await player.stop() await player.disconnect() + self._ll_guild_updates.discard(ctx.guild.id) await self.api_interface.persistent_queue_api.drop(ctx.guild.id) @commands.command(name="now") diff --git a/redbot/cogs/audio/core/commands/llset.py b/redbot/cogs/audio/core/commands/llset.py index 19357031d..72102b2e2 100644 --- a/redbot/cogs/audio/core/commands/llset.py +++ b/redbot/cogs/audio/core/commands/llset.py @@ -72,7 +72,7 @@ class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass): ctx, title=_("Failed To Shutdown Lavalink"), description=_( - "For it to take effect please reload " "Audio (`{prefix}reload audio`)." + "For it to take effect please reload Audio (`{prefix}reload audio`)." ).format( prefix=ctx.prefix, ), @@ -188,31 +188,6 @@ class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass): ), ) - @command_llsetup.command(name="restport") - async def command_llsetup_restport(self, ctx: commands.Context, rest_port: int): - """Set the Lavalink REST server port.""" - await self.config.rest_port.set(rest_port) - footer = None - if await self.update_external_status(): - footer = _("External Lavalink server set to True.") - await self.send_embed_msg( - ctx, - title=_("Setting Changed"), - description=_("REST port set to {port}.").format(port=rest_port), - footer=footer, - ) - - try: - self.lavalink_restart_connect() - except ProcessLookupError: - await self.send_embed_msg( - ctx, - title=_("Failed To Shutdown Lavalink"), - description=_("Please reload Audio (`{prefix}reload audio`).").format( - prefix=ctx.prefix - ), - ) - @command_llsetup.command(name="wsport") async def command_llsetup_wsport(self, ctx: commands.Context, ws_port: int): """Set the Lavalink websocket server port.""" @@ -248,8 +223,9 @@ class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass): ws_port = configs["ws_port"] msg = "----" + _("Connection Settings") + "---- \n" msg += _("Host: [{host}]\n").format(host=host) - msg += _("Rest Port: [{port}]\n").format(port=rest_port) msg += _("WS Port: [{port}]\n").format(port=ws_port) + if ws_port != rest_port: + msg += _("Rest Port: [{port}]\n").format(port=rest_port) msg += _("Password: [{password}]\n").format(password=password) try: await self.send_embed_msg(ctx.author, description=box(msg, lang="ini")) diff --git a/redbot/cogs/audio/core/commands/player.py b/redbot/cogs/audio/core/commands/player.py index eb69d6626..a87e685b4 100644 --- a/redbot/cogs/audio/core/commands/player.py +++ b/redbot/cogs/audio/core/commands/player.py @@ -278,7 +278,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass): if not await self.is_query_allowed( self.config, ctx, - f"{single_track.title} {single_track.author} {single_track.uri} " f"{str(query)}", + f"{single_track.title} {single_track.author} {single_track.uri} {str(query)}", query_obj=query, ): if IS_DEBUG: diff --git a/redbot/cogs/audio/core/events/dpy.py b/redbot/cogs/audio/core/events/dpy.py index 76ca3981f..85cd498d0 100644 --- a/redbot/cogs/audio/core/events/dpy.py +++ b/redbot/cogs/audio/core/events/dpy.py @@ -239,7 +239,11 @@ class DpyEvents(MixinMeta, metaclass=CompositeMetaClass): if self.cog_init_task: self.cog_init_task.cancel() + if self._restore_task: + self._restore_task.cancel() + lavalink.unregister_event_listener(self.lavalink_event_handler) + lavalink.unregister_update_listener(self.lavalink_update_handler) self.bot.loop.create_task(lavalink.close()) if self.player_manager is not None: self.bot.loop.create_task(self.player_manager.shutdown()) diff --git a/redbot/cogs/audio/core/events/lavalink.py b/redbot/cogs/audio/core/events/lavalink.py index 9b94f132c..a3be249c1 100644 --- a/redbot/cogs/audio/core/events/lavalink.py +++ b/redbot/cogs/audio/core/events/lavalink.py @@ -1,5 +1,6 @@ import asyncio import contextlib +import datetime import logging from pathlib import Path @@ -16,6 +17,12 @@ _ = Translator("Audio", Path(__file__)) class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass): + async def lavalink_update_handler( + self, player: lavalink.Player, event_type: lavalink.enums.PlayerState, extra + ): + self._last_ll_update = datetime.datetime.now(datetime.timezone.utc) + self._ll_guild_updates.add(int(extra.get("guildId", 0))) + async def lavalink_event_handler( self, player: lavalink.Player, event_type: lavalink.LavalinkEvents, extra ) -> None: @@ -160,6 +167,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass): if disconnect: self.bot.dispatch("red_audio_audio_disconnect", guild) await player.disconnect() + self._ll_guild_updates.discard(guild.id) if status: player_check = await self.get_active_player_count() await self.update_bot_presence(*player_check) @@ -193,6 +201,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass): await self.config.custom("EQUALIZER", guild_id).eq_bands.set(eq.bands) await player.stop() await player.disconnect() + self._ll_guild_updates.discard(guild_id) self.bot.dispatch("red_audio_audio_disconnect", guild) if message_channel: message_channel = self.bot.get_channel(message_channel) diff --git a/redbot/cogs/audio/core/tasks/lavalink.py b/redbot/cogs/audio/core/tasks/lavalink.py index f314f785f..1d8868c94 100644 --- a/redbot/cogs/audio/core/tasks/lavalink.py +++ b/redbot/cogs/audio/core/tasks/lavalink.py @@ -4,6 +4,7 @@ from pathlib import Path import lavalink +from redbot.core import data_manager from redbot.core.i18n import Translator from ...errors import LavalinkDownloadFailed from ...manager import ServerManager @@ -16,9 +17,16 @@ _ = Translator("Audio", Path(__file__)) class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass): def lavalink_restart_connect(self) -> None: + lavalink.unregister_event_listener(self.lavalink_event_handler) + lavalink.unregister_update_listener(self.lavalink_update_handler) if self.lavalink_connect_task: self.lavalink_connect_task.cancel() + if self._restore_task: + self._restore_task.cancel() + self._restore_task = None + lavalink.register_event_listener(self.lavalink_event_handler) + lavalink.register_update_listener(self.lavalink_update_handler) self.lavalink_connect_task = self.bot.loop.create_task(self.lavalink_attempt_connect()) async def lavalink_attempt_connect(self, timeout: int = 50) -> None: @@ -33,7 +41,6 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass): settings = self._default_lavalink_settings host = settings["host"] password = settings["password"] - rest_port = settings["rest_port"] ws_port = settings["ws_port"] if self.player_manager is not None: await self.player_manager.shutdown() @@ -73,7 +80,6 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass): else: host = configs["host"] password = configs["password"] - rest_port = configs["rest_port"] ws_port = configs["ws_port"] break else: @@ -86,14 +92,17 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass): retry_count = 0 while retry_count < max_retries: + if lavalink.node._nodes: + await lavalink.node.disconnect() try: await lavalink.initialize( bot=self.bot, host=host, password=password, - rest_port=rest_port, + rest_port=ws_port, ws_port=ws_port, timeout=timeout, + resume_key=f"Red-Core-Audio-{self.bot.user.id}-{data_manager.instance_name}", ) except asyncio.TimeoutError: log.error("Connecting to Lavalink server timed out, retrying...") @@ -115,3 +124,5 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass): "Connecting to the Lavalink server failed after multiple attempts. " "See above tracebacks for details." ) + return + self._restore_task = asyncio.create_task(self.restore_players()) diff --git a/redbot/cogs/audio/core/tasks/startup.py b/redbot/cogs/audio/core/tasks/startup.py index 56f1aca1e..3fd5c9658 100644 --- a/redbot/cogs/audio/core/tasks/startup.py +++ b/redbot/cogs/audio/core/tasks/startup.py @@ -57,8 +57,6 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass): self.player_automated_timer() ) self.player_automated_timer_task.add_done_callback(task_callback) - lavalink.register_event_listener(self.lavalink_event_handler) - await self.restore_players() except Exception as err: log.exception("Audio failed to start up, please report this issue.", exc_info=err) raise err @@ -68,6 +66,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass): async def restore_players(self): tries = 0 tracks_to_restore = await self.api_interface.persistent_queue_api.fetch_all() + await asyncio.sleep(10) for guild_id, track_data in itertools.groupby(tracks_to_restore, key=lambda x: x.guild_id): await asyncio.sleep(0) try: @@ -95,6 +94,12 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass): while tries < 25 and vc is not None: try: vc = guild.get_channel(track_data[-1].room_id) + if not vc: + break + perms = vc.permissions_for(guild.me) + if not (perms.connect and perms.speak): + vc = None + break await lavalink.connect(vc) player = lavalink.get_player(guild.id) player.store("connect", datetime.datetime.utcnow()) @@ -126,8 +131,8 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass): track = track.track_object player.add(guild.get_member(track.extras.get("requester")) or guild.me, track) player.maybe_shuffle() - - await player.play() + if not player.is_playing: + await player.play() except Exception as err: debug_exc_log(log, err, f"Error restoring player in {guild_id}") await self.api_interface.persistent_queue_api.drop(guild_id) diff --git a/redbot/cogs/audio/data/application.yml b/redbot/cogs/audio/data/application.yml index 2c7e586c9..8d28be6e6 100644 --- a/redbot/cogs/audio/data/application.yml +++ b/redbot/cogs/audio/data/application.yml @@ -1,6 +1,6 @@ server: host: "localhost" - port: 2333 # REST server + port: 2333 # WS port lavalink: server: password: "youshallnotpass" @@ -18,7 +18,7 @@ lavalink: youtubePlaylistLoadLimit: 10000 logging: file: - max-history: 30 + max-history: 7 max-size: 1GB path: ./logs/ level: diff --git a/setup.cfg b/setup.cfg index 0ed12927e..ffc5ee71b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -53,7 +53,7 @@ install_requires = python-Levenshtein-wheels==0.13.1 pytz==2020.1 PyYAML==5.3.1 - Red-Lavalink==0.6.0 + Red-Lavalink==0.7.1 schema==0.7.2 tqdm==4.48.0 typing-extensions==3.7.4.2