mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 03:08:55 -05:00
Audio Fixes (#4492)
* handles #4491 * add typing indicators to audio playlists commands like discussed with aika. * recheck perms upon change of token to avoid needing a reload. * Ensure the player lock is always released... on rewrite to this as a callback to the task. * ffs * resolves#4495 * missed one * aaaaaaaaa * fix https://canary.discord.com/channels/133049272517001216/387398816317440000/766711707921678396 * some tweaks * Clear errors to users around YouTube Quota
This commit is contained in:
parent
335e2a7c25
commit
e31196d19f
@ -3,18 +3,21 @@ import json
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import List, MutableMapping, Optional, Union
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.chat_formatting import humanize_list
|
||||
|
||||
from ..errors import InvalidPlaylistScope, MissingAuthor, MissingGuild
|
||||
from ..utils import PlaylistScope
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.utils")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -4,6 +4,7 @@ import json
|
||||
import logging
|
||||
|
||||
from copy import copy
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Mapping, Optional, Union
|
||||
|
||||
import aiohttp
|
||||
@ -12,6 +13,7 @@ from lavalink.rest_api import LoadResult
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands import Cog
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
from ..audio_dataclasses import Query
|
||||
from ..audio_logging import IS_DEBUG, debug_exc_log
|
||||
@ -20,7 +22,7 @@ if TYPE_CHECKING:
|
||||
from .. import Audio
|
||||
|
||||
_API_URL = "https://api.redbot.app/"
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
log = logging.getLogger("red.cogs.Audio.api.GlobalDB")
|
||||
|
||||
|
||||
@ -38,8 +40,9 @@ class GlobalCacheWrapper:
|
||||
self._token: Mapping[str, str] = {}
|
||||
self.cog = cog
|
||||
|
||||
def update_token(self, new_token: Mapping[str, str]):
|
||||
async def update_token(self, new_token: Mapping[str, str]):
|
||||
self._token = new_token
|
||||
await self.get_perms()
|
||||
|
||||
async def _get_api_key(
|
||||
self,
|
||||
@ -165,7 +168,6 @@ class GlobalCacheWrapper:
|
||||
global_api_user = copy(self.cog.global_api_user)
|
||||
await self._get_api_key()
|
||||
is_enabled = await self.config.global_db_enabled()
|
||||
await self._get_api_key()
|
||||
if (not is_enabled) or self.api_key is None:
|
||||
return global_api_user
|
||||
with contextlib.suppress(Exception):
|
||||
|
||||
@ -7,6 +7,7 @@ import random
|
||||
import time
|
||||
|
||||
from collections import namedtuple
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Callable, List, MutableMapping, Optional, Tuple, Union, cast
|
||||
|
||||
import aiohttp
|
||||
@ -23,7 +24,7 @@ from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
|
||||
from ..audio_dataclasses import Query
|
||||
from ..audio_logging import IS_DEBUG, debug_exc_log
|
||||
from ..errors import DatabaseError, SpotifyFetchError, TrackEnqueueError
|
||||
from ..errors import DatabaseError, SpotifyFetchError, TrackEnqueueError, YouTubeApiError
|
||||
from ..utils import CacheLevel, Notifier
|
||||
from .api_utils import LavalinkCacheFetchForGlobalResult
|
||||
from .global_db import GlobalCacheWrapper
|
||||
@ -37,7 +38,7 @@ from .youtube import YouTubeWrapper
|
||||
if TYPE_CHECKING:
|
||||
from .. import Audio
|
||||
|
||||
_ = Translator("Audio", __file__)
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
log = logging.getLogger("red.cogs.Audio.api.AudioAPIInterface")
|
||||
_TOP_100_US = "https://www.youtube.com/playlist?list=PL4fGSI1pDJn5rWitrRWFKdm-ulaFiIyoK"
|
||||
# TODO: Get random from global Cache
|
||||
@ -209,6 +210,7 @@ class AudioAPIInterface:
|
||||
track_count = 0
|
||||
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
||||
youtube_cache = CacheLevel.set_youtube().is_subset(current_cache_level)
|
||||
youtube_api_error = None
|
||||
async for track in AsyncIter(tracks):
|
||||
if isinstance(track, str):
|
||||
break
|
||||
@ -248,9 +250,13 @@ class AudioAPIInterface:
|
||||
debug_exc_log(log, exc, f"Failed to fetch {track_info} from YouTube table")
|
||||
|
||||
if val is None:
|
||||
try:
|
||||
val = await self.fetch_youtube_query(
|
||||
ctx, track_info, current_cache_level=current_cache_level
|
||||
)
|
||||
except YouTubeApiError as err:
|
||||
val = None
|
||||
youtube_api_error = err.message
|
||||
if youtube_cache and val:
|
||||
task = ("update", ("youtube", {"track": track_info}))
|
||||
self.append_task(ctx, *task)
|
||||
@ -261,6 +267,13 @@ class AudioAPIInterface:
|
||||
track_count += 1
|
||||
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")
|
||||
if notifier is not None and youtube_api_error:
|
||||
error_embed = discord.Embed(
|
||||
colour=await ctx.embed_colour(),
|
||||
title=_("Failing to get tracks, skipping remaining."),
|
||||
)
|
||||
await notifier.update_embed(error_embed)
|
||||
break
|
||||
if CacheLevel.set_spotify().is_subset(current_cache_level):
|
||||
task = ("insert", ("spotify", database_entries))
|
||||
self.append_task(ctx, *task)
|
||||
@ -438,6 +451,7 @@ class AudioAPIInterface:
|
||||
global_entry = globaldb_toggle and query_global
|
||||
track_list: List = []
|
||||
has_not_allowed = False
|
||||
youtube_api_error = None
|
||||
try:
|
||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||
guild_data = await self.config.guild(ctx.guild).all()
|
||||
@ -461,7 +475,6 @@ class AudioAPIInterface:
|
||||
return track_list
|
||||
database_entries = []
|
||||
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
||||
|
||||
youtube_cache = CacheLevel.set_youtube().is_subset(current_cache_level)
|
||||
spotify_cache = CacheLevel.set_spotify().is_subset(current_cache_level)
|
||||
async for track_count, track in AsyncIter(tracks_from_spotify).enumerate(start=1):
|
||||
@ -506,9 +519,14 @@ class AudioAPIInterface:
|
||||
llresponse = LoadResult(llresponse)
|
||||
val = llresponse or None
|
||||
if val is None:
|
||||
try:
|
||||
val = await self.fetch_youtube_query(
|
||||
ctx, track_info, current_cache_level=current_cache_level
|
||||
)
|
||||
except YouTubeApiError as err:
|
||||
val = None
|
||||
youtube_api_error = err.message
|
||||
if not youtube_api_error:
|
||||
if youtube_cache and val and llresponse is None:
|
||||
task = ("update", ("youtube", {"track": track_info}))
|
||||
self.append_task(ctx, *task)
|
||||
@ -537,7 +555,9 @@ class AudioAPIInterface:
|
||||
lock(ctx, False)
|
||||
error_embed = discord.Embed(
|
||||
colour=await ctx.embed_colour(),
|
||||
title=_("The connection was reset while loading the playlist."),
|
||||
title=_(
|
||||
"The connection was reset while loading the playlist."
|
||||
),
|
||||
)
|
||||
if notifier is not None:
|
||||
await notifier.update_embed(error_embed)
|
||||
@ -554,6 +574,8 @@ class AudioAPIInterface:
|
||||
track_object = result.tracks
|
||||
else:
|
||||
track_object = []
|
||||
else:
|
||||
track_object = []
|
||||
if (track_count % 2 == 0) or (track_count == total_tracks):
|
||||
key = "lavalink"
|
||||
seconds = "???"
|
||||
@ -567,13 +589,16 @@ class AudioAPIInterface:
|
||||
seconds=seconds,
|
||||
)
|
||||
|
||||
if consecutive_fails >= (100 if global_entry else 10):
|
||||
if youtube_api_error or consecutive_fails >= (20 if global_entry else 10):
|
||||
error_embed = discord.Embed(
|
||||
colour=await ctx.embed_colour(),
|
||||
title=_("Failing to get tracks, skipping remaining."),
|
||||
)
|
||||
if notifier is not None:
|
||||
await notifier.update_embed(error_embed)
|
||||
if youtube_api_error:
|
||||
lock(ctx, False)
|
||||
raise SpotifyFetchError(message=youtube_api_error)
|
||||
break
|
||||
if not track_object:
|
||||
consecutive_fails += 1
|
||||
@ -631,16 +656,6 @@ class AudioAPIInterface:
|
||||
|
||||
if not player.current:
|
||||
await player.play()
|
||||
if not track_list and not has_not_allowed:
|
||||
raise SpotifyFetchError(
|
||||
message=_(
|
||||
"Nothing found.\nThe YouTube API key may be invalid "
|
||||
"or you may be rate limited on YouTube's search service.\n"
|
||||
"Check the YouTube API key again and follow the instructions "
|
||||
"at `{prefix}audioset youtubeapi`."
|
||||
)
|
||||
)
|
||||
player.maybe_shuffle()
|
||||
if enqueue and tracks_from_spotify:
|
||||
if total_tracks > enqueued_tracks:
|
||||
maxlength_msg = _(" {bad_tracks} tracks cannot be queued.").format(
|
||||
@ -667,6 +682,16 @@ class AudioAPIInterface:
|
||||
if notifier is not None:
|
||||
await notifier.update_embed(embed)
|
||||
lock(ctx, False)
|
||||
if not track_list and not has_not_allowed:
|
||||
raise SpotifyFetchError(
|
||||
message=_(
|
||||
"Nothing found.\nThe YouTube API key may be invalid "
|
||||
"or you may be rate limited on YouTube's search service.\n"
|
||||
"Check the YouTube API key again and follow the instructions "
|
||||
"at `{prefix}audioset youtubeapi`."
|
||||
)
|
||||
)
|
||||
player.maybe_shuffle()
|
||||
|
||||
if spotify_cache:
|
||||
task = ("insert", ("spotify", database_entries))
|
||||
@ -718,9 +743,12 @@ class AudioAPIInterface:
|
||||
except Exception as exc:
|
||||
debug_exc_log(log, exc, f"Failed to fetch {track_info} from YouTube table")
|
||||
if val is None:
|
||||
try:
|
||||
youtube_url = await self.fetch_youtube_query(
|
||||
ctx, track_info, current_cache_level=current_cache_level
|
||||
)
|
||||
except YouTubeApiError as err:
|
||||
youtube_url = None
|
||||
else:
|
||||
if cache_enabled:
|
||||
task = ("update", ("youtube", {"track": track_info}))
|
||||
|
||||
@ -4,6 +4,7 @@ import datetime
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from types import SimpleNamespace
|
||||
from typing import TYPE_CHECKING, Callable, List, MutableMapping, Optional, Tuple, Union
|
||||
@ -11,6 +12,7 @@ from typing import TYPE_CHECKING, Callable, List, MutableMapping, Optional, Tupl
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands import Cog
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
|
||||
@ -59,7 +61,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.LocalDB")
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
_SCHEMA_VERSION = 3
|
||||
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import concurrent
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from types import SimpleNamespace
|
||||
from typing import TYPE_CHECKING, List, Union
|
||||
@ -11,6 +12,7 @@ import lavalink
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands import Cog
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
|
||||
@ -33,6 +35,7 @@ from ..sql_statements import (
|
||||
from .api_utils import QueueFetchResult
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.PersistQueueWrapper")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Audio
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from typing import List, MutableMapping, Optional, Union
|
||||
|
||||
@ -7,6 +8,7 @@ import lavalink
|
||||
|
||||
from redbot.core import Config, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
|
||||
from ..errors import NotAllowed
|
||||
@ -15,6 +17,7 @@ from .api_utils import PlaylistFetchResult, prepare_config_scope, standardize_sc
|
||||
from .playlist_wrapper import PlaylistWrapper
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.PlaylistsInterface")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class Playlist:
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import concurrent
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from types import SimpleNamespace
|
||||
from typing import List, MutableMapping, Optional
|
||||
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
|
||||
@ -33,6 +35,7 @@ from ..utils import PlaylistScope
|
||||
from .api_utils import PlaylistFetchResult
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.Playlists")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlaylistWrapper:
|
||||
|
||||
@ -3,6 +3,7 @@ import contextlib
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import TYPE_CHECKING, List, Mapping, MutableMapping, Optional, Tuple, Union
|
||||
|
||||
@ -19,7 +20,7 @@ from ..errors import SpotifyFetchError
|
||||
if TYPE_CHECKING:
|
||||
from .. import Audio
|
||||
|
||||
_ = Translator("Audio", __file__)
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.Spotify")
|
||||
|
||||
@ -104,7 +105,7 @@ class SpotifyWrapper:
|
||||
log.debug(f"Issue making GET request to {url}: [{r.status}] {data}")
|
||||
return data
|
||||
|
||||
def update_token(self, new_token: Mapping[str, str]):
|
||||
async def update_token(self, new_token: Mapping[str, str]):
|
||||
self._token = new_token
|
||||
|
||||
async def get_token(self) -> None:
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from typing import TYPE_CHECKING, Mapping, Optional, Union
|
||||
|
||||
@ -8,6 +9,7 @@ import aiohttp
|
||||
from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands import Cog
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
from ..errors import YouTubeApiError
|
||||
|
||||
@ -15,7 +17,7 @@ if TYPE_CHECKING:
|
||||
from .. import Audio
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.api.YouTube")
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
SEARCH_ENDPOINT = "https://www.googleapis.com/youtube/v3/search"
|
||||
|
||||
|
||||
@ -32,7 +34,7 @@ class YouTubeWrapper:
|
||||
self._token: Mapping[str, str] = {}
|
||||
self.cog = cog
|
||||
|
||||
def update_token(self, new_token: Mapping[str, str]):
|
||||
async def update_token(self, new_token: Mapping[str, str]):
|
||||
self._token = new_token
|
||||
|
||||
async def _get_api_key(
|
||||
@ -54,11 +56,28 @@ class YouTubeWrapper:
|
||||
"type": "video",
|
||||
}
|
||||
async with self.session.request("GET", SEARCH_ENDPOINT, params=params) as r:
|
||||
if r.status in [400, 404]:
|
||||
if r.status == 400:
|
||||
if r.reason == "Bad Request":
|
||||
raise YouTubeApiError(
|
||||
_(
|
||||
"Your YouTube Data API token is invalid.\n"
|
||||
"Check the YouTube API key again and follow the instructions "
|
||||
"at `{prefix}audioset youtubeapi`."
|
||||
)
|
||||
)
|
||||
return None
|
||||
elif r.status in [403, 429]:
|
||||
if r.reason == "quotaExceeded":
|
||||
raise YouTubeApiError("Your YouTube Data API quota has been reached.")
|
||||
elif r.status == 404:
|
||||
return None
|
||||
elif r.status == 403:
|
||||
if r.reason in ["Forbidden", "quotaExceeded"]:
|
||||
raise YouTubeApiError(
|
||||
_(
|
||||
"YouTube API error code: 403\nYour YouTube API key may have "
|
||||
"reached the account's query limit for today. Please check "
|
||||
"<https://developers.google.com/youtube/v3/getting-started#quota> "
|
||||
"for more information."
|
||||
)
|
||||
)
|
||||
return None
|
||||
else:
|
||||
search_response = await r.json(loads=json.loads)
|
||||
|
||||
@ -22,8 +22,11 @@ from urllib.parse import urlparse
|
||||
|
||||
import lavalink
|
||||
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
_RE_REMOVE_START: Final[Pattern] = re.compile(r"^(sc|list) ")
|
||||
_RE_YOUTUBE_TIMESTAMP: Final[Pattern] = re.compile(r"[&|?]t=(\d+)s?")
|
||||
_RE_YOUTUBE_INDEX: Final[Pattern] = re.compile(r"&index=(\d+)")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import argparse
|
||||
import functools
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Final, MutableMapping, Optional, Pattern, Tuple, Union
|
||||
|
||||
@ -16,7 +17,7 @@ from .apis.playlist_interface import get_all_playlist_converter
|
||||
from .errors import NoMatchesFound, TooManyMatches
|
||||
from .utils import PlaylistScope
|
||||
|
||||
_ = Translator("Audio", __file__)
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
__all__ = [
|
||||
"ComplexScopeParser",
|
||||
|
||||
@ -2,6 +2,7 @@ import asyncio
|
||||
import json
|
||||
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from typing import Mapping
|
||||
|
||||
import aiohttp
|
||||
@ -11,11 +12,13 @@ from redbot.core import Config
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands import Cog
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.i18n import cog_i18n
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
|
||||
from ..utils import PlaylistScope
|
||||
from . import abc, cog_utils, commands, events, tasks, utilities
|
||||
from .cog_utils import CompositeMetaClass, _
|
||||
from .cog_utils import CompositeMetaClass
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
from redbot import VersionInfo
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
from ..converters import get_lazy_converter, get_playlist_converter
|
||||
|
||||
@ -12,7 +10,6 @@ __version__ = VersionInfo.from_json({"major": 2, "minor": 3, "micro": 0, "releas
|
||||
|
||||
__author__ = ["aikaterna", "Draper"]
|
||||
|
||||
_ = Translator("Audio", Path(__file__).parent)
|
||||
_SCHEMA_VERSION: Final[int] = 3
|
||||
_OWNER_NOTIFICATION: Final[int] = 1
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Union
|
||||
|
||||
@ -9,6 +10,7 @@ import lavalink
|
||||
|
||||
from redbot.core import bank, commands
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.chat_formatting import box, humanize_number
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu, start_adding_reactions
|
||||
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
|
||||
@ -18,10 +20,12 @@ from ...converters import ScopeParser
|
||||
from ...errors import MissingGuild, TooManyMatches
|
||||
from ...utils import CacheLevel, PlaylistScope
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, PlaylistConverter, _, __version__
|
||||
from ..cog_utils import CompositeMetaClass, PlaylistConverter, __version__
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.audioset")
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@commands.group(name="audioset")
|
||||
@ -1378,7 +1382,7 @@ class AudioSetCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async def command_audioset_audiodb_toggle(self, ctx: commands.Context):
|
||||
"""Toggle the server settings.
|
||||
|
||||
Default is ON
|
||||
Default is OFF
|
||||
"""
|
||||
state = await self.config.global_db_enabled()
|
||||
await self.config.global_db_enabled.set(not state)
|
||||
|
||||
@ -3,6 +3,7 @@ import contextlib
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Optional, Union
|
||||
|
||||
@ -10,15 +11,17 @@ import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import humanize_number
|
||||
from redbot.core.utils.menus import start_adding_reactions
|
||||
from redbot.core.utils.predicates import ReactionPredicate
|
||||
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.player_controller")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -98,9 +101,8 @@ class PlayerControllerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
await self.get_track_description(player.current, self.local_folder_current_path)
|
||||
or ""
|
||||
)
|
||||
song += _("\n Requested by: **{track.requester}**")
|
||||
song += "\n\n{arrow}`{pos}`/`{dur}`"
|
||||
song = song.format(track=player.current, arrow=arrow, pos=pos, dur=dur)
|
||||
song += _("\n Requested by: **{track.requester}**").format(track=player.current)
|
||||
song += "\n\n{arrow}`{pos}`/`{dur}`".format(arrow=arrow, pos=pos, dur=dur)
|
||||
else:
|
||||
song = _("Nothing.")
|
||||
|
||||
|
||||
@ -2,20 +2,23 @@ import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.chat_formatting import box, humanize_number, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu, start_adding_reactions
|
||||
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
|
||||
|
||||
from ...equalizer import Equalizer
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.equalizer")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class EqualizerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -4,11 +4,13 @@ from pathlib import Path
|
||||
import discord
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.lavalink_setup")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class LavalinkSetupCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -7,13 +7,15 @@ from typing import MutableMapping
|
||||
import discord
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, close_menu, menu, next_page, prev_page
|
||||
|
||||
from ...audio_dataclasses import LocalPath, Query
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.local_track")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class LocalTrackCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -3,19 +3,22 @@ import heapq
|
||||
import logging
|
||||
import math
|
||||
import random
|
||||
from pathlib import Path
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import humanize_number, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
|
||||
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.miscellaneous")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class MiscellaneousCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -3,6 +3,7 @@ import datetime
|
||||
import logging
|
||||
import math
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import MutableMapping
|
||||
|
||||
@ -12,6 +13,7 @@ import lavalink
|
||||
from discord.embeds import EmptyEmbed
|
||||
from redbot.core import commands
|
||||
from redbot.core.commands import UserInputOptional
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, close_menu, menu, next_page, prev_page
|
||||
|
||||
@ -24,9 +26,10 @@ from ...errors import (
|
||||
TrackEnqueueError,
|
||||
)
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.player")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -125,6 +128,9 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx, title=_("Unable To Play Tracks"), description=err.message
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
@commands.command(name="bumpplay")
|
||||
@commands.guild_only()
|
||||
@ -230,6 +236,9 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx, title=_("Unable To Play Tracks"), description=err.message
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
if isinstance(tracks, discord.Message):
|
||||
return
|
||||
elif not tracks:
|
||||
@ -248,7 +257,7 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
title = _("Track is not playable.")
|
||||
embed = discord.Embed(title=title)
|
||||
embed.description = _(
|
||||
"**{suffix}** is not a fully supported format and some " "tracks may not play."
|
||||
"**{suffix}** is not a fully supported format and some tracks may not play."
|
||||
).format(suffix=query.suffix)
|
||||
return await self.send_embed_msg(ctx, embed=embed)
|
||||
queue_dur = await self.track_remaining_duration(ctx)
|
||||
@ -611,6 +620,9 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
if not guild_data["auto_play"]:
|
||||
await ctx.invoke(self.command_audioset_autoplay_toggle)
|
||||
@ -745,6 +757,9 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"try again in a few minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
tracks = result.tracks
|
||||
else:
|
||||
@ -761,6 +776,9 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"try again in a few minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
if not tracks:
|
||||
embed = discord.Embed(title=_("Nothing found."))
|
||||
if await self.config.use_external_lavalink() and query.is_local:
|
||||
@ -877,6 +895,9 @@ class PlayerCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"try again in a few minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
tracks = result.tracks
|
||||
if not tracks:
|
||||
embed = discord.Embed(title=_("Nothing found."))
|
||||
|
||||
@ -7,6 +7,7 @@ import tarfile
|
||||
import time
|
||||
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
import discord
|
||||
@ -15,6 +16,7 @@ import lavalink
|
||||
from redbot.core import commands
|
||||
from redbot.core.commands import UserInputOptional
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import bold, pagify
|
||||
from redbot.core.utils.menus import DEFAULT_CONTROLS, menu
|
||||
@ -28,9 +30,10 @@ from ...converters import ComplexScopeParser, ScopeParser
|
||||
from ...errors import MissingGuild, TooManyMatches, TrackEnqueueError
|
||||
from ...utils import PlaylistScope
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, LazyGreedyConverter, PlaylistConverter, _
|
||||
from ..cog_utils import CompositeMetaClass, LazyGreedyConverter, PlaylistConverter
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.playlist")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -107,6 +110,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
(scope, author, guild, specified_user) = scope_data
|
||||
if not await self._playlist_check(ctx):
|
||||
return
|
||||
async with ctx.typing():
|
||||
try:
|
||||
(playlist, playlist_arg, scope) = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -117,7 +121,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
if not await self.can_manage_playlist(scope, playlist, ctx, author, guild):
|
||||
return
|
||||
@ -156,7 +162,10 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
description=_(
|
||||
"{track} is already in {playlist} (`{id}`) [**{scope}**]."
|
||||
).format(
|
||||
track=to.title, playlist=playlist.name, id=playlist.id, scope=scope_name
|
||||
track=to.title,
|
||||
playlist=playlist.name,
|
||||
id=playlist.id,
|
||||
scope=scope_name,
|
||||
),
|
||||
footer=_("Playlist limit reached: Could not add track.").format(not_added)
|
||||
if not_added > 0
|
||||
@ -192,9 +201,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
)
|
||||
if to_append_count > appended:
|
||||
diff = to_append_count - appended
|
||||
desc += _("\n{existing} {plural} already in the playlist and were skipped.").format(
|
||||
existing=diff, plural=_("tracks are") if diff != 1 else _("track is")
|
||||
)
|
||||
desc += _(
|
||||
"\n{existing} {plural} already in the playlist and were skipped."
|
||||
).format(existing=diff, plural=_("tracks are") if diff != 1 else _("track is"))
|
||||
|
||||
embed = discord.Embed(title=_("Playlist Modified"), description=desc)
|
||||
await self.send_embed_msg(
|
||||
@ -282,6 +291,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
specified_to_user,
|
||||
) = scope_data
|
||||
to_scope = to_scope or PlaylistScope.GUILD.value
|
||||
async with ctx.typing():
|
||||
try:
|
||||
from_playlist, playlist_arg, from_scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, from_scope, from_author, from_guild, specified_from_user
|
||||
@ -295,11 +305,15 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
|
||||
temp_playlist = cast(Playlist, FakePlaylist(to_author.id, to_scope))
|
||||
if not await self.can_manage_playlist(to_scope, temp_playlist, ctx, to_author, to_guild):
|
||||
if not await self.can_manage_playlist(
|
||||
to_scope, temp_playlist, ctx, to_author, to_guild
|
||||
):
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
return
|
||||
|
||||
@ -392,6 +406,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
scope_name = self.humanize_scope(
|
||||
scope, ctx=guild if scope == PlaylistScope.GUILD.value else author
|
||||
)
|
||||
async with ctx.typing():
|
||||
if not await self.can_manage_playlist(scope, temp_playlist, ctx, author, guild):
|
||||
return
|
||||
playlist_name = playlist_name.split(" ")[0].strip('"')[:32]
|
||||
@ -465,7 +480,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
|
||||
async with ctx.typing():
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -476,7 +491,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
if not await self.can_manage_playlist(scope, playlist, ctx, author, guild):
|
||||
return
|
||||
@ -688,7 +705,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
|
||||
async with ctx.typing():
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -701,7 +718,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
|
||||
schema = 2
|
||||
@ -746,7 +765,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
with tarfile.open(str(temp_tar), "w:gz") as tar:
|
||||
tar.add(
|
||||
str(temp_file), arcname=str(temp_file.relative_to(datapath)), recursive=False
|
||||
str(temp_file),
|
||||
arcname=str(temp_file.relative_to(datapath)),
|
||||
recursive=False,
|
||||
)
|
||||
try:
|
||||
if os.path.getsize(str(temp_tar)) > ctx.guild.filesize_limit - 10000:
|
||||
@ -817,6 +838,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
async with ctx.typing():
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -833,7 +855,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
track_len = len(playlist.tracks)
|
||||
|
||||
@ -841,7 +865,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if track_len > 0:
|
||||
spaces = "\N{EN SPACE}" * (len(str(len(playlist.tracks))) + 2)
|
||||
async for track_idx, track in AsyncIter(playlist.tracks).enumerate(start=1):
|
||||
query = Query.process_input(track["info"]["uri"], self.local_folder_current_path)
|
||||
query = Query.process_input(
|
||||
track["info"]["uri"], self.local_folder_current_path
|
||||
)
|
||||
if query.is_local:
|
||||
if track["info"]["title"] != "Unknown title":
|
||||
msg += "`{}.` **{} - {}**\n{}{}\n".format(
|
||||
@ -862,9 +888,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
msg = "No tracks."
|
||||
|
||||
if not playlist.url:
|
||||
embed_title = _("Playlist info for {playlist_name} (`{id}`) [**{scope}**]:\n").format(
|
||||
playlist_name=playlist.name, id=playlist.id, scope=scope_name
|
||||
)
|
||||
embed_title = _(
|
||||
"Playlist info for {playlist_name} (`{id}`) [**{scope}**]:\n"
|
||||
).format(playlist_name=playlist.name, id=playlist.id, scope=scope_name)
|
||||
else:
|
||||
embed_title = _(
|
||||
"Playlist info for {playlist_name} (`{id}`) [**{scope}**]:\nURL: {url}"
|
||||
@ -935,7 +961,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
|
||||
async with ctx.typing():
|
||||
if scope is None:
|
||||
|
||||
global_matches = await get_all_playlist(
|
||||
@ -1003,9 +1029,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("No saved playlists for {scope} created by {author}.").format(
|
||||
scope=name, author=author
|
||||
),
|
||||
description=_(
|
||||
"No saved playlists for {scope} created by {author}."
|
||||
).format(scope=name, author=author),
|
||||
)
|
||||
elif not playlists:
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
@ -1029,7 +1055,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
or playlist.author
|
||||
or _("Unknown")
|
||||
),
|
||||
_("Scope: {scope}\n").format(scope=self.humanize_scope(playlist.scope)),
|
||||
_("Scope: {scope}\n").format(
|
||||
scope=self.humanize_scope(playlist.scope)
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1199,6 +1227,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
async with ctx.typing():
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -1212,7 +1241,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
if not await self.can_manage_playlist(scope, playlist, ctx, author, guild):
|
||||
return
|
||||
@ -1231,7 +1262,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
guild=guild,
|
||||
author=playlist.author,
|
||||
)
|
||||
return await self.send_embed_msg(ctx, title=_("No tracks left, removing playlist."))
|
||||
return await self.send_embed_msg(
|
||||
ctx, title=_("No tracks left, removing playlist.")
|
||||
)
|
||||
update = {"tracks": clean_list, "url": None}
|
||||
await playlist.edit(update)
|
||||
if del_count > 1:
|
||||
@ -1242,7 +1275,10 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"{num} entries have been removed "
|
||||
"from the playlist {playlist_name} (`{id}`) [**{scope}**]."
|
||||
).format(
|
||||
num=del_count, playlist_name=playlist.name, id=playlist.id, scope=scope_name
|
||||
num=del_count,
|
||||
playlist_name=playlist.name,
|
||||
id=playlist.id,
|
||||
scope=scope_name,
|
||||
),
|
||||
)
|
||||
else:
|
||||
@ -1313,6 +1349,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
scope_name = self.humanize_scope(
|
||||
scope, ctx=guild if scope == PlaylistScope.GUILD.value else author
|
||||
)
|
||||
async with ctx.typing():
|
||||
temp_playlist = cast(Playlist, FakePlaylist(author.id, scope))
|
||||
if not await self.can_manage_playlist(scope, temp_playlist, ctx, author, guild):
|
||||
return ctx.command.reset_cooldown(ctx)
|
||||
@ -1360,9 +1397,14 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
description=_(
|
||||
"Playlist {name} (`{id}`) [**{scope}**] saved: {num} tracks added."
|
||||
).format(
|
||||
name=playlist.name, num=len(tracklist), id=playlist.id, scope=scope_name
|
||||
name=playlist.name,
|
||||
num=len(tracklist),
|
||||
id=playlist.id,
|
||||
scope=scope_name,
|
||||
),
|
||||
footer=_("Playlist limit reached: Could not add {} tracks.").format(not_added)
|
||||
footer=_("Playlist limit reached: Could not add {} tracks.").format(
|
||||
not_added
|
||||
)
|
||||
if not_added > 0
|
||||
else None,
|
||||
)
|
||||
@ -1440,7 +1482,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
description=_("You need the DJ role to start playing playlists."),
|
||||
)
|
||||
return False
|
||||
|
||||
async with ctx.typing():
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -1453,7 +1495,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
|
||||
if not await self._playlist_check(ctx):
|
||||
@ -1608,6 +1652,8 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
embeds = None
|
||||
async with ctx.typing():
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -1621,7 +1667,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
|
||||
if not await self._playlist_check(ctx):
|
||||
@ -1632,7 +1680,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return
|
||||
if playlist.url:
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
added, removed, playlist = await self._maybe_update_playlist(ctx, player, playlist)
|
||||
added, removed, playlist = await self._maybe_update_playlist(
|
||||
ctx, player, playlist
|
||||
)
|
||||
else:
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
return await self.send_embed_msg(
|
||||
@ -1679,7 +1729,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if i % 10 == 0 or i == total_removed:
|
||||
page_count += 1
|
||||
embed = discord.Embed(
|
||||
title=_("Tracks removed"), colour=_colour, description=removed_text
|
||||
title=_("Tracks removed"),
|
||||
colour=_colour,
|
||||
description=removed_text,
|
||||
)
|
||||
text = _("Page {page_num}/{total_pages}").format(
|
||||
page_num=page_count, total_pages=total_pages
|
||||
@ -1708,7 +1760,6 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
added_embeds.append(embed)
|
||||
added_text = ""
|
||||
embeds = removed_embeds + added_embeds
|
||||
await menu(ctx, embeds, DEFAULT_CONTROLS)
|
||||
else:
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
@ -1717,6 +1768,8 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
id=playlist.id, name=playlist.name, scope=scope_name
|
||||
),
|
||||
)
|
||||
if embeds:
|
||||
await menu(ctx, embeds, DEFAULT_CONTROLS)
|
||||
|
||||
@command_playlist.command(name="upload", usage="[args]")
|
||||
@commands.is_owner()
|
||||
@ -1770,9 +1823,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
scope, author, guild, specified_user = scope_data
|
||||
scope = scope or PlaylistScope.GUILD.value
|
||||
temp_playlist = cast(Playlist, FakePlaylist(author.id, scope))
|
||||
async with ctx.typing():
|
||||
if not await self.can_manage_playlist(scope, temp_playlist, ctx, author, guild):
|
||||
return
|
||||
|
||||
if not await self._playlist_check(ctx):
|
||||
return
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
@ -1834,7 +1887,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
await self.api_interface.fetch_track(
|
||||
ctx,
|
||||
player,
|
||||
Query.process_input(uploaded_playlist_url, self.local_folder_current_path),
|
||||
Query.process_input(
|
||||
uploaded_playlist_url, self.local_folder_current_path
|
||||
),
|
||||
)
|
||||
)[0].tracks
|
||||
):
|
||||
@ -1874,6 +1929,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
@commands.cooldown(1, 60, commands.BucketType.member)
|
||||
@command_playlist.command(
|
||||
@ -1929,7 +1987,7 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if scope_data is None:
|
||||
scope_data = [None, ctx.author, ctx.guild, False]
|
||||
scope, author, guild, specified_user = scope_data
|
||||
|
||||
async with ctx.typing():
|
||||
new_name = new_name.split(" ")[0].strip('"')[:32]
|
||||
if new_name.isnumeric():
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
@ -1941,7 +1999,6 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"characters) and not numbers only."
|
||||
),
|
||||
)
|
||||
|
||||
try:
|
||||
playlist, playlist_arg, scope = await self.get_playlist_match(
|
||||
ctx, playlist_matches, scope, author, guild, specified_user
|
||||
@ -1954,7 +2011,9 @@ class PlaylistCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx,
|
||||
title=_("Playlist Not Found"),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(arg=playlist_arg),
|
||||
description=_("Could not match '{arg}' to a playlist.").format(
|
||||
arg=playlist_arg
|
||||
),
|
||||
)
|
||||
if not await self.can_manage_playlist(scope, playlist, ctx, author, guild):
|
||||
ctx.command.reset_cooldown(ctx)
|
||||
|
||||
@ -3,6 +3,7 @@ import contextlib
|
||||
import datetime
|
||||
import logging
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
from typing import MutableMapping, Optional
|
||||
|
||||
@ -10,6 +11,7 @@ import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.menus import (
|
||||
DEFAULT_CONTROLS,
|
||||
@ -22,9 +24,10 @@ from redbot.core.utils.menus import (
|
||||
from redbot.core.utils.predicates import ReactionPredicate
|
||||
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Commands.queue")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class QueueCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -71,9 +74,8 @@ class QueueCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
await self.get_track_description(player.current, self.local_folder_current_path)
|
||||
or ""
|
||||
)
|
||||
song += _("\n Requested by: **{track.requester}**")
|
||||
song += "\n\n{arrow}`{pos}`/`{dur}`"
|
||||
song = song.format(track=player.current, arrow=arrow, pos=pos, dur=dur)
|
||||
song += _("\n Requested by: **{track.requester}**").format(track=player.current)
|
||||
song += f"\n\n{arrow}`{pos}`/`{dur}`"
|
||||
embed = discord.Embed(title=_("Now Playing"), description=song)
|
||||
guild_data = await self.config.guild(ctx.guild).all()
|
||||
if guild_data["thumbnail"] and player.current and player.current.thumbnail:
|
||||
|
||||
@ -2,6 +2,7 @@ import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Optional
|
||||
|
||||
@ -9,6 +10,7 @@ import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
from ...apis.playlist_interface import Playlist, delete_playlist, get_playlist
|
||||
from ...audio_logging import debug_exc_log
|
||||
@ -17,6 +19,7 @@ from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.audio")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class AudioEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -13,15 +13,16 @@ import lavalink
|
||||
from aiohttp import ClientConnectorError
|
||||
from discord.ext.commands import CheckFailure
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils.chat_formatting import box, humanize_list
|
||||
|
||||
from ...audio_logging import debug_exc_log
|
||||
from ...errors import TrackEnqueueError
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import HUMANIZED_PERM, CompositeMetaClass, _
|
||||
from ..cog_utils import HUMANIZED_PERM, CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.dpy")
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
RE_CONVERSION: Final[Pattern] = re.compile('Converting to "(.*)" failed for parameter "(.*)".')
|
||||
|
||||
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import discord
|
||||
import lavalink
|
||||
|
||||
from redbot.core.i18n import Translator
|
||||
from ...errors import DatabaseError, TrackEnqueueError
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.lavalink")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -174,6 +177,7 @@ class LavalinkEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
player.current = None
|
||||
if not guild_id:
|
||||
return
|
||||
guild_id = int(guild_id)
|
||||
self._error_counter.setdefault(guild_id, 0)
|
||||
if guild_id not in self._error_counter:
|
||||
self._error_counter[guild_id] = 0
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Literal, Mapping
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Events.red")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class RedEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -15,11 +18,11 @@ class RedEvents(MixinMeta, metaclass=CompositeMetaClass):
|
||||
self, service_name: str, api_tokens: Mapping[str, str]
|
||||
) -> None:
|
||||
if service_name == "youtube":
|
||||
self.api_interface.youtube_api.update_token(api_tokens)
|
||||
await self.api_interface.youtube_api.update_token(api_tokens)
|
||||
elif service_name == "spotify":
|
||||
self.api_interface.spotify_api.update_token(api_tokens)
|
||||
await self.api_interface.spotify_api.update_token(api_tokens)
|
||||
elif service_name == "audiodb":
|
||||
self.api_interface.global_cache_api.update_token(api_tokens)
|
||||
await self.api_interface.global_cache_api.update_token(api_tokens)
|
||||
|
||||
async def red_delete_data_for_user(
|
||||
self,
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import lavalink
|
||||
|
||||
from redbot.core.i18n import Translator
|
||||
from ...errors import LavalinkDownloadFailed
|
||||
from ...manager import ServerManager
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks.lavalink")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class LavalinkTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Dict
|
||||
|
||||
import lavalink
|
||||
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
|
||||
from ...audio_logging import debug_exc_log
|
||||
@ -13,6 +15,7 @@ from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks.player")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlayerTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -2,12 +2,14 @@ import asyncio
|
||||
import datetime
|
||||
import itertools
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import lavalink
|
||||
|
||||
from redbot.core.data_manager import cog_data_path
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils._internal_utils import send_to_owners_with_prefix_replaced
|
||||
from redbot.core.utils.dbtools import APSWConnectionWrapper
|
||||
|
||||
@ -16,9 +18,10 @@ from ...apis.playlist_wrapper import PlaylistWrapper
|
||||
from ...audio_logging import debug_exc_log
|
||||
from ...utils import task_callback
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import _, _OWNER_NOTIFICATION, _SCHEMA_VERSION, CompositeMetaClass
|
||||
from ..cog_utils import _OWNER_NOTIFICATION, _SCHEMA_VERSION, CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Tasks.startup")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class StartUpTasks(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -3,6 +3,7 @@ import logging
|
||||
import math
|
||||
import re
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
@ -11,16 +12,17 @@ import lavalink
|
||||
|
||||
from discord.embeds import EmptyEmbed
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import box, escape
|
||||
|
||||
from ...audio_dataclasses import LocalPath, Query
|
||||
from ...audio_logging import IS_DEBUG
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.formatting")
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
RE_SQUARE = re.compile(r"[\[\]]")
|
||||
|
||||
|
||||
@ -157,7 +159,7 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if not await self.is_query_allowed(
|
||||
self.config,
|
||||
ctx,
|
||||
f"{search_choice.title} {search_choice.author} {search_choice.uri} " f"{str(query)}",
|
||||
f"{search_choice.title} {search_choice.author} {search_choice.uri} {str(query)}",
|
||||
query_obj=query,
|
||||
):
|
||||
if IS_DEBUG:
|
||||
@ -295,7 +297,7 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if shorten:
|
||||
string = f"{track.author} - {track.title}"
|
||||
if len(string) > 40:
|
||||
string = "{}...".format((string[:40]).rstrip(" "))
|
||||
string = f"{(string[:40]).rstrip(' ')}..."
|
||||
string = f'**{escape(f"{string}", formatting=True)}**'
|
||||
else:
|
||||
string = (
|
||||
@ -306,7 +308,7 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if shorten:
|
||||
string = f"{track.title}"
|
||||
if len(string) > 40:
|
||||
string = "{}...".format((string[:40]).rstrip(" "))
|
||||
string = f"{(string[:40]).rstrip(' ')}..."
|
||||
string = f'**{escape(f"{string}", formatting=True)}**'
|
||||
else:
|
||||
string = f'**{escape(f"{track.title}", formatting=True)}**' + escape(
|
||||
@ -315,7 +317,7 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
else:
|
||||
string = query.to_string_user()
|
||||
if shorten and len(string) > 40:
|
||||
string = "{}...".format((string[:40]).rstrip(" "))
|
||||
string = f"{(string[:40]).rstrip(' ')}..."
|
||||
string = f'**{escape(f"{string}", formatting=True)}**'
|
||||
else:
|
||||
if track.is_stream:
|
||||
@ -330,13 +332,13 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
title = track.title
|
||||
string = f"{title}"
|
||||
if shorten and len(string) > 40:
|
||||
string = "{}...".format((string[:40]).rstrip(" "))
|
||||
string = f"{(string[:40]).rstrip(' ')}..."
|
||||
string = re.sub(RE_SQUARE, "", string)
|
||||
string = f"**[{escape(string, formatting=True)}]({track.uri}) **"
|
||||
elif hasattr(track, "to_string_user") and track.is_local:
|
||||
string = track.to_string_user() + " "
|
||||
if shorten and len(string) > 40:
|
||||
string = "{}...".format((string[:40]).rstrip(" "))
|
||||
string = f"{(string[:40]).rstrip(' ')}..."
|
||||
string = f'**{escape(f"{string}", formatting=True)}**'
|
||||
return string
|
||||
|
||||
@ -391,7 +393,7 @@ class FormattingUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
async def draw_time(self, ctx) -> str:
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
paused = player.paused
|
||||
pos = player.position
|
||||
pos = player.position or 1
|
||||
dur = getattr(player.current, "length", player.position or 1)
|
||||
sections = 12
|
||||
loc_time = round((pos / dur if dur != 0 else pos) * sections)
|
||||
|
||||
@ -8,14 +8,16 @@ import lavalink
|
||||
|
||||
from fuzzywuzzy import process
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
|
||||
from ...audio_dataclasses import LocalPath, Query
|
||||
from ...errors import TrackEnqueueError
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.local_tracks")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class LocalTrackUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -5,6 +5,7 @@ import functools
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from typing import Any, Final, Mapping, MutableMapping, Pattern, Union, cast
|
||||
|
||||
@ -14,16 +15,17 @@ import lavalink
|
||||
from discord.embeds import EmptyEmbed
|
||||
from redbot.core import bank, commands
|
||||
from redbot.core.commands import Context
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import humanize_number
|
||||
|
||||
from ...apis.playlist_interface import get_all_playlist_for_migration23
|
||||
from ...utils import PlaylistScope, task_callback
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.miscellaneous")
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
_RE_TIME_CONVERTER: Final[Pattern] = re.compile(r"(?:(\d+):)?([0-5]?[0-9]):([0-5][0-9])")
|
||||
_prefer_lyrics_cache = {}
|
||||
|
||||
@ -204,11 +206,8 @@ class MiscellaneousUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
async def queue_duration(self, ctx: commands.Context) -> int:
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
duration = []
|
||||
async for i in AsyncIter(range(len(player.queue))):
|
||||
if not player.queue[i].is_stream:
|
||||
duration.append(player.queue[i].length)
|
||||
queue_dur = sum(duration)
|
||||
dur = [i.length async for i in AsyncIter(player.queue, steps=50)]
|
||||
queue_dur = sum(dur)
|
||||
if not player.queue:
|
||||
queue_dur = 0
|
||||
try:
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
@ -9,6 +10,7 @@ import lavalink
|
||||
|
||||
from discord.embeds import EmptyEmbed
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import bold, escape
|
||||
|
||||
@ -17,9 +19,10 @@ from ...audio_logging import IS_DEBUG, debug_exc_log
|
||||
from ...errors import QueryUnauthorized, SpotifyFetchError, TrackEnqueueError
|
||||
from ...utils import Notifier
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.player")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -279,6 +282,9 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
return await self.send_embed_msg(
|
||||
ctx, title=error.message.format(prefix=ctx.prefix)
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
self.update_player_lock(ctx, False)
|
||||
try:
|
||||
if enqueue_tracks:
|
||||
@ -327,7 +333,11 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"\nUse `{prefix}audioset spotifyapi` for instructions."
|
||||
).format(prefix=ctx.prefix),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
elif query.is_album or query.is_playlist:
|
||||
try:
|
||||
self.update_player_lock(ctx, True)
|
||||
track_list = await self.fetch_spotify_playlist(
|
||||
ctx,
|
||||
@ -336,6 +346,7 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
enqueue_tracks,
|
||||
forced=forced,
|
||||
)
|
||||
finally:
|
||||
self.update_player_lock(ctx, False)
|
||||
return track_list
|
||||
else:
|
||||
@ -387,6 +398,9 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"try again in a few minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
tracks = result.tracks
|
||||
playlist_data = result.playlist_info
|
||||
if not enqueue:
|
||||
@ -581,6 +595,9 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
if await self.bot.is_owner(ctx.author):
|
||||
desc = _("Please check your console or logs for details.")
|
||||
return await self.send_embed_msg(ctx, title=title, description=desc)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
description = await self.get_track_description(
|
||||
single_track, self.local_folder_current_path
|
||||
)
|
||||
@ -659,6 +676,7 @@ class PlayerUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
finally:
|
||||
self.update_player_lock(ctx, False)
|
||||
return track_list
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import datetime
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
from typing import List, MutableMapping, Optional, Tuple, Union
|
||||
|
||||
@ -12,6 +13,7 @@ import lavalink
|
||||
|
||||
from discord.embeds import EmptyEmbed
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
from redbot.core.utils.menus import start_adding_reactions
|
||||
@ -23,9 +25,10 @@ from ...audio_logging import debug_exc_log
|
||||
from ...errors import TooManyMatches, TrackEnqueueError
|
||||
from ...utils import Notifier, PlaylistScope
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.playlists")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
@ -413,6 +416,9 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"try again in a few minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
track = result.tracks[0]
|
||||
except Exception as err:
|
||||
@ -599,6 +605,9 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
tracks = result.tracks
|
||||
if not tracks:
|
||||
@ -625,6 +634,9 @@ class PlaylistUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
"minutes."
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
self.update_player_lock(ctx, False)
|
||||
raise e
|
||||
|
||||
tracks = result.tracks
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
from typing import List, Tuple
|
||||
|
||||
@ -8,14 +9,16 @@ import lavalink
|
||||
|
||||
from fuzzywuzzy import process
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
from redbot.core.utils import AsyncIter
|
||||
from redbot.core.utils.chat_formatting import humanize_number
|
||||
|
||||
from ...audio_dataclasses import LocalPath, Query
|
||||
from ..abc import MixinMeta
|
||||
from ..cog_utils import CompositeMetaClass, _
|
||||
from ..cog_utils import CompositeMetaClass
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.cog.Utilities.queue")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class QueueUtilities(MixinMeta, metaclass=CompositeMetaClass):
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
# The equalizer class and some audio eq functions are derived from
|
||||
# 180093157554388993's work, with his permission
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class Equalizer:
|
||||
def __init__(self):
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
from pathlib import Path
|
||||
|
||||
import aiohttp
|
||||
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class AudioError(Exception):
|
||||
"""Base exception for errors in the Audio cog."""
|
||||
@ -91,6 +97,10 @@ class SpotifyFetchError(SpotifyApiError):
|
||||
class YouTubeApiError(ApiError):
|
||||
"""Base exception for YouTube Data API errors."""
|
||||
|
||||
def __init__(self, message, *args):
|
||||
self.message = message
|
||||
super().__init__(*args)
|
||||
|
||||
|
||||
class DatabaseError(AudioError):
|
||||
"""Base exception for database errors in the Audio cog."""
|
||||
|
||||
@ -10,17 +10,18 @@ import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from typing import ClassVar, Final, List, Optional, Pattern, Tuple
|
||||
|
||||
import aiohttp
|
||||
from tqdm import tqdm
|
||||
|
||||
from redbot.core import data_manager
|
||||
from tqdm import tqdm
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
from .errors import LavalinkDownloadFailed
|
||||
from .utils import task_callback
|
||||
|
||||
_ = Translator("Audio", pathlib.Path(__file__))
|
||||
log = logging.getLogger("red.audio.manager")
|
||||
JAR_VERSION: Final[str] = "3.3.1.4"
|
||||
JAR_BUILD: Final[int] = 1115
|
||||
|
||||
@ -4,13 +4,16 @@ import logging
|
||||
import time
|
||||
|
||||
from enum import Enum, unique
|
||||
from pathlib import Path
|
||||
from typing import MutableMapping
|
||||
|
||||
import discord
|
||||
|
||||
from redbot.core import commands
|
||||
from redbot.core.i18n import Translator
|
||||
|
||||
log = logging.getLogger("red.cogs.Audio.task.callback")
|
||||
_ = Translator("Audio", Path(__file__))
|
||||
|
||||
|
||||
class CacheLevel:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user