Skip to content

Commit

Permalink
feat: add remove command; queue excludes current
Browse files Browse the repository at this point in the history
  • Loading branch information
zlalvani committed Jan 26, 2025
1 parent e8dd730 commit ce22d12
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 38 deletions.
96 changes: 73 additions & 23 deletions discord_sound_streamer/commands/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from discord_sound_streamer.services import play as play_service
from discord_sound_streamer.services import interaction as interactions_service
from lavalink import LoadType
import re

component = tanjun.Component()

Expand Down Expand Up @@ -34,38 +35,74 @@ async def play(ctx: tanjun.abc.Context, name: str) -> None:
await embed_service.reply_message(ctx, f"No results found for {name}...")


# This may have race conditions
@component.with_slash_command
@tanjun.with_int_slash_option(
"selection", "the number of your selection", default=1, min_value=1
@tanjun.with_str_slash_option(
"range", "a single queued item or a range of items (e.g. 1, 1:5, 1:, or :5)"
)
@tanjun.as_slash_command("skip", "skip the current song")
async def skip(ctx: tanjun.abc.Context, selection: int) -> None:
@tanjun.as_slash_command("remove", "remove a song from the queue")
async def remove(ctx: tanjun.abc.Context, range: str) -> None:
if ctx.guild_id:
player = play_service.get_player(ctx.guild_id)

adjusted_selection = selection - 1
# regex to match a single number or a range of numbers
range_pattern = re.compile(r"^(?P<start>\d*):(?P<end>\d*)$|^(?P<single>\d+)$")
match = range_pattern.match(range)

if adjusted_selection == 0 and player.current:
await embed_service.reply_message(
ctx, f"Skipping {player.current.title}..."
)
await player.skip()
elif len(player.queue) > 0:
try:
# If a song is currently playing, the queue will be one shorter
if player.current:
adjusted_selection -= 1
if not match:
await embed_service.reply_message(ctx, "Invalid range")
return

match_dict = match.groupdict()

if match_dict.get("single"):
selection = int(match_dict["single"])
index = selection - 1

try:
removed = player.queue.pop(index)
await embed_service.reply_message(
ctx, f"Skipping {player.queue[adjusted_selection].title}..."
ctx, f"Item {removed.title} removed from queue"
)
player.queue.pop(adjusted_selection)
return
except IndexError:
await embed_service.reply_message(
ctx, f"Selection {selection} not in queue"
)
return

start, end = match_dict.get("start"), match_dict.get("end")

adjusted_start = 0
adjusted_end = len(player.queue)

if start:
adjusted_start = int(start) - 1
if end: # switch from exclusive to inclusive of the end index
adjusted_end = int(end)

original_length = len(player.queue)
player.queue = player.queue[:adjusted_start] + player.queue[adjusted_end:]
new_length = len(player.queue)

await embed_service.reply_message(
ctx, f"{original_length - new_length} items removed from queue"
)


@component.with_slash_command
@tanjun.as_slash_command("skip", "skip the current song")
async def skip(ctx: tanjun.abc.Context) -> None:
if ctx.guild_id:
player = play_service.get_player(ctx.guild_id)

if player.current:
await embed_service.reply_message(
ctx, f"Skipping {player.current.title}..."
)
await player.skip()
else:
await embed_service.reply_message(ctx, "Queue empty")
await embed_service.reply_message(ctx, "No song playing")


@component.with_slash_command
Expand All @@ -85,13 +122,26 @@ async def unpause(ctx: tanjun.abc.Context) -> None:
async def queue(ctx: tanjun.abc.Context) -> None:
if ctx.guild_id:
player = play_service.get_player(ctx.guild_id)
queued = [*([player.current] if player.current else []), *player.queue]
await ctx.respond(
embed=embed_service.build_queue_embed(
player.position,
queued,
embeds=[
*(
[
embed_service.build_track_embed(
player.current, current_position=player.position
)
]
if player.current
else []
),
embed_service.build_queue_embed(
player.queue,
current_track=player.current,
current_track_position=player.position if player.current else None,
),
],
components=interactions_service.build_queue_paging_interaction(
player.queue
),
components=interactions_service.build_queue_paging_interaction(queued),
)


Expand Down
17 changes: 13 additions & 4 deletions discord_sound_streamer/services/embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ def build_track_embed(


def build_queue_embed(
current_position: int, tracks: List[AudioTrack], current_page=0, page_size=8
tracks: List[AudioTrack],
current_page=0,
page_size=8,
current_track: AudioTrack | None = None,
current_track_position: int | None = None,
) -> Embed:
total_pages = math.ceil(len(tracks) / page_size)
current_page = min(max(current_page, 0), total_pages - 1)
Expand All @@ -112,9 +116,14 @@ def build_queue_embed(
embed.add_field(
name="...", value=f"{len(tracks) - 8} more items", inline=False
)
embed.set_footer(
text=f"Total queue time remaining: {_format_timedelta(sum(t.duration for t in tracks) - current_position)}"
)
if current_track:
embed.set_footer(
text=f"Time remaining: {_format_timedelta(sum(t.duration for t in tracks) + current_track.duration - (current_track_position or 0) )}"
)
else:
embed.set_footer(
text=f"Total queue time: {_format_timedelta(sum(t.duration for t in tracks))}"
)
else:
embed.description = "Queue empty"
return embed
Expand Down
26 changes: 18 additions & 8 deletions discord_sound_streamer/services/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,16 +220,26 @@ async def _refresh_queue(

player = play_service.get_player(interaction.guild_id)

queued = [*([player.current] if player.current else []), *player.queue]

await interaction.create_initial_response(
ResponseType.MESSAGE_UPDATE,
embed=embed_service.build_queue_embed(
player.position,
queued,
current_page=current_page,
),
components=build_queue_paging_interaction(queued, current_page),
embeds=[
*(
[
embed_service.build_track_embed(
player.current, current_position=player.position
)
]
if player.current
else []
),
embed_service.build_queue_embed(
player.queue,
current_page=current_page,
current_track=player.current,
current_track_position=player.position if player.current else None,
),
],
components=build_queue_paging_interaction(player.queue, current_page),
)


Expand Down
6 changes: 3 additions & 3 deletions discord_sound_streamer/services/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def get_player(guild_id: Snowflake) -> DefaultPlayer:
return player


async def get_queue(guild_id: Snowflake) -> list[AudioTrack]:
def get_queue(guild_id: Snowflake) -> list[AudioTrack]:
player = get_player(guild_id)

return player.queue
Expand Down Expand Up @@ -55,7 +55,7 @@ async def _play_tracks(
tracks: list[AudioTrack],
playlist_info: PlaylistInfo | None = None,
) -> None:
queue = await get_queue(guild.id)
queue = get_queue(guild.id)
player = get_player(guild.id)

user_voice_state = guild.get_voice_state(author_id)
Expand Down Expand Up @@ -112,7 +112,7 @@ async def pause_control(ctx: tanjun.abc.Context, pause: bool) -> None:
if ctx.guild_id:
player = get_player(ctx.guild_id)

if queue := await get_queue(ctx.guild_id):
if queue := get_queue(ctx.guild_id):
await embed_service.reply_message(
ctx, f'{"Pausing" if pause else "Resuming"} {queue[0].title}...'
)
Expand Down

0 comments on commit ce22d12

Please sign in to comment.