Skip to content

Commit

Permalink
Merge pull request #54 from jareddantis-bots/next
Browse files Browse the repository at this point in the history
Release 0.5.6
  • Loading branch information
jareddantis authored Feb 23, 2024
2 parents 470f952 + f29bf77 commit d307415
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 34 deletions.
8 changes: 6 additions & 2 deletions cogs/player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,12 @@ async def jump(

# Dispatch to jockey
await itx.response.defer()
await jockey.skip(index=position - 1, auto=False)
await itx.followup.send(embed=create_success_embed(f'Jumped to track {str(position)}'))
try:
await jockey.skip(index=position - 1, auto=False)
except JockeyError as err:
await itx.followup.send(embed=create_error_embed(str(err)))
else:
await itx.followup.send(embed=create_success_embed(f'Jumped to track {str(position)}'))

@slash_command(name='loop')
@application_checks.check(check_mutual_voice)
Expand Down
78 changes: 54 additions & 24 deletions cogs/player/jockey.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ async def _edit_np_controls(self, show_controls: bool = True):
exc
)

async def _enqueue(self, index: int, auto: bool = True) -> bool:
async def _enqueue(self, index: int, auto: bool = True):
"""
Attempt to enqueue a track, for use with the skip() method.
Expand All @@ -148,29 +148,23 @@ async def _enqueue(self, index: int, auto: bool = True) -> bool:
"""
try:
track = self._queue_mgr.queue[index]
result = await self._play(track)
await self._play(track)
except PlayerNotConnected:
if not auto:
await self.status_channel.send(embed=create_error_embed(
'Attempted to skip while disconnected'
))
return False
except Exception as exc: # pylint: disable=broad-exception-caught
self._logger.error('Failed to play next track: %s', exc)
if auto:
await self.status_channel.send(embed=create_error_embed(
f'Unable to play next track: {exc}'
))
return False
raise JockeyError('Player is not connected')
except JockeyError as err:
self._logger.error('Failed to enqueue track: %s', err)
raise

# Scrobble if possible
await self._scrobble(self._queue_mgr.current)

# Update queue index
self._queue_mgr.current_index = index

return result

async def _get_now_playing(self) -> Optional[Message]:
np_msg_id = self._db.get_now_playing(self.guild.id)
if np_msg_id != -1:
Expand All @@ -186,7 +180,7 @@ async def _get_now_playing(self) -> Optional[Message]:

return None

async def _play(self, item: 'QueueItem', position: Optional[int] = None) -> bool:
async def _play(self, item: 'QueueItem', position: Optional[int] = None):
if item.lavalink_track is None:
try:
assert self._bot.config is not None
Expand All @@ -198,7 +192,7 @@ async def _play(self, item: 'QueueItem', position: Optional[int] = None) -> bool
)
except LavalinkSearchError as err:
self._logger.critical('Failed to play `%s\'.', item.title)
raise RuntimeError(err.args[0]) from err
raise JockeyError(err.args[0]) from err

# Play track
has_retried = False
Expand Down Expand Up @@ -245,8 +239,6 @@ async def _play(self, item: 'QueueItem', position: Optional[int] = None) -> bool
# Save start time for scrobbling
item.start_time = int(time())

return True

