mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-21 18:27:59 -05:00
Merge V3/feature/audio into V3/develop (a.k.a. audio refactor) (#3459)
This commit is contained in:
12
redbot/cogs/audio/core/tasks/__init__.py
Normal file
12
redbot/cogs/audio/core/tasks/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import logging
|
||||
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
from .lavalink import LavalinkTasks
|
||||
from .player import PlayerTasks
|
||||
from .startup import StartUpTasks
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks")
|
||||
|
||||
|
||||
class Tasks(LavalinkTasks, PlayerTasks, StartUpTasks, metaclass=CompositeMetaClass):
|
||||
"""Class joining all task subclasses"""
|
||||
113
redbot/cogs/audio/core/tasks/lavalink.py
Normal file
113
redbot/cogs/audio/core/tasks/lavalink.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import lavalink
|
||||
|
||||
from ...errors import LavalinkDownloadFailed
|
||||
from ...manager import ServerManager
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks.lavalink")
|
||||
|
||||
|
||||
class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
def lavalink_restart_connect(self) -> None:
|
||||
if self.lavalink_connect_task:
|
||||
self.lavalink_connect_task.cancel()
|
||||
|
||||
self.lavalink_connect_task = self.bot.loop.create_task(self.lavalink_attempt_connect())
|
||||
|
||||
async def lavalink_attempt_connect(self, timeout: int = 50) -> None:
|
||||
self.lavalink_connection_aborted = False
|
||||
max_retries = 5
|
||||
retry_count = 0
|
||||
while retry_count < max_retries:
|
||||
external = await self.config.use_external_lavalink()
|
||||
if external is False:
|
||||
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()
|
||||
self.player_manager = ServerManager()
|
||||
try:
|
||||
await self.player_manager.start()
|
||||
except LavalinkDownloadFailed as exc:
|
||||
await asyncio.sleep(1)
|
||||
if exc.should_retry:
|
||||
log.exception(
|
||||
"Exception whilst starting internal Lavalink server, retrying...",
|
||||
exc_info=exc,
|
||||
)
|
||||
retry_count += 1
|
||||
continue
|
||||
else:
|
||||
log.exception(
|
||||
"Fatal exception whilst starting internal Lavalink server, "
|
||||
"aborting...",
|
||||
exc_info=exc,
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
raise
|
||||
except asyncio.CancelledError:
|
||||
log.exception("Invalid machine architecture, cannot run Lavalink.")
|
||||
raise
|
||||
except Exception as exc:
|
||||
log.exception(
|
||||
"Unhandled exception whilst starting internal Lavalink server, "
|
||||
"aborting...",
|
||||
exc_info=exc,
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
raise
|
||||
else:
|
||||
break
|
||||
else:
|
||||
config_data = await self.config.all()
|
||||
host = config_data["host"]
|
||||
password = config_data["password"]
|
||||
rest_port = config_data["rest_port"]
|
||||
ws_port = config_data["ws_port"]
|
||||
break
|
||||
else:
|
||||
log.critical(
|
||||
"Setting up the Lavalink server failed after multiple attempts. "
|
||||
"See above tracebacks for details."
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
return
|
||||
|
||||
retry_count = 0
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
await lavalink.initialize(
|
||||
bot=self.bot,
|
||||
host=host,
|
||||
password=password,
|
||||
rest_port=rest_port,
|
||||
ws_port=ws_port,
|
||||
timeout=timeout,
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
log.error("Connecting to Lavalink server timed out, retrying...")
|
||||
if external is False and self.player_manager is not None:
|
||||
await self.player_manager.shutdown()
|
||||
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
|
||||
)
|
||||
self.lavalink_connection_aborted = True
|
||||
raise
|
||||
else:
|
||||
break
|
||||
else:
|
||||
self.lavalink_connection_aborted = True
|
||||
log.critical(
|
||||
"Connecting to the Lavalink server failed after multiple attempts. "
|
||||
"See above tracebacks for details."
|
||||
)
|
||||
70
redbot/cogs/audio/core/tasks/player.py
Normal file
70
redbot/cogs/audio/core/tasks/player.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
from typing import Dict
|
||||
|
||||
import lavalink
|
||||
from redbot.core.utils import AsyncIter
|
||||
|
||||
from ...audio_logging import debug_exc_log
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks.player")
|
||||
|
||||
|
||||
class PlayerTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async def player_automated_timer(self) -> None:
|
||||
stop_times: Dict = {}
|
||||
pause_times: Dict = {}
|
||||
while True:
|
||||
async for p in AsyncIter(lavalink.all_players()):
|
||||
server = p.channel.guild
|
||||
|
||||
if [self.bot.user] == p.channel.members:
|
||||
stop_times.setdefault(server.id, time.time())
|
||||
pause_times.setdefault(server.id, time.time())
|
||||
else:
|
||||
stop_times.pop(server.id, None)
|
||||
if p.paused and server.id in pause_times:
|
||||
try:
|
||||
await p.pause(False)
|
||||
except Exception as err:
|
||||
debug_exc_log(
|
||||
log,
|
||||
err,
|
||||
f"Exception raised in Audio's unpausing player for {server.id}.",
|
||||
)
|
||||
pause_times.pop(server.id, None)
|
||||
servers = stop_times.copy()
|
||||
servers.update(pause_times)
|
||||
async for sid in AsyncIter(servers, steps=5):
|
||||
server_obj = self.bot.get_guild(sid)
|
||||
if sid in stop_times and await self.config.guild(server_obj).emptydc_enabled():
|
||||
emptydc_timer = await self.config.guild(server_obj).emptydc_timer()
|
||||
if (time.time() - stop_times[sid]) >= emptydc_timer:
|
||||
stop_times.pop(sid)
|
||||
try:
|
||||
player = lavalink.get_player(sid)
|
||||
await player.stop()
|
||||
await player.disconnect()
|
||||
except Exception as err:
|
||||
if "No such player for that guild" in str(err):
|
||||
stop_times.pop(sid, None)
|
||||
debug_exc_log(
|
||||
log, err, f"Exception raised in Audio's emptydc_timer for {sid}."
|
||||
)
|
||||
elif (
|
||||
sid in pause_times and await self.config.guild(server_obj).emptypause_enabled()
|
||||
):
|
||||
emptypause_timer = await self.config.guild(server_obj).emptypause_timer()
|
||||
if (time.time() - pause_times.get(sid, 0)) >= emptypause_timer:
|
||||
try:
|
||||
await lavalink.get_player(sid).pause()
|
||||
except Exception as err:
|
||||
if "No such player for that guild" in str(err):
|
||||
pause_times.pop(sid, None)
|
||||
debug_exc_log(
|
||||
log, err, f"Exception raised in Audio's pausing for {sid}."
|
||||
)
|
||||
await asyncio.sleep(5)
|
||||
49
redbot/cogs/audio/core/tasks/startup.py
Normal file
49
redbot/cogs/audio/core/tasks/startup.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import logging
|
||||
|
||||
import lavalink
|
||||
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
|
||||
from ...apis.interface import AudioAPIInterface
|
||||
from ...apis.playlist_wrapper import PlaylistWrapper
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import _SCHEMA_VERSION, CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks.startup")
|
||||
|
||||
|
||||
class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
def start_up_task(self):
|
||||
# There has to be a task since this requires the bot to be ready
|
||||
# If it waits for ready in startup, we cause a deadlock during initial load
|
||||
# as initial load happens before the bot can ever be ready.
|
||||
self.cog_init_task = self.bot.loop.create_task(self.initialize())
|
||||
|
||||
async def initialize(self) -> None:
|
||||
await self.bot.wait_until_red_ready()
|
||||
# Unlike most cases, we want the cache to exit before migration.
|
||||
try:
|
||||
self.db_conn = APSWConnectionWrapper(
|
||||
str(cog_data_path(self.bot.get_cog("Audio")) / "Audio.db")
|
||||
)
|
||||
self.api_interface = AudioAPIInterface(
|
||||
self.bot, self.config, self.session, self.db_conn, self.bot.get_cog("Audio")
|
||||
)
|
||||
self.playlist_api = PlaylistWrapper(self.bot, self.config, self.db_conn)
|
||||
await self.playlist_api.init()
|
||||
await self.api_interface.initialize()
|
||||
await self.data_schema_migration(
|
||||
from_version=await self.config.schema_version(), to_version=_SCHEMA_VERSION
|
||||
)
|
||||
await self.playlist_api.delete_scheduled()
|
||||
self.lavalink_restart_connect()
|
||||
self.player_automated_timer_task = self.bot.loop.create_task(
|
||||
self.player_automated_timer()
|
||||
)
|
||||
lavalink.register_event_listener(self.lavalink_event_handler)
|
||||
except Exception as err:
|
||||
log.exception("Audio failed to start up, please report this issue.", exc_info=err)
|
||||
raise err
|
||||
|
||||
self.cog_ready_event.set()
|
||||
Reference in New Issue
Block a user