mirror of
https://github.com/deadcxap/YandexMusicDiscordBot.git
synced 2026-01-10 09:41:46 +03:00
feat: Add to playlist and dislike button.
This commit is contained in:
@@ -19,7 +19,7 @@ def setup(bot):
|
|||||||
bot.add_cog(General(bot))
|
bot.add_cog(General(bot))
|
||||||
|
|
||||||
async def get_search_suggestions(ctx: discord.AutocompleteContext) -> list[str]:
|
async def get_search_suggestions(ctx: discord.AutocompleteContext) -> list[str]:
|
||||||
if not ctx.interaction.user or not ctx.value:
|
if not ctx.interaction.user or not ctx.value or len(ctx.value) < 2:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
users_db = BaseUsersDatabase()
|
users_db = BaseUsersDatabase()
|
||||||
@@ -93,10 +93,11 @@ class General(Cog):
|
|||||||
|
|
||||||
if command == 'all':
|
if command == 'all':
|
||||||
embed.description = (
|
embed.description = (
|
||||||
"Этот бот позволяет слушать музыку из вашего аккаунта Yandex Music.\n"
|
"Этот бот позволяет слушать музыку из вашего аккаунта Яндекс Музыки.\n"
|
||||||
"Зарегистрируйте свой токен с помощью /login. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
|
"Зарегистрируйте свой токен с помощью /login. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
|
||||||
"Для получения помощи по конкретной команде, введите /help <команда>.\n"
|
"Для получения помощи по конкретной команде, введите /help <команда>.\n"
|
||||||
"Для изменения настроек необходимо иметь права управления каналами на сервере.\n\n"
|
"Для изменения настроек необходимо иметь права управления каналами на сервере.\n\n"
|
||||||
|
"Помните, что это **не замена Яндекс Музыки**, а лишь её дополнение. Не ожидайте безупречного звука.\n\n"
|
||||||
"**Для дополнительной помощи, присоединяйтесь к [серверу любителей Яндекс Музыки](https://discord.gg/gkmFDaPMeC).**"
|
"**Для дополнительной помощи, присоединяйтесь к [серверу любителей Яндекс Музыки](https://discord.gg/gkmFDaPMeC).**"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import aiofiles
|
||||||
import logging
|
import logging
|
||||||
|
import io
|
||||||
from typing import Any, Literal, cast
|
from typing import Any, Literal, cast
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
@@ -228,6 +230,16 @@ class VoiceExtension:
|
|||||||
logging.debug(f"[VIBE] Radio started feedback: {feedback}")
|
logging.debug(f"[VIBE] Radio started feedback: {feedback}")
|
||||||
tracks = await client.rotor_station_tracks(f"{type}:{id}")
|
tracks = await client.rotor_station_tracks(f"{type}:{id}")
|
||||||
self.db.update(gid, {'vibing': True})
|
self.db.update(gid, {'vibing': True})
|
||||||
|
|
||||||
|
if update_settings:
|
||||||
|
settings = user['vibe_settings']
|
||||||
|
await client.rotor_station_settings2(
|
||||||
|
f"{type}:{id}",
|
||||||
|
mood_energy=settings['mood'],
|
||||||
|
diversity=settings['diversity'],
|
||||||
|
language=settings['lang']
|
||||||
|
)
|
||||||
|
|
||||||
elif guild['current_track']:
|
elif guild['current_track']:
|
||||||
if update_settings:
|
if update_settings:
|
||||||
settings = user['vibe_settings']
|
settings = user['vibe_settings']
|
||||||
@@ -404,7 +416,12 @@ class VoiceExtension:
|
|||||||
await channel.send(f"😔 Не удалось загрузить трек. Попробуйте сбросить меню.", delete_after=15)
|
await channel.send(f"😔 Не удалось загрузить трек. Попробуйте сбросить меню.", delete_after=15)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
song = discord.FFmpegPCMAudio(f'music/{gid}.mp3', options='-vn -filter:a "volume=0.15"')
|
async with aiofiles.open(f'music/{gid}.mp3', "rb") as f:
|
||||||
|
track_bytes = io.BytesIO(await f.read())
|
||||||
|
song = discord.FFmpegPCMAudio(track_bytes, pipe=True, options='-vn -filter:a "volume=0.15"')
|
||||||
|
if not guild['current_menu']:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
vc.play(song, after=lambda exc: asyncio.run_coroutine_threadsafe(self.next_track(ctx, after=True), loop))
|
vc.play(song, after=lambda exc: asyncio.run_coroutine_threadsafe(self.next_track(ctx, after=True), loop))
|
||||||
logging.info(f"[VC_EXT] Playing track '{track.title}'")
|
logging.info(f"[VC_EXT] Playing track '{track.title}'")
|
||||||
|
|
||||||
@@ -422,13 +439,25 @@ class VoiceExtension:
|
|||||||
|
|
||||||
return track.title
|
return track.title
|
||||||
|
|
||||||
async def stop_playing(self, ctx: ApplicationContext | Interaction | RawReactionActionEvent, vc: discord.VoiceClient | None = None) -> None:
|
async def stop_playing(
|
||||||
|
self, ctx: ApplicationContext | Interaction | RawReactionActionEvent,
|
||||||
|
*,
|
||||||
|
vc: discord.VoiceClient | None = None,
|
||||||
|
full: bool = False
|
||||||
|
) -> None:
|
||||||
|
|
||||||
gid = ctx.guild_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.guild.id if ctx.guild else None
|
gid = ctx.guild_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.guild.id if ctx.guild else None
|
||||||
if not gid:
|
uid = ctx.user_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.user.id if ctx.user else None
|
||||||
|
|
||||||
|
if not gid or not uid:
|
||||||
logging.warning("[VC_EXT] Guild ID not found in context")
|
logging.warning("[VC_EXT] Guild ID not found in context")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
guild = self.db.get_guild(gid)
|
||||||
|
|
||||||
|
if gid in menu_views:
|
||||||
|
menu_views[gid].stop()
|
||||||
|
del menu_views[gid]
|
||||||
if not vc:
|
if not vc:
|
||||||
vc = await self.get_voice_client(ctx)
|
vc = await self.get_voice_client(ctx)
|
||||||
if vc:
|
if vc:
|
||||||
@@ -436,6 +465,49 @@ class VoiceExtension:
|
|||||||
self.db.update(gid, {'current_track': None, 'is_stopped': True})
|
self.db.update(gid, {'current_track': None, 'is_stopped': True})
|
||||||
vc.stop()
|
vc.stop()
|
||||||
|
|
||||||
|
if full:
|
||||||
|
if guild['current_menu']:
|
||||||
|
menu = await self.get_menu_message(ctx, guild['current_menu'])
|
||||||
|
if menu:
|
||||||
|
await menu.delete()
|
||||||
|
|
||||||
|
self.db.update(gid, {
|
||||||
|
'current_menu': None, 'repeat': False, 'shuffle': False, 'previous_tracks': [], 'next_tracks': [], 'vibing': False
|
||||||
|
})
|
||||||
|
logging.info(f"[VOICE] Playback stopped in guild {gid}")
|
||||||
|
|
||||||
|
if guild['vibing']:
|
||||||
|
user = self.users_db.get_user(uid)
|
||||||
|
token = user['ym_token']
|
||||||
|
if not token:
|
||||||
|
logging.info(f"[VOICE] User {uid} has no YM token")
|
||||||
|
if not isinstance(ctx, RawReactionActionEvent):
|
||||||
|
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
client = await self.init_ym_client(ctx, user['ym_token'])
|
||||||
|
if not client:
|
||||||
|
logging.info(f"[VOICE] Failed to init YM client for user {uid}")
|
||||||
|
if not isinstance(ctx, RawReactionActionEvent):
|
||||||
|
await ctx.respond("❌ Что-то пошло не так. Попробуйте позже.", delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
track = guild['current_track']
|
||||||
|
if not track:
|
||||||
|
logging.info(f"[VOICE] No current track in guild {gid}")
|
||||||
|
if not isinstance(ctx, RawReactionActionEvent):
|
||||||
|
await ctx.respond("❌ Что-то пошло не так. Попробуйте позже.", delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
res = await client.rotor_station_feedback_track_finished(
|
||||||
|
f"{user['vibe_type']}:{user['vibe_id']}",
|
||||||
|
track['id'],
|
||||||
|
track['duration_ms'] // 1000,
|
||||||
|
cast(str, user['vibe_batch_id']),
|
||||||
|
time()
|
||||||
|
)
|
||||||
|
logging.info(f"[VOICE] User {uid} finished vibing with result: {res}")
|
||||||
|
|
||||||
async def next_track(
|
async def next_track(
|
||||||
self,
|
self,
|
||||||
ctx: ApplicationContext | Interaction | RawReactionActionEvent,
|
ctx: ApplicationContext | Interaction | RawReactionActionEvent,
|
||||||
@@ -543,7 +615,7 @@ class VoiceExtension:
|
|||||||
next_track,
|
next_track,
|
||||||
client=client # type: ignore # Async client can be used here.
|
client=client # type: ignore # Async client can be used here.
|
||||||
)
|
)
|
||||||
await self.stop_playing(ctx, vc)
|
await self.stop_playing(ctx, vc=vc)
|
||||||
title = await self.play_track(
|
title = await self.play_track(
|
||||||
ctx,
|
ctx,
|
||||||
ym_track, # type: ignore # de_json should always work here.
|
ym_track, # type: ignore # de_json should always work here.
|
||||||
@@ -696,6 +768,34 @@ class VoiceExtension:
|
|||||||
await client.users_likes_tracks_remove(ym_track.id, client.me.account.uid)
|
await client.users_likes_tracks_remove(ym_track.id, client.me.account.uid)
|
||||||
return 'TRACK REMOVED'
|
return 'TRACK REMOVED'
|
||||||
|
|
||||||
|
async def dislike_track(self, ctx: ApplicationContext | Interaction) -> bool:
|
||||||
|
"""Dislike current track. Return track title on success.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ctx (ApplicationContext | Interaction): Context.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: Track title or None.
|
||||||
|
"""
|
||||||
|
if not ctx.guild or not ctx.user:
|
||||||
|
logging.warning("[VC_EXT] Guild or User not found in context inside 'dislike_track'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
current_track = self.db.get_track(ctx.guild.id, 'current')
|
||||||
|
if not current_track:
|
||||||
|
logging.debug("[VC_EXT] Current track not found in 'dislike_track'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
client = await self.init_ym_client(ctx)
|
||||||
|
if not client:
|
||||||
|
return False
|
||||||
|
|
||||||
|
res = await client.users_dislikes_tracks_add(
|
||||||
|
current_track['id'],
|
||||||
|
client.me.account.uid # type: ignore
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
async def _retry_update_menu_embed(
|
async def _retry_update_menu_embed(
|
||||||
self,
|
self,
|
||||||
ctx: ApplicationContext | Interaction,
|
ctx: ApplicationContext | Interaction,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
from time import time
|
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@@ -241,11 +240,8 @@ class Voice(Cog, VoiceExtension):
|
|||||||
await ctx.respond("❌ У вас нет прав для выполнения этой команды.", delete_after=15, ephemeral=True)
|
await ctx.respond("❌ У вас нет прав для выполнения этой команды.", delete_after=15, ephemeral=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
vc = await self.get_voice_client(ctx)
|
if await self.voice_check(ctx):
|
||||||
if await self.voice_check(ctx) and vc:
|
await self.stop_playing(ctx, full=True)
|
||||||
self.db.update(ctx.guild.id, {'previous_tracks': [], 'next_tracks': [], 'current_track': None, 'is_stopped': True})
|
|
||||||
vc.stop()
|
|
||||||
await vc.disconnect(force=True)
|
|
||||||
await ctx.respond("Отключение успешно!", delete_after=15, ephemeral=True)
|
await ctx.respond("Отключение успешно!", delete_after=15, ephemeral=True)
|
||||||
logging.info(f"[VOICE] Successfully disconnected from voice channel in guild {ctx.guild.id}")
|
logging.info(f"[VOICE] Successfully disconnected from voice channel in guild {ctx.guild.id}")
|
||||||
|
|
||||||
@@ -338,51 +334,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
await ctx.respond("❌ Вы не можете остановить воспроизведение, пока в канале находятся другие пользователи.", delete_after=15, ephemeral=True)
|
await ctx.respond("❌ Вы не можете остановить воспроизведение, пока в канале находятся другие пользователи.", delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
elif await self.voice_check(ctx):
|
elif await self.voice_check(ctx):
|
||||||
guild = self.db.get_guild(ctx.guild.id)
|
await self.stop_playing(ctx, full=True)
|
||||||
await self.stop_playing(ctx)
|
|
||||||
|
|
||||||
if guild['current_menu']:
|
|
||||||
menu = await self.get_menu_message(ctx, guild['current_menu'])
|
|
||||||
if menu:
|
|
||||||
await menu.delete()
|
|
||||||
|
|
||||||
self.db.update(ctx.guild.id, {
|
|
||||||
'current_menu': None, 'repeat': False, 'shuffle': False, 'previous_tracks': [], 'next_tracks': [], 'vibing': False
|
|
||||||
})
|
|
||||||
logging.info(f"[VOICE] Playback stopped in guild {ctx.guild.id}")
|
|
||||||
|
|
||||||
if guild['vibing']:
|
|
||||||
user = self.users_db.get_user(ctx.user.id)
|
|
||||||
token = user['ym_token']
|
|
||||||
if not token:
|
|
||||||
logging.info(f"[VOICE] User {ctx.user.id} has no YM token")
|
|
||||||
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
client = await self.init_ym_client(ctx, user['ym_token'])
|
|
||||||
if not client:
|
|
||||||
logging.info(f"[VOICE] Failed to init YM client for user {ctx.user.id}")
|
|
||||||
await ctx.respond("❌ Что-то пошло не так. Попробуйте позже.", delete_after=15, ephemeral=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
track = guild['current_track']
|
|
||||||
if not track:
|
|
||||||
logging.info(f"[VOICE] No current track in guild {ctx.guild.id}")
|
|
||||||
await ctx.respond("❌ Что-то пошло не так. Попробуйте позже.", delete_after=15, ephemeral=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
res = await client.rotor_station_feedback_track_finished(
|
|
||||||
f"{user['vibe_type']}:{user['vibe_id']}",
|
|
||||||
track['id'],
|
|
||||||
track['duration_ms'] // 1000,
|
|
||||||
cast(str, user['vibe_batch_id']),
|
|
||||||
time()
|
|
||||||
)
|
|
||||||
logging.info(f"[VOICE] User {ctx.user.id} finished vibing with result: {res}")
|
|
||||||
|
|
||||||
if ctx.guild.id in menu_views:
|
|
||||||
menu_views[ctx.guild.id].stop()
|
|
||||||
del menu_views[ctx.guild.id]
|
|
||||||
|
|
||||||
await ctx.respond("Воспроизведение остановлено.", delete_after=15, ephemeral=True)
|
await ctx.respond("Воспроизведение остановлено.", delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
@@ -453,7 +405,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
logging.info(f"[VOICE] Track added to favorites for user {ctx.author.id} in guild {ctx.guild.id}")
|
logging.info(f"[VOICE] Track added to favorites for user {ctx.author.id} in guild {ctx.guild.id}")
|
||||||
await ctx.respond(f"Трек **{result}** был добавлен в избранное.", delete_after=15, ephemeral=True)
|
await ctx.respond(f"Трек **{result}** был добавлен в избранное.", delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
@track.command(name='vibe', description="Запустить мою волну по текущему треку.")
|
@track.command(name='vibe', description="Запустить Мою Волну по текущему треку.")
|
||||||
async def track_vibe(self, ctx: discord.ApplicationContext) -> None:
|
async def track_vibe(self, ctx: discord.ApplicationContext) -> None:
|
||||||
logging.info(f"[VOICE] Vibe (track) command invoked by user {ctx.author.id} in guild {ctx.guild.id}")
|
logging.info(f"[VOICE] Vibe (track) command invoked by user {ctx.author.id} in guild {ctx.guild.id}")
|
||||||
if not await self.voice_check(ctx):
|
if not await self.voice_check(ctx):
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class VoiceGuildsDatabase(BaseGuildsDatabase):
|
|||||||
tracks = self.get_tracks_list(gid, 'next')
|
tracks = self.get_tracks_list(gid, 'next')
|
||||||
if not tracks:
|
if not tracks:
|
||||||
return None
|
return None
|
||||||
track = tracks.pop()
|
track = tracks.pop(randint(0, len(tracks)))
|
||||||
self.update(gid, {'next_tracks': tracks})
|
self.update(gid, {'next_tracks': tracks})
|
||||||
return track
|
return track
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from discord.ui import View, Button, Item, Select
|
|||||||
from discord import VoiceChannel, ButtonStyle, Interaction, ApplicationContext, RawReactionActionEvent, Embed, ComponentType, SelectOption
|
from discord import VoiceChannel, ButtonStyle, Interaction, ApplicationContext, RawReactionActionEvent, Embed, ComponentType, SelectOption
|
||||||
|
|
||||||
import yandex_music.exceptions
|
import yandex_music.exceptions
|
||||||
from yandex_music import Track, ClientAsync
|
from yandex_music import Track, Playlist, ClientAsync as YMClient
|
||||||
from MusicBot.cogs.utils.voice_extension import VoiceExtension, menu_views
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension, menu_views
|
||||||
|
|
||||||
class ToggleRepeatButton(Button, VoiceExtension):
|
class ToggleRepeatButton(Button, VoiceExtension):
|
||||||
@@ -119,6 +119,27 @@ class LikeButton(Button, VoiceExtension):
|
|||||||
menu_views[gid] = await MenuView(interaction).init()
|
menu_views[gid] = await MenuView(interaction).init()
|
||||||
await interaction.edit(view=menu_views[gid])
|
await interaction.edit(view=menu_views[gid])
|
||||||
|
|
||||||
|
class DislikeButton(Button, VoiceExtension):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
Button.__init__(self, **kwargs)
|
||||||
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
|
logging.info('[MENU] Dislike button callback...')
|
||||||
|
if not await self.voice_check(interaction):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not (vc := await self.get_voice_client(interaction)) or not vc.is_playing:
|
||||||
|
await interaction.respond("❌ Нет воспроизводимого трека.", delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
|
res = await self.dislike_track(interaction)
|
||||||
|
if res:
|
||||||
|
logging.debug("[VC_EXT] Disliked track")
|
||||||
|
await self.next_track(interaction, vc=vc, button_callback=True)
|
||||||
|
else:
|
||||||
|
logging.debug("[VC_EXT] Failed to dislike track")
|
||||||
|
await interaction.respond("❌ Не удалось поставить дизлайк. Попробуйте позже.")
|
||||||
|
|
||||||
class LyricsButton(Button, VoiceExtension):
|
class LyricsButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Button.__init__(self, **kwargs)
|
Button.__init__(self, **kwargs)
|
||||||
@@ -137,7 +158,7 @@ class LyricsButton(Button, VoiceExtension):
|
|||||||
|
|
||||||
track = cast(Track, Track.de_json(
|
track = cast(Track, Track.de_json(
|
||||||
current_track,
|
current_track,
|
||||||
ClientAsync(ym_token), # type: ignore # Async client can be used here
|
YMClient(ym_token), # type: ignore # Async client can be used here
|
||||||
))
|
))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -300,11 +321,85 @@ class MyVibeSettingsButton(Button, VoiceExtension):
|
|||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.info('[VIBE] My vibe settings button callback')
|
logging.info('[VIBE] My vibe settings button callback')
|
||||||
if not await self.voice_check(interaction) or not interaction.user:
|
if not await self.voice_check(interaction):
|
||||||
return
|
return
|
||||||
|
|
||||||
await interaction.respond('Настройки "Моей Волны"', view=MyVibeSettingsView(interaction), ephemeral=True)
|
await interaction.respond('Настройки "Моей Волны"', view=MyVibeSettingsView(interaction), ephemeral=True)
|
||||||
|
|
||||||
|
class AddToPlaylistSelect(Select, VoiceExtension):
|
||||||
|
def __init__(self, ym_client: YMClient, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
VoiceExtension.__init__(self, None)
|
||||||
|
self.ym_client = ym_client
|
||||||
|
|
||||||
|
async def callback(self, interaction: Interaction):
|
||||||
|
if not interaction.data or not interaction.guild_id:
|
||||||
|
return
|
||||||
|
if not interaction.data or 'values' not in interaction.data:
|
||||||
|
logging.warning('[MENU] No data in select callback')
|
||||||
|
return
|
||||||
|
|
||||||
|
data = interaction.data['values'][0].split(';')
|
||||||
|
logging.debug(f"[MENU] Add to playlist select callback: {data}")
|
||||||
|
|
||||||
|
playlist = cast(Playlist, await self.ym_client.users_playlists(kind=data[0], user_id=data[1]))
|
||||||
|
current_track = self.db.get_track(interaction.guild_id, 'current')
|
||||||
|
if not current_track:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = await self.ym_client.users_playlists_insert_track(
|
||||||
|
kind=f"{playlist.kind}",
|
||||||
|
track_id=current_track['id'],
|
||||||
|
album_id=current_track['albums'][0]['id'],
|
||||||
|
revision=playlist.revision or 1,
|
||||||
|
user_id=f"{playlist.uid}"
|
||||||
|
)
|
||||||
|
except yandex_music.exceptions.NetworkError:
|
||||||
|
res = None
|
||||||
|
|
||||||
|
# value=f"{playlist.kind or "-1"};{current_track['id']};{current_track['albums'][0]['id']};{playlist.revision};{playlist.uid}"
|
||||||
|
|
||||||
|
if res:
|
||||||
|
await interaction.respond('✅ Добавлено в плейлист', delete_after=15, ephemeral=True)
|
||||||
|
else:
|
||||||
|
await interaction.respond('❌ Что-то пошло не так. Попробуйте позже.', delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
|
class AddToPlaylistButton(Button, VoiceExtension):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
Button.__init__(self, **kwargs)
|
||||||
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
|
async def callback(self, interaction: Interaction):
|
||||||
|
if not await self.voice_check(interaction) or not interaction.guild_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
client = await self.init_ym_client(interaction)
|
||||||
|
if not client or not client.me or not client.me.account or not client.me.account.uid:
|
||||||
|
await interaction.respond('❌ Что-то пошло не так. Попробуйте позже.', ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not (vc := await self.get_voice_client(interaction)) or not vc.is_playing:
|
||||||
|
await interaction.respond("❌ Нет воспроизводимого трека.", delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
view = View(
|
||||||
|
AddToPlaylistSelect(
|
||||||
|
client,
|
||||||
|
ComponentType.string_select,
|
||||||
|
placeholder='Выберите плейлист',
|
||||||
|
options=[
|
||||||
|
SelectOption(
|
||||||
|
label=playlist.title or "Без названия",
|
||||||
|
value=f"{playlist.kind or "-1"};{playlist.uid}"
|
||||||
|
) for playlist in await client.users_playlists_list(client.me.account.uid)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
await interaction.respond(view=view, ephemeral=True, delete_after=360)
|
||||||
|
|
||||||
|
|
||||||
class MenuView(View, VoiceExtension):
|
class MenuView(View, VoiceExtension):
|
||||||
|
|
||||||
def __init__(self, ctx: ApplicationContext | Interaction | RawReactionActionEvent, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = False):
|
def __init__(self, ctx: ApplicationContext | Interaction | RawReactionActionEvent, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = False):
|
||||||
@@ -322,7 +417,9 @@ class MenuView(View, VoiceExtension):
|
|||||||
self.prev_button = PrevTrackButton(style=ButtonStyle.primary, emoji='⏮', row=0)
|
self.prev_button = PrevTrackButton(style=ButtonStyle.primary, emoji='⏮', row=0)
|
||||||
|
|
||||||
self.like_button = LikeButton(style=ButtonStyle.secondary, emoji='❤️', row=1)
|
self.like_button = LikeButton(style=ButtonStyle.secondary, emoji='❤️', row=1)
|
||||||
|
self.dislike_button = DislikeButton(style=ButtonStyle.secondary, emoji='💔', row=1)
|
||||||
self.lyrics_button = LyricsButton(style=ButtonStyle.secondary, emoji='📋', row=1)
|
self.lyrics_button = LyricsButton(style=ButtonStyle.secondary, emoji='📋', row=1)
|
||||||
|
self.add_to_playlist_button = AddToPlaylistButton(style=ButtonStyle.secondary, emoji='📁', row=1)
|
||||||
self.vibe_button = MyVibeButton(style=ButtonStyle.secondary, emoji='🌊', row=1)
|
self.vibe_button = MyVibeButton(style=ButtonStyle.secondary, emoji='🌊', row=1)
|
||||||
self.vibe_settings_button = MyVibeSettingsButton(style=ButtonStyle.success, emoji='🛠', row=1)
|
self.vibe_settings_button = MyVibeSettingsButton(style=ButtonStyle.success, emoji='🛠', row=1)
|
||||||
|
|
||||||
@@ -345,7 +442,9 @@ class MenuView(View, VoiceExtension):
|
|||||||
self.lyrics_button.disabled = True
|
self.lyrics_button.disabled = True
|
||||||
|
|
||||||
self.add_item(self.like_button)
|
self.add_item(self.like_button)
|
||||||
|
self.add_item(self.dislike_button)
|
||||||
self.add_item(self.lyrics_button)
|
self.add_item(self.lyrics_button)
|
||||||
|
self.add_item(self.add_to_playlist_button)
|
||||||
|
|
||||||
if self.guild['vibing']:
|
if self.guild['vibing']:
|
||||||
self.add_item(self.vibe_settings_button)
|
self.add_item(self.vibe_settings_button)
|
||||||
|
|||||||
Reference in New Issue
Block a user