chore: snapshot 2026-02-08

This commit is contained in:
joelilas
2026-02-08 17:04:57 +01:00
parent 2d19d17d8e
commit 2fd0f622f7
5 changed files with 83 additions and 10 deletions

View File

@@ -23,7 +23,7 @@ A discord music bot that seamlessly streams music from your personal music serve
| `/queue` | View the current queue | | `/queue` | View the current queue |
| `/clear` | Clear the current queue | | `/clear` | Clear the current queue |
| `/shuffle` | Shuffles the current queue | | `/shuffle` | Shuffles the current queue |
| `/skip` | Skip the current track | | `/skip [count]` | Skip the current track or `count` tracks |
| `/stop` | Stop playing the current track | | `/stop` | Stop playing the current track |
| `/autoplay` | Toggle autoplay | | `/autoplay` | Toggle autoplay |
| `/playlists` | List available playlists | | `/playlists` | List available playlists |

View File

@@ -76,8 +76,7 @@ class DiscodromeClient(commands.Bot):
async def on_ready(self) -> None: async def on_ready(self) -> None:
''' Event called when the client is done preparing. ''' ''' Event called when the client is done preparing. '''
activity = discord.Activity(type=discord.ActivityType.playing, name=env.BOT_STATUS) await self.set_default_presence()
await self.change_presence(activity=activity)
logger.info( logger.info(
"Logged as: %s | Connected Guilds: %s | Loaded Extensions: %s", "Logged as: %s | Connected Guilds: %s | Loaded Extensions: %s",
@@ -87,6 +86,22 @@ class DiscodromeClient(commands.Bot):
) )
logger.info("Bot status set to: '%s'", env.BOT_STATUS) logger.info("Bot status set to: '%s'", env.BOT_STATUS)
async def set_default_presence(self) -> None:
''' Sets the default bot presence (idle status). '''
activity = discord.Activity(type=discord.ActivityType.playing, name=env.BOT_STATUS)
await self.change_presence(activity=activity)
async def set_now_playing(self, song_title: str, artist: str, cover_url: str = None) -> None:
''' Sets the bot presence to show the currently playing song. '''
activity = discord.Activity(
type=discord.ActivityType.listening,
name=f"{song_title} - {artist}",
large_image=cover_url,
large_text=f"{song_title} - {artist}"
)
await self.change_presence(activity=activity)
logger.debug("Bot presence updated: Listening to '%s - %s'", song_title, artist)
if __name__ == "__main__": if __name__ == "__main__":
logs.setup_logging() logs.setup_logging()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -209,6 +209,12 @@ class MusicCog(commands.Cog):
# Add current song back to the queue if exists # Add current song back to the queue if exists
player.queue.insert(0, player.current_song) player.queue.insert(0, player.current_song)
# Reset bot presence to default
try:
await self.bot.set_default_presence()
except Exception as e:
logger.error(f"Failed to reset bot presence: {e}")
# Display disconnect confirmation # Display disconnect confirmation
await ui.SysMsg.stopping_queue_playback(interaction) await ui.SysMsg.stopping_queue_playback(interaction)
@@ -269,8 +275,9 @@ class MusicCog(commands.Cog):
await ui.ErrMsg.msg(ctx, f"An unknown error has occurred and has been logged to console. Please contact an administrator. {error}") await ui.ErrMsg.msg(ctx, f"An unknown error has occurred and has been logged to console. Please contact an administrator. {error}")
@app_commands.command(name="skip", description="Skip the current track") @app_commands.command(name="skip", description="Skip one or more tracks")
async def skip(self, interaction: discord.Interaction) -> None: @app_commands.describe(count="Optional number of tracks to skip (>= 1)")
async def skip(self, interaction: discord.Interaction, count: int=None) -> None:
''' Skip the current track ''' ''' Skip the current track '''
# Get the voice client instance # Get the voice client instance
@@ -281,12 +288,17 @@ class MusicCog(commands.Cog):
await ui.ErrMsg.bot_not_in_voice_channel(interaction) await ui.ErrMsg.bot_not_in_voice_channel(interaction)
return return
# Validate count if provided
if count is not None and count < 1:
await ui.ErrMsg.msg(interaction, "Skip count must be at least 1.")
return
# Check if the bot is playing music # Check if the bot is playing music
if not voice_client.is_playing(): if not voice_client.is_playing():
await ui.ErrMsg.not_playing(interaction) await ui.ErrMsg.not_playing(interaction)
return return
await data.guild_data(interaction.guild_id).player.skip_track(interaction, voice_client) await data.guild_data(interaction.guild_id).player.skip_track(interaction, voice_client, count=count)
@skip.error @skip.error
async def skip_error(self, ctx, error): async def skip_error(self, ctx, error):
@@ -463,6 +475,11 @@ class MusicCog(commands.Cog):
player = data.guild_data(member.guild.id).player player = data.guild_data(member.guild.id).player
player.queue.clear() player.queue.clear()
player.current_song = None player.current_song = None
# Reset bot presence to default
try:
await self.bot.set_default_presence()
except Exception as e:
logger.error(f"Failed to reset bot presence: {e}")
logger.info("The bot has disconnected and cleared the queue as there are no users in the voice channel.") logger.info("The bot has disconnected and cleared the queue as there are no users in the voice channel.")
else: else:
logger.debug("Bot is no longer alone in voice channel, aborting disconnect...") logger.debug("Bot is no longer alone in voice channel, aborting disconnect...")

