Если нажать назад включается следующий а не предидущий трек

import asyncio
import disnake
from disnake.ext import commands
import yt_dlp
import logging
from collections import deque

# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Настройки FFmpeg
ffmpeg_options = {
    'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
    'options': '-vn'  # Видео отключено
}

# Настройки yt_dlp для плейлиста (быстрое извлечение списка треков)
ytdl_playlist_opts = {
    'format': 'bestaudio/best',
    'noplaylist': False,          # Разрешаем обработку плейлистов
    'extract_flat': True,         # Быстрое получение списка треков без полной информации
    'quiet': True,
    'no_warnings': True,
    'default_search': 'auto'
}

ytdl_playlist = yt_dlp.YoutubeDL(ytdl_playlist_opts)

# Настройки yt_dlp для отдельных треков (полное извлечение информации)
ytdl_track_opts = {
    'format': 'bestaudio/best',
    'noplaylist': False,
    'extract_flat': False,        # Полное извлечение информации о треке
    'quiet': True,
    'no_warnings': True,
    'default_search': 'auto'
}

ytdl_track = yt_dlp.YoutubeDL(ytdl_track_opts)


class YTDLSource(disnake.PCMVolumeTransformer):
    """
    Класс для обработки аудио источника с YouTube.
    """
    def __init__(self, source, *, data, volume=0.5):
        super().__init__(source, volume)
        self.data = data
        self.title = data.get('title', 'Unknown Title')
        self.url = data.get('url')
        self.thumbnail = data.get('thumbnail', '')

    @classmethod
    async def from_url(cls, url, *, loop, stream=False):
        """
        Извлекает информацию о песне и создаёт аудио источник.
        """
        try:
            logger.info(f"Извлечение информации о треке: {url}")
            # Извлекаем полную информацию о записи с помощью отдельного экземпляра yt_dlp
            data = await loop.run_in_executor(None, lambda: ytdl_track.extract_info(url, download=not stream))
            if data is None:
                logger.error("Не удалось извлечь информацию о записи.")
                return None

            if 'entries' in data:
                # Если это плейлист, берём первую песню
                data = data['entries'][0]

            # Получаем URL аудио
            audio_url = data.get('url')
            title = data.get('title', 'Unknown Title')
            thumbnail = data.get('thumbnail', '')

            logger.info(f"Извлечено видео: {title}")
            logger.info(f"Thumbnail URL: {thumbnail}")

            # Создаём источник для FFmpeg
            return cls(disnake.FFmpegPCMAudio(audio_url, **ffmpeg_options), data=data)
        except Exception as e:
            logger.error(f"Ошибка при извлечении записи: {e}")
            return None


