[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
This commit is contained in:
Draper 2020-10-27 16:16:19 +00:00 committed by GitHub
parent af8af1934c
commit d421c1c240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 61 additions and 42 deletions

View File

@ -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,

View File

@ -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

View File

@ -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"),

View File

@ -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")

View File

@ -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"))

View File

@ -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:

View File

@ -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())

View File

@ -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)

View File

@ -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())

View File

@ -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)

View File

@ -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:

View File

@ -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