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:
13
redbot/cogs/audio/core/events/__init__.py
Normal file
13
redbot/cogs/audio/core/events/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import logging
|
||||
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
from .cog import AudioEvents
|
||||
from .dpy import DpyEvents
|
||||
from .lavalink import LavalinkEvents
|
||||
from .red import RedEvents
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events")
|
||||
|
||||
|
||||
class Events(AudioEvents, DpyEvents, LavalinkEvents, RedEvents, metaclass=CompositeMetaClass):
|
||||
"""Class joining all event subclasses"""
|
||||
147
redbot/cogs/audio/core/events/cog.py
Normal file
147
redbot/cogs/audio/core/events/cog.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import commands
|
||||
|
||||
from ...apis.playlist_interface import Playlist, delete_playlist, get_playlist
|
||||
from ...audio_logging import debug_exc_log
|
||||
from ...utils import PlaylistScope
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.audio")
|
||||
|
||||
|
||||
class AudioEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@commands.Cog.listener()
|
||||
async def on_red_audio_track_start(
|
||||
self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member
|
||||
):
|
||||
if not (track and guild):
|
||||
return
|
||||
track_identifier = track.track_identifier
|
||||
if self.playlist_api is not None:
|
||||
daily_cache = self._daily_playlist_cache.setdefault(
|
||||
guild.id, await self.config.guild(guild).daily_playlists()
|
||||
)
|
||||
global_daily_playlists = self._daily_global_playlist_cache.setdefault(
|
||||
self.bot.user.id, await self.config.daily_playlists()
|
||||
)
|
||||
today = datetime.date.today()
|
||||
midnight = datetime.datetime.combine(today, datetime.datetime.min.time())
|
||||
today_id = int(time.mktime(today.timetuple()))
|
||||
track = self.track_to_json(track)
|
||||
if daily_cache:
|
||||
name = f"Daily playlist - {today}"
|
||||
playlist: Optional[Playlist]
|
||||
try:
|
||||
playlist = await get_playlist(
|
||||
playlist_api=self.playlist_api,
|
||||
playlist_number=today_id,
|
||||
scope=PlaylistScope.GUILD.value,
|
||||
bot=self.bot,
|
||||
guild=guild,
|
||||
author=self.bot.user,
|
||||
)
|
||||
except RuntimeError:
|
||||
playlist = None
|
||||
|
||||
if playlist:
|
||||
tracks = playlist.tracks
|
||||
tracks.append(track)
|
||||
await playlist.edit({"tracks": tracks})
|
||||
else:
|
||||
playlist = Playlist(
|
||||
bot=self.bot,
|
||||
scope=PlaylistScope.GUILD.value,
|
||||
author=self.bot.user.id,
|
||||
playlist_id=today_id,
|
||||
name=name,
|
||||
playlist_url=None,
|
||||
tracks=[track],
|
||||
guild=guild,
|
||||
playlist_api=self.playlist_api,
|
||||
)
|
||||
await playlist.save()
|
||||
if global_daily_playlists:
|
||||
global_name = f"Global Daily playlist - {today}"
|
||||
try:
|
||||
playlist = await get_playlist(
|
||||
playlist_number=today_id,
|
||||
scope=PlaylistScope.GLOBAL.value,
|
||||
bot=self.bot,
|
||||
guild=guild,
|
||||
author=self.bot.user,
|
||||
playlist_api=self.playlist_api,
|
||||
)
|
||||
except RuntimeError:
|
||||
playlist = None
|
||||
if playlist:
|
||||
tracks = playlist.tracks
|
||||
tracks.append(track)
|
||||
await playlist.edit({"tracks": tracks})
|
||||
else:
|
||||
playlist = Playlist(
|
||||
bot=self.bot,
|
||||
scope=PlaylistScope.GLOBAL.value,
|
||||
author=self.bot.user.id,
|
||||
playlist_id=today_id,
|
||||
name=global_name,
|
||||
playlist_url=None,
|
||||
tracks=[track],
|
||||
guild=guild,
|
||||
playlist_api=self.playlist_api,
|
||||
)
|
||||
await playlist.save()
|
||||
too_old = midnight - datetime.timedelta(days=8)
|
||||
too_old_id = int(time.mktime(too_old.timetuple()))
|
||||
try:
|
||||
await delete_playlist(
|
||||
scope=PlaylistScope.GUILD.value,
|
||||
playlist_id=too_old_id,
|
||||
guild=guild,
|
||||
author=self.bot.user,
|
||||
playlist_api=self.playlist_api,
|
||||
bot=self.bot,
|
||||
)
|
||||
except Exception as err:
|
||||
debug_exc_log(log, err, f"Failed to delete daily playlist ID: {too_old_id}")
|
||||
try:
|
||||
await delete_playlist(
|
||||
scope=PlaylistScope.GLOBAL.value,
|
||||
playlist_id=too_old_id,
|
||||
guild=guild,
|
||||
author=self.bot.user,
|
||||
playlist_api=self.playlist_api,
|
||||
bot=self.bot,
|
||||
)
|
||||
except Exception as err:
|
||||
debug_exc_log(log, err, f"Failed to delete global daily playlist ID: {too_old_id}")
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_red_audio_queue_end(
|
||||
self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member
|
||||
):
|
||||
if not (track and guild):
|
||||
return
|
||||
if self.api_interface is not None and self.playlist_api is not None:
|
||||
await self.api_interface.local_cache_api.youtube.clean_up_old_entries()
|
||||
await asyncio.sleep(5)
|
||||
await self.playlist_api.delete_scheduled()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_red_audio_track_end(
|
||||
self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member
|
||||
):
|
||||
if not (track and guild):
|
||||
return
|
||||
if self.api_interface is not None and self.playlist_api is not None:
|
||||
await self.api_interface.local_cache_api.youtube.clean_up_old_entries()
|
||||
await asyncio.sleep(5)
|
||||
await self.playlist_api.delete_scheduled()
|
||||
184
redbot/cogs/audio/core/events/dpy.py
Normal file
184
redbot/cogs/audio/core/events/dpy.py
Normal file
@@ -0,0 +1,184 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Final, Pattern
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
from aiohttp import ClientConnectorError
|
||||
|
||||
from redbot.core import commands
|
||||
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ...audio_logging import debug_exc_log
|
||||
from ...errors import TrackEnqueueError
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.dpy")
|
||||
|
||||
RE_CONVERSION: Final[Pattern] = re.compile('Converting to "(.*)" failed for parameter "(.*)".')
|
||||
|
||||
|
||||
class DpyEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async def cog_before_invoke(self, ctx: commands.Context) -> None:
|
||||
await self.cog_ready_event.wait()
|
||||
# check for unsupported arch
|
||||
# Check on this needs refactoring at a later date
|
||||
# so that we have a better way to handle the tasks
|
||||
if self.command_llsetup in [ctx.command, ctx.command.root_parent]:
|
||||
pass
|
||||
|
||||
elif self.lavalink_connect_task and self.lavalink_connect_task.cancelled():
|
||||
await ctx.send(
|
||||
_(
|
||||
"You have attempted to run Audio's Lavalink server on an unsupported"
|
||||
" architecture. Only settings related commands will be available."
|
||||
)
|
||||
)
|
||||
raise RuntimeError(
|
||||
"Not running audio command due to invalid machine architecture for Lavalink."
|
||||
)
|
||||
# with contextlib.suppress(Exception):
|
||||
# player = lavalink.get_player(ctx.guild.id)
|
||||
# notify_channel = player.fetch("channel")
|
||||
# if not notify_channel:
|
||||
# player.store("channel", ctx.channel.id)
|
||||
dj_enabled = self._dj_status_cache.setdefault(
|
||||
ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()
|
||||
)
|
||||
self._daily_playlist_cache.setdefault(
|
||||
ctx.guild.id, await self.config.guild(ctx.guild).daily_playlists()
|
||||
)
|
||||
self._daily_global_playlist_cache.setdefault(
|
||||
self.bot.user.id, await self.config.daily_playlists()
|
||||
)
|
||||
if self.local_folder_current_path is None:
|
||||
self.local_folder_current_path = Path(await self.config.localpath())
|
||||
if dj_enabled:
|
||||
dj_role = self._dj_role_cache.setdefault(
|
||||
ctx.guild.id, await self.config.guild(ctx.guild).dj_role()
|
||||
)
|
||||
dj_role_obj = ctx.guild.get_role(dj_role)
|
||||
if not dj_role_obj:
|
||||
await self.config.guild(ctx.guild).dj_enabled.set(None)
|
||||
self._dj_status_cache[ctx.guild.id] = None
|
||||
await self.config.guild(ctx.guild).dj_role.set(None)
|
||||
self._dj_role_cache[ctx.guild.id] = None
|
||||
await self.send_embed_msg(ctx, title=_("No DJ role found. Disabling DJ mode."))
|
||||
|
||||
async def cog_after_invoke(self, ctx: commands.Context) -> None:
|
||||
await self.maybe_run_pending_db_tasks(ctx)
|
||||
|
||||
async def cog_command_error(self, ctx: commands.Context, error: Exception) -> None:
|
||||
error = getattr(error, "original", error)
|
||||
handled = False
|
||||
if isinstance(error, commands.ArgParserFailure):
|
||||
handled = True
|
||||
msg = _("`{user_input}` is not a valid value for `{command}`").format(
|
||||
user_input=error.user_input, command=error.cmd,
|
||||
)
|
||||
if error.custom_help_msg:
|
||||
msg += f"\n{error.custom_help_msg}"
|
||||
await self.send_embed_msg(
|
||||
ctx, title=_("Unable To Parse Argument"), description=msg, error=True,
|
||||
)
|
||||
if error.send_cmd_help:
|
||||
await ctx.send_help()
|
||||
elif isinstance(error, commands.ConversionFailure):
|
||||
handled = True
|
||||
if error.args:
|
||||
if match := RE_CONVERSION.search(error.args[0]):
|
||||
await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Invalid Argument"),
|
||||
description=_(
|
||||
"The argument you gave for `{}` is not valid: I was expecting a `{}`."
|
||||
).format(match.group(2), match.group(1)),
|
||||
error=True,
|
||||
)
|
||||
else:
|
||||
await self.send_embed_msg(
|
||||
ctx, title=_("Invalid Argument"), description=error.args[0], error=True,
|
||||
)
|
||||
else:
|
||||
await ctx.send_help()
|
||||
elif isinstance(error, (IndexError, ClientConnectorError)) and any(
|
||||
e in str(error).lower() for e in ["no nodes found.", "cannot connect to host"]
|
||||
):
|
||||
handled = True
|
||||
await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Invalid Environment"),
|
||||
description=_("Connection to Lavalink has been lost."),
|
||||
error=True,
|
||||
)
|
||||
debug_exc_log(log, error, "This is a handled error")
|
||||
elif isinstance(error, KeyError) and "such player for that guild" in str(error):
|
||||
handled = True
|
||||
await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("No Player Available"),
|
||||
description=_("The bot is not connected to a voice channel."),
|
||||
error=True,
|
||||
)
|
||||
debug_exc_log(log, error, "This is a handled error")
|
||||
elif isinstance(error, (TrackEnqueueError, asyncio.exceptions.TimeoutError)):
|
||||
handled = True
|
||||
await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Unable to Get Track"),
|
||||
description=_(
|
||||
"I'm unable get a track from Lavalink at the moment,"
|
||||
"try again in a few minutes."
|
||||
),
|
||||
error=True,
|
||||
)
|
||||
debug_exc_log(log, error, "This is a handled error")
|
||||
if not isinstance(
|
||||
error,
|
||||
(
|
||||
commands.CheckFailure,
|
||||
commands.UserInputError,
|
||||
commands.DisabledCommand,
|
||||
commands.CommandOnCooldown,
|
||||
commands.MaxConcurrencyReached,
|
||||
),
|
||||
):
|
||||
self.update_player_lock(ctx, False)
|
||||
if self.api_interface is not None:
|
||||
await self.api_interface.run_tasks(ctx)
|
||||
if not handled:
|
||||
await self.bot.on_command_error(ctx, error, unhandled_by_cog=True)
|
||||
|
||||
def cog_unload(self) -> None:
|
||||
if not self.cog_cleaned_up:
|
||||
self.bot.dispatch("red_audio_unload", self)
|
||||
self.session.detach()
|
||||
self.bot.loop.create_task(self._close_database())
|
||||
if self.player_automated_timer_task:
|
||||
self.player_automated_timer_task.cancel()
|
||||
|
||||
if self.lavalink_connect_task:
|
||||
self.lavalink_connect_task.cancel()
|
||||
|
||||
if self.cog_init_task:
|
||||
self.cog_init_task.cancel()
|
||||
|
||||
lavalink.unregister_event_listener(self.lavalink_event_handler)
|
||||
self.bot.loop.create_task(lavalink.close())
|
||||
if self.player_manager is not None:
|
||||
self.bot.loop.create_task(self.player_manager.shutdown())
|
||||
|
||||
self.cog_cleaned_up = True
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_voice_state_update(
|
||||
self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState
|
||||
) -> None:
|
||||
await self.cog_ready_event.wait()
|
||||
if after.channel != before.channel:
|
||||
try:
|
||||
self.skip_votes[before.channel.guild].remove(member.id)
|
||||
except (ValueError, KeyError, AttributeError):
|
||||
pass
|
||||
192
redbot/cogs/audio/core/events/lavalink.py
Normal file
192
redbot/cogs/audio/core/events/lavalink.py
Normal file
@@ -0,0 +1,192 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from ...errors import DatabaseError
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.lavalink")
|
||||
|
||||
|
||||
class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async def lavalink_event_handler(
|
||||
self, player: lavalink.Player, event_type: lavalink.LavalinkEvents, extra
|
||||
) -> None:
|
||||
current_track = player.current
|
||||
current_channel = player.channel
|
||||
guild = self.rgetattr(current_channel, "guild", None)
|
||||
guild_id = self.rgetattr(guild, "id", None)
|
||||
current_requester = self.rgetattr(current_track, "requester", None)
|
||||
current_stream = self.rgetattr(current_track, "is_stream", None)
|
||||
current_length = self.rgetattr(current_track, "length", None)
|
||||
current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
|
||||
current_extras = self.rgetattr(current_track, "extras", {})
|
||||
guild_data = await self.config.guild(guild).all()
|
||||
repeat = guild_data["repeat"]
|
||||
notify = guild_data["notify"]
|
||||
disconnect = guild_data["disconnect"]
|
||||
autoplay = guild_data["auto_play"]
|
||||
description = self.get_track_description(current_track, self.local_folder_current_path)
|
||||
status = await self.config.status()
|
||||
log.debug(f"Received a new lavalink event for {guild_id}: {event_type}: {extra}")
|
||||
prev_song: lavalink.Track = player.fetch("prev_song")
|
||||
await self.maybe_reset_error_counter(player)
|
||||
|
||||
if event_type == lavalink.LavalinkEvents.TRACK_START:
|
||||
self.skip_votes[guild] = []
|
||||
playing_song = player.fetch("playing_song")
|
||||
requester = player.fetch("requester")
|
||||
player.store("prev_song", playing_song)
|
||||
player.store("prev_requester", requester)
|
||||
player.store("playing_song", current_track)
|
||||
player.store("requester", current_requester)
|
||||
self.bot.dispatch("red_audio_track_start", guild, current_track, current_requester)
|
||||
if event_type == lavalink.LavalinkEvents.TRACK_END:
|
||||
prev_requester = player.fetch("prev_requester")
|
||||
self.bot.dispatch("red_audio_track_end", guild, prev_song, prev_requester)
|
||||
if event_type == lavalink.LavalinkEvents.QUEUE_END:
|
||||
prev_requester = player.fetch("prev_requester")
|
||||
self.bot.dispatch("red_audio_queue_end", guild, prev_song, prev_requester)
|
||||
if (
|
||||
autoplay
|
||||
and not player.queue
|
||||
and player.fetch("playing_song") is not None
|
||||
and self.playlist_api is not None
|
||||
and self.api_interface is not None
|
||||
):
|
||||
try:
|
||||
await self.api_interface.autoplay(player, self.playlist_api)
|
||||
except DatabaseError:
|
||||
notify_channel = player.fetch("channel")
|
||||
if notify_channel:
|
||||
notify_channel = self.bot.get_channel(notify_channel)
|
||||
await self.send_embed_msg(
|
||||
notify_channel, title=_("Couldn't get a valid track.")
|
||||
)
|
||||
return
|
||||
if event_type == lavalink.LavalinkEvents.TRACK_START and notify:
|
||||
notify_channel = player.fetch("channel")
|
||||
if notify_channel:
|
||||
notify_channel = self.bot.get_channel(notify_channel)
|
||||
if player.fetch("notify_message") is not None:
|
||||
with contextlib.suppress(discord.HTTPException):
|
||||
await player.fetch("notify_message").delete()
|
||||
|
||||
if (
|
||||
autoplay
|
||||
and current_extras.get("autoplay")
|
||||
and (
|
||||
prev_song is None
|
||||
or (hasattr(prev_song, "extras") and not prev_song.extras.get("autoplay"))
|
||||
)
|
||||
):
|
||||
await self.send_embed_msg(notify_channel, title=_("Auto Play started."))
|
||||
|
||||
if not description:
|
||||
return
|
||||
if current_stream:
|
||||
dur = "LIVE"
|
||||
else:
|
||||
dur = self.format_time(current_length)
|
||||
|
||||
thumb = None
|
||||
if await self.config.guild(guild).thumbnail() and current_thumbnail:
|
||||
thumb = current_thumbnail
|
||||
|
||||
notify_message = await self.send_embed_msg(
|
||||
notify_channel,
|
||||
title=_("Now Playing"),
|
||||
description=description,
|
||||
footer=_("Track length: {length} | Requested by: {user}").format(
|
||||
length=dur, user=current_requester
|
||||
),
|
||||
thumbnail=thumb,
|
||||
)
|
||||
player.store("notify_message", notify_message)
|
||||
if event_type == lavalink.LavalinkEvents.TRACK_START and status:
|
||||
player_check = self.get_active_player_count()
|
||||
await self.update_bot_presence(*player_check)
|
||||
|
||||
if event_type == lavalink.LavalinkEvents.TRACK_END and status:
|
||||
await asyncio.sleep(1)
|
||||
if not player.is_playing:
|
||||
player_check = self.get_active_player_count()
|
||||
await self.update_bot_presence(*player_check)
|
||||
|
||||
if event_type == lavalink.LavalinkEvents.QUEUE_END:
|
||||
if not autoplay:
|
||||
notify_channel = player.fetch("channel")
|
||||
if notify_channel and notify:
|
||||
notify_channel = self.bot.get_channel(notify_channel)
|
||||
await self.send_embed_msg(notify_channel, title=_("Queue ended."))
|
||||
if disconnect:
|
||||
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||
await player.disconnect()
|
||||
if status:
|
||||
player_check = self.get_active_player_count()
|
||||
await self.update_bot_presence(*player_check)
|
||||
|
||||
if event_type in [
|
||||
lavalink.LavalinkEvents.TRACK_EXCEPTION,
|
||||
lavalink.LavalinkEvents.TRACK_STUCK,
|
||||
]:
|
||||
message_channel = player.fetch("channel")
|
||||
while True:
|
||||
if current_track in player.queue:
|
||||
player.queue.remove(current_track)
|
||||
else:
|
||||
break
|
||||
if repeat:
|
||||
player.current = None
|
||||
if not guild_id:
|
||||
return
|
||||
self._error_counter.setdefault(guild_id, 0)
|
||||
if guild_id not in self._error_counter:
|
||||
self._error_counter[guild_id] = 0
|
||||
early_exit = await self.increase_error_counter(player)
|
||||
if early_exit:
|
||||
self._disconnected_players[guild_id] = True
|
||||
self.play_lock[guild_id] = False
|
||||
eq = player.fetch("eq")
|
||||
player.queue = []
|
||||
player.store("playing_song", None)
|
||||
if eq:
|
||||
await self.config.custom("EQUALIZER", guild_id).eq_bands.set(eq.bands)
|
||||
await player.stop()
|
||||
await player.disconnect()
|
||||
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||
if message_channel:
|
||||
message_channel = self.bot.get_channel(message_channel)
|
||||
if early_exit:
|
||||
embed = discord.Embed(
|
||||
colour=await self.bot.get_embed_color(message_channel),
|
||||
title=_("Multiple Errors Detected"),
|
||||
description=_(
|
||||
"Closing the audio player "
|
||||
"due to multiple errors being detected. "
|
||||
"If this persists, please inform the bot owner "
|
||||
"as the Audio cog may be temporally unavailable."
|
||||
),
|
||||
)
|
||||
await message_channel.send(embed=embed)
|
||||
return
|
||||
else:
|
||||
description = description or ""
|
||||
if event_type == lavalink.LavalinkEvents.TRACK_STUCK:
|
||||
embed = discord.Embed(
|
||||
colour=await self.bot.get_embed_color(message_channel),
|
||||
title=_("Track Stuck"),
|
||||
description="{}".format(description),
|
||||
)
|
||||
else:
|
||||
embed = discord.Embed(
|
||||
title=_("Track Error"),
|
||||
colour=await self.bot.get_embed_color(message_channel),
|
||||
description="{}\n{}".format(extra.replace("\n", ""), description),
|
||||
)
|
||||
await message_channel.send(embed=embed)
|
||||
await player.skip()
|
||||
21
redbot/cogs/audio/core/events/red.py
Normal file
21
redbot/cogs/audio/core/events/red.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import logging
|
||||
from typing import Mapping
|
||||
|
||||
from redbot.core import commands
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.red")
|
||||
|
||||
|
||||
class RedEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@commands.Cog.listener()
|
||||
async def on_red_api_tokens_update(
|
||||
self, service_name: str, api_tokens: Mapping[str, str]
|
||||
) -> None:
|
||||
if service_name == "youtube":
|
||||
self.api_interface.youtube_api.update_token(api_tokens)
|
||||
elif service_name == "spotify":
|
||||
self.api_interface.spotify_api.update_token(api_tokens)
|
||||
elif service_name == "audiodb":
|
||||
self.api_interface.global_cache_api.update_token(api_tokens)
|
||||
Reference in New Issue
Block a user