diff --git a/cogs/music.py b/cogs/music.py index d182703..8946706 100644 --- a/cogs/music.py +++ b/cogs/music.py @@ -199,6 +199,46 @@ async def on_submit(self, interaction: discord.Interaction): ) +class VolumeSelect(discord.ui.Select): + def __init__(self, cog: "MusicCog", player: lavalink.DefaultPlayer): + self.cog = cog + self.player = player + current = player.volume + + options = [] + for vol in range(0, 101, 10): + emoji = {0: "πŸ”‡", 10: "πŸ”ˆ", 20: "πŸ”ˆ", 30: "πŸ”‰", 40: "πŸ”‰", + 50: "πŸ”‰", 60: "πŸ”Š", 70: "πŸ”Š", 80: "πŸ”Š", 90: "πŸ”Š", 100: "πŸ”Š"}.get(vol, "πŸ”Š") + label = f"{vol}%" + desc = "Mute" if vol == 0 else "MΓ‘ximo" if vol == 100 else "" + options.append( + discord.SelectOption( + label=label, value=str(vol), emoji=emoji, description=desc, default=(vol == current) + ) + ) + + super().__init__(placeholder="Selecciona un volumen...", min_values=1, max_values=1, options=options) + + async def callback(self, interaction: discord.Interaction): + vol = int(self.values[0]) + await self.player.set_volume(vol) + print(f"[VOLUME] guild={interaction.guild.id} volumen={vol}%") + + for opt in self.options: + opt.default = (opt.value == str(vol)) + embed = discord.Embed( + description=f"πŸ”Š Volumen: **{vol}%**", + color=0x2B2D31, + ) + await interaction.response.edit_message(embed=embed, view=self.view) + + +class VolumeSelectView(discord.ui.View): + def __init__(self, cog: "MusicCog", player: lavalink.DefaultPlayer): + super().__init__(timeout=60) + self.add_item(VolumeSelect(cog, player)) + + class NowPlayingView(discord.ui.View): def __init__(self, cog: "MusicCog", player: lavalink.DefaultPlayer, interaction: discord.Interaction | None = None): super().__init__(timeout=None) @@ -212,6 +252,10 @@ def __init__(self, cog: "MusicCog", player: lavalink.DefaultPlayer, interaction: style=discord.ButtonStyle.url, url=track.uri, label="Abrir", emoji="πŸ”—", row=1, )) + for child in self.children: + if isinstance(child, discord.ui.Button) and "np_volume" in (child.custom_id or ""): + child.label = f"{player.volume}%" + break async def _check_control(self, interaction: discord.Interaction) -> bool: if not interaction.user.voice or interaction.user.voice.channel.id != self.player.channel_id: @@ -246,6 +290,9 @@ async def _refresh_view(self, interaction: discord.Interaction): elif "np_sort" in cid: child.disabled = len(self.player.queue) < 2 continue + elif "np_volume" in cid: + child.label = f"{self.player.volume}%" + continue elif child.style == discord.ButtonStyle.url: track = self.player.current if track and track.uri: @@ -388,6 +435,23 @@ async def sort_queue(self, interaction: discord.Interaction, button: discord.ui. view = MoveQueueView(self.cog, self.player) await interaction.response.send_message(embed=embed, view=view, ephemeral=True) + @discord.ui.button(emoji="πŸ”Š", label="Volumen", style=discord.ButtonStyle.secondary, row=2, custom_id="np_volume") + async def volume_btn(self, interaction: discord.Interaction, button: discord.ui.Button): + if not await self._check_control(interaction): + return + embed = discord.Embed( + title="πŸ”Š Selecciona un volumen", + description=( + "Elige un nivel con el menΓΊ desplegable.\n" + "⏱️ Este menΓΊ expira en **60 segundos** β€” si se cierra, " + "solo toca πŸ”Š para abrir uno nuevo.\n\n" + "TambiΓ©n puedes usar `/volume 0-100`." + ), + color=0x2B2D31, + ) + view = VolumeSelectView(self.cog, self.player) + await interaction.response.send_message(embed=embed, view=view, ephemeral=True) + async def _cleanup(self): if self.message is None: return @@ -622,6 +686,10 @@ async def _build_nowplaying_embed( percent = min((position_ms / max(track.duration, 1)) * 100, 100.0) embed.add_field(name="Avance", value=f"{percent:.1f}%", inline=True) + vol = player.volume + vol_bar = "β–ˆ" * (vol // 10) + "β–‘" * (10 - vol // 10) + embed.add_field(name="Volumen", value=f"{vol}% {vol_bar}", inline=True) + return embed async def _build_nowplaying_embed_auto( @@ -696,6 +764,10 @@ async def _build_nowplaying_embed_auto( percent = min((position_ms / max(track.duration, 1)) * 100, 100.0) embed.add_field(name="Avance", value=f"{percent:.1f}%", inline=True) + vol = player.volume + vol_bar = "β–ˆ" * (vol // 10) + "β–‘" * (10 - vol // 10) + embed.add_field(name="Volumen", value=f"{vol}% {vol_bar}", inline=True) + return embed async def _send_embed(self, interaction: discord.Interaction, embed: discord.Embed): @@ -1697,6 +1769,38 @@ async def shuffle(self, interaction: discord.Interaction): print(f"[SHUFFLE] βœ— Error: {e}") await self._send_error(interaction, f"Error: {e}") + @app_commands.command( + name="volume", description="Ajusta el volumen (0-100%)", + ) + @app_commands.describe(vol="Nivel de volumen de 0 a 100") + async def volume(self, interaction: discord.Interaction, vol: int | None = None): + """Ver o cambiar el volumen actual (0-100).""" + try: + player, error_message = self._require_control_player(interaction) + if player is None: + return await self._send_error(interaction, error_message) + + if vol is None: + return await self._send_embed( + interaction, + self._build_embed( + interaction, + "πŸ”Š Volumen actual", + f"El volumen estΓ‘ al **{player.volume}%**.", + color=BOT_PRIMARY, + ), + ) + + if not 0 <= vol <= 100: + return await self._send_error(interaction, "El volumen debe estar entre 0 y 100.") + + await player.set_volume(vol) + print(f"[VOLUME] guild={interaction.guild.id} volumen={vol}%") + await interaction.response.defer() + except Exception as e: + print(f"[VOLUME] βœ— Error: {e}") + await self._send_error(interaction, f"Error: {e}") + @app_commands.command( name="play", description="Reproduce una canciΓ³n (nombre o URL)" )