From 7367eb2cc4dbb33e79f1404436414890abd955ba Mon Sep 17 00:00:00 2001 From: esefxdz Date: Sat, 11 Jul 2026 22:08:04 +0300 Subject: [PATCH] now playing feature added --- music/music_wrapper.py | 1 + music/player/commands.py | 6 +++++ music/player/events.py | 53 +++++++++++++++++++++++++++++++++++++--- music/shared/state.py | 3 ++- 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/music/music_wrapper.py b/music/music_wrapper.py index 1e78d2a..1084759 100644 --- a/music/music_wrapper.py +++ b/music/music_wrapper.py @@ -34,6 +34,7 @@ class Music(PlayerCommands, QueueCommands, MusicEvents, RadioCommands, RadioEven self._states: dict[int, GuildState] = {} self._voice_channels: dict[int, int] = {} # guild_id โ†’ voice channel id (for status cleanup) self._radio_channels: dict[int, int] = {} # guild_id โ†’ text channel id (for drop notifications) + self._music_channels: dict[int, int] = {} # guild_id โ†’ text channel id (for now-playing announcements) self._locks: dict[int, asyncio.Lock] = {} # Load radio stations diff --git a/music/player/commands.py b/music/player/commands.py index 2f68f9b..8852e07 100644 --- a/music/player/commands.py +++ b/music/player/commands.py @@ -55,10 +55,12 @@ class PlayerCommands: if not vc.playing and not vc.paused and st.queue: st.current = st.queue.popleft() + st.suppress_np = True await vc.play(st.current) await self._set_channel_status(vc, st.current) st.mode = MODE_MUSIC + self._music_channels[ctx.guild.id] = ctx.channel.id embed = discord.Embed( title="๐Ÿ“‹ Playlist Queued", @@ -74,6 +76,8 @@ class PlayerCommands: track = results[0] track.extras = {"requester_id": ctx.author.id} + self._music_channels[ctx.guild.id] = ctx.channel.id + if vc.playing or vc.paused: st.queue.append(track) embed = discord.Embed( @@ -89,6 +93,7 @@ class PlayerCommands: return await ctx.reply(embed=embed) st.current = track + st.suppress_np = True await vc.play(track) await self._set_channel_status(vc, track) st.mode = MODE_MUSIC @@ -159,6 +164,7 @@ class PlayerCommands: self._voice_channels.pop(ctx.guild.id, None) self._radio_channels.pop(ctx.guild.id, None) + self._music_channels.pop(ctx.guild.id, None) self._locks.pop(ctx.guild.id, None) msg = "โน๏ธ Stopped radio and disconnected." if current_mode == MODE_RADIO else "โน๏ธ Stopped and disconnected." diff --git a/music/player/events.py b/music/player/events.py index 6114d50..f5824c8 100644 --- a/music/player/events.py +++ b/music/player/events.py @@ -2,7 +2,8 @@ import wavelink import discord import logging from discord.ext import commands -from ..shared.constants import LOOP_TRACK, LOOP_QUEUE, MODE_RADIO +from ..shared.constants import LOOP_TRACK, LOOP_QUEUE, MODE_RADIO, MODE_MUSIC, C_MAIN +from ..shared.utils import fmt_duration log = logging.getLogger(__name__) @@ -78,6 +79,7 @@ class MusicEvents: st.queue.clear() self._locks.pop(guild_id, None) self._radio_channels.pop(guild_id, None) + self._music_channels.pop(guild_id, None) await self._clear_channel_status(guild_id) # โ”€โ”€ track end โ†’ advance queue โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -118,7 +120,7 @@ class MusicEvents: st.mode = None await self._set_channel_status(player) - # โ”€โ”€ track start โ†’ update voice status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + # โ”€โ”€ track start โ†’ update voice status & announce โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @commands.Cog.listener() async def on_wavelink_track_start(self, payload: wavelink.TrackStartEventPayload): player: wavelink.Player = payload.player @@ -126,6 +128,51 @@ class MusicEvents: return st = self.state(player.guild.id) st.current = payload.track - # Radio sets its own title; only update status for music mode + if st.mode != MODE_RADIO: await self._set_channel_status(player, st.current) + + # Send polished now-playing announcement to the music text channel + if not st.suppress_np: + await self._send_now_playing(player.guild.id, st.current) + st.suppress_np = False + + # โ”€โ”€ now-playing announcement helper โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + async def _send_now_playing(self, guild_id: int, track: wavelink.Playable): + """Send a compact, polished 'Now Playing' embed to the music channel.""" + channel_id = self._music_channels.get(guild_id) + if channel_id is None: + return + guild = self.bot.get_guild(guild_id) + if guild is None: + return + channel = guild.get_channel_or_thread(channel_id) + if channel is None: + self._music_channels.pop(guild_id, None) + return + + # Build a clean now-playing embed + embed = discord.Embed(color=C_MAIN) + desc_parts = [f"## ๐ŸŽต {track.title}"] + if track.author: + desc_parts.append(f"-# {track.author}") + embed.description = "\n".join(desc_parts) + + meta: list[str] = [] + if track.length: + meta.append(f"`{fmt_duration(track.length)}`") + if track.extras and isinstance(track.extras, dict): + requester_id = track.extras.get("requester_id") + if requester_id: + meta.append(f"Requested by <@{requester_id}>") + if meta: + embed.add_field(name="\u200b", value=" ยท ".join(meta), inline=False) + + if track.artwork: + embed.set_thumbnail(url=track.artwork) + embed.set_footer(text="yuuka music") + + try: + await channel.send(embed=embed) + except Exception: + log.warning(f"Failed to send now-playing message in guild {guild_id}", exc_info=True) diff --git a/music/shared/state.py b/music/shared/state.py index 470fd5b..ae6e8d6 100644 --- a/music/shared/state.py +++ b/music/shared/state.py @@ -10,7 +10,8 @@ class GuildState: self.loop: int = LOOP_OFF self.autoplay: bool = False self.current: wavelink.Playable | None = None - self.mode: str | None = None + self.mode: str | None = None + self.suppress_np: bool = False # skip auto now-playing embed (set by !play) def push_history(self, track: wavelink.Playable): self.history.insert(0, track)