mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-12-08 18:32:32 -05:00
Audio changes (#5593)
* Squash tested commits * remove the code jack is concerned about * Apply suggestions from code review * more log lines * more log lines * format * formatting * style(Rename Xms and Xmx mentions): Rename Xms and Xmx to more use friendly names - Change Xms to "Initial Heapsize" - Change Xmx to "Max Heapsize" Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com>
This commit is contained in:
@@ -6,18 +6,16 @@ from red_commons.logging import getLogger
|
||||
|
||||
from redbot.core import data_manager
|
||||
from redbot.core.i18n import Translator
|
||||
from ...errors import LavalinkDownloadFailed
|
||||
from ...manager import ServerManager
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
from ...utils import task_callback_debug
|
||||
|
||||
log = getLogger("red.cogs.Audio.cog.Tasks.lavalink")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
def lavalink_restart_connect(self) -> None:
|
||||
def lavalink_restart_connect(self, manual: bool = False) -> None:
|
||||
lavalink.unregister_event_listener(self.lavalink_event_handler)
|
||||
lavalink.unregister_update_listener(self.lavalink_update_handler)
|
||||
if self.lavalink_connect_task:
|
||||
@@ -28,93 +26,106 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
self._restore_task = None
|
||||
lavalink.register_event_listener(self.lavalink_event_handler)
|
||||
lavalink.register_update_listener(self.lavalink_update_handler)
|
||||
self.lavalink_connect_task = asyncio.create_task(self.lavalink_attempt_connect())
|
||||
self.lavalink_connect_task.add_done_callback(task_callback_debug)
|
||||
self.lavalink_connect_task = asyncio.create_task(
|
||||
self.lavalink_attempt_connect(manual=manual)
|
||||
)
|
||||
|
||||
async def lavalink_attempt_connect(self, timeout: int = 50) -> None:
|
||||
async def lavalink_attempt_connect(self, timeout: int = 50, manual: bool = False) -> None:
|
||||
self.lavalink_connection_aborted = False
|
||||
max_retries = 5
|
||||
retry_count = 0
|
||||
if nodes := lavalink.get_all_nodes():
|
||||
for node in nodes:
|
||||
await node.disconnect()
|
||||
# This ensures that the restore task is ended before this connect attempt is started up.
|
||||
if self._restore_task:
|
||||
self._restore_task.cancel()
|
||||
if self.managed_node_controller is not None:
|
||||
if not self.managed_node_controller._shutdown:
|
||||
await self.managed_node_controller.shutdown()
|
||||
await asyncio.sleep(5)
|
||||
await lavalink.close(self.bot)
|
||||
while retry_count < max_retries:
|
||||
configs = await self.config.all()
|
||||
external = configs["use_external_lavalink"]
|
||||
java_exec = configs["java_exc_path"]
|
||||
if external is False:
|
||||
settings = self._default_lavalink_settings
|
||||
host = settings["host"]
|
||||
password = settings["password"]
|
||||
ws_port = settings["ws_port"]
|
||||
if self.player_manager is not None:
|
||||
await self.player_manager.shutdown()
|
||||
self.player_manager = ServerManager()
|
||||
# Change these values to use whatever is set on the YAML
|
||||
host = configs["yaml"]["server"]["address"]
|
||||
port = configs["yaml"]["server"]["port"]
|
||||
password = configs["yaml"]["lavalink"]["server"]["password"]
|
||||
secured = False
|
||||
# Make this timeout customizable for lower powered machines?
|
||||
self.managed_node_controller = ServerManager(self.config, timeout=60, cog=self)
|
||||
try:
|
||||
await self.player_manager.start(java_exec)
|
||||
except LavalinkDownloadFailed as exc:
|
||||
await asyncio.sleep(1)
|
||||
if exc.should_retry:
|
||||
log.exception(
|
||||
"Exception whilst starting internal Lavalink server, retrying...",
|
||||
exc_info=exc,
|
||||
await self.managed_node_controller.start(java_exec)
|
||||
# timeout is the same as ServerManager.timeout -
|
||||
# 60s in case of ServerManager(self.config, timeout=60)
|
||||
await self.managed_node_controller.wait_until_ready()
|
||||
except asyncio.TimeoutError:
|
||||
if self.managed_node_controller is not None:
|
||||
await self.managed_node_controller.shutdown()
|
||||
if self.lavalink_connection_aborted is not True:
|
||||
log.critical(
|
||||
"Managed node startup timeout, aborting managed node startup."
|
||||
)
|
||||
retry_count += 1
|
||||
continue
|
||||
else:
|
||||
log.exception(
|
||||
"Fatal exception whilst starting internal Lavalink server, "
|
||||
"aborting...",
|
||||
exc_info=exc,
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
return
|
||||
except asyncio.CancelledError:
|
||||
log.critical("Invalid machine architecture, cannot run Lavalink.")
|
||||
self.lavalink_connection_aborted = True
|
||||
return
|
||||
except Exception as exc:
|
||||
log.exception(
|
||||
"Unhandled exception whilst starting internal Lavalink server, "
|
||||
"Unhandled exception whilst starting managed Lavalink node, "
|
||||
"aborting...",
|
||||
exc_info=exc,
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
if self.managed_node_controller is not None:
|
||||
await self.managed_node_controller.shutdown()
|
||||
return
|
||||
else:
|
||||
break
|
||||
else:
|
||||
host = configs["host"]
|
||||
password = configs["password"]
|
||||
ws_port = configs["ws_port"]
|
||||
port = configs["ws_port"]
|
||||
secured = configs["secured_ws"]
|
||||
break
|
||||
else:
|
||||
log.critical(
|
||||
"Setting up the Lavalink server failed after multiple attempts. "
|
||||
"See above tracebacks for details."
|
||||
"Setting up the managed Lavalink node failed after multiple attempts. "
|
||||
"See above logs for details."
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
if self.managed_node_controller is not None:
|
||||
await self.managed_node_controller.shutdown()
|
||||
return
|
||||
|
||||
log.debug("Attempting to initialize Red-Lavalink")
|
||||
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,
|
||||
ws_port=ws_port,
|
||||
ws_port=port,
|
||||
timeout=timeout,
|
||||
resume_key=f"Red-Core-Audio-{self.bot.user.id}-{data_manager.instance_name}",
|
||||
secured=secured,
|
||||
)
|
||||
except lavalink.AbortingNodeConnection:
|
||||
await lavalink.close(self.bot)
|
||||
log.warning("Connection attempt to Lavalink node aborted")
|
||||
return
|
||||
except asyncio.TimeoutError:
|
||||
log.warning("Connecting to Lavalink server timed out, retrying...")
|
||||
if external is False and self.player_manager is not None:
|
||||
await self.player_manager.shutdown()
|
||||
await lavalink.close(self.bot)
|
||||
log.warning("Connecting to Lavalink node timed out, retrying...")
|
||||
retry_count += 1
|
||||
await asyncio.sleep(1) # prevent busylooping
|
||||
except Exception as exc:
|
||||
log.exception(
|
||||
"Unhandled exception whilst connecting to Lavalink, aborting...", exc_info=exc
|
||||
"Unhandled exception whilst connecting to Lavalink node, aborting...",
|
||||
exc_info=exc,
|
||||
)
|
||||
await lavalink.close(self.bot)
|
||||
self.lavalink_connection_aborted = True
|
||||
return
|
||||
else:
|
||||
@@ -122,11 +133,11 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
else:
|
||||
self.lavalink_connection_aborted = True
|
||||
log.critical(
|
||||
"Connecting to the Lavalink server failed after multiple attempts. "
|
||||
"Connecting to the Lavalink node failed after multiple attempts. "
|
||||
"See above tracebacks for details."
|
||||
)
|
||||
await lavalink.close(self.bot)
|
||||
return
|
||||
if external:
|
||||
await asyncio.sleep(5)
|
||||
self._restore_task = asyncio.create_task(self.restore_players())
|
||||
self._restore_task.add_done_callback(task_callback_debug)
|
||||
|
||||
@@ -5,6 +5,7 @@ from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import lavalink
|
||||
from lavalink import NodeNotFound, PlayerNotFound
|
||||
from red_commons.logging import getLogger
|
||||
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
@@ -15,7 +16,6 @@ from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
from ...apis.interface import AudioAPIInterface
|
||||
from ...apis.playlist_wrapper import PlaylistWrapper
|
||||
from ...errors import DatabaseError, TrackEnqueueError
|
||||
from ...utils import task_callback_debug
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import _SCHEMA_VERSION, CompositeMetaClass
|
||||
|
||||
@@ -30,7 +30,6 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
# as initial load happens before the bot can ever be ready.
|
||||
lavalink.set_logging_level(self.bot._cli_flags.logging_level)
|
||||
self.cog_init_task = asyncio.create_task(self.initialize())
|
||||
self.cog_init_task.add_done_callback(task_callback_debug)
|
||||
|
||||
async def initialize(self) -> None:
|
||||
await self.bot.wait_until_red_ready()
|
||||
@@ -54,7 +53,6 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
await self._build_bundled_playlist()
|
||||
self.lavalink_restart_connect()
|
||||
self.player_automated_timer_task = asyncio.create_task(self.player_automated_timer())
|
||||
self.player_automated_timer_task.add_done_callback(task_callback_debug)
|
||||
except Exception as exc:
|
||||
log.critical("Audio failed to start up, please report this issue.", exc_info=exc)
|
||||
return
|
||||
@@ -62,14 +60,27 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
self.cog_ready_event.set()
|
||||
|
||||
async def restore_players(self):
|
||||
log.debug("Starting new restore player task")
|
||||
tries = 0
|
||||
tracks_to_restore = await self.api_interface.persistent_queue_api.fetch_all()
|
||||
while not lavalink.get_all_nodes():
|
||||
await asyncio.sleep(1)
|
||||
log.trace("Waiting for node to be available")
|
||||
tries += 1
|
||||
if tries > 60:
|
||||
log.warning("Unable to restore players, couldn't connect to Lavalink.")
|
||||
if tries > 600: # Give 10 minutes from node creation date.
|
||||
log.warning("Unable to restore players, couldn't connect to Lavalink node.")
|
||||
return
|
||||
try:
|
||||
for node in lavalink.get_all_nodes():
|
||||
if not node.ready:
|
||||
log.trace("Waiting for node: %r", node)
|
||||
await node.wait_until_ready(timeout=60) # In theory this should be instant.
|
||||
except asyncio.TimeoutError:
|
||||
log.error(
|
||||
"Restoring player task aborted due to a timeout waiting for Lavalink node to be ready."
|
||||
)
|
||||
log.warning("Audio will attempt queue restore on next restart.")
|
||||
return
|
||||
metadata = {}
|
||||
all_guilds = await self.config.all_guilds()
|
||||
async for guild_id, guild_data in AsyncIter(all_guilds.items(), steps=100):
|
||||
@@ -77,7 +88,9 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if guild_data["currently_auto_playing_in"]:
|
||||
notify_channel, vc_id = guild_data["currently_auto_playing_in"]
|
||||
metadata[guild_id] = (notify_channel, vc_id)
|
||||
|
||||
if self.lavalink_connection_aborted:
|
||||
log.warning("Aborting player restore due to Lavalink connection being aborted.")
|
||||
return
|
||||
for guild_id, track_data in itertools.groupby(tracks_to_restore, key=lambda x: x.guild_id):
|
||||
await asyncio.sleep(0)
|
||||
tries = 0
|
||||
@@ -86,22 +99,24 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
track_data = list(track_data)
|
||||
guild = self.bot.get_guild(guild_id)
|
||||
if not guild:
|
||||
log.verbose(
|
||||
"Skipping player restore - Bot is no longer in Guild (%s)", guild_id
|
||||
)
|
||||
continue
|
||||
persist_cache = self._persist_queue_cache.setdefault(
|
||||
guild_id, await self.config.guild(guild).persist_queue()
|
||||
)
|
||||
if not persist_cache:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s) does not have a persist cache",
|
||||
guild_id,
|
||||
)
|
||||
await self.api_interface.persistent_queue_api.drop(guild_id)
|
||||
continue
|
||||
if self.lavalink_connection_aborted:
|
||||
try:
|
||||
player = lavalink.get_player(guild_id)
|
||||
except (NodeNotFound, PlayerNotFound):
|
||||
player = None
|
||||
else:
|
||||
try:
|
||||
player = lavalink.get_player(guild_id)
|
||||
except IndexError:
|
||||
player = None
|
||||
except KeyError:
|
||||
player = None
|
||||
vc = 0
|
||||
guild_data = await self.config.guild_from_id(guild.id).all()
|
||||
shuffle = guild_data["shuffle"]
|
||||
@@ -126,7 +141,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
player = await lavalink.connect(vc, deafen=auto_deafen)
|
||||
player.store("notify_channel", notify_channel_id)
|
||||
break
|
||||
except IndexError:
|
||||
except NodeNotFound:
|
||||
await asyncio.sleep(5)
|
||||
tries += 1
|
||||
except Exception as exc:
|
||||
@@ -139,7 +154,24 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
else:
|
||||
await asyncio.sleep(1)
|
||||
|
||||
if tries >= 5 or guild is None or vc is None or player is None:
|
||||
if tries >= 5 or vc is None or player is None:
|
||||
if tries >= 5:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s), 5 attempts to restore player failed.",
|
||||
guild_id,
|
||||
)
|
||||
elif vc is None:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s), VC (%s) does not exist.",
|
||||
guild_id,
|
||||
vc_id,
|
||||
)
|
||||
else:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s), Unable to create player for VC (%s).",
|
||||
guild_id,
|
||||
vc_id,
|
||||
)
|
||||
await self.api_interface.persistent_queue_api.drop(guild_id)
|
||||
continue
|
||||
|
||||
@@ -156,7 +188,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
await player.play()
|
||||
log.debug("Restored %r", player)
|
||||
except Exception as exc:
|
||||
log.debug("Error restoring player in %d", guild_id, exc_info=exc)
|
||||
log.debug("Error restoring player in %s", guild_id, exc_info=exc)
|
||||
await self.api_interface.persistent_queue_api.drop(guild_id)
|
||||
|
||||
for guild_id, (notify_channel_id, vc_id) in metadata.items():
|
||||
@@ -171,9 +203,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
else:
|
||||
try:
|
||||
player = lavalink.get_player(guild_id)
|
||||
except IndexError:
|
||||
player = None
|
||||
except KeyError:
|
||||
except (NodeNotFound, PlayerNotFound):
|
||||
player = None
|
||||
if player is None:
|
||||
guild_data = await self.config.guild_from_id(guild.id).all()
|
||||
@@ -195,7 +225,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
player = await lavalink.connect(vc, deafen=auto_deafen)
|
||||
player.store("notify_channel", notify_channel_id)
|
||||
break
|
||||
except IndexError:
|
||||
except NodeNotFound:
|
||||
await asyncio.sleep(5)
|
||||
tries += 1
|
||||
except Exception as exc:
|
||||
@@ -205,7 +235,24 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
break
|
||||
else:
|
||||
await asyncio.sleep(1)
|
||||
if tries >= 5 or guild is None or vc is None or player is None:
|
||||
if tries >= 5 or vc is None or player is None:
|
||||
if tries >= 5:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s), 5 attempts to restore player failed.",
|
||||
guild_id,
|
||||
)
|
||||
elif vc is None:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s), VC (%s) does not exist.",
|
||||
guild_id,
|
||||
vc_id,
|
||||
)
|
||||
else:
|
||||
log.verbose(
|
||||
"Skipping player restore - Guild (%s), Unable to create player for VC (%s).",
|
||||
guild_id,
|
||||
vc_id,
|
||||
)
|
||||
continue
|
||||
|
||||
player.repeat = repeat
|
||||
@@ -220,23 +267,24 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
try:
|
||||
await self.api_interface.autoplay(player, self.playlist_api)
|
||||
except DatabaseError:
|
||||
notify_channel = self.bot.get_channel(notify_channel)
|
||||
notify_channel = guild.get_channel(notify_channel)
|
||||
if notify_channel:
|
||||
await self.send_embed_msg(
|
||||
notify_channel, title=_("Couldn't get a valid track.")
|
||||
)
|
||||
return
|
||||
except TrackEnqueueError:
|
||||
notify_channel = self.bot.get_channel(notify_channel)
|
||||
notify_channel = guild.get_channel(notify_channel)
|
||||
if notify_channel:
|
||||
await self.send_embed_msg(
|
||||
notify_channel,
|
||||
title=_("Unable to Get Track"),
|
||||
description=_(
|
||||
"I'm unable to get a track from Lavalink at the moment, "
|
||||
"I'm unable to get a track from the Lavalink node at the moment, "
|
||||
"try again in a few minutes."
|
||||
),
|
||||
)
|
||||
return
|
||||
del metadata
|
||||
del all_guilds
|
||||
log.debug("Player restore task completed successfully")
|
||||
|
||||
Reference in New Issue
Block a user