View File

@@ -8,7 +8,7 @@ import data
import ui import ui
import logging import logging
from subsonic import Song, APIError, get_random_songs, get_similar_songs, stream, scrobble from subsonic import Song, APIError, get_random_songs, get_similar_songs, stream, scrobble, get_album_art_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -263,6 +263,12 @@ class Player():
song = self.queue.pop(0) song = self.queue.pop(0)
self.current_song = song self.current_song = song
await ui.SysMsg.now_playing(interaction, song) await ui.SysMsg.now_playing(interaction, song)
# Update bot presence with now playing info
try:
cover_url = get_album_art_url(song.cover_id)
await interaction.client.set_now_playing(song.title, song.artist, cover_url)
except Exception as e:
logger.error(f"Failed to update bot presence: {e}")
await self.stream_track(interaction, song, voice_client) await self.stream_track(interaction, song, voice_client)
else: else:
logger.debug("Queue is empty.") logger.debug("Queue is empty.")
@@ -278,10 +284,15 @@ class Player():
return return
# If the queue is empty, playback has ended; we should let the user know # If the queue is empty, playback has ended; we should let the user know
await ui.SysMsg.playback_ended(interaction) await ui.SysMsg.playback_ended(interaction)
# Reset bot presence to default
try:
await interaction.client.set_default_presence()
except Exception as e:
logger.error(f"Failed to reset bot presence: {e}")
async def skip_track(self, interaction: discord.Interaction, voice_client: discord.VoiceClient) -> None: async def skip_track(self, interaction: discord.Interaction, voice_client: discord.VoiceClient, count: int=None) -> None:
''' Skips the current track and plays the next one in the queue ''' ''' Skips the current track and optionally additional tracks from the queue '''
# Check if the bot is connected to a voice channel; it's the caller's responsibility to open a voice channel # Check if the bot is connected to a voice channel; it's the caller's responsibility to open a voice channel
if voice_client is None: if voice_client is None:
@@ -290,8 +301,25 @@ class Player():
logger.debug("Skipping track...") logger.debug("Skipping track...")
# Check if the bot is already playing something # Check if the bot is already playing something
if voice_client.is_playing(): if voice_client.is_playing():
# Determine how many tracks to skip in total (current + additional)
total_skips = 1 if count is None or count < 1 else count
# Stop current playback (this counts as 1 skip)
voice_client.stop() voice_client.stop()
await ui.SysMsg.skipping(interaction)
# Remove additional tracks from the front of the queue, if any
additional_to_skip = max(0, total_skips - 1)
if additional_to_skip > 0 and self.queue:
removed = min(additional_to_skip, len(self.queue))
# Slice off the skipped items
self.queue = self.queue[removed:]
logger.debug(f"Skipped {removed} additional track(s) from queue")
# Notify user
if total_skips <= 1:
await ui.SysMsg.skipping(interaction)
else:
await ui.SysMsg.msg(interaction, f"Skipped {total_skips} track{'s' if total_skips != 1 else ''}", ephemeral=True)
else: else:
await ui.ErrMsg.not_playing(interaction) await ui.ErrMsg.not_playing(interaction)

View File

@@ -449,6 +449,19 @@ async def get_artist_discography(query: str) -> Album:
return album_list return album_list
def get_album_art_url(cover_id: str, size: int=300) -> str:
''' Get the URL for album art from the subsonic API '''
if not cover_id:
return None
params = {
**SUBSONIC_REQUEST_PARAMS,
"id": cover_id,
"size": str(size)
}
query_string = "&".join(f"{k}={v}" for k, v in params.items())
return f"{env.SUBSONIC_SERVER}/rest/getCoverArt?{query_string}"
async def get_album_art_file(cover_id: str, size: int=300) -> str: async def get_album_art_file(cover_id: str, size: int=300) -> str:
''' Request album art from the subsonic API ''' ''' Request album art from the subsonic API '''
target_path = f"cache/{cover_id}.jpg" target_path = f"cache/{cover_id}.jpg"