Skip to content

Commit

Permalink
jockey: Invalidate cached track on error (for #45)
Browse files Browse the repository at this point in the history
  • Loading branch information
jareddantis committed Oct 18, 2023
1 parent 18ef29f commit fbd61fc
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
23 changes: 21 additions & 2 deletions cogs/player/jockey.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from nextcord import (Colour, Forbidden, HTTPException, Message, NotFound,
StageChannel, VoiceChannel)

from database.redis import REDIS
from dataclass.custom_embed import CustomEmbed
from utils.embeds import create_error_embed
from utils.exceptions import (EndOfQueueError, JockeyError, JockeyException,
Expand All @@ -18,7 +19,8 @@
from utils.time import human_readable_time
from views.now_playing import NowPlayingView

from .jockey_helpers import find_lavalink_track, parse_query
from .jockey_helpers import (find_lavalink_track, invalidate_lavalink_track,
parse_query)
from .queue import QueueManager

if TYPE_CHECKING:
Expand Down Expand Up @@ -196,7 +198,24 @@ async def _play(self, item: 'QueueItem') -> bool:
raise RuntimeError(err.args[0]) from err

# Play track
await self.play(item.lavalink_track, volume=self.volume)
has_retried = False
while True:
try:
await self.play(item.lavalink_track, volume=self.volume)
except PlayerNotConnected:
# If we've already retried, give up
if has_retried or REDIS is None:
raise

# Remove cached Lavalink track and try again
self._logger.warning(
'PlayerNotConnected raised while playing `%s\'. Retrying.',
item.title
)
invalidate_lavalink_track(item)
has_retried = True
else:
break

# We don't want to play if the player is not idle
# as that will effectively skip the current track.
Expand Down
32 changes: 32 additions & 0 deletions cogs/player/jockey_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,38 @@ async def find_lavalink_track( # pylint: disable=too-many-statements
return lavalink_track


def invalidate_lavalink_track(item: QueueItem):
"""
Removes a cached Lavalink track from Redis.
:param item: The QueueItem to invalidate the track for.
"""
if REDIS is None:
return

# Determine key type
redis_key = None
redis_key_type = None
if item.spotify_id is not None:
redis_key = item.spotify_id
redis_key_type = 'spotify_id'
elif item.isrc is not None:
redis_key = item.isrc
redis_key_type = 'isrc'

# Invalidate cached Lavalink track
if redis_key is not None and redis_key_type is not None:
REDIS.invalidate_lavalink_track(
redis_key,
key_type=redis_key_type
)
else:
LOGGER.warning(
'Could not invalidate cached track for `%s\': no key',
item.title
)


async def parse_query(
node: 'Node',
spotify: Spotify,
Expand Down
11 changes: 11 additions & 0 deletions database/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ def get_lavalink_track(self, key: str, *, key_type: str) -> Optional[str]:
self._logger.debug('Got cached Lavalink track for %s:%s', key_type, key)
return self._client.get(f'lavalink:{key_type}:{key}') # type: ignore

def invalidate_lavalink_track(self, key: str, *, key_type: str):
"""
Removes a cached Lavalink track.
:param key: The key to remove the track for.
:param key_type: The type of key to remove the track for, e.g. 'isrc' or 'spotify_id'.
"""
self._logger.debug('Invalidating Lavalink track for %s:%s', key_type, key)
if self._client.exists(f'lavalink:{key_type}:{key}'):
self._client.delete(f'lavalink:{key_type}:{key}')

def set_spotify_track(self, spotify_id: str, track: 'SpotifyTrack'):
"""
Save a Spotify track.
Expand Down

0 comments on commit fbd61fc

Please sign in to comment.