class MusicBot(commands.Cog):
    """
    Cog для управления музыкальным воспроизведением.
    """
    def __init__(self, bot: commands.Bot):
        self.bot = bot
        self.song_queue = deque()  # Изменено: Используем deque вместо asyncio.Queue
        self.previous_songs = []
        self.current_embed_message = None
        self.loop_album = False
        self.loop_single = False
        self.voice_client = None
        self.current_text_channel = None
        self.playlist_loading_task = None  # Задача для асинхронной загрузки плейлиста

    @commands.slash_command(name="серегапират", description="Начать воспроизведение плейлиста.")
    async def play_playlist_command(self, inter: disnake.ApplicationCommandInteraction):
        """
        Команда для запуска воспроизведения плейлиста.
        """
        playlist_url = "https://www.youtube.com/playlist?list=PLbjb_5lp8GmuqJPTvd7mliaQNdi8e0kRf"

        # Проверяем, находится ли пользователь в голосовом канале
        if not inter.author.voice or not inter.author.voice.channel:
            await inter.response.send_message(
                "Войдите в голосовой канал, прежде чем использовать эту команду.",
                ephemeral=True
            )
            return

        voice_channel = inter.author.voice.channel

        # Подключаемся или перемещаемся в голосовой канал
        if inter.guild.voice_client is None:
            self.voice_client = await voice_channel.connect()
            logger.info(f"Подключился к голосовому каналу: {voice_channel.name}")
        else:
            self.voice_client = inter.guild.voice_client
            if self.voice_client.channel != voice_channel:
                await self.voice_client.move_to(voice_channel)
                logger.info(f"Перемещён в голосовой канал: {voice_channel.name}")

        self.current_text_channel = inter.channel

        # Отложенный ответ, чтобы избежать таймаута
        await inter.response.defer()

        # Загружаем плейлист и начинаем воспроизведение
        self.playlist_loading_task = self.bot.loop.create_task(self.load_playlist(playlist_url))
        await self.current_text_channel.send("Плейлист начал воспроизведение.")

    async def load_playlist(self, playlist_url):
        """
        Загружает песни из плейлиста и инициирует воспроизведение.
        Первую песню добавляет в очередь и запускает её воспроизведение.
        Остальные песни загружаются асинхронно во время воспроизведения.
        """
        try:
            # Извлекаем информацию о плейлисте с extract_flat=True для быстрого получения списка
            logger.info(f"Извлечение плейлиста: {playlist_url}")
            data = await asyncio.get_event_loop().run_in_executor(None, lambda: ytdl_playlist.extract_info(playlist_url, download=False))
            
            if 'entries' not in data:
                await self.current_text_channel.send("Не удалось загрузить плейлист.")
                return
            entries = data['entries']
            if not entries:
                await self.current_text_channel.send("Плейлист пуст.")
                return

            logger.info(f"Количество треков в плейлисте: {len(entries)}")

            # Создаём генератор для последовательного извлечения треков
            playlist_generator = self.get_playlist_entries(entries)

            # Загрузка и добавление первой песни в очередь
            try:
                first_song_url = next(playlist_generator)
                first_song = await YTDLSource.from_url(first_song_url, loop=self.bot.loop, stream=True)
                if first_song:
                    self.song_queue.append(first_song)  # Добавляем первую песню в конец очереди
                    logger.info(f"Добавлена в очередь: {first_song.title}")
                    # Начинаем воспроизведение, если не воспроизводится
                    if not self.voice_client.is_playing():
                        await self.play_next_song()
                else:
                    logger.error("Не удалось добавить первую песню в очередь.")
            except StopIteration:
                logger.error("Плейлист пуст при попытке загрузить первую песню.")
                return

            # Асинхронная загрузка остальных треков
            asyncio.create_task(self.async_load_tracks(playlist_generator))

        except Exception as e:
            await self.current_text_channel.send(f"Произошла ошибка при загрузке плейлиста: {e}")
            logger.error(f"Ошибка в load_playlist: {e}")

    def get_playlist_entries(self, entries):
        """
        Генератор для последовательного извлечения URL треков из плейлиста.
        """
        for entry in entries:
            if 'url' in entry:
                yield entry['url']
            else:
                logger.error("Отсутствует URL трека в записи плейлиста.")

    async def async_load_tracks(self, playlist_generator):
        """
        Асинхронно загружает треки из плейлиста и добавляет их в очередь.
        """
        try:
            for song_url in playlist_generator:
                song = await YTDLSource.from_url(song_url, loop=self.bot.loop, stream=True)
                if song:
                    self.song_queue.append(song)
                    logger.info(f"Добавлена в очередь: {song.title}")
                else:
                    logger.error(f"Не удалось добавить трек: {song_url}")
        except Exception as e:
            logger.error(f"Ошибка в async_load_tracks: {e}")

    async def play_next_song(self):
        """
        Воспроизводит следующую песню из очереди.
        """
        try:
            if self.voice_client is None:
                logger.error("Voice client не подключён.")
                return

            if self.voice_client.is_playing() or self.voice_client.is_paused():
                # Если уже играет, ничего не делаем
                return

            if not self.song_queue:
                logger.info("Очередь песен пуста.")
                return

            song = self.song_queue.popleft()
            if not isinstance(song, YTDLSource):
                logger.error("Неверный тип песни в очереди.")
                return

            def after_playing(error):
                if error:
                    logger.error(f"Ошибка при воспроизведении песни: {error}")
                # Запускаем воспроизведение следующей песни
                self.bot.loop.call_soon_threadsafe(asyncio.create_task, self.play_next_song())

            self.voice_client.play(song, after=after_playing)
            logger.info(f"Сейчас играет: {song.title}")

            # Добавляем текущую песню в историю
            self.previous_songs.append(song)

            # Создание или обновление embed-сообщения
            embed = disnake.Embed(
                title=f"Сейчас играет: {song.title}",
                description="Музыка с YouTube",
                color=disnake.Color.blue()
            )
            if song.thumbnail:
                embed.set_thumbnail(url=song.thumbnail)
            else:
                embed.description += "\n[Миниатюра недоступна]"

            components = disnake.ui.ActionRow(
                disnake.ui.Button(emoji="⏮", custom_id="previous", style=disnake.ButtonStyle.secondary),
                disnake.ui.Button(emoji="⏸", custom_id="pause", style=disnake.ButtonStyle.secondary),
                disnake.ui.Button(emoji="⏭", custom_id="next", style=disnake.ButtonStyle.secondary),
                disnake.ui.Button(
                    emoji="🔁",
                    custom_id="loop_album",
                    style=disnake.ButtonStyle.green if self.loop_album else disnake.ButtonStyle.gray
                ),
                disnake.ui.Button(
                    emoji="🔂",
                    custom_id="loop_single",
                    style=disnake.ButtonStyle.green if self.loop_single else disnake.ButtonStyle.gray
                )
            )

            if self.current_embed_message:
                try:
                    await self.current_embed_message.edit(embed=embed, components=components)
                except disnake.NotFound:
                    # Если сообщение было удалено, отправляем новое
                    self.current_embed_message = await self.current_text_channel.send(embed=embed, components=components)
            else:
                self.current_embed_message = await self.current_text_channel.send(embed=embed, components=components)

        except Exception as e:
            logger.error(f"Ошибка в play_next_song: {e}")

    @commands.Cog.listener()
    async def on_interaction(self, inter: disnake.Interaction):
        """
        Обработка нажатия на кнопки управления.
        """
        if not isinstance(inter, disnake.MessageInteraction):
            return

        if inter.message != self.current_embed_message:
            # Игнорируем взаимодействия с другими сообщениями
            return

        if inter.data.custom_id == "pause":
            if self.voice_client.is_playing():
                self.voice_client.pause()
                await inter.response.send_message("Музыка на паузе.", ephemeral=True)
            elif self.voice_client.is_paused():
                self.voice_client.resume()
                await inter.response.send_message("Музыка продолжена.", ephemeral=True)
            else:
                await inter.response.send_message("Музыка не воспроизводится.", ephemeral=True)

        elif inter.data.custom_id == "next":
            # Пропуск текущей песни
            self.voice_client.stop()
            await inter.response.send_message("Следующая песня.", ephemeral=True)

        elif inter.data.custom_id == "previous":
            # Реализация функции "Предыдущая песня"
            if self.previous_songs:
                last_song = self.previous_songs.pop()
                # Добавляем предыдущую песню в начало очереди
                self.song_queue.appendleft(last_song)
                # Пропускаем текущую песню
                self.voice_client.stop()
                await inter.response.send_message(f"Возвращаемся к: {last_song.title}", ephemeral=True)
            else:
                await inter.response.send_message("Нет предыдущих песен в истории.", ephemeral=True)

        elif inter.data.custom_id == "loop_album":
            # Переключаем состояние зацикливания альбома
            self.loop_album = not self.loop_album
            state = "включено" if self.loop_album else "выключено"
            await inter.response.send_message(f"Зацикливание альбома {state}.", ephemeral=True)

            # Обновляем стиль кнопки
            if self.current_embed_message:
                try:
                    new_style = disnake.ButtonStyle.green if self.loop_album else disnake.ButtonStyle.gray
                    components = disnake.ui.ActionRow(
                        disnake.ui.Button(emoji="⏮", custom_id="previous", style=disnake.ButtonStyle.secondary),
                        disnake.ui.Button(emoji="⏸", custom_id="pause", style=disnake.ButtonStyle.secondary),
                        disnake.ui.Button(emoji="⏭", custom_id="next", style=disnake.ButtonStyle.secondary),
                        disnake.ui.Button(
                            emoji="🔁",
                            custom_id="loop_album",
                            style=new_style
                        ),
                        disnake.ui.Button(
                            emoji="🔂",
                            custom_id="loop_single",
                            style=disnake.ButtonStyle.green if self.loop_single else disnake.ButtonStyle.gray
                        )
                    )
                    await self.current_embed_message.edit(components=components)
                except disnake.NotFound:
                    pass

        elif inter.data.custom_id == "loop_single":
            # Переключаем состояние зацикливания текущей песни
            self.loop_single = not self.loop_single
            state = "включено" if self.loop_single else "выключено"
            await inter.response.send_message(f"Зацикливание текущей песни {state}.", ephemeral=True)

            # Обновляем стиль кнопки
            if self.current_embed_message:
                try:
                    new_style = disnake.ButtonStyle.green if self.loop_single else disnake.ButtonStyle.gray
                    components = disnake.ui.ActionRow(
                        disnake.ui.Button(emoji="⏮", custom_id="previous", style=disnake.ButtonStyle.secondary),
                        disnake.ui.Button(emoji="⏸", custom_id="pause", style=disnake.ButtonStyle.secondary),
                        disnake.ui.Button(emoji="⏭", custom_id="next", style=disnake.ButtonStyle.secondary),
                        disnake.ui.Button(
                            emoji="🔁",
                            custom_id="loop_album",
                            style=disnake.ButtonStyle.green if self.loop_album else disnake.ButtonStyle.gray
                        ),
                        disnake.ui.Button(
                            emoji="🔂",
                            custom_id="loop_single",
                            style=new_style
                        )
                    )
                    await self.current_embed_message.edit(components=components)
                except disnake.NotFound:
                    pass

    async def play_next_song_if_needed(self):
        """
        Проверяет очередь и запускает воспроизведение следующей песни, учитывая зацикливание.
        """
        if self.voice_client.is_playing() or self.voice_client.is_paused():
            # Если уже играет, ничего не делаем
            return

        if not self.song_queue and not self.loop_album:
            logger.info("Очередь песен пуста и зацикливание альбома выключено.")
            return

        if self.song_queue:
            await self.play_next_song()
        elif self.loop_album:
            # Если активировано зацикливание альбома, перезапускаем загрузку плейлиста
            logger.info("Перезапуск плейлиста для зацикливания альбома.")
            playlist_url = "https://www.youtube.com/playlist?list=PLbjb_5lp8GmuqJPTvd7mliaQNdi8e0kRf"  # Убедитесь, что это правильный URL
            self.playlist_loading_task = self.bot.loop.create_task(self.load_playlist(playlist_url))

    async def check_loop_single(self):
        """
        Проверяет, активировано ли зацикливание текущей песни, и действует соответственно.
        """
        if self.loop_single and self.previous_songs:
            current_song = self.previous_songs[-1]
            self.song_queue.appendleft(current_song)
            logger.info("Зацикливание текущей песни включено.")

    def cog_unload(self):
        """
        Корректное завершение работы Cog.
        """
        if self.playlist_loading_task and not self.playlist_loading_task.done():
            self.playlist_loading_task.cancel()

    @commands.Cog.listener()
    async def on_disconnect(self):
        """
        Обработка отключения от голосового канала.
        """
        if self.voice_client:
            await self.voice_client.disconnect()


# Настройка и запуск бота
intents = disnake.Intents.default()
intents.message_content = True
intents.guilds = True
intents.voice_states = True

bot = commands.Bot(command_prefix="!", intents=intents)

@bot.event
async def on_ready():
    print(f"Бот {bot.user} подключился и готов к работе!")

# Добавляем Cog в бота
bot.add_cog(MusicBot(bot))

if __name__ == "__main__":
    bot.run("M")  # Замените на ваш реальный токен