async def _scrobble(self, item: 'QueueItem'):
"""
Scrobbles a track in a separate thread.
Expand Down Expand Up @@ -408,7 +400,7 @@ async def on_load_failed(self, failed_source: 'Track'):
failed_track = self._queue_mgr.current
index = self._queue_mgr.current_shuffled_index + 1
queue_size = self._queue_mgr.size

# Send error embed
embed = CustomEmbed(
color=Colour.red(),
Expand Down Expand Up @@ -486,15 +478,17 @@ async def play_impl(self, query: str, requester: int) -> str:
old_index = self._queue_mgr.current_index
self._queue_mgr.current_index = old_size

if not await self._play(new_tracks[0]):
try:
await self._play(new_tracks[0])
except (JockeyError, PlayerNotConnected) as err:
# Remove enqueued tracks
for _ in range(old_size, self._queue_mgr.size):
self._queue_mgr.remove(old_size)

# Restore old index
self._queue_mgr.current_index = old_index

raise JockeyError(f'Failed to play "{first.title}"')
raise JockeyError(f'Failed to play "{first.title}"') from err

# Send embed
return first_name if len(new_tracks) == 1 else f'{len(new_tracks)} item(s)'
Expand Down Expand Up @@ -555,32 +549,68 @@ async def skip(self, *, forward: bool = True, index: int = -1, auto: bool = True

# If index is specified, use that instead
if index != -1:
if not await self._enqueue(index, auto=auto):
try:
await self._enqueue(index, auto=auto)
except JockeyError:
await self._edit_np_controls(show_controls=True)
await self.status_channel.send(embed=create_error_embed(
f'Unable to skip to index {index}'
))
raise

return

# Is this autoskipping?
if auto:
# Check if we're looping the current track
if self._queue_mgr.is_looping_one:
# Re-enqueue the current track
await self._enqueue(self._queue_mgr.current_index, auto=auto)
try:
await self._enqueue(self._queue_mgr.current_index, auto=auto)
except JockeyError as err:
await self._edit_np_controls(show_controls=True)
await self.status_channel.send(embed=create_error_embed(
f'Unable to loop track: {err}'
))

return

# Try to enqueue the next playable track
delta = 1 if forward else -1
while True:
# Get next index
try:
next_i = self._queue_mgr.calc_next_index(forward=forward)
next_i = self._queue_mgr.calc_next_index(delta=delta)
except EndOfQueueError:
# We've reached the end of the queue and looping is disabled
return

# Get details of next track for logging
next_track = self._queue_mgr.queue[next_i]
next_title = next_track.title if next_track.title is not None else 'Unknown track'
next_artist = next_track.artist if next_track.artist is not None else 'Unknown artist'

# Try to enqueue the next track
if not await self._enqueue(next_i, auto=auto):
try:
await self._enqueue(next_i, auto=auto)
except JockeyError as err:
await self._edit_np_controls(show_controls=True)
delta += 1 if forward else -1

await self.status_channel.send(embed=CustomEmbed(
color=Colour.red(),
title=':warning:|Failed to skip to track',
description='It might be unavailable temporarily '
'or restricted to specific regions.\n',
fields=[
['Track', f'`{next_title}`\n{next_artist}'],
['Position in queue', f'{next_i + 1} of {self.queue_size}'],
['Error', f'```{err}```']
],
footer='Skipping to next track...' if auto else None
).get())
else:
return
break

async def update_now_playing(self):
"""
Expand Down
12 changes: 6 additions & 6 deletions cogs/player/queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def next_track(self) -> Tuple[int, QueueItem]:
raise EmptyQueueError

try:
i = self.calc_next_index(forward=True)
i = self.calc_next_index()
track = self.queue[i]
except EndOfQueueError as err:
raise EndOfQueueError('No next track in queue.') from err
Expand All @@ -174,20 +174,20 @@ def previous_track(self) -> Tuple[int, QueueItem]:
raise EmptyQueueError

try:
i = self.calc_next_index(forward=False)
i = self.calc_next_index(delta=-1)
track = self.queue[i]
except EndOfQueueError as err:
raise EndOfQueueError('No previous track in queue.') from err

return i, track

def calc_next_index(self, *, forward: bool) -> int:
def calc_next_index(self, *, delta: int = 1) -> int:
"""
Calculate the next track index, accounting for shuffling and
looping a single track.
Args:
forward: Whether to go forward or backward in the queue.
delta: How far ahead or back to seek the next index.
Returns:
The next track index in self._queue.
Expand All @@ -196,7 +196,7 @@ def calc_next_index(self, *, forward: bool) -> int:
EndOfQueueError: If one of the ends of the queue is reached,
and the queue is not looping all tracks.
"""
delta = 1 if forward else -1
forward = delta > 0

# Return the current index if the queue is looping a single track.
next_i = self.current_index
Expand Down Expand Up @@ -372,7 +372,7 @@ def remove(self, index: int, /) -> QueueItem:

# If we're removing the current track, adjust the current track index.
if adjusted_index == self.current_index:
self._i = self.calc_next_index(forward=True)
self._i = self.calc_next_index()

# Remove the element from self._queue.
return self.queue.pop(adjusted_index)
20 changes: 18 additions & 2 deletions utils/blanco.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from views.now_playing import NowPlayingView

from .embeds import create_error_embed
from .exceptions import EndOfQueueError
from .exceptions import EndOfQueueError, LavalinkSearchError
from .logger import create_logger
from .scrobbler import Scrobbler
from .spotify_client import Spotify
Expand All @@ -37,6 +37,21 @@
StatusChannel = Union[PartialMessageable, VoiceChannel, TextChannel, StageChannel, Thread]


# Match-ahead wrapper for finding a Lavalink track with exception handling
async def match_ahead(logger: 'Logger', *args, **kwargs):
"""
Wrapper for find_lavalink_track with exception handling.
"""
try:
return await find_lavalink_track(*args, **kwargs)
except LavalinkSearchError:
logger.warning('Failed to match track ahead')

# No need to do anything special, the user will see the causes
# when Blanco tries to play the track for real
return None


class BlancoBot(Bot):
"""
Custom bot class for Blanco.
Expand Down Expand Up @@ -276,7 +291,8 @@ async def on_track_start(self, event: 'TrackStartEvent[Jockey]'):
next_track.title
)
task = get_event_loop().create_task(
find_lavalink_track(
match_ahead(
self._logger,
node,
next_track,
deezer_enabled=deezer_enabled,
Expand Down

0 comments on commit d307415

Please sign in to comment.