mirror of
https://github.com/deadcxap/YandexMusicDiscordBot.git
synced 2026-01-15 20:11:45 +03:00
impr: Further code review.
This commit is contained in:
@@ -11,9 +11,9 @@ from yandex_music import ClientAsync as YMClient
|
|||||||
from yandex_music import Track, Album, Artist, Playlist
|
from yandex_music import Track, Album, Artist, Playlist
|
||||||
|
|
||||||
from MusicBot.database import BaseUsersDatabase, BaseGuildsDatabase
|
from MusicBot.database import BaseUsersDatabase, BaseGuildsDatabase
|
||||||
from MusicBot.cogs.utils.find import ListenView, generate_item_embed
|
|
||||||
from MusicBot.cogs.utils.misc import generate_playlists_embed, generate_likes_embed
|
from MusicBot.ui import ListenView, MyPlaylists, generate_playlists_embed
|
||||||
from MusicBot.cogs.utils.views import MyPlaylists
|
from MusicBot.cogs.utils.embeds import generate_item_embed
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(General(bot))
|
bot.add_cog(General(bot))
|
||||||
@@ -38,17 +38,17 @@ class General(Cog):
|
|||||||
logging.info(f"Help command invoked by {ctx.user.id} for command '{command}'")
|
logging.info(f"Help command invoked by {ctx.user.id} for command '{command}'")
|
||||||
response_message = None
|
response_message = None
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
|
title='Помощь',
|
||||||
color=0xfed42b
|
color=0xfed42b
|
||||||
)
|
)
|
||||||
|
embed.set_author(name='YandexMusic')
|
||||||
embed.description = '__Использование__\n'
|
embed.description = '__Использование__\n'
|
||||||
embed.set_author(name='Помощь')
|
|
||||||
|
|
||||||
if command == 'all':
|
if command == 'all':
|
||||||
embed.description = ("Данный бот позволяет вам слушать музыку из вашего аккаунта Yandex Music.\n"
|
embed.description = ("Данный бот позволяет вам слушать музыку из вашего аккаунта Yandex Music.\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\n"
|
"Для получения помощи для конкретной команды, введите /help <команда>.\n\n"
|
||||||
"**Для доп. помощи, зайдите на [сервер любителей Яндекс Музыки](https://discord.gg/gkmFDaPMeC).**")
|
"**Для доп. помощи, зайдите на [сервер любителей Яндекс Музыки](https://discord.gg/gkmFDaPMeC).**")
|
||||||
embed.title = 'Помощь'
|
|
||||||
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name='__Основные команды__',
|
name='__Основные команды__',
|
||||||
@@ -64,7 +64,6 @@ class General(Cog):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
embed.set_author(name='YandexMusic')
|
|
||||||
embed.set_footer(text='©️ Bananchiki')
|
embed.set_footer(text='©️ Bananchiki')
|
||||||
elif command == 'account':
|
elif command == 'account':
|
||||||
embed.description += ("Ввести токен от Яндекс Музыки. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
|
embed.description += ("Ввести токен от Яндекс Музыки. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
|
||||||
@@ -87,7 +86,7 @@ class General(Cog):
|
|||||||
elif command == 'settings':
|
elif command == 'settings':
|
||||||
embed.description += ("Получить текущие настройки.\n```/settings show```\n"
|
embed.description += ("Получить текущие настройки.\n```/settings show```\n"
|
||||||
"Разрешить или запретить воспроизведение Explicit треков и альбомов. Если автор или плейлист содержат Explicit треки, убираются кнопки для доступа к ним.\n```/settings explicit```\n"
|
"Разрешить или запретить воспроизведение Explicit треков и альбомов. Если автор или плейлист содержат Explicit треки, убираются кнопки для доступа к ним.\n```/settings explicit```\n"
|
||||||
"Разрешить или запретить создание меню проигрывателя, даже если в канале больше одного человека.\n```/settings menu```\n"
|
"Разрешить или запретить создание меню проигрывателя, когда в канале больше одного человека.\n```/settings menu```\n"
|
||||||
"Разрешить или запретить голосование.\n```/settings vote <тип голосования>```\n"
|
"Разрешить или запретить голосование.\n```/settings vote <тип голосования>```\n"
|
||||||
"`Примечание`: Только пользователи с разрешением управления каналом могут менять настройки.")
|
"`Примечание`: Только пользователи с разрешением управления каналом могут менять настройки.")
|
||||||
elif command == 'track':
|
elif command == 'track':
|
||||||
@@ -99,7 +98,7 @@ class General(Cog):
|
|||||||
elif command == 'voice':
|
elif command == 'voice':
|
||||||
embed.description += ("Присоединить бота в голосовой канал. Требует разрешения управления каналом.\n ```/voice join```\n"
|
embed.description += ("Присоединить бота в голосовой канал. Требует разрешения управления каналом.\n ```/voice join```\n"
|
||||||
"Заставить бота покинуть голосовой канал. Требует разрешения управления каналом.\n ```/voice leave```\n"
|
"Заставить бота покинуть голосовой канал. Требует разрешения управления каналом.\n ```/voice leave```\n"
|
||||||
"Создать меню проигрывателя. Доступно только если вы единственный в голосовом канале.\n```/voice menu```")
|
"Создать меню проигрывателя. Доступность зависит от настроек сервера. По умолчанию работает только когда в канале один человек.\n```/voice menu```")
|
||||||
else:
|
else:
|
||||||
response_message = '❌ Неизвестная команда.'
|
response_message = '❌ Неизвестная команда.'
|
||||||
embed = None
|
embed = None
|
||||||
@@ -154,7 +153,7 @@ class General(Cog):
|
|||||||
|
|
||||||
real_tracks = await gather(*[track_short.fetch_track_async() for track_short in likes.tracks], return_exceptions=True)
|
real_tracks = await gather(*[track_short.fetch_track_async() for track_short in likes.tracks], return_exceptions=True)
|
||||||
tracks = [track for track in real_tracks if not isinstance(track, BaseException)] # Can't fetch user tracks
|
tracks = [track for track in real_tracks if not isinstance(track, BaseException)] # Can't fetch user tracks
|
||||||
embed = generate_likes_embed(tracks)
|
embed = await generate_item_embed(tracks)
|
||||||
logging.info(f"Successfully fetched likes for user {ctx.user.id}")
|
logging.info(f"Successfully fetched likes for user {ctx.user.id}")
|
||||||
await ctx.respond(embed=embed, view=ListenView(tracks))
|
await ctx.respond(embed=embed, view=ListenView(tracks))
|
||||||
|
|
||||||
|
|||||||
7
MusicBot/cogs/utils/__init__.py
Normal file
7
MusicBot/cogs/utils/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from .embeds import generate_item_embed
|
||||||
|
from .voice_extension import VoiceExtension
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"generate_item_embed",
|
||||||
|
"VoiceExtension",
|
||||||
|
]
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import Any, cast
|
import logging
|
||||||
|
from typing import cast
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
@@ -9,7 +10,31 @@ from PIL import Image
|
|||||||
from yandex_music import Track, Album, Artist, Playlist, Label
|
from yandex_music import Track, Album, Artist, Playlist, Label
|
||||||
from discord import Embed
|
from discord import Embed
|
||||||
|
|
||||||
def generate_likes_embed(tracks: list[Track]) -> Embed:
|
async def generate_item_embed(item: Track | Album | Artist | Playlist | list[Track]) -> Embed:
|
||||||
|
"""Generate item embed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
item (yandex_music.Track | yandex_music.Album | yandex_music.Artist | yandex_music.Playlist): Item to be processed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
discord.Embed: Item embed.
|
||||||
|
"""
|
||||||
|
logging.debug(f"Generating embed for type: '{type(item).__name__}'")
|
||||||
|
|
||||||
|
if isinstance(item, Track):
|
||||||
|
return await _generate_track_embed(item)
|
||||||
|
elif isinstance(item, Album):
|
||||||
|
return await _generate_album_embed(item)
|
||||||
|
elif isinstance(item, Artist):
|
||||||
|
return await _generate_artist_embed(item)
|
||||||
|
elif isinstance(item, Playlist):
|
||||||
|
return await _generate_playlist_embed(item)
|
||||||
|
elif isinstance(item, list):
|
||||||
|
return _generate_likes_embed(item)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown item type: {type(item).__name__}")
|
||||||
|
|
||||||
|
def _generate_likes_embed(tracks: list[Track]) -> Embed:
|
||||||
track_count = len(tracks)
|
track_count = len(tracks)
|
||||||
cover_url = "https://avatars.yandex.net/get-music-user-playlist/11418140/favorit-playlist-cover.bb48fdb9b9f4/300x300"
|
cover_url = "https://avatars.yandex.net/get-music-user-playlist/11418140/favorit-playlist-cover.bb48fdb9b9f4/300x300"
|
||||||
|
|
||||||
@@ -34,37 +59,7 @@ def generate_likes_embed(tracks: list[Track]) -> Embed:
|
|||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
def generate_playlists_embed(page: int, playlists: list[tuple[str, int]]) -> Embed:
|
async def _generate_track_embed(track: Track) -> Embed:
|
||||||
count = 15 * page
|
|
||||||
length = len(playlists)
|
|
||||||
embed = Embed(
|
|
||||||
title=f"Всего плейлистов: {length}",
|
|
||||||
color=0xfed42b
|
|
||||||
)
|
|
||||||
embed.set_author(name="Ваши плейлисты")
|
|
||||||
embed.set_footer(text=f"Страница {page + 1} из {ceil(length / 10)}")
|
|
||||||
for playlist in playlists[count:count + 10]:
|
|
||||||
embed.add_field(name=playlist[0], value=f"{playlist[1]} треков", inline=False)
|
|
||||||
return embed
|
|
||||||
|
|
||||||
def generate_queue_embed(page: int, tracks_list: list[dict[str, Any]]) -> Embed:
|
|
||||||
count = 15 * page
|
|
||||||
length = len(tracks_list)
|
|
||||||
embed = Embed(
|
|
||||||
title=f"Всего: {length}",
|
|
||||||
color=0xfed42b,
|
|
||||||
)
|
|
||||||
embed.set_author(name="Очередь треков")
|
|
||||||
embed.set_footer(text=f"Страница {page + 1} из {ceil(length / 15)}")
|
|
||||||
for i, track in enumerate(tracks_list[count:count + 15], start=1 + count):
|
|
||||||
duration = track['duration_ms']
|
|
||||||
if duration:
|
|
||||||
duration_m = duration // 60000
|
|
||||||
duration_s = ceil(duration / 1000) - duration_m * 60
|
|
||||||
embed.add_field(name=f"{i} - {track['title']} - {duration_m}:{duration_s:02d}", value="", inline=False)
|
|
||||||
return embed
|
|
||||||
|
|
||||||
async def generate_track_embed(track: Track) -> Embed:
|
|
||||||
title = cast(str, track.title)
|
title = cast(str, track.title)
|
||||||
avail = cast(bool, track.available)
|
avail = cast(bool, track.available)
|
||||||
artists = track.artists_name()
|
artists = track.artists_name()
|
||||||
@@ -78,7 +73,7 @@ async def generate_track_embed(track: Track) -> Embed:
|
|||||||
artist = track.artists[0]
|
artist = track.artists[0]
|
||||||
|
|
||||||
cover_url = track.get_cover_url('400x400')
|
cover_url = track.get_cover_url('400x400')
|
||||||
color = await get_average_color_from_url(cover_url)
|
color = await _get_average_color_from_url(cover_url)
|
||||||
|
|
||||||
if explicit:
|
if explicit:
|
||||||
explicit_eid = getenv('EXPLICIT_EID')
|
explicit_eid = getenv('EXPLICIT_EID')
|
||||||
@@ -131,7 +126,7 @@ async def generate_track_embed(track: Track) -> Embed:
|
|||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def generate_album_embed(album: Album) -> Embed:
|
async def _generate_album_embed(album: Album) -> Embed:
|
||||||
title = cast(str, album.title)
|
title = cast(str, album.title)
|
||||||
track_count = album.track_count
|
track_count = album.track_count
|
||||||
artists = album.artists_name()
|
artists = album.artists_name()
|
||||||
@@ -146,7 +141,7 @@ async def generate_album_embed(album: Album) -> Embed:
|
|||||||
artist = album.artists[0]
|
artist = album.artists[0]
|
||||||
|
|
||||||
cover_url = album.get_cover_url('400x400')
|
cover_url = album.get_cover_url('400x400')
|
||||||
color = await get_average_color_from_url(cover_url)
|
color = await _get_average_color_from_url(cover_url)
|
||||||
|
|
||||||
if isinstance(album.labels[0], Label):
|
if isinstance(album.labels[0], Label):
|
||||||
labels = [cast(Label, label).name for label in album.labels]
|
labels = [cast(Label, label).name for label in album.labels]
|
||||||
@@ -204,7 +199,7 @@ async def generate_album_embed(album: Album) -> Embed:
|
|||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def generate_artist_embed(artist: Artist) -> Embed:
|
async def _generate_artist_embed(artist: Artist) -> Embed:
|
||||||
name = cast(str, artist.name)
|
name = cast(str, artist.name)
|
||||||
likes_count = artist.likes_count
|
likes_count = artist.likes_count
|
||||||
avail = cast(bool, artist.available)
|
avail = cast(bool, artist.available)
|
||||||
@@ -217,7 +212,7 @@ async def generate_artist_embed(artist: Artist) -> Embed:
|
|||||||
cover_url = artist.get_op_image_url('400x400')
|
cover_url = artist.get_op_image_url('400x400')
|
||||||
else:
|
else:
|
||||||
cover_url = artist.cover.get_url(size='400x400')
|
cover_url = artist.cover.get_url(size='400x400')
|
||||||
color = await get_average_color_from_url(cover_url)
|
color = await _get_average_color_from_url(cover_url)
|
||||||
|
|
||||||
embed = Embed(
|
embed = Embed(
|
||||||
title=name,
|
title=name,
|
||||||
@@ -249,7 +244,7 @@ async def generate_artist_embed(artist: Artist) -> Embed:
|
|||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def generate_playlist_embed(playlist: Playlist) -> Embed:
|
async def _generate_playlist_embed(playlist: Playlist) -> Embed:
|
||||||
title = cast(str, playlist.title)
|
title = cast(str, playlist.title)
|
||||||
track_count = playlist.track_count
|
track_count = playlist.track_count
|
||||||
avail = cast(bool, playlist.available)
|
avail = cast(bool, playlist.available)
|
||||||
@@ -272,7 +267,7 @@ async def generate_playlist_embed(playlist: Playlist) -> Embed:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if cover_url:
|
if cover_url:
|
||||||
color = await get_average_color_from_url(cover_url)
|
color = await _get_average_color_from_url(cover_url)
|
||||||
|
|
||||||
embed = Embed(
|
embed = Embed(
|
||||||
title=title,
|
title=title,
|
||||||
@@ -303,7 +298,7 @@ async def generate_playlist_embed(playlist: Playlist) -> Embed:
|
|||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
async def get_average_color_from_url(url: str) -> int:
|
async def _get_average_color_from_url(url: str) -> int:
|
||||||
"""Get image from url and calculate its average color to use in embeds.
|
"""Get image from url and calculate its average color to use in embeds.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -2,12 +2,12 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Literal, cast
|
from typing import Any, Literal, cast
|
||||||
|
|
||||||
from yandex_music import Track, ClientAsync
|
from yandex_music import Track, TrackShort, ClientAsync
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord import Interaction, ApplicationContext, RawReactionActionEvent
|
from discord import Interaction, ApplicationContext, RawReactionActionEvent
|
||||||
|
|
||||||
from MusicBot.cogs.utils.misc import generate_track_embed
|
from MusicBot.cogs.utils import generate_item_embed
|
||||||
from MusicBot.database import VoiceGuildsDatabase, BaseUsersDatabase
|
from MusicBot.database import VoiceGuildsDatabase, BaseUsersDatabase
|
||||||
|
|
||||||
class VoiceExtension:
|
class VoiceExtension:
|
||||||
@@ -17,7 +17,7 @@ class VoiceExtension:
|
|||||||
self.db = VoiceGuildsDatabase()
|
self.db = VoiceGuildsDatabase()
|
||||||
self.users_db = BaseUsersDatabase()
|
self.users_db = BaseUsersDatabase()
|
||||||
|
|
||||||
async def update_player_embed(self, ctx: ApplicationContext | Interaction | RawReactionActionEvent, player_mid: int) -> bool:
|
async def update_menu_embed(self, ctx: ApplicationContext | Interaction | RawReactionActionEvent, player_mid: int) -> bool:
|
||||||
"""Update current player message by its id. Return True if updated, False if not.
|
"""Update current player message by its id. Return True if updated, False if not.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -59,7 +59,7 @@ class VoiceExtension:
|
|||||||
current_track,
|
current_track,
|
||||||
client=ClientAsync(token) # type: ignore # Async client can be used here.
|
client=ClientAsync(token) # type: ignore # Async client can be used here.
|
||||||
))
|
))
|
||||||
embed = await generate_track_embed(track)
|
embed = await generate_item_embed(track)
|
||||||
|
|
||||||
if isinstance(ctx, Interaction) and ctx.message and ctx.message.id == player_mid:
|
if isinstance(ctx, Interaction) and ctx.message and ctx.message.id == player_mid:
|
||||||
# If interaction from player buttons
|
# If interaction from player buttons
|
||||||
@@ -230,7 +230,7 @@ class VoiceExtension:
|
|||||||
|
|
||||||
player = guild['current_player']
|
player = guild['current_player']
|
||||||
if player is not None:
|
if player is not None:
|
||||||
await self.update_player_embed(ctx, player)
|
await self.update_menu_embed(ctx, player)
|
||||||
|
|
||||||
return track.title
|
return track.title
|
||||||
|
|
||||||
@@ -369,6 +369,35 @@ class VoiceExtension:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
async def get_likes(self, ctx: ApplicationContext | Interaction) -> list[TrackShort] | None:
|
||||||
|
"""Get liked tracks. Return list of tracks on success.
|
||||||
|
Return None if no token found.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ctx (ApplicationContext | Interaction): Context.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Track] | None: List of tracks or None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not ctx.guild or not ctx.user:
|
||||||
|
logging.warning("Guild or User not found in context inside 'like_track'")
|
||||||
|
return None
|
||||||
|
|
||||||
|
current_track = self.db.get_track(ctx.guild.id, 'current')
|
||||||
|
token = self.users_db.get_ym_token(ctx.user.id)
|
||||||
|
if not current_track or not token:
|
||||||
|
logging.debug("Current track or token not found")
|
||||||
|
return None
|
||||||
|
|
||||||
|
client = await ClientAsync(token).init()
|
||||||
|
likes = await client.users_likes_tracks()
|
||||||
|
if not likes:
|
||||||
|
logging.debug("No likes found")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return likes.tracks
|
||||||
|
|
||||||
async def like_track(self, ctx: ApplicationContext | Interaction) -> str | Literal['TRACK REMOVED'] | None:
|
async def like_track(self, ctx: ApplicationContext | Interaction) -> str | Literal['TRACK REMOVED'] | None:
|
||||||
"""Like current track. Return track title on success.
|
"""Like current track. Return track title on success.
|
||||||
|
|
||||||
@@ -389,9 +418,8 @@ class VoiceExtension:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
client = await ClientAsync(token).init()
|
client = await ClientAsync(token).init()
|
||||||
likes = await client.users_likes_tracks()
|
likes = await self.get_likes(ctx)
|
||||||
if not likes:
|
if not likes:
|
||||||
logging.debug("No likes found")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
ym_track = cast(Track, Track.de_json(
|
ym_track = cast(Track, Track.de_json(
|
||||||
@@ -399,7 +427,7 @@ class VoiceExtension:
|
|||||||
client=client # type: ignore # Async client can be used here.
|
client=client # type: ignore # Async client can be used here.
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if ym_track.id not in [track.id for track in likes.tracks]:
|
if str(ym_track.id) not in [str(track.id) for track in likes]:
|
||||||
logging.debug("Track not found in likes. Adding...")
|
logging.debug("Track not found in likes. Adding...")
|
||||||
await ym_track.like_async()
|
await ym_track.like_async()
|
||||||
return ym_track.title
|
return ym_track.title
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ from discord.ext.commands import Cog
|
|||||||
|
|
||||||
from yandex_music import Track, ClientAsync
|
from yandex_music import Track, ClientAsync
|
||||||
|
|
||||||
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
from MusicBot.cogs.utils import VoiceExtension, generate_item_embed
|
||||||
from MusicBot.cogs.utils.player import Player
|
from MusicBot.ui import MenuView, QueueView, generate_queue_embed
|
||||||
from MusicBot.cogs.utils.misc import generate_queue_embed, generate_track_embed
|
|
||||||
from MusicBot.cogs.utils.views import QueueView
|
|
||||||
|
|
||||||
def setup(bot: discord.Bot):
|
def setup(bot: discord.Bot):
|
||||||
bot.add_cog(Voice(bot))
|
bot.add_cog(Voice(bot))
|
||||||
@@ -31,6 +29,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
gid = member.guild.id
|
gid = member.guild.id
|
||||||
guild = self.db.get_guild(gid)
|
guild = self.db.get_guild(gid)
|
||||||
discord_guild = await self.typed_bot.fetch_guild(gid)
|
discord_guild = await self.typed_bot.fetch_guild(gid)
|
||||||
|
current_player = self.db.get_current_player(gid)
|
||||||
|
|
||||||
channel = after.channel or before.channel
|
channel = after.channel or before.channel
|
||||||
if not channel:
|
if not channel:
|
||||||
@@ -44,7 +43,6 @@ class Voice(Cog, VoiceExtension):
|
|||||||
self.db.update(gid, {'previous_tracks': [], 'next_tracks': [], 'current_track': None, 'is_stopped': True})
|
self.db.update(gid, {'previous_tracks': [], 'next_tracks': [], 'current_track': None, 'is_stopped': True})
|
||||||
vc.stop()
|
vc.stop()
|
||||||
elif len(channel.members) > 2 and not guild['always_allow_menu']:
|
elif len(channel.members) > 2 and not guild['always_allow_menu']:
|
||||||
current_player = self.db.get_current_player(gid)
|
|
||||||
if current_player:
|
if current_player:
|
||||||
logging.info(f"Disabling current player for guild {gid} due to multiple members")
|
logging.info(f"Disabling current player for guild {gid} due to multiple members")
|
||||||
|
|
||||||
@@ -52,6 +50,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
try:
|
try:
|
||||||
message = await channel.fetch_message(current_player)
|
message = await channel.fetch_message(current_player)
|
||||||
await message.delete()
|
await message.delete()
|
||||||
|
await channel.send("Меню отключено из-за большого количества участников.", delete_after=15)
|
||||||
except (discord.NotFound, discord.Forbidden):
|
except (discord.NotFound, discord.Forbidden):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -201,7 +200,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if guild['current_track']:
|
if guild['current_track']:
|
||||||
embed = await generate_track_embed(
|
embed = await generate_item_embed(
|
||||||
Track.de_json(
|
Track.de_json(
|
||||||
guild['current_track'],
|
guild['current_track'],
|
||||||
client=ClientAsync() # type: ignore # Async client can be used here.
|
client=ClientAsync() # type: ignore # Async client can be used here.
|
||||||
@@ -215,10 +214,11 @@ class Voice(Cog, VoiceExtension):
|
|||||||
|
|
||||||
if guild['current_player']:
|
if guild['current_player']:
|
||||||
logging.info(f"Deleteing old player menu {guild['current_player']} in guild {ctx.guild.id}")
|
logging.info(f"Deleteing old player menu {guild['current_player']} in guild {ctx.guild.id}")
|
||||||
message = await ctx.fetch_message(guild['current_player'])
|
message = await self.get_player_message(ctx, guild['current_player'])
|
||||||
|
if message:
|
||||||
await message.delete()
|
await message.delete()
|
||||||
|
|
||||||
interaction = cast(discord.Interaction, await ctx.respond(view=Player(ctx), embed=embed, delete_after=3600))
|
interaction = cast(discord.Interaction, await ctx.respond(view=await MenuView(ctx).init(), embed=embed, delete_after=3600))
|
||||||
response = await interaction.original_response()
|
response = await interaction.original_response()
|
||||||
self.db.update(ctx.guild.id, {'current_player': response.id})
|
self.db.update(ctx.guild.id, {'current_player': response.id})
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
|
|
||||||
player = self.db.get_current_player(ctx.guild.id)
|
player = self.db.get_current_player(ctx.guild.id)
|
||||||
if player:
|
if player:
|
||||||
await self.update_player_embed(ctx, player)
|
await self.update_menu_embed(ctx, player)
|
||||||
|
|
||||||
logging.info(f"Track paused in guild {ctx.guild.id}")
|
logging.info(f"Track paused in guild {ctx.guild.id}")
|
||||||
await ctx.respond("Воспроизведение приостановлено.", delete_after=15, ephemeral=True)
|
await ctx.respond("Воспроизведение приостановлено.", delete_after=15, ephemeral=True)
|
||||||
@@ -330,7 +330,7 @@ class Voice(Cog, VoiceExtension):
|
|||||||
vc.resume()
|
vc.resume()
|
||||||
player = self.db.get_current_player(ctx.guild.id)
|
player = self.db.get_current_player(ctx.guild.id)
|
||||||
if player:
|
if player:
|
||||||
await self.update_player_embed(ctx, player)
|
await self.update_menu_embed(ctx, player)
|
||||||
logging.info(f"Track resumed in guild {ctx.guild.id}")
|
logging.info(f"Track resumed in guild {ctx.guild.id}")
|
||||||
await ctx.respond("Воспроизведение восстановлено.", delete_after=15, ephemeral=True)
|
await ctx.respond("Воспроизведение восстановлено.", delete_after=15, ephemeral=True)
|
||||||
else:
|
else:
|
||||||
|
|||||||
12
MusicBot/ui/__init__.py
Normal file
12
MusicBot/ui/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from .other import MyPlaylists, QueueView, generate_queue_embed, generate_playlists_embed
|
||||||
|
from .menu import MenuView
|
||||||
|
from .find import ListenView
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'MyPlaylists',
|
||||||
|
'QueueView',
|
||||||
|
'MenuView',
|
||||||
|
'ListenView',
|
||||||
|
'generate_queue_embed',
|
||||||
|
'generate_playlists_embed'
|
||||||
|
]
|
||||||
@@ -5,10 +5,9 @@ import discord
|
|||||||
from yandex_music import Track, Album, Artist, Playlist
|
from yandex_music import Track, Album, Artist, Playlist
|
||||||
|
|
||||||
from discord.ui import View, Button, Item
|
from discord.ui import View, Button, Item
|
||||||
from discord import ButtonStyle, Interaction, Embed
|
from discord import ButtonStyle, Interaction
|
||||||
|
|
||||||
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
||||||
from MusicBot.cogs.utils.misc import generate_track_embed, generate_album_embed, generate_artist_embed, generate_playlist_embed
|
|
||||||
|
|
||||||
class PlayButton(Button, VoiceExtension):
|
class PlayButton(Button, VoiceExtension):
|
||||||
def __init__(self, item: Track | Album | Artist | Playlist | list[Track], **kwargs):
|
def __init__(self, item: Track | Album | Artist | Playlist | list[Track], **kwargs):
|
||||||
@@ -38,7 +37,6 @@ class PlayButton(Button, VoiceExtension):
|
|||||||
action = 'add_track'
|
action = 'add_track'
|
||||||
vote_message = f"{member.mention} хочет добавить трек **{self.item.title}** в очередь.\n\n Голосуйте за добавление."
|
vote_message = f"{member.mention} хочет добавить трек **{self.item.title}** в очередь.\n\n Голосуйте за добавление."
|
||||||
response_message = f"Трек **{self.item.title}** был добавлен в очередь."
|
response_message = f"Трек **{self.item.title}** был добавлен в очередь."
|
||||||
play_message = f"Сейчас играет: **{self.item.title}**!"
|
|
||||||
|
|
||||||
elif isinstance(self.item, Album):
|
elif isinstance(self.item, Album):
|
||||||
album = await self.item.with_tracks_async()
|
album = await self.item.with_tracks_async()
|
||||||
@@ -51,7 +49,6 @@ class PlayButton(Button, VoiceExtension):
|
|||||||
action = 'add_album'
|
action = 'add_album'
|
||||||
vote_message = f"{member.mention} хочет добавить альбом **{self.item.title}** в очередь.\n\n Голосуйте за добавление."
|
vote_message = f"{member.mention} хочет добавить альбом **{self.item.title}** в очередь.\n\n Голосуйте за добавление."
|
||||||
response_message = f"Альбом **{self.item.title}** был добавлен в очередь."
|
response_message = f"Альбом **{self.item.title}** был добавлен в очередь."
|
||||||
play_message = f"Сейчас играет: **{self.item.title}**!"
|
|
||||||
|
|
||||||
elif isinstance(self.item, Artist):
|
elif isinstance(self.item, Artist):
|
||||||
artist_tracks = await self.item.get_tracks_async()
|
artist_tracks = await self.item.get_tracks_async()
|
||||||
@@ -64,7 +61,6 @@ class PlayButton(Button, VoiceExtension):
|
|||||||
action = 'add_artist'
|
action = 'add_artist'
|
||||||
vote_message = f"{member.mention} хочет добавить треки от **{self.item.name}** в очередь.\n\n Голосуйте за добавление."
|
vote_message = f"{member.mention} хочет добавить треки от **{self.item.name}** в очередь.\n\n Голосуйте за добавление."
|
||||||
response_message = f"Песни артиста **{self.item.name}** были добавлены в очередь."
|
response_message = f"Песни артиста **{self.item.name}** были добавлены в очередь."
|
||||||
play_message = f"Сейчас играет: **{self.item.name}**!"
|
|
||||||
|
|
||||||
elif isinstance(self.item, Playlist):
|
elif isinstance(self.item, Playlist):
|
||||||
short_tracks = await self.item.fetch_tracks_async()
|
short_tracks = await self.item.fetch_tracks_async()
|
||||||
@@ -77,7 +73,6 @@ class PlayButton(Button, VoiceExtension):
|
|||||||
action = 'add_playlist'
|
action = 'add_playlist'
|
||||||
vote_message = f"{member.mention} хочет добавить плейлист **{self.item.title}** в очередь.\n\n Голосуйте за добавление."
|
vote_message = f"{member.mention} хочет добавить плейлист **{self.item.title}** в очередь.\n\n Голосуйте за добавление."
|
||||||
response_message = f"Плейлист **{self.item.title}** был добавлен в очередь."
|
response_message = f"Плейлист **{self.item.title}** был добавлен в очередь."
|
||||||
play_message = f"Сейчас играет: **{self.item.title}**!"
|
|
||||||
|
|
||||||
elif isinstance(self.item, list):
|
elif isinstance(self.item, list):
|
||||||
tracks = self.item.copy()
|
tracks = self.item.copy()
|
||||||
@@ -122,14 +117,14 @@ class PlayButton(Button, VoiceExtension):
|
|||||||
track = tracks.pop(0)
|
track = tracks.pop(0)
|
||||||
self.db.modify_track(gid, tracks, 'next', 'extend')
|
self.db.modify_track(gid, tracks, 'next', 'extend')
|
||||||
await self.play_track(interaction, track)
|
await self.play_track(interaction, track)
|
||||||
response_message = f"Сейчас играет: **{tracks[0].title}**!"
|
response_message = f"Сейчас играет: **{track.title}**!"
|
||||||
|
|
||||||
current_player = None
|
current_player = None
|
||||||
if guild['current_player']:
|
if guild['current_player']:
|
||||||
current_player = await self.get_player_message(interaction, guild['current_player'])
|
current_player = await self.get_player_message(interaction, guild['current_player'])
|
||||||
|
|
||||||
if current_player and interaction.message:
|
if current_player and interaction.message:
|
||||||
logging.debug(f"Deleting interaction message '{interaction.message.id}': current player '{current_player.id}' found")
|
logging.debug(f"Deleting interaction message {interaction.message.id}: current player {current_player.id} found")
|
||||||
await interaction.message.delete()
|
await interaction.message.delete()
|
||||||
else:
|
else:
|
||||||
await interaction.respond(response_message, delete_after=15)
|
await interaction.respond(response_message, delete_after=15)
|
||||||
@@ -163,25 +158,3 @@ class ListenView(View):
|
|||||||
# self.add_item(self.button1) # Discord doesn't allow well formed URLs in buttons for some reason.
|
# self.add_item(self.button1) # Discord doesn't allow well formed URLs in buttons for some reason.
|
||||||
self.add_item(self.button2)
|
self.add_item(self.button2)
|
||||||
self.add_item(self.button3)
|
self.add_item(self.button3)
|
||||||
|
|
||||||
async def generate_item_embed(item: Track | Album | Artist | Playlist) -> Embed:
|
|
||||||
"""Generate item embed.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
item (yandex_music.Track | yandex_music.Album | yandex_music.Artist | yandex_music.Playlist): Item to be processed.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
discord.Embed: Item embed.
|
|
||||||
"""
|
|
||||||
logging.debug(f"Generating embed for type: '{type(item).__name__}'")
|
|
||||||
|
|
||||||
if isinstance(item, Track):
|
|
||||||
return await generate_track_embed(item)
|
|
||||||
elif isinstance(item, Album):
|
|
||||||
return await generate_album_embed(item)
|
|
||||||
elif isinstance(item, Artist):
|
|
||||||
return await generate_artist_embed(item)
|
|
||||||
elif isinstance(item, Playlist):
|
|
||||||
return await generate_playlist_embed(item)
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unknown item type: {type(item).__name__}")
|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
from discord.ui import View, Button, Item
|
from typing import Self, cast
|
||||||
from discord import ButtonStyle, Interaction, ApplicationContext
|
|
||||||
|
|
||||||
|
from discord.ui import View, Button, Item
|
||||||
|
from discord import VoiceChannel, ButtonStyle, Interaction, ApplicationContext, RawReactionActionEvent, Embed
|
||||||
|
|
||||||
|
from yandex_music import Track, ClientAsync
|
||||||
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
||||||
|
|
||||||
class ToggleRepeatButton(Button, VoiceExtension):
|
class ToggleRepeatButton(Button, VoiceExtension):
|
||||||
@@ -10,13 +13,13 @@ class ToggleRepeatButton(Button, VoiceExtension):
|
|||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.debug('Repeat button callback...')
|
logging.info('Repeat button callback...')
|
||||||
if not interaction.guild:
|
if not interaction.guild:
|
||||||
return
|
return
|
||||||
gid = interaction.guild.id
|
gid = interaction.guild.id
|
||||||
guild = self.db.get_guild(gid)
|
guild = self.db.get_guild(gid)
|
||||||
self.db.update(gid, {'repeat': not guild['repeat']})
|
self.db.update(gid, {'repeat': not guild['repeat']})
|
||||||
await interaction.edit(view=Player(interaction))
|
await interaction.edit(view=await MenuView(interaction).init())
|
||||||
|
|
||||||
class ToggleShuffleButton(Button, VoiceExtension):
|
class ToggleShuffleButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -24,13 +27,13 @@ class ToggleShuffleButton(Button, VoiceExtension):
|
|||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.debug('Shuffle button callback...')
|
logging.info('Shuffle button callback...')
|
||||||
if not interaction.guild:
|
if not interaction.guild:
|
||||||
return
|
return
|
||||||
gid = interaction.guild.id
|
gid = interaction.guild.id
|
||||||
guild = self.db.get_guild(gid)
|
guild = self.db.get_guild(gid)
|
||||||
self.db.update(gid, {'shuffle': not guild['shuffle']})
|
self.db.update(gid, {'shuffle': not guild['shuffle']})
|
||||||
await interaction.edit(view=Player(interaction))
|
await interaction.edit(view=await MenuView(interaction).init())
|
||||||
|
|
||||||
class PlayPauseButton(Button, VoiceExtension):
|
class PlayPauseButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -38,7 +41,7 @@ class PlayPauseButton(Button, VoiceExtension):
|
|||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.debug('Play/Pause button callback...')
|
logging.info('Play/Pause button callback...')
|
||||||
if not await self.voice_check(interaction):
|
if not await self.voice_check(interaction):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ class NextTrackButton(Button, VoiceExtension):
|
|||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.debug('Next track button callback...')
|
logging.info('Next track button callback...')
|
||||||
if not await self.voice_check(interaction):
|
if not await self.voice_check(interaction):
|
||||||
return
|
return
|
||||||
title = await self.next_track(interaction)
|
title = await self.next_track(interaction)
|
||||||
@@ -76,7 +79,7 @@ class PrevTrackButton(Button, VoiceExtension):
|
|||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.debug('Previous track button callback...')
|
logging.info('Previous track button callback...')
|
||||||
if not await self.voice_check(interaction):
|
if not await self.voice_check(interaction):
|
||||||
return
|
return
|
||||||
title = await self.prev_track(interaction)
|
title = await self.prev_track(interaction)
|
||||||
@@ -89,34 +92,74 @@ class LikeButton(Button, VoiceExtension):
|
|||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
async def callback(self, interaction: Interaction) -> None:
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
logging.debug('Like button callback...')
|
logging.info('Like button callback...')
|
||||||
if await self.voice_check(interaction):
|
if not await self.voice_check(interaction):
|
||||||
vc = await self.get_voice_client(interaction)
|
return
|
||||||
if not vc or not vc.is_playing:
|
|
||||||
await interaction.respond("Нет воспроизводимого трека.", delete_after=15, ephemeral=True)
|
|
||||||
result = await self.like_track(interaction)
|
|
||||||
if not result:
|
|
||||||
await interaction.respond("❌ Операция не удалась.", delete_after=15, ephemeral=True)
|
|
||||||
elif result == 'TRACK REMOVED':
|
|
||||||
await interaction.respond("Трек был удалён из избранного.", delete_after=15, ephemeral=True)
|
|
||||||
else:
|
|
||||||
await interaction.respond(f"Трек **{result}** был добавлен в избранное.", delete_after=15, ephemeral=True)
|
|
||||||
|
|
||||||
class Player(View, VoiceExtension):
|
if not (vc := await self.get_voice_client(interaction)) or not vc.is_playing:
|
||||||
|
await interaction.respond("❌ Нет воспроизводимого трека.", delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
|
await self.like_track(interaction)
|
||||||
|
await interaction.edit(view=await MenuView(interaction).init())
|
||||||
|
|
||||||
|
class LyricsButton(Button, VoiceExtension):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
Button.__init__(self, **kwargs)
|
||||||
|
VoiceExtension.__init__(self, None)
|
||||||
|
|
||||||
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
|
logging.info('Lyrics button callback...')
|
||||||
|
|
||||||
|
if not await self.voice_check(interaction) or not interaction.guild_id or not interaction.user:
|
||||||
|
return
|
||||||
|
|
||||||
|
ym_token = self.users_db.get_ym_token(interaction.user.id)
|
||||||
|
current_track = self.db.get_track(interaction.guild_id, 'current')
|
||||||
|
if not current_track or not ym_token:
|
||||||
|
return
|
||||||
|
|
||||||
|
track = cast(Track, Track.de_json(
|
||||||
|
current_track,
|
||||||
|
ClientAsync(ym_token), # type: ignore # Async client can be used here
|
||||||
|
))
|
||||||
|
|
||||||
|
lyrics = await track.get_lyrics_async()
|
||||||
|
if not lyrics:
|
||||||
|
return
|
||||||
|
|
||||||
|
embed = Embed(
|
||||||
|
title=track.title,
|
||||||
|
description='**Текст песни**',
|
||||||
|
color=0xfed42b,
|
||||||
|
)
|
||||||
|
text = await lyrics.fetch_lyrics_async()
|
||||||
|
for subtext in text.split('\n\n'):
|
||||||
|
embed.add_field(name='', value=subtext, inline=False)
|
||||||
|
await interaction.respond(embed=embed, ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
|
class MenuView(View, VoiceExtension):
|
||||||
|
|
||||||
def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = True):
|
def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = True):
|
||||||
View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout)
|
View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout)
|
||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
if not ctx.guild:
|
if not ctx.guild_id:
|
||||||
return
|
return
|
||||||
guild = self.db.get_guild(ctx.guild.id)
|
self.ctx = ctx
|
||||||
|
self.guild = self.db.get_guild(ctx.guild_id)
|
||||||
|
|
||||||
self.repeat_button = ToggleRepeatButton(style=ButtonStyle.success if guild['repeat'] else ButtonStyle.secondary, emoji='🔂', row=0)
|
self.repeat_button = ToggleRepeatButton(style=ButtonStyle.success if self.guild['repeat'] else ButtonStyle.secondary, emoji='🔂', row=0)
|
||||||
self.shuffle_button = ToggleShuffleButton(style=ButtonStyle.success if guild['shuffle'] else ButtonStyle.secondary, emoji='🔀', row=0)
|
self.shuffle_button = ToggleShuffleButton(style=ButtonStyle.success if self.guild['shuffle'] else ButtonStyle.secondary, emoji='🔀', row=0)
|
||||||
self.play_pause_button = PlayPauseButton(style=ButtonStyle.primary, emoji='⏯', row=0)
|
self.play_pause_button = PlayPauseButton(style=ButtonStyle.primary, emoji='⏯', row=0)
|
||||||
self.next_button = NextTrackButton(style=ButtonStyle.primary, emoji='⏭', row=0)
|
self.next_button = NextTrackButton(style=ButtonStyle.primary, emoji='⏭', row=0)
|
||||||
self.prev_button = PrevTrackButton(style=ButtonStyle.primary, emoji='⏮', row=0)
|
self.prev_button = PrevTrackButton(style=ButtonStyle.primary, emoji='⏮', row=0)
|
||||||
self.queue_button = Button(style=ButtonStyle.primary, emoji='📋', row=1)
|
|
||||||
|
self.like_button = LikeButton(style=ButtonStyle.secondary, emoji='❤️', row=1)
|
||||||
|
self.lyrics_button = LyricsButton(style=ButtonStyle.secondary, emoji='📋', row=1)
|
||||||
|
|
||||||
|
async def init(self) -> Self:
|
||||||
|
current_track = self.guild['current_track']
|
||||||
|
likes = await self.get_likes(self.ctx)
|
||||||
|
|
||||||
self.add_item(self.repeat_button)
|
self.add_item(self.repeat_button)
|
||||||
self.add_item(self.prev_button)
|
self.add_item(self.prev_button)
|
||||||
@@ -124,3 +167,15 @@ class Player(View, VoiceExtension):
|
|||||||
self.add_item(self.next_button)
|
self.add_item(self.next_button)
|
||||||
self.add_item(self.shuffle_button)
|
self.add_item(self.shuffle_button)
|
||||||
|
|
||||||
|
if len(cast(VoiceChannel, self.ctx.channel).members) > 2:
|
||||||
|
self.like_button.disabled = True
|
||||||
|
elif likes and current_track and str(current_track['id']) in [str(like.id) for like in likes]:
|
||||||
|
self.like_button.style = ButtonStyle.success
|
||||||
|
|
||||||
|
if not current_track or not current_track['lyrics_available']:
|
||||||
|
self.lyrics_button.disabled = True
|
||||||
|
|
||||||
|
self.add_item(self.like_button)
|
||||||
|
self.add_item(self.lyrics_button)
|
||||||
|
|
||||||
|
return self
|
||||||
@@ -1,8 +1,46 @@
|
|||||||
|
from math import ceil
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from discord.ui import View, Button, Item
|
||||||
|
from discord import ButtonStyle, Interaction, Embed
|
||||||
|
|
||||||
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
||||||
|
|
||||||
from discord.ui import View, Button, Item
|
from discord.ui import View, Button, Item
|
||||||
from discord import ButtonStyle, Interaction, ApplicationContext
|
from discord import ButtonStyle, Interaction, ApplicationContext
|
||||||
|
|
||||||
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
||||||
from MusicBot.cogs.utils.misc import generate_playlists_embed, generate_queue_embed
|
|
||||||
|
def generate_playlists_embed(page: int, playlists: list[tuple[str, int]]) -> Embed:
|
||||||
|
count = 15 * page
|
||||||
|
length = len(playlists)
|
||||||
|
embed = Embed(
|
||||||
|
title=f"Всего плейлистов: {length}",
|
||||||
|
color=0xfed42b
|
||||||
|
)
|
||||||
|
embed.set_author(name="Ваши плейлисты")
|
||||||
|
embed.set_footer(text=f"Страница {page + 1} из {ceil(length / 10)}")
|
||||||
|
for playlist in playlists[count:count + 10]:
|
||||||
|
embed.add_field(name=playlist[0], value=f"{playlist[1]} треков", inline=False)
|
||||||
|
return embed
|
||||||
|
|
||||||
|
def generate_queue_embed(page: int, tracks_list: list[dict[str, Any]]) -> Embed:
|
||||||
|
count = 15 * page
|
||||||
|
length = len(tracks_list)
|
||||||
|
embed = Embed(
|
||||||
|
title=f"Всего: {length}",
|
||||||
|
color=0xfed42b,
|
||||||
|
)
|
||||||
|
embed.set_author(name="Очередь треков")
|
||||||
|
embed.set_footer(text=f"Страница {page + 1} из {ceil(length / 15)}")
|
||||||
|
for i, track in enumerate(tracks_list[count:count + 15], start=1 + count):
|
||||||
|
duration = track['duration_ms']
|
||||||
|
if duration:
|
||||||
|
duration_m = duration // 60000
|
||||||
|
duration_s = ceil(duration / 1000) - duration_m * 60
|
||||||
|
embed.add_field(name=f"{i} - {track['title']} - {duration_m}:{duration_s:02d}", value="", inline=False)
|
||||||
|
return embed
|
||||||
|
|
||||||
|
|
||||||
class MPNextButton(Button, VoiceExtension):
|
class MPNextButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -52,7 +90,7 @@ class MyPlaylists(View, VoiceExtension):
|
|||||||
self.add_item(prev_button)
|
self.add_item(prev_button)
|
||||||
self.add_item(next_button)
|
self.add_item(next_button)
|
||||||
|
|
||||||
class QNextButton(Button, VoiceExtension):
|
class QueueNextButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Button.__init__(self, **kwargs)
|
Button.__init__(self, **kwargs)
|
||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
@@ -67,7 +105,7 @@ class QNextButton(Button, VoiceExtension):
|
|||||||
embed = generate_queue_embed(page, tracks)
|
embed = generate_queue_embed(page, tracks)
|
||||||
await interaction.edit(embed=embed, view=QueueView(interaction))
|
await interaction.edit(embed=embed, view=QueueView(interaction))
|
||||||
|
|
||||||
class QPrevButton(Button, VoiceExtension):
|
class QueuePrevButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Button.__init__(self, **kwargs)
|
Button.__init__(self, **kwargs)
|
||||||
VoiceExtension.__init__(self, None)
|
VoiceExtension.__init__(self, None)
|
||||||
@@ -93,8 +131,8 @@ class QueueView(View, VoiceExtension):
|
|||||||
user = self.users_db.get_user(ctx.user.id)
|
user = self.users_db.get_user(ctx.user.id)
|
||||||
count = 15 * user['queue_page']
|
count = 15 * user['queue_page']
|
||||||
|
|
||||||
next_button = QNextButton(style=ButtonStyle.primary, emoji='▶️')
|
next_button = QueueNextButton(style=ButtonStyle.primary, emoji='▶️')
|
||||||
prev_button = QPrevButton(style=ButtonStyle.primary, emoji='◀️')
|
prev_button = QueuePrevButton(style=ButtonStyle.primary, emoji='◀️')
|
||||||
|
|
||||||
if not tracks[count + 15:]:
|
if not tracks[count + 15:]:
|
||||||
next_button.disabled = True
|
next_button.disabled = True
|
||||||
Reference in New Issue
Block a user