mirror of
https://github.com/deadcxap/YandexMusicDiscordBot.git
synced 2026-01-11 22:01:39 +03:00
feat: Add /account likes and user playlists search.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
from math import ceil
|
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
from asyncio import gather
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext.commands import Cog
|
from discord.ext.commands import Cog
|
||||||
@@ -13,7 +13,7 @@ from MusicBot.cogs.utils.find import (
|
|||||||
process_album, process_track, process_artist, process_playlist,
|
process_album, process_track, process_artist, process_playlist,
|
||||||
ListenAlbum, ListenTrack, ListenArtist, ListenPlaylist
|
ListenAlbum, ListenTrack, ListenArtist, ListenPlaylist
|
||||||
)
|
)
|
||||||
from MusicBot.cogs.utils.misc import MyPlalistsView, generate_playlist_embed
|
from MusicBot.cogs.utils.misc import MyPlalists, ListenLikesPlaylist, generate_playlist_embed, generate_likes_embed
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(General(bot))
|
bot.add_cog(General(bot))
|
||||||
@@ -51,9 +51,10 @@ class General(Cog):
|
|||||||
embed.add_field(
|
embed.add_field(
|
||||||
name='__Основные команды__',
|
name='__Основные команды__',
|
||||||
value="""
|
value="""
|
||||||
|
`account`
|
||||||
`find`
|
`find`
|
||||||
`help`
|
`help`
|
||||||
`account`
|
`like`
|
||||||
`queue`
|
`queue`
|
||||||
`track`
|
`track`
|
||||||
`voice`
|
`voice`
|
||||||
@@ -62,16 +63,20 @@ class General(Cog):
|
|||||||
|
|
||||||
embed.set_author(name='YandexMusic')
|
embed.set_author(name='YandexMusic')
|
||||||
embed.set_footer(text='©️ Bananchiki')
|
embed.set_footer(text='©️ Bananchiki')
|
||||||
|
elif command == 'account':
|
||||||
|
embed.description += ("Ввести токен от Яндекс Музыки. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
|
||||||
|
"```/account login <token>```\n"
|
||||||
|
"Удалить токен из датабазы бота.\n```/account remove```\n"
|
||||||
|
"Получить ваши плейлисты. Чтобы добавить плейлист в очередь, используйте команду /find.\n```/account playlists```\n"
|
||||||
|
"Получить плейлист «Мне нравится». \n```/account likes```\n")
|
||||||
elif command == 'find':
|
elif command == 'find':
|
||||||
embed.description += ("Вывести информацию о треке (по умолчанию), альбоме, авторе или плейлисте. Позволяет добавить музыку в очередь. "
|
embed.description += ("Вывести информацию о треке (по умолчанию), альбоме, авторе или плейлисте. Позволяет добавить музыку в очередь. "
|
||||||
"В названии можно уточнить автора или версию. Возвращается лучшее совпадение.\n```/find <название> <тип>```")
|
"В названии можно уточнить автора или версию. Возвращается лучшее совпадение.\n```/find <название> <тип>```")
|
||||||
elif command == 'help':
|
elif command == 'help':
|
||||||
embed.description += ("Вывести список всех команд.\n```/help```\n"
|
embed.description += ("Вывести список всех команд.\n```/help```\n"
|
||||||
"Получить информацию о конкретной команде.\n```/help <команда>```")
|
"Получить информацию о конкретной команде.\n```/help <команда>```")
|
||||||
elif command == 'account':
|
elif command == 'like':
|
||||||
embed.description += ("Ввести токен от Яндекс Музыки. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
|
embed.description += "Добавить трек в плейлист «Мне нравится».\n```/like```"
|
||||||
"```/account login <token>```\n"
|
|
||||||
"Удалить токен из датабазы бота.\n```/account remove```")
|
|
||||||
elif command == 'queue':
|
elif command == 'queue':
|
||||||
embed.description += ("Получить очередь треков. По 15 элементов на страницу.\n```/queue get```\n"
|
embed.description += ("Получить очередь треков. По 15 элементов на страницу.\n```/queue get```\n"
|
||||||
"Очистить очередь треков и историю прослушивания. Требует согласия части слушателей.\n```/queue clear```\n"
|
"Очистить очередь треков и историю прослушивания. Требует согласия части слушателей.\n```/queue clear```\n"
|
||||||
@@ -111,7 +116,27 @@ class General(Cog):
|
|||||||
self.db.update(ctx.user.id, {'ym_token': None})
|
self.db.update(ctx.user.id, {'ym_token': None})
|
||||||
await ctx.respond(f'Токен был удалён.', delete_after=15, ephemeral=True)
|
await ctx.respond(f'Токен был удалён.', delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
@account.command(description="Получить плейлисты пользователя.")
|
@account.command(description="Получить плейлист «Мне нравится»")
|
||||||
|
async def likes(self, ctx: discord.ApplicationContext) -> None:
|
||||||
|
token = self.db.get_ym_token(ctx.user.id)
|
||||||
|
if not token:
|
||||||
|
await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
client = await YMClient(token).init()
|
||||||
|
if not client.me or not client.me.account or not client.me.account.uid:
|
||||||
|
await ctx.respond('❌ Что-то пошло не так. Повторите попытку позже.', delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
likes = await client.users_likes_tracks()
|
||||||
|
if not likes:
|
||||||
|
await ctx.respond('❌ Что-то пошло не так. Повторите попытку позже.', delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
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
|
||||||
|
embed = generate_likes_embed(tracks)
|
||||||
|
await ctx.respond(embed=embed, view=ListenLikesPlaylist(tracks))
|
||||||
|
|
||||||
|
@account.command(description="Получить ваши плейлисты.")
|
||||||
async def playlists(self, ctx: discord.ApplicationContext) -> None:
|
async def playlists(self, ctx: discord.ApplicationContext) -> None:
|
||||||
token = self.db.get_ym_token(ctx.user.id)
|
token = self.db.get_ym_token(ctx.user.id)
|
||||||
if not token:
|
if not token:
|
||||||
@@ -125,7 +150,7 @@ class General(Cog):
|
|||||||
playlists: list[tuple[str, int]] = [(playlist.title, playlist.track_count) for playlist in playlists_list] # type: ignore
|
playlists: list[tuple[str, int]] = [(playlist.title, playlist.track_count) for playlist in playlists_list] # type: ignore
|
||||||
self.db.update(ctx.user.id, {'playlists': playlists, 'playlists_page': 0})
|
self.db.update(ctx.user.id, {'playlists': playlists, 'playlists_page': 0})
|
||||||
embed = generate_playlist_embed(0, playlists)
|
embed = generate_playlist_embed(0, playlists)
|
||||||
await ctx.respond(embed=embed, view=MyPlalistsView(ctx), ephemeral=True)
|
await ctx.respond(embed=embed, view=MyPlalists(ctx), ephemeral=True)
|
||||||
|
|
||||||
@discord.slash_command(description="Найти контент и отправить информацию о нём. Возвращается лучшее совпадение.")
|
@discord.slash_command(description="Найти контент и отправить информацию о нём. Возвращается лучшее совпадение.")
|
||||||
@discord.option(
|
@discord.option(
|
||||||
@@ -137,7 +162,7 @@ class General(Cog):
|
|||||||
"content_type",
|
"content_type",
|
||||||
description="Тип искомого контента.",
|
description="Тип искомого контента.",
|
||||||
type=discord.SlashCommandOptionType.string,
|
type=discord.SlashCommandOptionType.string,
|
||||||
choices=['Artist', 'Album', 'Track', 'Playlist'],
|
choices=['Artist', 'Album', 'Track', 'Playlist', 'User Playlist'],
|
||||||
default='Track'
|
default='Track'
|
||||||
)
|
)
|
||||||
async def find(
|
async def find(
|
||||||
@@ -146,10 +171,9 @@ class General(Cog):
|
|||||||
name: str,
|
name: str,
|
||||||
content_type: str = 'Track'
|
content_type: str = 'Track'
|
||||||
) -> None:
|
) -> None:
|
||||||
if content_type not in ['Artist', 'Album', 'Track', 'Playlist']:
|
if content_type not in ['Artist', 'Album', 'Track', 'Playlist', 'User Playlist']:
|
||||||
await ctx.respond("❌ Недопустимый тип.", delete_after=15, ephemeral=True)
|
await ctx.respond("❌ Недопустимый тип.", delete_after=15, ephemeral=True)
|
||||||
return
|
return
|
||||||
content_type = content_type.lower()
|
|
||||||
|
|
||||||
token = self.db.get_ym_token(ctx.user.id)
|
token = self.db.get_ym_token(ctx.user.id)
|
||||||
if not token:
|
if not token:
|
||||||
@@ -161,28 +185,45 @@ class General(Cog):
|
|||||||
await ctx.respond("❌ Недействительный токен. Если это не так, попробуйте ещё раз.", delete_after=15, ephemeral=True)
|
await ctx.respond("❌ Недействительный токен. Если это не так, попробуйте ещё раз.", delete_after=15, ephemeral=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
result = await client.search(name, True, content_type)
|
if content_type == 'User Playlist':
|
||||||
|
if not client.me or not client.me.account or not client.me.account.uid:
|
||||||
if not result:
|
await ctx.respond("❌ Не удалось получить информацию о пользователе.", delete_after=15, ephemeral=True)
|
||||||
await ctx.respond("❌ Что-то пошло не так. Повторите попытку позже", delete_after=15, ephemeral=True)
|
return
|
||||||
return
|
playlists = await client.users_playlists_list(client.me.account.uid)
|
||||||
|
result = None
|
||||||
if content_type == 'album' and result.albums:
|
for playlist in playlists:
|
||||||
album = result.albums.results[0]
|
if playlist.title == name:
|
||||||
embed = await process_album(album)
|
result = playlist
|
||||||
await ctx.respond(embed=embed, view=ListenAlbum(album))
|
break
|
||||||
elif content_type == 'track' and result.tracks:
|
else:
|
||||||
track: yandex_music.Track = result.tracks.results[0]
|
await ctx.respond("❌ Плейлист не найден.", delete_after=15, ephemeral=True)
|
||||||
album_id = cast(int, track.albums[0].id)
|
return
|
||||||
embed = await process_track(track)
|
|
||||||
await ctx.respond(embed=embed, view=ListenTrack(track, album_id))
|
embed = await process_playlist(result)
|
||||||
elif content_type == 'artist' and result.artists:
|
await ctx.respond(embed=embed, view=ListenPlaylist(result))
|
||||||
artist = result.artists.results[0]
|
|
||||||
embed = await process_artist(artist)
|
|
||||||
await ctx.respond(embed=embed, view=ListenArtist(artist))
|
|
||||||
elif content_type == 'playlist' and result.playlists:
|
|
||||||
playlist = result.playlists.results[0]
|
|
||||||
embed = await process_playlist(playlist)
|
|
||||||
await ctx.respond(embed=embed, view=ListenPlaylist(playlist))
|
|
||||||
else:
|
else:
|
||||||
await ctx.respond("❌ По запросу ничего не найдено.", delete_after=15, ephemeral=True)
|
result = await client.search(name, True, content_type.lower())
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
await ctx.respond("❌ Что-то пошло не так. Повторите попытку позже", delete_after=15, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
if content_type == 'Album' and result.albums:
|
||||||
|
album = result.albums.results[0]
|
||||||
|
embed = await process_album(album)
|
||||||
|
await ctx.respond(embed=embed, view=ListenAlbum(album))
|
||||||
|
elif content_type == 'Track' and result.tracks:
|
||||||
|
track: yandex_music.Track = result.tracks.results[0]
|
||||||
|
album_id = cast(int, track.albums[0].id)
|
||||||
|
embed = await process_track(track)
|
||||||
|
await ctx.respond(embed=embed, view=ListenTrack(track, album_id))
|
||||||
|
elif content_type == 'Artist' and result.artists:
|
||||||
|
artist = result.artists.results[0]
|
||||||
|
embed = await process_artist(artist)
|
||||||
|
await ctx.respond(embed=embed, view=ListenArtist(artist))
|
||||||
|
elif content_type == 'Playlist' and result.playlists:
|
||||||
|
playlist = result.playlists.results[0]
|
||||||
|
embed = await process_playlist(playlist)
|
||||||
|
await ctx.respond(embed=embed, view=ListenPlaylist(playlist))
|
||||||
|
else:
|
||||||
|
await ctx.respond("❌ По запросу ничего не найдено.", delete_after=15, ephemeral=True)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from asyncio import gather
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import cast
|
from typing import cast
|
||||||
@@ -8,7 +9,7 @@ from yandex_music import Track, Album, Artist, Playlist, Label
|
|||||||
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, Embed
|
||||||
|
|
||||||
from MusicBot.cogs.utils.voice import VoiceExtension, get_average_color_from_url
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension, get_average_color_from_url
|
||||||
|
|
||||||
class PlayTrackButton(Button, VoiceExtension):
|
class PlayTrackButton(Button, VoiceExtension):
|
||||||
|
|
||||||
@@ -120,7 +121,8 @@ class PlayPlaylistButton(Button, VoiceExtension):
|
|||||||
gid = interaction.guild.id
|
gid = interaction.guild.id
|
||||||
guild = self.db.get_guild(gid)
|
guild = self.db.get_guild(gid)
|
||||||
|
|
||||||
tracks: list[Track] = [cast(Track, short_track.track) for short_track in short_tracks]
|
real_tracks = await gather(*[track_short.fetch_track_async() for track_short in self.playlist.tracks], return_exceptions=True)
|
||||||
|
tracks = [track for track in real_tracks if not isinstance(track, BaseException)] # Can't fetch user tracks
|
||||||
|
|
||||||
if guild['current_track'] is not None:
|
if guild['current_track'] is not None:
|
||||||
self.db.modify_track(gid, tracks, 'next', 'extend')
|
self.db.modify_track(gid, tracks, 'next', 'extend')
|
||||||
@@ -418,8 +420,23 @@ async def process_playlist(playlist: Playlist) -> Embed:
|
|||||||
duration = playlist.duration_ms
|
duration = playlist.duration_ms
|
||||||
likes_count = playlist.likes_count
|
likes_count = playlist.likes_count
|
||||||
|
|
||||||
cover_url = f"https://{playlist.cover.uri.replace('%%', '400x400')}" # type: ignore
|
color = 0x000
|
||||||
color = await get_average_color_from_url(cover_url)
|
cover_url = None
|
||||||
|
|
||||||
|
if playlist.cover and playlist.cover.uri:
|
||||||
|
cover_url = f"https://{playlist.cover.uri.replace('%%', '400x400')}"
|
||||||
|
else:
|
||||||
|
tracks = await playlist.fetch_tracks_async()
|
||||||
|
for i in range(len(tracks)):
|
||||||
|
track = tracks[i].track
|
||||||
|
try:
|
||||||
|
cover_url = f"https://{track.albums[0].cover_uri.replace('%%', '400x400')}" # type: ignore
|
||||||
|
break
|
||||||
|
except TypeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cover_url:
|
||||||
|
color = await get_average_color_from_url(cover_url)
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title=title,
|
title=title,
|
||||||
|
|||||||
@@ -1,10 +1,36 @@
|
|||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from yandex_music import Track
|
||||||
from discord.ui import View, Button, Item
|
from discord.ui import View, Button, Item
|
||||||
from discord import ButtonStyle, Interaction, ApplicationContext, Embed
|
from discord import ButtonStyle, Interaction, ApplicationContext, Embed
|
||||||
|
|
||||||
from MusicBot.cogs.utils.voice import VoiceExtension
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
||||||
|
|
||||||
|
def generate_likes_embed(tracks: list[Track]) -> Embed:
|
||||||
|
track_count = len(tracks)
|
||||||
|
cover_url = "https://avatars.yandex.net/get-music-user-playlist/11418140/favorit-playlist-cover.bb48fdb9b9f4/300x300"
|
||||||
|
|
||||||
|
embed = Embed(
|
||||||
|
title="Мне нравится",
|
||||||
|
description="Треки, которые вам понравились.",
|
||||||
|
color=0xce3a26,
|
||||||
|
)
|
||||||
|
embed.set_thumbnail(url=cover_url)
|
||||||
|
|
||||||
|
duration = 0
|
||||||
|
for track in tracks:
|
||||||
|
if track.duration_ms:
|
||||||
|
duration += track.duration_ms
|
||||||
|
|
||||||
|
duration_m = duration // 60000
|
||||||
|
duration_s = ceil(duration / 1000) - duration_m * 60
|
||||||
|
embed.add_field(name="Длительность", value=f"{duration_m}:{duration_s:02}")
|
||||||
|
|
||||||
|
if track_count is not None:
|
||||||
|
embed.add_field(name="Треки", value=str(track_count))
|
||||||
|
|
||||||
|
return embed
|
||||||
|
|
||||||
def generate_playlist_embed(page: int, playlists: list[tuple[str, int]]) -> Embed:
|
def generate_playlist_embed(page: int, playlists: list[tuple[str, int]]) -> Embed:
|
||||||
count = 15 * page
|
count = 15 * page
|
||||||
@@ -35,6 +61,38 @@ def generate_queue_embed(page: int, tracks_list: list[dict[str, Any]]) -> Embed:
|
|||||||
embed.add_field(name=f"{i} - {track['title']} - {duration_m}:{duration_s:02d}", value="", inline=False)
|
embed.add_field(name=f"{i} - {track['title']} - {duration_m}:{duration_s:02d}", value="", inline=False)
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
class PlayLikesButton(Button, VoiceExtension):
|
||||||
|
def __init__(self, playlist: list[Track], **kwargs):
|
||||||
|
Button.__init__(self, **kwargs)
|
||||||
|
VoiceExtension.__init__(self)
|
||||||
|
self.playlist = playlist
|
||||||
|
|
||||||
|
async def callback(self, interaction: Interaction):
|
||||||
|
if not interaction.guild or not await self.voice_check(interaction):
|
||||||
|
return
|
||||||
|
|
||||||
|
gid = interaction.guild.id
|
||||||
|
guild = self.db.get_guild(gid)
|
||||||
|
|
||||||
|
if guild['current_track'] is not None:
|
||||||
|
self.db.modify_track(gid, self.playlist, 'next', 'extend')
|
||||||
|
response_message = f"Плейлист **«Мне нравится»** был добавлен в очередь."
|
||||||
|
else:
|
||||||
|
track = self.playlist.pop(0)
|
||||||
|
self.db.modify_track(gid, self.playlist, 'next', 'extend')
|
||||||
|
await self.play_track(interaction, track)
|
||||||
|
response_message = f"Сейчас играет плейлист **«Мне нравится»**!"
|
||||||
|
|
||||||
|
if guild['current_player'] is not None and interaction.message:
|
||||||
|
await interaction.message.delete()
|
||||||
|
|
||||||
|
await interaction.respond(response_message, delete_after=15)
|
||||||
|
|
||||||
|
class ListenLikesPlaylist(View):
|
||||||
|
def __init__(self, playlist: list[Track], *items: Item, timeout: float | None = None, disable_on_timeout: bool = False):
|
||||||
|
super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout)
|
||||||
|
self.add_item(PlayLikesButton(playlist, label="Слушать в голосовом канале", style=ButtonStyle.gray))
|
||||||
|
|
||||||
class MPNextButton(Button, VoiceExtension):
|
class MPNextButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Button.__init__(self, **kwargs)
|
Button.__init__(self, **kwargs)
|
||||||
@@ -47,7 +105,7 @@ class MPNextButton(Button, VoiceExtension):
|
|||||||
page = user['playlists_page'] + 1
|
page = user['playlists_page'] + 1
|
||||||
self.users_db.update(interaction.user.id, {'playlists_page': page})
|
self.users_db.update(interaction.user.id, {'playlists_page': page})
|
||||||
embed = generate_playlist_embed(page, user['playlists'])
|
embed = generate_playlist_embed(page, user['playlists'])
|
||||||
await interaction.edit(embed=embed, view=MyPlalistsView(interaction))
|
await interaction.edit(embed=embed, view=MyPlalists(interaction))
|
||||||
|
|
||||||
class MPPrevButton(Button, VoiceExtension):
|
class MPPrevButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -61,9 +119,9 @@ class MPPrevButton(Button, VoiceExtension):
|
|||||||
page = user['playlists_page'] - 1
|
page = user['playlists_page'] - 1
|
||||||
self.users_db.update(interaction.user.id, {'playlists_page': page})
|
self.users_db.update(interaction.user.id, {'playlists_page': page})
|
||||||
embed = generate_playlist_embed(page, user['playlists'])
|
embed = generate_playlist_embed(page, user['playlists'])
|
||||||
await interaction.edit(embed=embed, view=MyPlalistsView(interaction))
|
await interaction.edit(embed=embed, view=MyPlalists(interaction))
|
||||||
|
|
||||||
class MyPlalistsView(View, VoiceExtension):
|
class MyPlalists(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)
|
VoiceExtension.__init__(self)
|
||||||
@@ -83,7 +141,6 @@ class MyPlalistsView(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 QNextButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
Button.__init__(self, **kwargs)
|
Button.__init__(self, **kwargs)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
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 import VoiceExtension
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension
|
||||||
|
|
||||||
class ToggleRepeatButton(Button, VoiceExtension):
|
class ToggleRepeatButton(Button, VoiceExtension):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@@ -77,6 +77,24 @@ class PrevTrackButton(Button, VoiceExtension):
|
|||||||
if not title:
|
if not title:
|
||||||
await interaction.respond(f"Нет треков в истории.", delete_after=15, ephemeral=True)
|
await interaction.respond(f"Нет треков в истории.", delete_after=15, ephemeral=True)
|
||||||
|
|
||||||
|
class LikeButton(Button, VoiceExtension):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
Button.__init__(self, **kwargs)
|
||||||
|
VoiceExtension.__init__(self)
|
||||||
|
|
||||||
|
async def callback(self, interaction: Interaction) -> None:
|
||||||
|
if await self.voice_check(interaction):
|
||||||
|
vc = self.get_voice_client(interaction)
|
||||||
|
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):
|
class Player(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):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import aiohttp
|
|||||||
import asyncio
|
import asyncio
|
||||||
from os import getenv
|
from os import getenv
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import cast
|
from typing import Literal, cast
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ class VoiceExtension:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def like_track(self, ctx: ApplicationContext | Interaction) -> str | 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.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -385,6 +385,7 @@ class VoiceExtension:
|
|||||||
if ym_track.id not in [track.id for track in likes.tracks]:
|
if ym_track.id not in [track.id for track in likes.tracks]:
|
||||||
await ym_track.like_async()
|
await ym_track.like_async()
|
||||||
return ym_track.title
|
return ym_track.title
|
||||||
|
else:
|
||||||
return None
|
await client.users_likes_tracks_remove(ym_track.id, client.me.account.uid) # type: ignore
|
||||||
|
return 'TRACK REMOVED'
|
||||||
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
from math import ceil
|
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@@ -6,7 +5,7 @@ from discord.ext.commands import Cog
|
|||||||
|
|
||||||
from yandex_music import Track, ClientAsync
|
from yandex_music import Track, ClientAsync
|
||||||
|
|
||||||
from MusicBot.cogs.utils.voice import VoiceExtension, generate_player_embed
|
from MusicBot.cogs.utils.voice_extension import VoiceExtension, generate_player_embed
|
||||||
from MusicBot.cogs.utils.player import Player
|
from MusicBot.cogs.utils.player import Player
|
||||||
from MusicBot.cogs.utils.misc import QueueView, generate_queue_embed
|
from MusicBot.cogs.utils.misc import QueueView, generate_queue_embed
|
||||||
|
|
||||||
@@ -136,6 +135,8 @@ class Voice(Cog, VoiceExtension):
|
|||||||
await ctx.respond("Нет воспроизводимого трека.", delete_after=15, ephemeral=True)
|
await ctx.respond("Нет воспроизводимого трека.", delete_after=15, ephemeral=True)
|
||||||
result = await self.like_track(ctx)
|
result = await self.like_track(ctx)
|
||||||
if not result:
|
if not result:
|
||||||
await ctx.respond("Трек уже добавлен в избранное.", delete_after=15, ephemeral=True)
|
await ctx.respond("❌ Операция не удалась.", delete_after=15, ephemeral=True)
|
||||||
|
elif result == 'TRACK REMOVED':
|
||||||
|
await ctx.respond("Трек был удалён из избранного.", delete_after=15, ephemeral=True)
|
||||||
else:
|
else:
|
||||||
await ctx.respond(f"Трек **{result}** был добавлен в избранное.", delete_after=15, ephemeral=True)
|
await ctx.respond(f"Трек **{result}** был добавлен в избранное.", delete_after=15, ephemeral=True)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.errors import NotFound
|
|
||||||
from discord.ext.commands import Bot
|
from discord.ext.commands import Bot
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user