mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
I promise I'm not doing this on purpose (#4565)
* Prep for 0.7.2
* So What di i do here? I done Magic, magic only found in the tales of old.
* turns out formatting is something important
* fixes
* improved Error handling when Global API is enabled
* further improve resuming logic
* more of dat dark voodoo blood magic
* major tweaks to auto restore when auto play is enabled 👀
* fix duplicated "Auto play stated." message + Auto play restart :feelsgoodman:
* missed these
* fix the new duplicated fucking message bitch.
* Let discord handle player reconnects
* eh
* `Requires force install`, utilize new Exponential Backoff object on player and safer reconnect logic, emulating d.py and WL.
* hmmmmm gotta monitor
* mother fucking brackets
* Why didnt i consider this the first time?????????????
* new error code to handle?
* soooooooooooooooo these are import so why arent we ensuring they are set.
* improved logging
* improved logging
* aaaaaaaaaaaaaaa
* We need our own error and special handling to not conflict with dpy
* (Last Known Bug) Fix the infinite loop of 4006 that sometimes could happen as an edge case after a successful resume.
* This will require a force reinstall to install `RLL 0.8.0`, this properly fixes the bug mentioned on the previous commit.
* address "Localtrack names/paths need to be escaped." comment
* address Fixators crash mentioned in #AT
* style
* fix preda's crash mentioned in PR
* add a thing here add a thing there add a thing everywhere
* style
* fixes here, fixes there, and backbone for curated playlist.
* bypass aiohttp and githubs and cloudflare and yo mammas cache
* I propose the new style is no style.
* allow curated playlist to be updated it `[p]playlist update` and show the diff
* fix `[p]summon` not resuming playback until next track.
* Hopefully handle predas rate limits.
* what else did i break now
* Update Lavalink.jar build
* lets try this
* reset the queue
* Bring Edge commits over fix a bunch of shiz again
* Bring Edge commits over fix a bunch of shiz again
* Handle 4014 OPs, Change `skip_votes` key to be an int rather than guild object
* aaaaaaaaaaaaaaa im dumb
* ...
* Simplify some shiz + use a set instead of a list for votes.
Co-authored-by: aikaterna <20862007+aikaterna@users.noreply.github.com>
This commit is contained in:
parent
1199f160d0
commit
b7d8b0552e
@ -211,6 +211,7 @@ class AudioAPIInterface:
|
|||||||
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
||||||
youtube_cache = CacheLevel.set_youtube().is_subset(current_cache_level)
|
youtube_cache = CacheLevel.set_youtube().is_subset(current_cache_level)
|
||||||
youtube_api_error = None
|
youtube_api_error = None
|
||||||
|
global_api = self.cog.global_api_user.get("can_read")
|
||||||
async for track in AsyncIter(tracks):
|
async for track in AsyncIter(tracks):
|
||||||
if isinstance(track, str):
|
if isinstance(track, str):
|
||||||
break
|
break
|
||||||
@ -267,13 +268,15 @@ class AudioAPIInterface:
|
|||||||
track_count += 1
|
track_count += 1
|
||||||
if notifier is not None and ((track_count % 2 == 0) or (track_count == total_tracks)):
|
if notifier is not None and ((track_count % 2 == 0) or (track_count == total_tracks)):
|
||||||
await notifier.notify_user(current=track_count, total=total_tracks, key="youtube")
|
await notifier.notify_user(current=track_count, total=total_tracks, key="youtube")
|
||||||
if notifier is not None and youtube_api_error:
|
if notifier is not None and (youtube_api_error and not global_api):
|
||||||
error_embed = discord.Embed(
|
error_embed = discord.Embed(
|
||||||
colour=await ctx.embed_colour(),
|
colour=await ctx.embed_colour(),
|
||||||
title=_("Failing to get tracks, skipping remaining."),
|
title=_("Failing to get tracks, skipping remaining."),
|
||||||
)
|
)
|
||||||
await notifier.update_embed(error_embed)
|
await notifier.update_embed(error_embed)
|
||||||
break
|
break
|
||||||
|
elif notifier is not None and (youtube_api_error and global_api):
|
||||||
|
continue
|
||||||
if CacheLevel.set_spotify().is_subset(current_cache_level):
|
if CacheLevel.set_spotify().is_subset(current_cache_level):
|
||||||
task = ("insert", ("spotify", database_entries))
|
task = ("insert", ("spotify", database_entries))
|
||||||
self.append_task(ctx, *task)
|
self.append_task(ctx, *task)
|
||||||
@ -447,11 +450,12 @@ class AudioAPIInterface:
|
|||||||
List of Youtube URLs.
|
List of Youtube URLs.
|
||||||
"""
|
"""
|
||||||
await self.global_cache_api._get_api_key()
|
await self.global_cache_api._get_api_key()
|
||||||
globaldb_toggle = await self.config.global_db_enabled()
|
globaldb_toggle = self.cog.global_api_user.get("can_read")
|
||||||
global_entry = globaldb_toggle and query_global
|
global_entry = globaldb_toggle and query_global
|
||||||
track_list: List = []
|
track_list: List = []
|
||||||
has_not_allowed = False
|
has_not_allowed = False
|
||||||
youtube_api_error = None
|
youtube_api_error = None
|
||||||
|
skip_youtube_api = False
|
||||||
try:
|
try:
|
||||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||||
guild_data = await self.config.guild(ctx.guild).all()
|
guild_data = await self.config.guild(ctx.guild).all()
|
||||||
@ -518,7 +522,7 @@ class AudioAPIInterface:
|
|||||||
llresponse["loadType"] = "V2_COMPAT"
|
llresponse["loadType"] = "V2_COMPAT"
|
||||||
llresponse = LoadResult(llresponse)
|
llresponse = LoadResult(llresponse)
|
||||||
val = llresponse or None
|
val = llresponse or None
|
||||||
if val is None:
|
if val is None and not skip_youtube_api:
|
||||||
try:
|
try:
|
||||||
val = await self.fetch_youtube_query(
|
val = await self.fetch_youtube_query(
|
||||||
ctx, track_info, current_cache_level=current_cache_level
|
ctx, track_info, current_cache_level=current_cache_level
|
||||||
@ -526,6 +530,7 @@ class AudioAPIInterface:
|
|||||||
except YouTubeApiError as err:
|
except YouTubeApiError as err:
|
||||||
val = None
|
val = None
|
||||||
youtube_api_error = err.message
|
youtube_api_error = err.message
|
||||||
|
skip_youtube_api = True
|
||||||
if not youtube_api_error:
|
if not youtube_api_error:
|
||||||
if youtube_cache and val and llresponse is None:
|
if youtube_cache and val and llresponse is None:
|
||||||
task = ("update", ("youtube", {"track": track_info}))
|
task = ("update", ("youtube", {"track": track_info}))
|
||||||
@ -589,7 +594,9 @@ class AudioAPIInterface:
|
|||||||
seconds=seconds,
|
seconds=seconds,
|
||||||
)
|
)
|
||||||
|
|
||||||
if youtube_api_error or consecutive_fails >= (20 if global_entry else 10):
|
if (youtube_api_error and not global_entry) or consecutive_fails >= (
|
||||||
|
20 if global_entry else 10
|
||||||
|
):
|
||||||
error_embed = discord.Embed(
|
error_embed = discord.Embed(
|
||||||
colour=await ctx.embed_colour(),
|
colour=await ctx.embed_colour(),
|
||||||
title=_("Failing to get tracks, skipping remaining."),
|
title=_("Failing to get tracks, skipping remaining."),
|
||||||
@ -793,7 +800,7 @@ class AudioAPIInterface:
|
|||||||
val = None
|
val = None
|
||||||
query = Query.process_input(query, self.cog.local_folder_current_path)
|
query = Query.process_input(query, self.cog.local_folder_current_path)
|
||||||
query_string = str(query)
|
query_string = str(query)
|
||||||
globaldb_toggle = await self.config.global_db_enabled()
|
globaldb_toggle = self.cog.global_api_user.get("can_read")
|
||||||
valid_global_entry = False
|
valid_global_entry = False
|
||||||
results = None
|
results = None
|
||||||
called_api = False
|
called_api = False
|
||||||
@ -925,6 +932,7 @@ class AudioAPIInterface:
|
|||||||
autoplaylist = await self.config.guild(player.channel.guild).autoplaylist()
|
autoplaylist = await self.config.guild(player.channel.guild).autoplaylist()
|
||||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||||
cache_enabled = CacheLevel.set_lavalink().is_subset(current_cache_level)
|
cache_enabled = CacheLevel.set_lavalink().is_subset(current_cache_level)
|
||||||
|
notify_channel_id = player.fetch("channel")
|
||||||
playlist = None
|
playlist = None
|
||||||
tracks = None
|
tracks = None
|
||||||
if autoplaylist["enabled"]:
|
if autoplaylist["enabled"]:
|
||||||
@ -973,7 +981,7 @@ class AudioAPIInterface:
|
|||||||
and not query.local_track_path.exists()
|
and not query.local_track_path.exists()
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
notify_channel = self.bot.get_channel(player.fetch("channel"))
|
notify_channel = self.bot.get_channel(notify_channel_id)
|
||||||
if not await self.cog.is_query_allowed(
|
if not await self.cog.is_query_allowed(
|
||||||
self.config,
|
self.config,
|
||||||
notify_channel,
|
notify_channel,
|
||||||
@ -997,8 +1005,20 @@ class AudioAPIInterface:
|
|||||||
)
|
)
|
||||||
player.add(player.channel.guild.me, track)
|
player.add(player.channel.guild.me, track)
|
||||||
self.bot.dispatch(
|
self.bot.dispatch(
|
||||||
"red_audio_track_auto_play", player.channel.guild, track, player.channel.guild.me
|
"red_audio_track_auto_play",
|
||||||
|
player.channel.guild,
|
||||||
|
track,
|
||||||
|
player.channel.guild.me,
|
||||||
|
player,
|
||||||
)
|
)
|
||||||
|
if notify_channel_id:
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=player.channel.guild.id
|
||||||
|
).currently_auto_playing_in.set([notify_channel_id, player.channel.id])
|
||||||
|
else:
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=player.channel.guild.id
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
if not player.current:
|
if not player.current:
|
||||||
await player.play()
|
await player.play()
|
||||||
|
|
||||||
|
|||||||
@ -235,7 +235,7 @@ class PlaylistCompat23:
|
|||||||
Trying to access the User scope without an user id.
|
Trying to access the User scope without an user id.
|
||||||
"""
|
"""
|
||||||
guild = data.get("guild") or kwargs.get("guild")
|
guild = data.get("guild") or kwargs.get("guild")
|
||||||
author: int = data.get("author") or 0
|
author: int = data.get("author") or kwargs.get("author") or 0
|
||||||
playlist_id = data.get("id") or playlist_number
|
playlist_id = data.get("id") or playlist_number
|
||||||
name = data.get("name", "Unnamed")
|
name = data.get("name", "Unnamed")
|
||||||
playlist_url = data.get("playlist_url", None)
|
playlist_url = data.get("playlist_url", None)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import asyncio
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from collections import Counter
|
from collections import Counter, defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
|
|
||||||
@ -77,6 +77,9 @@ class Audio(
|
|||||||
|
|
||||||
self.session = aiohttp.ClientSession(json_serialize=json.dumps)
|
self.session = aiohttp.ClientSession(json_serialize=json.dumps)
|
||||||
self.cog_ready_event = asyncio.Event()
|
self.cog_ready_event = asyncio.Event()
|
||||||
|
self._ws_resume = defaultdict(asyncio.Event)
|
||||||
|
self._ws_op_codes = defaultdict(asyncio.LifoQueue)
|
||||||
|
|
||||||
self.cog_init_task = None
|
self.cog_init_task = None
|
||||||
self.global_api_user = {
|
self.global_api_user = {
|
||||||
"fetched": False,
|
"fetched": False,
|
||||||
@ -85,10 +88,12 @@ class Audio(
|
|||||||
"can_delete": False,
|
"can_delete": False,
|
||||||
}
|
}
|
||||||
self._ll_guild_updates = set()
|
self._ll_guild_updates = set()
|
||||||
|
self._diconnected_shard = set()
|
||||||
self._last_ll_update = datetime.datetime.now(datetime.timezone.utc)
|
self._last_ll_update = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
|
||||||
default_global = dict(
|
default_global = dict(
|
||||||
schema_version=1,
|
schema_version=1,
|
||||||
|
bundled_playlist_version=0,
|
||||||
owner_notification=0,
|
owner_notification=0,
|
||||||
cache_level=0,
|
cache_level=0,
|
||||||
cache_age=365,
|
cache_age=365,
|
||||||
@ -107,8 +112,14 @@ class Audio(
|
|||||||
|
|
||||||
default_guild = dict(
|
default_guild = dict(
|
||||||
auto_play=False,
|
auto_play=False,
|
||||||
|
currently_auto_playing_in=None,
|
||||||
auto_deafen=True,
|
auto_deafen=True,
|
||||||
autoplaylist={"enabled": False, "id": None, "name": None, "scope": None},
|
autoplaylist=dict(
|
||||||
|
enabled=True,
|
||||||
|
id=42069,
|
||||||
|
name="Aikaterna's curated tracks",
|
||||||
|
scope=PlaylistScope.GLOBAL.value,
|
||||||
|
),
|
||||||
persist_queue=True,
|
persist_queue=True,
|
||||||
disconnect=False,
|
disconnect=False,
|
||||||
dj_enabled=False,
|
dj_enabled=False,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import asyncio
|
|||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from collections import Counter
|
from collections import Counter, defaultdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Set, TYPE_CHECKING, Any, List, Mapping, MutableMapping, Optional, Tuple, Union
|
from typing import Set, TYPE_CHECKING, Any, List, Mapping, MutableMapping, Optional, Tuple, Union
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ class MixinMeta(ABC):
|
|||||||
db_conn: Optional[APSWConnectionWrapper]
|
db_conn: Optional[APSWConnectionWrapper]
|
||||||
session: aiohttp.ClientSession
|
session: aiohttp.ClientSession
|
||||||
|
|
||||||
skip_votes: MutableMapping[int, List[int]]
|
skip_votes: MutableMapping[int, Set[int]]
|
||||||
play_lock: MutableMapping[int, bool]
|
play_lock: MutableMapping[int, bool]
|
||||||
_daily_playlist_cache: MutableMapping[int, bool]
|
_daily_playlist_cache: MutableMapping[int, bool]
|
||||||
_daily_global_playlist_cache: MutableMapping[int, bool]
|
_daily_global_playlist_cache: MutableMapping[int, bool]
|
||||||
@ -62,12 +62,14 @@ class MixinMeta(ABC):
|
|||||||
player_automated_timer_task: Optional[asyncio.Task]
|
player_automated_timer_task: Optional[asyncio.Task]
|
||||||
cog_init_task: Optional[asyncio.Task]
|
cog_init_task: Optional[asyncio.Task]
|
||||||
cog_ready_event: asyncio.Event
|
cog_ready_event: asyncio.Event
|
||||||
|
_ws_resume: defaultdict[Any, asyncio.Event]
|
||||||
|
_ws_op_codes: defaultdict[int, asyncio.LifoQueue]
|
||||||
_default_lavalink_settings: Mapping
|
_default_lavalink_settings: Mapping
|
||||||
permission_cache = discord.Permissions
|
permission_cache = discord.Permissions
|
||||||
|
|
||||||
_last_ll_update: datetime.datetime
|
_last_ll_update: datetime.datetime
|
||||||
_ll_guild_updates: Set[int]
|
_ll_guild_updates: Set[int]
|
||||||
|
_diconnected_shard: Set[int]
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def command_llsetup(self, ctx: commands.Context):
|
async def command_llsetup(self, ctx: commands.Context):
|
||||||
@ -306,6 +308,14 @@ class MixinMeta(ABC):
|
|||||||
async def _playlist_check(self, ctx: commands.Context) -> bool:
|
async def _playlist_check(self, ctx: commands.Context) -> bool:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def _build_bundled_playlist(self, forced: bool = None) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def decode_track(self, track: str, decode_errors: str = "") -> MutableMapping:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def can_manage_playlist(
|
async def can_manage_playlist(
|
||||||
self, scope: str, playlist: "Playlist", ctx: commands.Context, user, guild
|
self, scope: str, playlist: "Playlist", ctx: commands.Context, user, guild
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
from abc import ABC
|
from abc import ABC
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
from base64 import b64decode
|
||||||
|
from io import BytesIO
|
||||||
|
import struct
|
||||||
|
|
||||||
from redbot import VersionInfo
|
from redbot import VersionInfo
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
|
|
||||||
from ..converters import get_lazy_converter, get_playlist_converter
|
from ..converters import get_lazy_converter, get_playlist_converter
|
||||||
|
|
||||||
__version__ = VersionInfo.from_json({"major": 2, "minor": 3, "micro": 0, "releaselevel": "final"})
|
__version__ = VersionInfo.from_json({"major": 2, "minor": 4, "micro": 0, "releaselevel": "final"})
|
||||||
|
|
||||||
__author__ = ["aikaterna", "Draper"]
|
__author__ = ["aikaterna", "Draper"]
|
||||||
|
|
||||||
@ -57,3 +60,90 @@ class CompositeMetaClass(type(commands.Cog), type(ABC)):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Both DataReader and DataWriter are taken from https://github.com/Devoxin/Lavalink.py/blob/master/lavalink/datarw.py
|
||||||
|
# These are licenced under MIT, Thanks Devoxin for putting these together!
|
||||||
|
# The license can be found in https://github.com/Devoxin/Lavalink.py/blob/master/LICENSE
|
||||||
|
|
||||||
|
|
||||||
|
class DataReader:
|
||||||
|
def __init__(self, ts):
|
||||||
|
self._buf = BytesIO(b64decode(ts))
|
||||||
|
|
||||||
|
def _read(self, n):
|
||||||
|
return self._buf.read(n)
|
||||||
|
|
||||||
|
def read_byte(self):
|
||||||
|
return self._read(1)
|
||||||
|
|
||||||
|
def read_boolean(self):
|
||||||
|
(result,) = struct.unpack("B", self.read_byte())
|
||||||
|
return result != 0
|
||||||
|
|
||||||
|
def read_unsigned_short(self):
|
||||||
|
(result,) = struct.unpack(">H", self._read(2))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def read_int(self):
|
||||||
|
(result,) = struct.unpack(">i", self._read(4))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def read_long(self):
|
||||||
|
(result,) = struct.unpack(">Q", self._read(8))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def read_utf(self):
|
||||||
|
text_length = self.read_unsigned_short()
|
||||||
|
return self._read(text_length)
|
||||||
|
|
||||||
|
|
||||||
|
class DataWriter:
|
||||||
|
def __init__(self):
|
||||||
|
self._buf = BytesIO()
|
||||||
|
|
||||||
|
def _write(self, data):
|
||||||
|
self._buf.write(data)
|
||||||
|
|
||||||
|
def write_byte(self, byte):
|
||||||
|
self._buf.write(byte)
|
||||||
|
|
||||||
|
def write_boolean(self, b):
|
||||||
|
enc = struct.pack("B", 1 if b else 0)
|
||||||
|
self.write_byte(enc)
|
||||||
|
|
||||||
|
def write_unsigned_short(self, s):
|
||||||
|
enc = struct.pack(">H", s)
|
||||||
|
self._write(enc)
|
||||||
|
|
||||||
|
def write_int(self, i):
|
||||||
|
enc = struct.pack(">i", i)
|
||||||
|
self._write(enc)
|
||||||
|
|
||||||
|
def write_long(self, l):
|
||||||
|
enc = struct.pack(">Q", l)
|
||||||
|
self._write(enc)
|
||||||
|
|
||||||
|
def write_utf(self, s):
|
||||||
|
utf = s.encode("utf8")
|
||||||
|
byte_len = len(utf)
|
||||||
|
|
||||||
|
if byte_len > 65535:
|
||||||
|
raise OverflowError("UTF string may not exceed 65535 bytes!")
|
||||||
|
|
||||||
|
self.write_unsigned_short(byte_len)
|
||||||
|
self._write(utf)
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
with BytesIO() as track_buf:
|
||||||
|
byte_len = self._buf.getbuffer().nbytes
|
||||||
|
flags = byte_len | (1 << 30)
|
||||||
|
enc_flags = struct.pack(">i", flags)
|
||||||
|
track_buf.write(enc_flags)
|
||||||
|
|
||||||
|
self._buf.seek(0)
|
||||||
|
track_buf.write(self._buf.read())
|
||||||
|
self._buf.close()
|
||||||
|
|
||||||
|
track_buf.seek(0)
|
||||||
|
return track_buf.read()
|
||||||
|
|||||||
@ -558,7 +558,13 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
@command_audioset_autoplay.command(name="reset")
|
@command_audioset_autoplay.command(name="reset")
|
||||||
async def command_audioset_autoplay_reset(self, ctx: commands.Context):
|
async def command_audioset_autoplay_reset(self, ctx: commands.Context):
|
||||||
"""Resets auto-play to the default playlist."""
|
"""Resets auto-play to the default playlist."""
|
||||||
playlist_data = dict(enabled=False, id=None, name=None, scope=None)
|
playlist_data = dict(
|
||||||
|
enabled=True,
|
||||||
|
id=42069,
|
||||||
|
name="Aikaterna's curated tracks",
|
||||||
|
scope=PlaylistScope.GLOBAL.value,
|
||||||
|
)
|
||||||
|
|
||||||
await self.config.guild(ctx.guild).autoplaylist.set(playlist_data)
|
await self.config.guild(ctx.guild).autoplaylist.set(playlist_data)
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@ -68,10 +68,14 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
eq = player.fetch("eq")
|
eq = player.fetch("eq")
|
||||||
player.queue = []
|
player.queue = []
|
||||||
player.store("playing_song", None)
|
player.store("playing_song", None)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
if eq:
|
if eq:
|
||||||
await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands)
|
await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands)
|
||||||
await player.stop()
|
await player.stop()
|
||||||
await player.disconnect()
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(guild_id=ctx.guild.id).currently_auto_playing_in.set(
|
||||||
|
[]
|
||||||
|
)
|
||||||
self._ll_guild_updates.discard(ctx.guild.id)
|
self._ll_guild_updates.discard(ctx.guild.id)
|
||||||
await self.api_interface.persistent_queue_api.drop(ctx.guild.id)
|
await self.api_interface.persistent_queue_api.drop(ctx.guild.id)
|
||||||
|
|
||||||
@ -91,6 +95,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
}
|
}
|
||||||
expected = tuple(emoji.values())
|
expected = tuple(emoji.values())
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if player.current:
|
if player.current:
|
||||||
arrow = await self.draw_time(ctx)
|
arrow = await self.draw_time(ctx)
|
||||||
pos = self.format_time(player.position)
|
pos = self.format_time(player.position)
|
||||||
@ -212,7 +218,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Manage Tracks"),
|
title=_("Unable To Manage Tracks"),
|
||||||
description=_("You need the DJ role to pause or resume tracks."),
|
description=_("You need the DJ role to pause or resume tracks."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if not player.current:
|
if not player.current:
|
||||||
return await self.send_embed_msg(ctx, title=_("Nothing playing."))
|
return await self.send_embed_msg(ctx, title=_("Nothing playing."))
|
||||||
description = await self.get_track_description(
|
description = await self.get_track_description(
|
||||||
@ -266,7 +273,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
"to enqueue the previous song tracks."
|
"to enqueue the previous song tracks."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if player.fetch("prev_song") is None:
|
if player.fetch("prev_song") is None:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx, title=_("Unable To Play Tracks"), description=_("No previous track.")
|
ctx, title=_("Unable To Play Tracks"), description=_("No previous track.")
|
||||||
@ -332,7 +340,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Seek Tracks"),
|
title=_("Unable To Seek Tracks"),
|
||||||
description=_("You need the DJ role or be the track requester to use seek."),
|
description=_("You need the DJ role or be the track requester to use seek."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if player.current:
|
if player.current:
|
||||||
if player.current.is_stream:
|
if player.current.is_stream:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
@ -405,6 +414,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Toggle Shuffle"),
|
title=_("Unable To Toggle Shuffle"),
|
||||||
description=_("You must be in the voice channel to toggle shuffle."),
|
description=_("You must be in the voice channel to toggle shuffle."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
|
|
||||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||||
await self.config.guild(ctx.guild).shuffle.set(not shuffle)
|
await self.config.guild(ctx.guild).shuffle.set(not shuffle)
|
||||||
@ -448,6 +459,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Toggle Shuffle"),
|
title=_("Unable To Toggle Shuffle"),
|
||||||
description=_("You must be in the voice channel to toggle shuffle."),
|
description=_("You must be in the voice channel to toggle shuffle."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
|
|
||||||
bumped = await self.config.guild(ctx.guild).shuffle_bumped()
|
bumped = await self.config.guild(ctx.guild).shuffle_bumped()
|
||||||
await self.config.guild(ctx.guild).shuffle_bumped.set(not bumped)
|
await self.config.guild(ctx.guild).shuffle_bumped.set(not bumped)
|
||||||
@ -504,7 +517,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Skip Tracks"),
|
title=_("Unable To Skip Tracks"),
|
||||||
description=_("You can only skip the current track."),
|
description=_("You can only skip the current track."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if vote_enabled:
|
if vote_enabled:
|
||||||
if not can_skip:
|
if not can_skip:
|
||||||
if skip_to_track is not None:
|
if skip_to_track is not None:
|
||||||
@ -516,10 +530,10 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
if ctx.author.id in self.skip_votes[ctx.guild.id]:
|
if ctx.author.id in self.skip_votes[ctx.guild.id]:
|
||||||
self.skip_votes[ctx.guild.id].remove(ctx.author.id)
|
self.skip_votes[ctx.guild.id].discard(ctx.author.id)
|
||||||
reply = _("I removed your vote to skip.")
|
reply = _("I removed your vote to skip.")
|
||||||
else:
|
else:
|
||||||
self.skip_votes[ctx.guild.id].append(ctx.author.id)
|
self.skip_votes[ctx.guild.id].add(ctx.author.id)
|
||||||
reply = _("You voted to skip.")
|
reply = _("You voted to skip.")
|
||||||
|
|
||||||
num_votes = len(self.skip_votes[ctx.guild.id])
|
num_votes = len(self.skip_votes[ctx.guild.id])
|
||||||
@ -532,7 +546,7 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
vote = int(100 * num_votes / num_members)
|
vote = int(100 * num_votes / num_members)
|
||||||
percent = await self.config.guild(ctx.guild).vote_percent()
|
percent = await self.config.guild(ctx.guild).vote_percent()
|
||||||
if vote >= percent:
|
if vote >= percent:
|
||||||
self.skip_votes[ctx.guild.id] = []
|
self.skip_votes[ctx.guild.id] = set()
|
||||||
await self.send_embed_msg(ctx, title=_("Vote threshold met."))
|
await self.send_embed_msg(ctx, title=_("Vote threshold met."))
|
||||||
return await self._skip_action(ctx)
|
return await self._skip_action(ctx)
|
||||||
else:
|
else:
|
||||||
@ -583,6 +597,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Stop Player"),
|
title=_("Unable To Stop Player"),
|
||||||
description=_("You need the DJ role to stop the music."),
|
description=_("You need the DJ role to stop the music."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if (
|
if (
|
||||||
player.is_playing
|
player.is_playing
|
||||||
or (not player.is_playing and player.paused)
|
or (not player.is_playing and player.paused)
|
||||||
@ -597,7 +613,11 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
player.store("prev_requester", None)
|
player.store("prev_requester", None)
|
||||||
player.store("prev_song", None)
|
player.store("prev_song", None)
|
||||||
player.store("requester", None)
|
player.store("requester", None)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
await player.stop()
|
await player.stop()
|
||||||
|
await self.config.guild_from_id(guild_id=ctx.guild.id).currently_auto_playing_in.set(
|
||||||
|
[]
|
||||||
|
)
|
||||||
await self.send_embed_msg(ctx, title=_("Stopping..."))
|
await self.send_embed_msg(ctx, title=_("Stopping..."))
|
||||||
await self.api_interface.persistent_queue_api.drop(ctx.guild.id)
|
await self.api_interface.persistent_queue_api.drop(ctx.guild.id)
|
||||||
|
|
||||||
@ -642,17 +662,28 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
if not self._player_check(ctx):
|
if not self._player_check(ctx):
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
else:
|
else:
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
if ctx.author.voice.channel == player.channel:
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
|
if (
|
||||||
|
ctx.author.voice.channel == player.channel
|
||||||
|
and ctx.guild.me in ctx.author.voice.channel.members
|
||||||
|
):
|
||||||
ctx.command.reset_cooldown(ctx)
|
ctx.command.reset_cooldown(ctx)
|
||||||
return
|
return
|
||||||
await player.move_to(ctx.author.voice.channel)
|
await player.move_to(
|
||||||
await self.self_deafen(player)
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
ctx.command.reset_cooldown(ctx)
|
ctx.command.reset_cooldown(ctx)
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
@ -693,23 +724,33 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Change Volume"),
|
title=_("Unable To Change Volume"),
|
||||||
description=_("You must be in the voice channel to change the volume."),
|
description=_("You must be in the voice channel to change the volume."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if dj_enabled and not can_skip and not await self._has_dj_role(ctx, ctx.author):
|
if dj_enabled and not can_skip and not await self._has_dj_role(ctx, ctx.author):
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
title=_("Unable To Change Volume"),
|
title=_("Unable To Change Volume"),
|
||||||
description=_("You need the DJ role to change the volume."),
|
description=_("You need the DJ role to change the volume."),
|
||||||
)
|
)
|
||||||
|
|
||||||
if vol < 0:
|
if vol < 0:
|
||||||
vol = 0
|
vol = 0
|
||||||
if vol > 150:
|
if vol > 150:
|
||||||
vol = 150
|
vol = 150
|
||||||
await self.config.guild(ctx.guild).volume.set(vol)
|
await self.config.guild(ctx.guild).volume.set(vol)
|
||||||
if self._player_check(ctx):
|
if self._player_check(ctx):
|
||||||
await lavalink.get_player(ctx.guild.id).set_volume(vol)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
await player.set_volume(vol)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
else:
|
else:
|
||||||
await self.config.guild(ctx.guild).volume.set(vol)
|
await self.config.guild(ctx.guild).volume.set(vol)
|
||||||
if self._player_check(ctx):
|
if self._player_check(ctx):
|
||||||
await lavalink.get_player(ctx.guild.id).set_volume(vol)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
await player.set_volume(vol)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
|
|
||||||
embed = discord.Embed(title=_("Volume:"), description=str(vol) + "%")
|
embed = discord.Embed(title=_("Volume:"), description=str(vol) + "%")
|
||||||
if not self._player_check(ctx):
|
if not self._player_check(ctx):
|
||||||
embed.set_footer(text=_("Nothing playing."))
|
embed.set_footer(text=_("Nothing playing."))
|
||||||
@ -741,6 +782,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Toggle Repeat"),
|
title=_("Unable To Toggle Repeat"),
|
||||||
description=_("You must be in the voice channel to toggle repeat."),
|
description=_("You must be in the voice channel to toggle repeat."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
|
|
||||||
autoplay = await self.config.guild(ctx.guild).auto_play()
|
autoplay = await self.config.guild(ctx.guild).auto_play()
|
||||||
repeat = await self.config.guild(ctx.guild).repeat()
|
repeat = await self.config.guild(ctx.guild).repeat()
|
||||||
@ -784,6 +827,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Modify Queue"),
|
title=_("Unable To Modify Queue"),
|
||||||
description=_("You must be in the voice channel to manage the queue."),
|
description=_("You must be in the voice channel to manage the queue."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
if isinstance(index_or_url, int):
|
if isinstance(index_or_url, int):
|
||||||
if index_or_url > len(player.queue) or index_or_url < 1:
|
if index_or_url > len(player.queue) or index_or_url < 1:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
@ -864,7 +909,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Bump Track"),
|
title=_("Unable To Bump Track"),
|
||||||
description=_("Song number must be greater than 1 and within the queue limit."),
|
description=_("Song number must be greater than 1 and within the queue limit."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
bump_index = index - 1
|
bump_index = index - 1
|
||||||
bump_song = player.queue[bump_index]
|
bump_song = player.queue[bump_index]
|
||||||
bump_song.extras["bumped"] = True
|
bump_song.extras["bumped"] = True
|
||||||
|
|||||||
@ -173,7 +173,8 @@ class EqualizerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Load Preset"),
|
title=_("Unable To Load Preset"),
|
||||||
description=_("You need the DJ role to load equalizer presets."),
|
description=_("You need the DJ role to load equalizer presets."),
|
||||||
)
|
)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq_values)
|
await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq_values)
|
||||||
await self._eq_check(ctx, player)
|
await self._eq_check(ctx, player)
|
||||||
eq = player.fetch("eq", Equalizer())
|
eq = player.fetch("eq", Equalizer())
|
||||||
@ -202,6 +203,8 @@ class EqualizerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
description=_("You need the DJ role to reset the equalizer."),
|
description=_("You need the DJ role to reset the equalizer."),
|
||||||
)
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
eq = player.fetch("eq", Equalizer())
|
eq = player.fetch("eq", Equalizer())
|
||||||
|
|
||||||
for band in range(eq.band_count):
|
for band in range(eq.band_count):
|
||||||
@ -284,6 +287,8 @@ class EqualizerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
return await eq_exists_msg.edit(embed=embed2)
|
return await eq_exists_msg.edit(embed=embed2)
|
||||||
|
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
eq = player.fetch("eq", Equalizer())
|
eq = player.fetch("eq", Equalizer())
|
||||||
to_append = {eq_preset: {"author": ctx.author.id, "bands": eq.bands}}
|
to_append = {eq_preset: {"author": ctx.author.id, "bands": eq.bands}}
|
||||||
new_eq_presets = {**eq_presets, **to_append}
|
new_eq_presets = {**eq_presets, **to_append}
|
||||||
@ -325,6 +330,8 @@ class EqualizerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
band_names = [
|
band_names = [
|
||||||
"25",
|
"25",
|
||||||
"40",
|
"40",
|
||||||
|
|||||||
@ -223,7 +223,7 @@ class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
msg = "----" + _("Connection Settings") + "---- \n"
|
msg = "----" + _("Connection Settings") + "---- \n"
|
||||||
msg += _("Host: [{host}]\n").format(host=host)
|
msg += _("Host: [{host}]\n").format(host=host)
|
||||||
msg += _("WS Port: [{port}]\n").format(port=ws_port)
|
msg += _("WS Port: [{port}]\n").format(port=ws_port)
|
||||||
if ws_port != rest_port:
|
if ws_port != rest_port and rest_port != 2333:
|
||||||
msg += _("Rest Port: [{port}]\n").format(port=rest_port)
|
msg += _("Rest Port: [{port}]\n").format(port=rest_port)
|
||||||
msg += _("Password: [{password}]\n").format(password=password)
|
msg += _("Password: [{password}]\n").format(password=password)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -78,10 +78,14 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Play Tracks"),
|
title=_("Unable To Play Tracks"),
|
||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
@ -185,10 +189,14 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Play Tracks"),
|
title=_("Unable To Play Tracks"),
|
||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
@ -450,10 +458,14 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Play Tracks"),
|
title=_("Unable To Play Tracks"),
|
||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
@ -566,10 +578,14 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Play Tracks"),
|
title=_("Unable To Play Tracks"),
|
||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
@ -626,10 +642,8 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
|
|
||||||
if not guild_data["auto_play"]:
|
if not guild_data["auto_play"]:
|
||||||
await ctx.invoke(self.command_audioset_autoplay_toggle)
|
await ctx.invoke(self.command_audioset_autoplay_toggle)
|
||||||
if not guild_data["notify"] and (
|
if not guild_data["notify"] and not player.fetch("autoplay_notified", False):
|
||||||
(player.current and not player.current.extras.get("autoplay")) or not player.current
|
pass
|
||||||
):
|
|
||||||
await self.send_embed_msg(ctx, title=_("Auto play started."))
|
|
||||||
elif player.current:
|
elif player.current:
|
||||||
await self.send_embed_msg(ctx, title=_("Adding a track to queue."))
|
await self.send_embed_msg(ctx, title=_("Adding a track to queue."))
|
||||||
|
|
||||||
@ -692,10 +706,14 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Search For Tracks"),
|
title=_("Unable To Search For Tracks"),
|
||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@ -1678,7 +1678,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
try:
|
try:
|
||||||
if not await self.can_manage_playlist(scope, playlist, ctx, author, guild):
|
if not await self.can_manage_playlist(scope, playlist, ctx, author, guild):
|
||||||
return
|
return
|
||||||
if playlist.url:
|
if playlist.url or playlist.id == 42069:
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
added, removed, playlist = await self._maybe_update_playlist(
|
added, removed, playlist = await self._maybe_update_playlist(
|
||||||
ctx, player, playlist
|
ctx, player, playlist
|
||||||
|
|||||||
@ -338,10 +338,14 @@ class QueueCommands(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
title=_("Unable To Shuffle Queue"),
|
title=_("Unable To Shuffle Queue"),
|
||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
ctx.command.reset_cooldown(ctx)
|
ctx.command.reset_cooldown(ctx)
|
||||||
return await self.send_embed_msg(
|
return await self.send_embed_msg(
|
||||||
|
|||||||
@ -32,8 +32,10 @@ class AudioEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
|
|
||||||
if await self.bot.cog_disabled_in_guild(self, guild):
|
if await self.bot.cog_disabled_in_guild(self, guild):
|
||||||
player = lavalink.get_player(guild.id)
|
player = lavalink.get_player(guild.id)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
await player.stop()
|
await player.stop()
|
||||||
await player.disconnect()
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(guild_id=guild.id).currently_auto_playing_in.set([])
|
||||||
return
|
return
|
||||||
|
|
||||||
track_identifier = track.track_identifier
|
track_identifier = track.track_identifier
|
||||||
@ -157,7 +159,9 @@ class AudioEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
await self.api_interface.persistent_queue_api.delete_scheduled()
|
await self.api_interface.persistent_queue_api.delete_scheduled()
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_red_audio_track_enqueue(self, guild: discord.Guild, track, requester):
|
async def on_red_audio_track_enqueue(
|
||||||
|
self, guild: discord.Guild, track: lavalink.Track, requester: discord.Member
|
||||||
|
):
|
||||||
if not (track and guild):
|
if not (track and guild):
|
||||||
return
|
return
|
||||||
persist_cache = self._persist_queue_cache.setdefault(
|
persist_cache = self._persist_queue_cache.setdefault(
|
||||||
@ -181,3 +185,28 @@ class AudioEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
await self.api_interface.persistent_queue_api.drop(guild.id)
|
await self.api_interface.persistent_queue_api.drop(guild.id)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
await self.api_interface.persistent_queue_api.delete_scheduled()
|
await self.api_interface.persistent_queue_api.delete_scheduled()
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_red_audio_track_auto_play(
|
||||||
|
self,
|
||||||
|
guild: discord.Guild,
|
||||||
|
track: lavalink.Track,
|
||||||
|
requester: discord.Member,
|
||||||
|
player: lavalink.Player,
|
||||||
|
):
|
||||||
|
notify_channel = self.bot.get_channel(player.fetch("channel"))
|
||||||
|
tries = 0
|
||||||
|
while not player._is_playing:
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
if tries > 1000:
|
||||||
|
return
|
||||||
|
|
||||||
|
if notify_channel and not player.fetch("autoplay_notified", False):
|
||||||
|
if (
|
||||||
|
len(player.manager.players) < 10
|
||||||
|
or not player._last_resume
|
||||||
|
and player._last_resume + datetime.timedelta(seconds=60)
|
||||||
|
> datetime.datetime.now(tz=datetime.timezone.utc)
|
||||||
|
):
|
||||||
|
await self.send_embed_msg(notify_channel, title=_("Auto Play started."))
|
||||||
|
player.store("autoplay_notified", True)
|
||||||
|
|||||||
@ -244,7 +244,7 @@ class DpyEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
|
|
||||||
lavalink.unregister_event_listener(self.lavalink_event_handler)
|
lavalink.unregister_event_listener(self.lavalink_event_handler)
|
||||||
lavalink.unregister_update_listener(self.lavalink_update_handler)
|
lavalink.unregister_update_listener(self.lavalink_update_handler)
|
||||||
self.bot.loop.create_task(lavalink.close())
|
self.bot.loop.create_task(lavalink.close(self.bot))
|
||||||
if self.player_manager is not None:
|
if self.player_manager is not None:
|
||||||
self.bot.loop.create_task(self.player_manager.shutdown())
|
self.bot.loop.create_task(self.player_manager.shutdown())
|
||||||
|
|
||||||
@ -259,12 +259,31 @@ class DpyEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
await self.cog_ready_event.wait()
|
await self.cog_ready_event.wait()
|
||||||
if after.channel != before.channel:
|
if after.channel != before.channel:
|
||||||
try:
|
try:
|
||||||
self.skip_votes[before.channel.guild.id].remove(member.id)
|
self.skip_votes[before.channel.guild.id].discard(member.id)
|
||||||
except (ValueError, KeyError, AttributeError):
|
except (ValueError, KeyError, AttributeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# if (
|
||||||
|
# member == member.guild.me
|
||||||
|
# and before.channel
|
||||||
|
# and after.channel
|
||||||
|
# and after.channel.id != before.channel.id
|
||||||
|
# ):
|
||||||
|
# try:
|
||||||
|
# player = lavalink.get_player(member.guild.id)
|
||||||
|
# if player.is_playing:
|
||||||
|
# await player.resume(player.current, start=player.position, replace=False)
|
||||||
|
# log.debug("Bot changed channel - Resume playback")
|
||||||
|
# except:
|
||||||
|
# log.debug("Bot changed channel - Unable to resume playback")
|
||||||
|
|
||||||
channel = self.rgetattr(member, "voice.channel", None)
|
channel = self.rgetattr(member, "voice.channel", None)
|
||||||
bot_voice_state = self.rgetattr(member, "guild.me.voice.self_deaf", None)
|
bot_voice_state = self.rgetattr(member, "guild.me.voice.self_deaf", None)
|
||||||
if channel and bot_voice_state is False:
|
if (
|
||||||
|
channel
|
||||||
|
and bot_voice_state is False
|
||||||
|
and await self.config.guild(member.guild).auto_deafen()
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
player = lavalink.get_player(channel.guild.id)
|
player = lavalink.get_player(channel.guild.id)
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
@ -272,3 +291,15 @@ class DpyEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
else:
|
else:
|
||||||
if player.channel.id == channel.id:
|
if player.channel.id == channel.id:
|
||||||
await self.self_deafen(player)
|
await self.self_deafen(player)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_shard_disconnect(self, shard_id):
|
||||||
|
self._diconnected_shard.add(shard_id)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_shard_ready(self, shard_id):
|
||||||
|
self._diconnected_shard.discard(shard_id)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_shard_resumed(self, shard_id):
|
||||||
|
self._diconnected_shard.discard(shard_id)
|
||||||
|
|||||||
@ -3,9 +3,12 @@ import contextlib
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import lavalink
|
import lavalink
|
||||||
|
from discord.backoff import ExponentialBackoff
|
||||||
|
from discord.gateway import DiscordWebSocket
|
||||||
|
|
||||||
from redbot.core.i18n import Translator, set_contextual_locales_from_guild
|
from redbot.core.i18n import Translator, set_contextual_locales_from_guild
|
||||||
from ...errors import DatabaseError, TrackEnqueueError
|
from ...errors import DatabaseError, TrackEnqueueError
|
||||||
@ -13,6 +16,9 @@ from ..abc import MixinMeta
|
|||||||
from ..cog_utils import CompositeMetaClass
|
from ..cog_utils import CompositeMetaClass
|
||||||
|
|
||||||
log = logging.getLogger("red.cogs.Audio.cog.Events.lavalink")
|
log = logging.getLogger("red.cogs.Audio.cog.Events.lavalink")
|
||||||
|
ws_audio_log = logging.getLogger("red.Audio.WS.Audio")
|
||||||
|
ws_audio_log.setLevel(logging.WARNING)
|
||||||
|
|
||||||
_ = Translator("Audio", Path(__file__))
|
_ = Translator("Audio", Path(__file__))
|
||||||
|
|
||||||
|
|
||||||
@ -28,27 +34,62 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
) -> None:
|
) -> None:
|
||||||
current_track = player.current
|
current_track = player.current
|
||||||
current_channel = player.channel
|
current_channel = player.channel
|
||||||
if not current_channel:
|
|
||||||
return
|
|
||||||
guild = self.rgetattr(current_channel, "guild", None)
|
guild = self.rgetattr(current_channel, "guild", None)
|
||||||
|
if not (current_channel and guild):
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
|
await player.stop()
|
||||||
|
await player.disconnect()
|
||||||
|
return
|
||||||
if await self.bot.cog_disabled_in_guild(self, guild):
|
if await self.bot.cog_disabled_in_guild(self, guild):
|
||||||
await player.stop()
|
await player.stop()
|
||||||
await player.disconnect()
|
await player.disconnect()
|
||||||
|
if guild:
|
||||||
|
await self.config.guild_from_id(guild_id=guild.id).currently_auto_playing_in.set(
|
||||||
|
[]
|
||||||
|
)
|
||||||
return
|
return
|
||||||
guild_id = self.rgetattr(guild, "id", None)
|
guild_id = self.rgetattr(guild, "id", None)
|
||||||
if not guild:
|
if not guild:
|
||||||
return
|
return
|
||||||
|
guild_data = await self.config.guild(guild).all()
|
||||||
|
disconnect = guild_data["disconnect"]
|
||||||
|
if event_type == lavalink.LavalinkEvents.FORCED_DISCONNECT:
|
||||||
|
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||||
|
await self.config.guild_from_id(guild_id=guild_id).currently_auto_playing_in.set([])
|
||||||
|
self._ll_guild_updates.discard(guild.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if event_type == lavalink.LavalinkEvents.WEBSOCKET_CLOSED:
|
||||||
|
deafen = guild_data["auto_deafen"]
|
||||||
|
event_channel_id = extra.get("channelID")
|
||||||
|
_error_code = extra.get("code")
|
||||||
|
if _error_code in [1000] or not guild:
|
||||||
|
if _error_code == 1000:
|
||||||
|
await player.resume(player.current, start=player.position, replace=False)
|
||||||
|
return
|
||||||
|
await self._ws_op_codes[guild_id].put((event_channel_id, _error_code))
|
||||||
|
try:
|
||||||
|
if guild_id not in self._ws_resume:
|
||||||
|
self._ws_resume[guild_id].set()
|
||||||
|
|
||||||
|
await self._websocket_closed_handler(
|
||||||
|
guild=guild, player=player, extra=extra, deafen=deafen, disconnect=disconnect
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
log.exception(
|
||||||
|
f"Error in WEBSOCKET_CLOSED handling for guild: {player.channel.guild.id}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
await set_contextual_locales_from_guild(self.bot, guild)
|
await set_contextual_locales_from_guild(self.bot, guild)
|
||||||
current_requester = self.rgetattr(current_track, "requester", None)
|
current_requester = self.rgetattr(current_track, "requester", None)
|
||||||
current_stream = self.rgetattr(current_track, "is_stream", None)
|
current_stream = self.rgetattr(current_track, "is_stream", None)
|
||||||
current_length = self.rgetattr(current_track, "length", None)
|
current_length = self.rgetattr(current_track, "length", None)
|
||||||
current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
|
current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
|
||||||
current_extras = self.rgetattr(current_track, "extras", {})
|
|
||||||
current_id = self.rgetattr(current_track, "_info", {}).get("identifier")
|
current_id = self.rgetattr(current_track, "_info", {}).get("identifier")
|
||||||
guild_data = await self.config.guild(guild).all()
|
|
||||||
repeat = guild_data["repeat"]
|
repeat = guild_data["repeat"]
|
||||||
notify = guild_data["notify"]
|
notify = guild_data["notify"]
|
||||||
disconnect = guild_data["disconnect"]
|
|
||||||
autoplay = guild_data["auto_play"]
|
autoplay = guild_data["auto_play"]
|
||||||
description = await self.get_track_description(
|
description = await self.get_track_description(
|
||||||
current_track, self.local_folder_current_path
|
current_track, self.local_folder_current_path
|
||||||
@ -59,7 +100,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
await self.maybe_reset_error_counter(player)
|
await self.maybe_reset_error_counter(player)
|
||||||
|
|
||||||
if event_type == lavalink.LavalinkEvents.TRACK_START:
|
if event_type == lavalink.LavalinkEvents.TRACK_START:
|
||||||
self.skip_votes[guild_id] = []
|
self.skip_votes[guild_id] = set()
|
||||||
playing_song = player.fetch("playing_song")
|
playing_song = player.fetch("playing_song")
|
||||||
requester = player.fetch("requester")
|
requester = player.fetch("requester")
|
||||||
player.store("prev_song", playing_song)
|
player.store("prev_song", playing_song)
|
||||||
@ -71,25 +112,35 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
await self.api_interface.persistent_queue_api.played(
|
await self.api_interface.persistent_queue_api.played(
|
||||||
guild_id=guild_id, track_id=current_track.track_identifier
|
guild_id=guild_id, track_id=current_track.track_identifier
|
||||||
)
|
)
|
||||||
|
notify_channel = player.fetch("channel")
|
||||||
|
if notify_channel and autoplay:
|
||||||
|
await self.config.guild_from_id(guild_id=guild_id).currently_auto_playing_in.set(
|
||||||
|
[notify_channel, player.channel.id]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self.config.guild_from_id(guild_id=guild_id).currently_auto_playing_in.set(
|
||||||
|
[]
|
||||||
|
)
|
||||||
if event_type == lavalink.LavalinkEvents.TRACK_END:
|
if event_type == lavalink.LavalinkEvents.TRACK_END:
|
||||||
prev_requester = player.fetch("prev_requester")
|
prev_requester = player.fetch("prev_requester")
|
||||||
self.bot.dispatch("red_audio_track_end", guild, prev_song, prev_requester)
|
self.bot.dispatch("red_audio_track_end", guild, prev_song, prev_requester)
|
||||||
|
player.store("resume_attempts", 0)
|
||||||
if event_type == lavalink.LavalinkEvents.QUEUE_END:
|
if event_type == lavalink.LavalinkEvents.QUEUE_END:
|
||||||
prev_requester = player.fetch("prev_requester")
|
prev_requester = player.fetch("prev_requester")
|
||||||
self.bot.dispatch("red_audio_queue_end", guild, prev_song, prev_requester)
|
self.bot.dispatch("red_audio_queue_end", guild, prev_song, prev_requester)
|
||||||
if guild_id:
|
if guild_id:
|
||||||
await self.api_interface.persistent_queue_api.drop(guild_id)
|
await self.api_interface.persistent_queue_api.drop(guild_id)
|
||||||
if (
|
if player.is_auto_playing or (
|
||||||
autoplay
|
autoplay
|
||||||
and not player.queue
|
and not player.queue
|
||||||
and player.fetch("playing_song") is not None
|
and player.fetch("playing_song") is not None
|
||||||
and self.playlist_api is not None
|
and self.playlist_api is not None
|
||||||
and self.api_interface is not None
|
and self.api_interface is not None
|
||||||
):
|
):
|
||||||
|
notify_channel = player.fetch("channel")
|
||||||
try:
|
try:
|
||||||
await self.api_interface.autoplay(player, self.playlist_api)
|
await self.api_interface.autoplay(player, self.playlist_api)
|
||||||
except DatabaseError:
|
except DatabaseError:
|
||||||
notify_channel = player.fetch("channel")
|
|
||||||
notify_channel = self.bot.get_channel(notify_channel)
|
notify_channel = self.bot.get_channel(notify_channel)
|
||||||
if notify_channel:
|
if notify_channel:
|
||||||
await self.send_embed_msg(
|
await self.send_embed_msg(
|
||||||
@ -97,7 +148,6 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
except TrackEnqueueError:
|
except TrackEnqueueError:
|
||||||
notify_channel = player.fetch("channel")
|
|
||||||
notify_channel = self.bot.get_channel(notify_channel)
|
notify_channel = self.bot.get_channel(notify_channel)
|
||||||
if notify_channel:
|
if notify_channel:
|
||||||
await self.send_embed_msg(
|
await self.send_embed_msg(
|
||||||
@ -116,18 +166,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
if player.fetch("notify_message") is not None:
|
if player.fetch("notify_message") is not None:
|
||||||
with contextlib.suppress(discord.HTTPException):
|
with contextlib.suppress(discord.HTTPException):
|
||||||
await player.fetch("notify_message").delete()
|
await player.fetch("notify_message").delete()
|
||||||
|
if not (description and notify_channel):
|
||||||
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
|
return
|
||||||
if current_stream:
|
if current_stream:
|
||||||
dur = "LIVE"
|
dur = "LIVE"
|
||||||
@ -166,6 +205,9 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
await self.send_embed_msg(notify_channel, title=_("Queue ended."))
|
await self.send_embed_msg(notify_channel, title=_("Queue ended."))
|
||||||
if disconnect:
|
if disconnect:
|
||||||
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=guild_id
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
await player.disconnect()
|
await player.disconnect()
|
||||||
self._ll_guild_updates.discard(guild.id)
|
self._ll_guild_updates.discard(guild.id)
|
||||||
if status:
|
if status:
|
||||||
@ -197,10 +239,14 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
eq = player.fetch("eq")
|
eq = player.fetch("eq")
|
||||||
player.queue = []
|
player.queue = []
|
||||||
player.store("playing_song", None)
|
player.store("playing_song", None)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
if eq:
|
if eq:
|
||||||
await self.config.custom("EQUALIZER", guild_id).eq_bands.set(eq.bands)
|
await self.config.custom("EQUALIZER", guild_id).eq_bands.set(eq.bands)
|
||||||
await player.stop()
|
await player.stop()
|
||||||
await player.disconnect()
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(guild_id=guild_id).currently_auto_playing_in.set(
|
||||||
|
[]
|
||||||
|
)
|
||||||
self._ll_guild_updates.discard(guild_id)
|
self._ll_guild_updates.discard(guild_id)
|
||||||
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||||
if message_channel:
|
if message_channel:
|
||||||
@ -240,3 +286,209 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
)
|
)
|
||||||
await message_channel.send(embed=embed)
|
await message_channel.send(embed=embed)
|
||||||
await player.skip()
|
await player.skip()
|
||||||
|
|
||||||
|
async def _websocket_closed_handler(
|
||||||
|
self,
|
||||||
|
guild: discord.Guild,
|
||||||
|
player: lavalink.Player,
|
||||||
|
extra: Dict,
|
||||||
|
deafen: bool,
|
||||||
|
disconnect: bool,
|
||||||
|
) -> None:
|
||||||
|
guild_id = guild.id
|
||||||
|
event_channel_id = extra.get("channelID")
|
||||||
|
try:
|
||||||
|
if not self._ws_resume[guild_id].is_set():
|
||||||
|
await self._ws_resume[guild_id].wait()
|
||||||
|
else:
|
||||||
|
self._ws_resume[guild_id].clear()
|
||||||
|
node = player.node
|
||||||
|
voice_ws: DiscordWebSocket = node.get_voice_ws(guild_id)
|
||||||
|
code = extra.get("code")
|
||||||
|
by_remote = extra.get("byRemote", "")
|
||||||
|
reason = extra.get("reason", "No Specified Reason").strip()
|
||||||
|
channel_id = player.channel.id
|
||||||
|
try:
|
||||||
|
event_channel_id, to_handle_code = await self._ws_op_codes[guild_id].get()
|
||||||
|
except asyncio.QueueEmpty:
|
||||||
|
log.debug("Empty queue - Resuming Processor - Early exit")
|
||||||
|
return
|
||||||
|
|
||||||
|
if code != to_handle_code:
|
||||||
|
code = to_handle_code
|
||||||
|
if player.channel.id != event_channel_id:
|
||||||
|
code = 4014
|
||||||
|
if event_channel_id != channel_id:
|
||||||
|
ws_audio_log.info(
|
||||||
|
f"Received an op code for a channel that is no longer valid; {event_channel_id} "
|
||||||
|
f"in guild: {guild_id} - Active channel {channel_id} | "
|
||||||
|
f"Reason: Error code {code} & {reason}."
|
||||||
|
)
|
||||||
|
self._ws_op_codes[guild_id]._init(self._ws_op_codes[guild_id]._maxsize)
|
||||||
|
return
|
||||||
|
if player.channel:
|
||||||
|
current_perms = player.channel.permissions_for(player.channel.guild.me)
|
||||||
|
has_perm = current_perms.speak and current_perms.connect
|
||||||
|
else:
|
||||||
|
has_perm = False
|
||||||
|
if code in (1000,) and has_perm and player.current and player.is_playing:
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.resume(player.current, start=player.position, replace=True)
|
||||||
|
ws_audio_log.info(
|
||||||
|
f"Player resumed in channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & {reason}."
|
||||||
|
)
|
||||||
|
self._ws_op_codes[guild_id]._init(self._ws_op_codes[guild_id]._maxsize)
|
||||||
|
return
|
||||||
|
|
||||||
|
if voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
|
||||||
|
if player._con_delay:
|
||||||
|
delay = player._con_delay.delay()
|
||||||
|
else:
|
||||||
|
player._con_delay = ExponentialBackoff(base=1)
|
||||||
|
delay = player._con_delay.delay()
|
||||||
|
ws_audio_log.warning(
|
||||||
|
"YOU CAN IGNORE THIS UNLESS IT'S CONSISTENTLY REPEATING FOR THE SAME GUILD - "
|
||||||
|
f"Voice websocket closed for guild {guild_id} -> "
|
||||||
|
f"Socket Closed {voice_ws.socket._closing or voice_ws.socket.closed}. "
|
||||||
|
f"Code: {code} -- Remote: {by_remote} -- {reason}"
|
||||||
|
)
|
||||||
|
ws_audio_log.debug(
|
||||||
|
f"Reconnecting to channel {channel_id} in guild: {guild_id} | {delay:.2f}s"
|
||||||
|
)
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
while voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
|
||||||
|
voice_ws = node.get_voice_ws(guild_id)
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
if has_perm and player.current and player.is_playing:
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
await player.resume(player.current, start=player.position, replace=True)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket reconnected "
|
||||||
|
f"to channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Currently playing."
|
||||||
|
)
|
||||||
|
elif has_perm and player.paused and player.current:
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
await player.resume(
|
||||||
|
player.current, start=player.position, replace=True, pause=True
|
||||||
|
)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket reconnected "
|
||||||
|
f"to channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Currently Paused."
|
||||||
|
)
|
||||||
|
elif has_perm and (not disconnect) and (not player.is_playing):
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket reconnected "
|
||||||
|
f"to channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Not playing, but auto disconnect disabled."
|
||||||
|
)
|
||||||
|
self._ll_guild_updates.discard(guild_id)
|
||||||
|
elif not has_perm:
|
||||||
|
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket disconnected "
|
||||||
|
f"from channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Missing permissions."
|
||||||
|
)
|
||||||
|
self._ll_guild_updates.discard(guild_id)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
|
await player.stop()
|
||||||
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=guild_id
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
|
else:
|
||||||
|
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket disconnected "
|
||||||
|
f"from channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Unknown."
|
||||||
|
)
|
||||||
|
self._ll_guild_updates.discard(guild_id)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
|
await player.stop()
|
||||||
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=guild_id
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
|
elif code in (42069,) and has_perm and player.current and player.is_playing:
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
await player.resume(player.current, start=player.position, replace=True)
|
||||||
|
ws_audio_log.info(
|
||||||
|
f"Player resumed in channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & {reason}."
|
||||||
|
)
|
||||||
|
elif code in (4015, 4009, 4006, 4000, 1006):
|
||||||
|
if player._con_delay:
|
||||||
|
delay = player._con_delay.delay()
|
||||||
|
else:
|
||||||
|
player._con_delay = ExponentialBackoff(base=1)
|
||||||
|
delay = player._con_delay.delay()
|
||||||
|
ws_audio_log.debug(
|
||||||
|
f"Reconnecting to channel {channel_id} in guild: {guild_id} | {delay:.2f}s"
|
||||||
|
)
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
if has_perm and player.current and player.is_playing:
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
await player.resume(player.current, start=player.position, replace=True)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket reconnected "
|
||||||
|
f"to channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Player is active."
|
||||||
|
)
|
||||||
|
elif has_perm and player.paused and player.current:
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
await player.resume(
|
||||||
|
player.current, start=player.position, replace=True, pause=True
|
||||||
|
)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket reconnected "
|
||||||
|
f"to channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Player is paused."
|
||||||
|
)
|
||||||
|
elif has_perm and (not disconnect) and (not player.is_playing):
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.connect(deafen=deafen)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket reconnected "
|
||||||
|
f"to channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Not playing."
|
||||||
|
)
|
||||||
|
self._ll_guild_updates.discard(guild_id)
|
||||||
|
elif not has_perm:
|
||||||
|
self.bot.dispatch("red_audio_audio_disconnect", guild)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"Voice websocket disconnected "
|
||||||
|
f"from channel {channel_id} in guild: {guild_id} | "
|
||||||
|
f"Reason: Error code {code} & Missing permissions."
|
||||||
|
)
|
||||||
|
self._ll_guild_updates.discard(guild_id)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
|
await player.stop()
|
||||||
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=guild_id
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
|
else:
|
||||||
|
if not player.paused and player.current:
|
||||||
|
player.store("resumes", player.fetch("resumes", 0) + 1)
|
||||||
|
await player.resume(player.current, start=player.position, replace=False)
|
||||||
|
ws_audio_log.info(
|
||||||
|
"WS EVENT - IGNORED (Healthy Socket) | "
|
||||||
|
f"Voice websocket closed event for guild {guild_id} -> "
|
||||||
|
f"Code: {code} -- Remote: {by_remote} -- {reason}"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
log.exception("Error in task")
|
||||||
|
finally:
|
||||||
|
self._ws_op_codes[guild_id]._init(self._ws_op_codes[guild_id]._maxsize)
|
||||||
|
self._ws_resume[guild_id].set()
|
||||||
|
|||||||
@ -99,7 +99,6 @@ class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
bot=self.bot,
|
bot=self.bot,
|
||||||
host=host,
|
host=host,
|
||||||
password=password,
|
password=password,
|
||||||
rest_port=ws_port,
|
|
||||||
ws_port=ws_port,
|
ws_port=ws_port,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
resume_key=f"Red-Core-Audio-{self.bot.user.id}-{data_manager.instance_name}",
|
resume_key=f"Red-Core-Audio-{self.bot.user.id}-{data_manager.instance_name}",
|
||||||
|
|||||||
@ -47,15 +47,36 @@ class PlayerTasks(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
servers.update(pause_times)
|
servers.update(pause_times)
|
||||||
async for sid in AsyncIter(servers, steps=5):
|
async for sid in AsyncIter(servers, steps=5):
|
||||||
server_obj = self.bot.get_guild(sid)
|
server_obj = self.bot.get_guild(sid)
|
||||||
if sid in stop_times and await self.config.guild(server_obj).emptydc_enabled():
|
if not server_obj:
|
||||||
|
stop_times.pop(sid, None)
|
||||||
|
pause_times.pop(sid, None)
|
||||||
|
try:
|
||||||
|
player = lavalink.get_player(sid)
|
||||||
|
await self.api_interface.persistent_queue_api.drop(sid)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
|
await player.stop()
|
||||||
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=sid
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
|
except Exception as err:
|
||||||
|
debug_exc_log(
|
||||||
|
log, err, f"Exception raised in Audio's emptydc_timer for {sid}."
|
||||||
|
)
|
||||||
|
|
||||||
|
elif sid in stop_times and await self.config.guild(server_obj).emptydc_enabled():
|
||||||
emptydc_timer = await self.config.guild(server_obj).emptydc_timer()
|
emptydc_timer = await self.config.guild(server_obj).emptydc_timer()
|
||||||
if (time.time() - stop_times[sid]) >= emptydc_timer:
|
if (time.time() - stop_times[sid]) >= emptydc_timer:
|
||||||
stop_times.pop(sid)
|
stop_times.pop(sid)
|
||||||
try:
|
try:
|
||||||
player = lavalink.get_player(sid)
|
player = lavalink.get_player(sid)
|
||||||
await self.api_interface.persistent_queue_api.drop(sid)
|
await self.api_interface.persistent_queue_api.drop(sid)
|
||||||
|
player.store("autoplay_notified", False)
|
||||||
await player.stop()
|
await player.stop()
|
||||||
await player.disconnect()
|
await player.disconnect()
|
||||||
|
await self.config.guild_from_id(
|
||||||
|
guild_id=sid
|
||||||
|
).currently_auto_playing_in.set([])
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
if "No such player for that guild" in str(err):
|
if "No such player for that guild" in str(err):
|
||||||
stop_times.pop(sid, None)
|
stop_times.pop(sid, None)
|
||||||
|
|||||||
@ -10,12 +10,14 @@ import lavalink
|
|||||||
|
|
||||||
from redbot.core.data_manager import cog_data_path
|
from redbot.core.data_manager import cog_data_path
|
||||||
from redbot.core.i18n import Translator
|
from redbot.core.i18n import Translator
|
||||||
|
from redbot.core.utils import AsyncIter
|
||||||
from redbot.core.utils._internal_utils import send_to_owners_with_prefix_replaced
|
from redbot.core.utils._internal_utils import send_to_owners_with_prefix_replaced
|
||||||
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||||
|
|
||||||
from ...apis.interface import AudioAPIInterface
|
from ...apis.interface import AudioAPIInterface
|
||||||
from ...apis.playlist_wrapper import PlaylistWrapper
|
from ...apis.playlist_wrapper import PlaylistWrapper
|
||||||
from ...audio_logging import debug_exc_log
|
from ...audio_logging import debug_exc_log
|
||||||
|
from ...errors import DatabaseError, TrackEnqueueError
|
||||||
from ...utils import task_callback
|
from ...utils import task_callback
|
||||||
from ..abc import MixinMeta
|
from ..abc import MixinMeta
|
||||||
from ..cog_utils import _OWNER_NOTIFICATION, _SCHEMA_VERSION, CompositeMetaClass
|
from ..cog_utils import _OWNER_NOTIFICATION, _SCHEMA_VERSION, CompositeMetaClass
|
||||||
@ -52,6 +54,7 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
)
|
)
|
||||||
await self.playlist_api.delete_scheduled()
|
await self.playlist_api.delete_scheduled()
|
||||||
await self.api_interface.persistent_queue_api.delete_scheduled()
|
await self.api_interface.persistent_queue_api.delete_scheduled()
|
||||||
|
await self._build_bundled_playlist()
|
||||||
self.lavalink_restart_connect()
|
self.lavalink_restart_connect()
|
||||||
self.player_automated_timer_task = self.bot.loop.create_task(
|
self.player_automated_timer_task = self.bot.loop.create_task(
|
||||||
self.player_automated_timer()
|
self.player_automated_timer()
|
||||||
@ -66,13 +69,29 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
async def restore_players(self):
|
async def restore_players(self):
|
||||||
tries = 0
|
tries = 0
|
||||||
tracks_to_restore = await self.api_interface.persistent_queue_api.fetch_all()
|
tracks_to_restore = await self.api_interface.persistent_queue_api.fetch_all()
|
||||||
await asyncio.sleep(10)
|
while not lavalink.node._nodes:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
tries += 1
|
||||||
|
if tries > 60:
|
||||||
|
log.exception("Unable to restore players, couldn't connect to Lavalink.")
|
||||||
|
return
|
||||||
|
metadata = {}
|
||||||
|
all_guilds = await self.config.all_guilds()
|
||||||
|
async for guild_id, guild_data in AsyncIter(all_guilds.items(), steps=100):
|
||||||
|
if guild_data["auto_play"]:
|
||||||
|
if guild_data["currently_auto_playing_in"]:
|
||||||
|
notify_channel, vc_id = guild_data["currently_auto_playing_in"]
|
||||||
|
metadata[guild_id] = (notify_channel, vc_id)
|
||||||
|
|
||||||
for guild_id, track_data in itertools.groupby(tracks_to_restore, key=lambda x: x.guild_id):
|
for guild_id, track_data in itertools.groupby(tracks_to_restore, key=lambda x: x.guild_id):
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
|
tries = 0
|
||||||
try:
|
try:
|
||||||
player: Optional[lavalink.Player]
|
player: Optional[lavalink.Player] = None
|
||||||
track_data = list(track_data)
|
track_data = list(track_data)
|
||||||
guild = self.bot.get_guild(guild_id)
|
guild = self.bot.get_guild(guild_id)
|
||||||
|
if not guild:
|
||||||
|
continue
|
||||||
persist_cache = self._persist_queue_cache.setdefault(
|
persist_cache = self._persist_queue_cache.setdefault(
|
||||||
guild_id, await self.config.guild(guild).persist_queue()
|
guild_id, await self.config.guild(guild).persist_queue()
|
||||||
)
|
)
|
||||||
@ -88,40 +107,48 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
player = None
|
player = None
|
||||||
except KeyError:
|
except KeyError:
|
||||||
player = None
|
player = None
|
||||||
|
|
||||||
vc = 0
|
vc = 0
|
||||||
|
guild_data = await self.config.guild_from_id(guild.id).all()
|
||||||
|
shuffle = guild_data["shuffle"]
|
||||||
|
repeat = guild_data["repeat"]
|
||||||
|
volume = guild_data["volume"]
|
||||||
|
shuffle_bumped = guild_data["shuffle_bumped"]
|
||||||
|
auto_deafen = guild_data["auto_deafen"]
|
||||||
|
|
||||||
if player is None:
|
if player is None:
|
||||||
while tries < 25 and vc is not None:
|
while tries < 5 and vc is not None:
|
||||||
try:
|
try:
|
||||||
vc = guild.get_channel(track_data[-1].room_id)
|
notify_channel_id, vc_id = metadata.pop(
|
||||||
|
guild_id, (None, track_data[-1].room_id)
|
||||||
|
)
|
||||||
|
vc = guild.get_channel(vc_id)
|
||||||
if not vc:
|
if not vc:
|
||||||
break
|
break
|
||||||
perms = vc.permissions_for(guild.me)
|
perms = vc.permissions_for(guild.me)
|
||||||
if not (perms.connect and perms.speak):
|
if not (perms.connect and perms.speak):
|
||||||
vc = None
|
vc = None
|
||||||
break
|
break
|
||||||
await lavalink.connect(vc)
|
await lavalink.connect(vc, deafen=auto_deafen)
|
||||||
player = lavalink.get_player(guild.id)
|
player = lavalink.get_player(guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
player.store("guild", guild_id)
|
player.store("guild", guild_id)
|
||||||
await self.self_deafen(player)
|
player.store("channel", notify_channel_id)
|
||||||
break
|
break
|
||||||
except IndexError:
|
except IndexError:
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
tries += 1
|
tries += 1
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
tries += 1
|
||||||
debug_exc_log(log, exc, "Failed to restore music voice channel")
|
debug_exc_log(log, exc, "Failed to restore music voice channel")
|
||||||
if vc is None:
|
if vc is None:
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
if tries >= 25 or guild is None or vc is None:
|
if tries >= 5 or guild is None or vc is None or player is None:
|
||||||
await self.api_interface.persistent_queue_api.drop(guild_id)
|
await self.api_interface.persistent_queue_api.drop(guild_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
shuffle = await self.config.guild(guild).shuffle()
|
|
||||||
repeat = await self.config.guild(guild).repeat()
|
|
||||||
volume = await self.config.guild(guild).volume()
|
|
||||||
shuffle_bumped = await self.config.guild(guild).shuffle_bumped()
|
|
||||||
player.repeat = repeat
|
player.repeat = repeat
|
||||||
player.shuffle = shuffle
|
player.shuffle = shuffle
|
||||||
player.shuffle_bumped = shuffle_bumped
|
player.shuffle_bumped = shuffle_bumped
|
||||||
@ -137,6 +164,90 @@ class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
debug_exc_log(log, err, f"Error restoring player in {guild_id}")
|
debug_exc_log(log, err, f"Error restoring player in {guild_id}")
|
||||||
await self.api_interface.persistent_queue_api.drop(guild_id)
|
await self.api_interface.persistent_queue_api.drop(guild_id)
|
||||||
|
|
||||||
|
for guild_id, (notify_channel_id, vc_id) in metadata.items():
|
||||||
|
guild = self.bot.get_guild(guild_id)
|
||||||
|
player: Optional[lavalink.Player] = None
|
||||||
|
vc = 0
|
||||||
|
tries = 0
|
||||||
|
if not guild:
|
||||||
|
continue
|
||||||
|
if self.lavalink_connection_aborted:
|
||||||
|
player = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
player = lavalink.get_player(guild_id)
|
||||||
|
except IndexError:
|
||||||
|
player = None
|
||||||
|
except KeyError:
|
||||||
|
player = None
|
||||||
|
if player is None:
|
||||||
|
guild_data = await self.config.guild_from_id(guild.id).all()
|
||||||
|
shuffle = guild_data["shuffle"]
|
||||||
|
repeat = guild_data["repeat"]
|
||||||
|
volume = guild_data["volume"]
|
||||||
|
shuffle_bumped = guild_data["shuffle_bumped"]
|
||||||
|
auto_deafen = guild_data["auto_deafen"]
|
||||||
|
|
||||||
|
while tries < 5 and vc is not None:
|
||||||
|
try:
|
||||||
|
vc = guild.get_channel(vc_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, deafen=auto_deafen)
|
||||||
|
player = lavalink.get_player(guild.id)
|
||||||
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
|
player.store("guild", guild_id)
|
||||||
|
player.store("channel", notify_channel_id)
|
||||||
|
break
|
||||||
|
except IndexError:
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
tries += 1
|
||||||
|
except Exception as exc:
|
||||||
|
tries += 1
|
||||||
|
debug_exc_log(log, exc, "Failed to restore music voice channel")
|
||||||
|
if vc is None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
if tries >= 5 or guild is None or vc is None or player is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
player.repeat = repeat
|
||||||
|
player.shuffle = shuffle
|
||||||
|
player.shuffle_bumped = shuffle_bumped
|
||||||
|
if player.volume != volume:
|
||||||
|
await player.set_volume(volume)
|
||||||
|
player.maybe_shuffle()
|
||||||
|
if not player.is_playing:
|
||||||
|
notify_channel = player.fetch("channel")
|
||||||
|
try:
|
||||||
|
await self.api_interface.autoplay(player, self.playlist_api)
|
||||||
|
except DatabaseError:
|
||||||
|
notify_channel = self.bot.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)
|
||||||
|
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, "
|
||||||
|
"try again in a few minutes."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
del metadata
|
||||||
|
del all_guilds
|
||||||
|
|
||||||
async def maybe_message_all_owners(self):
|
async def maybe_message_all_owners(self):
|
||||||
current_notification = await self.config.owner_notification()
|
current_notification = await self.config.owner_notification()
|
||||||
if current_notification == _OWNER_NOTIFICATION:
|
if current_notification == _OWNER_NOTIFICATION:
|
||||||
|
|||||||
@ -99,10 +99,14 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
description = _("Please check your console or logs for details.")
|
description = _("Please check your console or logs for details.")
|
||||||
return await self.send_embed_msg(ctx, title=msg, description=description)
|
return await self.send_embed_msg(ctx, title=msg, description=description)
|
||||||
try:
|
try:
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self.send_embed_msg(ctx, title=_("Connect to a voice channel first."))
|
return await self.send_embed_msg(ctx, title=_("Connect to a voice channel first."))
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -239,24 +243,26 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
if query.is_local:
|
if query.is_local:
|
||||||
search_list += "`{0}.` **{1}**\n[{2}]\n".format(
|
search_list += "`{0}.` **{1}**\n[{2}]\n".format(
|
||||||
search_track_num,
|
search_track_num,
|
||||||
track.title,
|
discord.utils.escape_markdown(track.title),
|
||||||
LocalPath(track.uri, self.local_folder_current_path).to_string_user(),
|
discord.utils.escape_markdown(
|
||||||
|
LocalPath(track.uri, self.local_folder_current_path).to_string_user()
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
search_list += "`{0}.` **[{1}]({2})**\n".format(
|
search_list += "`{0}.` **[{1}]({2})**\n".format(
|
||||||
search_track_num, track.title, track.uri
|
search_track_num, discord.utils.escape_markdown(track.title), track.uri
|
||||||
)
|
)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
track = Query.process_input(track, self.local_folder_current_path)
|
track = Query.process_input(track, self.local_folder_current_path)
|
||||||
if track.is_local and command != "search":
|
if track.is_local and command != "search":
|
||||||
search_list += "`{}.` **{}**\n".format(
|
search_list += "`{}.` **{}**\n".format(
|
||||||
search_track_num, track.to_string_user()
|
search_track_num, discord.utils.escape_markdown(track.to_string_user())
|
||||||
)
|
)
|
||||||
if track.is_album:
|
if track.is_album:
|
||||||
folder = True
|
folder = True
|
||||||
else:
|
else:
|
||||||
search_list += "`{}.` **{}**\n".format(
|
search_list += "`{}.` **{}**\n".format(
|
||||||
search_track_num, track.to_string_user()
|
search_track_num, discord.utils.escape_markdown(track.to_string_user())
|
||||||
)
|
)
|
||||||
if hasattr(tracks[0], "uri") and hasattr(tracks[0], "track_identifier"):
|
if hasattr(tracks[0], "uri") and hasattr(tracks[0], "track_identifier"):
|
||||||
title = _("Tracks Found:")
|
title = _("Tracks Found:")
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
|
import discord
|
||||||
import lavalink
|
import lavalink
|
||||||
|
|
||||||
from fuzzywuzzy import process
|
from fuzzywuzzy import process
|
||||||
@ -121,7 +122,7 @@ class LocalTrackUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
if percent_match > 85:
|
if percent_match > 85:
|
||||||
search_list.extend(
|
search_list.extend(
|
||||||
[
|
[
|
||||||
i.to_string_user()
|
discord.utils.escape_markdown(i.to_string_user())
|
||||||
for i in to_search
|
for i in to_search
|
||||||
if i.local_track_path is not None
|
if i.local_track_path is not None
|
||||||
and i.local_track_path.name == track_match
|
and i.local_track_path.name == track_match
|
||||||
|
|||||||
@ -5,14 +5,14 @@ import functools
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import struct
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from typing import Any, Final, Mapping, MutableMapping, Pattern, Union, cast
|
from typing import Any, Final, Mapping, MutableMapping, Pattern, Union, cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import lavalink
|
import lavalink
|
||||||
|
|
||||||
from discord.embeds import EmptyEmbed
|
from discord.embeds import EmptyEmbed
|
||||||
|
|
||||||
from redbot.core import bank, commands
|
from redbot.core import bank, commands
|
||||||
from redbot.core.commands import Context
|
from redbot.core.commands import Context
|
||||||
from redbot.core.i18n import Translator
|
from redbot.core.i18n import Translator
|
||||||
@ -22,7 +22,7 @@ from redbot.core.utils.chat_formatting import humanize_number
|
|||||||
from ...apis.playlist_interface import get_all_playlist_for_migration23
|
from ...apis.playlist_interface import get_all_playlist_for_migration23
|
||||||
from ...utils import PlaylistScope, task_callback
|
from ...utils import PlaylistScope, task_callback
|
||||||
from ..abc import MixinMeta
|
from ..abc import MixinMeta
|
||||||
from ..cog_utils import CompositeMetaClass
|
from ..cog_utils import CompositeMetaClass, DataReader
|
||||||
|
|
||||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.miscellaneous")
|
log = logging.getLogger("red.cogs.Audio.cog.Utilities.miscellaneous")
|
||||||
_ = Translator("Audio", Path(__file__))
|
_ = Translator("Audio", Path(__file__))
|
||||||
@ -338,3 +338,47 @@ class MiscellaneousUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
|
|
||||||
if database_entries:
|
if database_entries:
|
||||||
await self.api_interface.local_cache_api.lavalink.insert(database_entries)
|
await self.api_interface.local_cache_api.lavalink.insert(database_entries)
|
||||||
|
|
||||||
|
def decode_track(self, track: str, decode_errors: str = "ignore") -> MutableMapping:
|
||||||
|
"""
|
||||||
|
Decodes a base64 track string into an AudioTrack object.
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
track: :class:`str`
|
||||||
|
The base64 track string.
|
||||||
|
decode_errors: :class:`str`
|
||||||
|
The action to take upon encountering erroneous characters within track titles.
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class:`AudioTrack`
|
||||||
|
"""
|
||||||
|
reader = DataReader(track)
|
||||||
|
|
||||||
|
flags = (reader.read_int() & 0xC0000000) >> 30
|
||||||
|
(version,) = (
|
||||||
|
struct.unpack("B", reader.read_byte()) if flags & 1 != 0 else 1
|
||||||
|
) # pylint: disable=unused-variable
|
||||||
|
|
||||||
|
title = reader.read_utf().decode(errors=decode_errors)
|
||||||
|
author = reader.read_utf().decode()
|
||||||
|
length = reader.read_long()
|
||||||
|
identifier = reader.read_utf().decode()
|
||||||
|
is_stream = reader.read_boolean()
|
||||||
|
uri = reader.read_utf().decode() if reader.read_boolean() else None
|
||||||
|
source = reader.read_utf().decode()
|
||||||
|
position = reader.read_long() # noqa: F841 pylint: disable=unused-variable
|
||||||
|
|
||||||
|
track_object = {
|
||||||
|
"track": track,
|
||||||
|
"info": {
|
||||||
|
"title": title,
|
||||||
|
"author": author,
|
||||||
|
"length": length,
|
||||||
|
"identifier": identifier,
|
||||||
|
"isStream": is_stream,
|
||||||
|
"uri": uri,
|
||||||
|
"isSeekable": not is_stream,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return track_object
|
||||||
|
|||||||
@ -218,10 +218,7 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
return
|
return
|
||||||
if not await self.config.guild_from_id(guild_id).auto_deafen():
|
if not await self.config.guild_from_id(guild_id).auto_deafen():
|
||||||
return
|
return
|
||||||
channel_id = player.channel.id
|
await player.channel.guild.change_voice_state(channel=player.channel, self_deaf=True)
|
||||||
node = player.manager.node
|
|
||||||
voice_ws = node.get_voice_ws(guild_id)
|
|
||||||
await voice_ws.voice_state(guild_id, channel_id, self_deaf=True)
|
|
||||||
|
|
||||||
async def _get_spotify_tracks(
|
async def _get_spotify_tracks(
|
||||||
self, ctx: commands.Context, query: Query, forced: bool = False
|
self, ctx: commands.Context, query: Query, forced: bool = False
|
||||||
@ -646,7 +643,7 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
lock=self.update_player_lock,
|
lock=self.update_player_lock,
|
||||||
notifier=notifier,
|
notifier=notifier,
|
||||||
forced=forced,
|
forced=forced,
|
||||||
query_global=await self.config.global_db_enabled(),
|
query_global=self.global_api_user.get("can_read"),
|
||||||
)
|
)
|
||||||
except SpotifyFetchError as error:
|
except SpotifyFetchError as error:
|
||||||
self.update_player_lock(ctx, False)
|
self.update_player_lock(ctx, False)
|
||||||
@ -716,8 +713,10 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
and player.position == 0
|
and player.position == 0
|
||||||
and len(player.queue) == 0
|
and len(player.queue) == 0
|
||||||
):
|
):
|
||||||
await player.move_to(user_channel)
|
await player.move_to(
|
||||||
await self.self_deafen(player)
|
user_channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -4,14 +4,17 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
import random
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from typing import List, MutableMapping, Optional, Tuple, Union
|
from typing import List, MutableMapping, Optional, Tuple, Union
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
import lavalink
|
import lavalink
|
||||||
|
|
||||||
from discord.embeds import EmptyEmbed
|
from discord.embeds import EmptyEmbed
|
||||||
|
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
from redbot.core.i18n import Translator
|
from redbot.core.i18n import Translator
|
||||||
from redbot.core.utils import AsyncIter
|
from redbot.core.utils import AsyncIter
|
||||||
@ -19,7 +22,7 @@ from redbot.core.utils.chat_formatting import box
|
|||||||
from redbot.core.utils.menus import start_adding_reactions
|
from redbot.core.utils.menus import start_adding_reactions
|
||||||
from redbot.core.utils.predicates import ReactionPredicate
|
from redbot.core.utils.predicates import ReactionPredicate
|
||||||
|
|
||||||
from ...apis.playlist_interface import Playlist, create_playlist
|
from ...apis.playlist_interface import Playlist, PlaylistCompat23, create_playlist
|
||||||
from ...audio_dataclasses import _PARTIALLY_SUPPORTED_MUSIC_EXT, Query
|
from ...audio_dataclasses import _PARTIALLY_SUPPORTED_MUSIC_EXT, Query
|
||||||
from ...audio_logging import debug_exc_log
|
from ...audio_logging import debug_exc_log
|
||||||
from ...errors import TooManyMatches, TrackEnqueueError
|
from ...errors import TooManyMatches, TrackEnqueueError
|
||||||
@ -29,6 +32,9 @@ from ..cog_utils import CompositeMetaClass
|
|||||||
|
|
||||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.playlists")
|
log = logging.getLogger("red.cogs.Audio.cog.Utilities.playlists")
|
||||||
_ = Translator("Audio", Path(__file__))
|
_ = Translator("Audio", Path(__file__))
|
||||||
|
CURRATED_DATA = (
|
||||||
|
"https://gist.githubusercontent.com/Drapersniper/cbe10d7053c844f8c69637bb4fd9c5c3/raw/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||||
@ -470,6 +476,18 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
async def _maybe_update_playlist(
|
async def _maybe_update_playlist(
|
||||||
self, ctx: commands.Context, player: lavalink.player_manager.Player, playlist: Playlist
|
self, ctx: commands.Context, player: lavalink.player_manager.Player, playlist: Playlist
|
||||||
) -> Tuple[List[lavalink.Track], List[lavalink.Track], Playlist]:
|
) -> Tuple[List[lavalink.Track], List[lavalink.Track], Playlist]:
|
||||||
|
if playlist.id == 42069:
|
||||||
|
_, updated_tracks = await self._get_bundled_playlist_tracks()
|
||||||
|
results = {}
|
||||||
|
old_tracks = playlist.tracks_obj
|
||||||
|
new_tracks = [lavalink.Track(data=track) for track in updated_tracks]
|
||||||
|
removed = list(set(old_tracks) - set(new_tracks))
|
||||||
|
added = list(set(new_tracks) - set(old_tracks))
|
||||||
|
if removed or added:
|
||||||
|
await playlist.edit(results)
|
||||||
|
|
||||||
|
return added, removed, playlist
|
||||||
|
|
||||||
if playlist.url is None:
|
if playlist.url is None:
|
||||||
return [], [], playlist
|
return [], [], playlist
|
||||||
results = {}
|
results = {}
|
||||||
@ -517,10 +535,14 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
description=_("I don't have permission to connect to your channel."),
|
description=_("I don't have permission to connect to your channel."),
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
await lavalink.connect(ctx.author.voice.channel)
|
await lavalink.connect(
|
||||||
|
ctx.author.voice.channel,
|
||||||
|
deafen=await self.config.guild_from_id(ctx.guild.id).auto_deafen(),
|
||||||
|
)
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
await self.self_deafen(player)
|
player.store("channel", ctx.channel.id)
|
||||||
|
player.store("guild", ctx.guild.id)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
await self.send_embed_msg(
|
await self.send_embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
@ -659,3 +681,50 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
|||||||
return ctx.name if ctx else _("the Server") if the else _("Server")
|
return ctx.name if ctx else _("the Server") if the else _("Server")
|
||||||
elif scope == PlaylistScope.USER.value:
|
elif scope == PlaylistScope.USER.value:
|
||||||
return str(ctx) if ctx else _("the User") if the else _("User")
|
return str(ctx) if ctx else _("the User") if the else _("User")
|
||||||
|
|
||||||
|
async def _get_bundled_playlist_tracks(self):
|
||||||
|
async with aiohttp.ClientSession(json_serialize=json.dumps) as session:
|
||||||
|
async with session.get(
|
||||||
|
CURRATED_DATA + f"?timestamp={int(time.time())}",
|
||||||
|
headers={"content-type": "application/json"},
|
||||||
|
) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
return 0, []
|
||||||
|
try:
|
||||||
|
data = json.loads(await response.read())
|
||||||
|
except Exception:
|
||||||
|
log.exception("Curated playlist couldn't be parsed, report this error.")
|
||||||
|
data = {}
|
||||||
|
web_version = data.get("version", 0)
|
||||||
|
entries = data.get("entries", [])
|
||||||
|
if entries:
|
||||||
|
random.shuffle(entries)
|
||||||
|
tracks = []
|
||||||
|
async for entry in AsyncIter(entries, steps=25):
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
tracks.append(self.decode_track(entry))
|
||||||
|
return web_version, tracks
|
||||||
|
|
||||||
|
async def _build_bundled_playlist(self, forced=False):
|
||||||
|
current_version = await self.config.bundled_playlist_version()
|
||||||
|
web_version, tracks = await self._get_bundled_playlist_tracks()
|
||||||
|
|
||||||
|
if not forced and current_version >= web_version:
|
||||||
|
return
|
||||||
|
|
||||||
|
playlist_data = dict()
|
||||||
|
playlist_data["name"] = "Aikaterna's curated tracks"
|
||||||
|
playlist_data["tracks"] = tracks
|
||||||
|
|
||||||
|
playlist = await PlaylistCompat23.from_json(
|
||||||
|
bot=self.bot,
|
||||||
|
playlist_api=self.playlist_api,
|
||||||
|
scope=PlaylistScope.GLOBAL.value,
|
||||||
|
playlist_number=42069,
|
||||||
|
data=playlist_data,
|
||||||
|
guild=None,
|
||||||
|
author=self.bot.user.id,
|
||||||
|
)
|
||||||
|
await playlist.save()
|
||||||
|
await self.config.bundled_playlist_version.set(web_version)
|
||||||
|
log.info("Curated playlist has been updated.")
|
||||||
|
|||||||
@ -14,7 +14,7 @@ lavalink:
|
|||||||
http: true
|
http: true
|
||||||
local: true
|
local: true
|
||||||
sentryDsn: ""
|
sentryDsn: ""
|
||||||
bufferDurationMs: 400
|
bufferDurationMs: 1000
|
||||||
youtubePlaylistLoadLimit: 10000
|
youtubePlaylistLoadLimit: 10000
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
|
|||||||
@ -22,7 +22,7 @@ from .errors import LavalinkDownloadFailed
|
|||||||
from .utils import task_callback
|
from .utils import task_callback
|
||||||
|
|
||||||
_ = Translator("Audio", pathlib.Path(__file__))
|
_ = Translator("Audio", pathlib.Path(__file__))
|
||||||
log = logging.getLogger("red.audio.manager")
|
log = logging.getLogger("red.Audio.manager")
|
||||||
JAR_VERSION: Final[str] = "3.3.2.3"
|
JAR_VERSION: Final[str] = "3.3.2.3"
|
||||||
JAR_BUILD: Final[int] = 1212
|
JAR_BUILD: Final[int] = 1212
|
||||||
LAVALINK_DOWNLOAD_URL: Final[str] = (
|
LAVALINK_DOWNLOAD_URL: Final[str] = (
|
||||||
@ -234,6 +234,7 @@ class ServerManager:
|
|||||||
line = await self._proc.stdout.readline()
|
line = await self._proc.stdout.readline()
|
||||||
if _RE_READY_LINE.search(line):
|
if _RE_READY_LINE.search(line):
|
||||||
self.ready.set()
|
self.ready.set()
|
||||||
|
log.info("Internal Lavalink server is ready to receive requests.")
|
||||||
break
|
break
|
||||||
if _FAILED_TO_START.search(line):
|
if _FAILED_TO_START.search(line):
|
||||||
raise RuntimeError(f"Lavalink failed to start: {line.decode().strip()}")
|
raise RuntimeError(f"Lavalink failed to start: {line.decode().strip()}")
|
||||||
|
|||||||
@ -51,12 +51,13 @@ install_requires =
|
|||||||
idna==2.10
|
idna==2.10
|
||||||
Markdown==3.3.3
|
Markdown==3.3.3
|
||||||
multidict==5.1.0
|
multidict==5.1.0
|
||||||
|
PyNaCl==1.3.0
|
||||||
Pygments==2.7.4
|
Pygments==2.7.4
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.1
|
||||||
python-Levenshtein-wheels==0.13.2
|
python-Levenshtein-wheels==0.13.2
|
||||||
pytz==2021.1
|
pytz==2021.1
|
||||||
PyYAML==5.4.1
|
PyYAML==5.4.1
|
||||||
Red-Lavalink==0.7.2
|
Red-Lavalink==0.8.0
|
||||||
rich==9.9.0
|
rich==9.9.0
|
||||||
schema==0.7.4
|
schema==0.7.4
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
|
|||||||
@ -26,6 +26,7 @@ install_requires =
|
|||||||
schema
|
schema
|
||||||
tqdm
|
tqdm
|
||||||
uvloop; sys_platform != "win32" and platform_python_implementation == "CPython"
|
uvloop; sys_platform != "win32" and platform_python_implementation == "CPython"
|
||||||
|
PyNaCl
|
||||||
|
|
||||||
[options.extras_require]
|
[options.extras_require]
|
||||||
docs =
|
docs =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user