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:
Draper
2020-10-20 17:57:02 +01:00
committed by GitHub
parent 335e2a7c25
commit e31196d19f
39 changed files with 1255 additions and 1014 deletions

View File

@@ -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:
val = await self.fetch_youtube_query(
ctx, track_info, current_cache_level=current_cache_level
)
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,52 +519,61 @@ class AudioAPIInterface:
llresponse = LoadResult(llresponse)
val = llresponse or None
if val is None:
val = await self.fetch_youtube_query(
ctx, track_info, current_cache_level=current_cache_level
)
if youtube_cache and val and llresponse is None:
task = ("update", ("youtube", {"track": track_info}))
self.append_task(ctx, *task)
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)
if isinstance(llresponse, LoadResult):
track_object = llresponse.tracks
elif val:
result = None
if should_query_global:
llresponse = await self.global_cache_api.get_call(val)
if llresponse:
if llresponse.get("loadType") == "V2_COMPACT":
llresponse["loadType"] = "V2_COMPAT"
llresponse = LoadResult(llresponse)
result = llresponse or None
if not result:
try:
(result, called_api) = await self.fetch_track(
ctx,
player,
Query.process_input(val, self.cog.local_folder_current_path),
forced=forced,
should_query_global=not should_query_global,
)
except (RuntimeError, aiohttp.ServerDisconnectedError):
lock(ctx, False)
error_embed = discord.Embed(
colour=await ctx.embed_colour(),
title=_("The connection was reset while loading the playlist."),
)
if notifier is not None:
await notifier.update_embed(error_embed)
break
except asyncio.TimeoutError:
lock(ctx, False)
error_embed = discord.Embed(
colour=await ctx.embed_colour(),
title=_("Player timeout, skipping remaining tracks."),
)
if notifier is not None:
await notifier.update_embed(error_embed)
break
track_object = result.tracks
if isinstance(llresponse, LoadResult):
track_object = llresponse.tracks
elif val:
result = None
if should_query_global:
llresponse = await self.global_cache_api.get_call(val)
if llresponse:
if llresponse.get("loadType") == "V2_COMPACT":
llresponse["loadType"] = "V2_COMPAT"
llresponse = LoadResult(llresponse)
result = llresponse or None
if not result:
try:
(result, called_api) = await self.fetch_track(
ctx,
player,
Query.process_input(val, self.cog.local_folder_current_path),
forced=forced,
should_query_global=not should_query_global,
)
except (RuntimeError, aiohttp.ServerDisconnectedError):
lock(ctx, False)
error_embed = discord.Embed(
colour=await ctx.embed_colour(),
title=_(
"The connection was reset while loading the playlist."
),
)
if notifier is not None:
await notifier.update_embed(error_embed)
break
except asyncio.TimeoutError:
lock(ctx, False)
error_embed = discord.Embed(
colour=await ctx.embed_colour(),
title=_("Player timeout, skipping remaining tracks."),
)
if notifier is not None:
await notifier.update_embed(error_embed)
break
track_object = result.tracks
else:
track_object = []
else:
track_object = []
if (track_count % 2 == 0) or (track_count == total_tracks):
@@ -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:
youtube_url = await self.fetch_youtube_query(
ctx, track_info, current_cache_level=current_cache_level
)
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}))