после нажатия кнопки предидущее, песня возвращается на примерно 3 секунды, но потом все треки начинают быстро пролистываться вперёд


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

# Настройка логирования
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 -loglevel warning'  # Уровень логирования можно изменить по необходимости
}

# Настройки yt_dlp для извлечения аудио
ytdl_opts = {
    'format': 'bestaudio/best',
    'noplaylist': True,            # Отключаем обработку плейлистов, так как используем статический список
    'quiet': True,
    'no_warnings': True,
    'default_search': 'auto'
}

ytdl = yt_dlp.YoutubeDL(ytdl_opts)


class Song:
    """Класс для хранения информации о песне."""
    def __init__(self, title: str, url: str, thumbnail: str):
        self.title = title
        self.url = url
        self.thumbnail = thumbnail


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_song(cls, song: Song, *, loop, stream=True):
        """
        Извлекает информацию о песне и создает аудио источник.
        """
        try:
            logger.info(f"Извлечение информации о треке: {song.title}")
            # Извлекаем полную информацию о записи с помощью yt_dlp
            data = await loop.run_in_executor(None, lambda: ytdl.extract_info(song.url, download=not stream))
            if data is None:
                logger.error("Не удалось извлечь информацию о записи.")
                return None

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

            logger.info(f"Извлечено видео: {title}")
            logger.info(f"Thumbnail URL: {thumbnail}")
            logger.info(f"Audio URL: {audio_url}")  # Для отладки

            # Создаём источник для 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.playlist = []  # Список песен
        self.current_index = -1  # Индекс текущей песни
        self.current_embed_message = None
        self.loop_album = False
        self.loop_single = False
        self.voice_client = None
        self.current_text_channel = None
        self.play_lock = asyncio.Lock()  # Блокировка для управления воспроизведением

        # Статический плейлист: добавьте сюда свои 18 песен
        self.song_urls = [
            "https://www.youtube.com/watch?v=Vr-DOV-TP5s",
            "https://www.youtube.com/watch?v=apWlgn-nrEE",
            "https://www.youtube.com/watch?v=RGF6nj6tZKA",
            "https://www.youtube.com/watch?v=OQGdrezi0Y4",
            "https://www.youtube.com/watch?v=ocRN6Hz3LrI",
            "https://www.youtube.com/watch?v=FGeaG4eL4OY",
            "https://www.youtube.com/watch?v=ioiG_BNTuOA",
            "https://www.youtube.com/watch?v=T2aNrTrnFAw",
            "https://www.youtube.com/watch?v=3e1fUvqozAM",
            "https://www.youtube.com/watch?v=k13LlCabKdQ",
            "https://www.youtube.com/watch?v=UrJMZfMELVQ",
            "https://www.youtube.com/watch?v=7yNwmvNI0k8",
            "https://www.youtube.com/watch?v=u7un-AAd3Z0",
            "https://www.youtube.com/watch?v=6X-G4sgzHiA",
            "https://www.youtube.com/watch?v=jirQpN2wr9A",
            "https://www.youtube.com/watch?v=KxpGxVQOkG0",
            "https://www.youtube.com/watch?v=oghlaYD2_FQ",
            "https://www.youtube.com/watch?v=HX6NAxBgfKM",
        ]

    @commands.slash_command(name="серегапират", description="Начать воспроизведение музыки.")
    async def play_command(self, inter: disnake.ApplicationCommandInteraction):
        """
        Команда для запуска воспроизведения музыки.
        """
        # Проверяем, находится ли пользователь в голосовом канале
        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:
            try:
                self.voice_client = await voice_channel.connect()
                logger.info(f"Подключился к голосовому каналу: {voice_channel.name}")
            except Exception as e:
                logger.error(f"Не удалось подключиться к голосовому каналу: {e}")
                await inter.response.send_message(
                    "Не удалось подключиться к голосовому каналу.",
                    ephemeral=True
                )
                return
        else:
            self.voice_client = inter.guild.voice_client
            if self.voice_client.channel != voice_channel:
                try:
                    await self.voice_client.move_to(voice_channel)
                    logger.info(f"Перемещён в голосовой канал: {voice_channel.name}")
                except Exception as e:
                    logger.error(f"Не удалось переместиться в голосовой канал: {e}")
                    await inter.response.send_message(
                        "Не удалось переместиться в голосовой канал.",
                        ephemeral=True
                    )
                    return

        self.current_text_channel = inter.channel

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

        # Загрузка плейлиста и начало воспроизведения
        await self.load_playlist()

        # Начинаем воспроизведение первой песни, если не воспроизводится
        async with self.play_lock:
            if not self.voice_client.is_playing() and not self.voice_client.is_paused():
                if self.playlist:
                    self.current_index = 0
                    await self.play_current_song()
                else:
                    await self.current_text_channel.send("Плейлист пуст.")

    async def load_playlist(self):
        """
        Загружает информацию о песнях из статического списка URL и добавляет их в плейлист.
        После загрузки первой песни начинает её воспроизведение, а остальные загружает асинхронно.
        """
        try:
            logger.info("Загрузка плейлиста...")
            # Загрузка первой песни
            if self.song_urls:
                first_url = self.song_urls[0]
                try:
                    info = await self.bot.loop.run_in_executor(None, lambda: ytdl.extract_info(first_url, download=False))
                    if info is None:
                        logger.warning(f"Не удалось извлечь информацию для URL: {first_url}")
                    else:
                        title = info.get('title', 'Unknown Title')
                        thumbnail = info.get('thumbnail', '')

                        song = Song(title=title, url=first_url, thumbnail=thumbnail)
                        self.playlist.append(song)
                        logger.info(f"Добавлена песня: {song.title}")
                except Exception as e:
                    logger.error(f"Ошибка при обработке URL {first_url}: {e}")

                # Асинхронная загрузка остальных песен
                asyncio.create_task(self.async_load_rest_songs())
            else:
                logger.warning("Список песен пуст.")
        except Exception as e:
            logger.error(f"Ошибка при загрузке плейлиста: {e}")
            await self.current_text_channel.send(f"Произошла ошибка при загрузке плейлиста: {e}")

    async def async_load_rest_songs(self):
        """
        Асинхронно загружает остальные песни и добавляет их в плейлист.
        """
        try:
            for url in self.song_urls[1:]:
                try:
                    info = await self.bot.loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=False))
                    if info is None:
                        logger.warning(f"Не удалось извлечь информацию для URL: {url}")
                        continue

                    # Если это плейлист, пропускаем (на случай ошибок)
                    if 'entries' in info:
                        logger.warning(f"URL содержит плейлист, а не отдельную песню: {url}")
                        continue

                    title = info.get('title', 'Unknown Title')
                    thumbnail = info.get('thumbnail', '')

                    song = Song(title=title, url=url, thumbnail=thumbnail)
                    self.playlist.append(song)
                    logger.info(f"Добавлена песня: {song.title}")
                except Exception as e:
                    logger.error(f"Ошибка при обработке URL {url}: {e}")
        except Exception as e:
            logger.error(f"Ошибка при асинхронной загрузке песен: {e}")

    async def play_current_song(self):
        """
        Воспроизводит текущую песню по self.current_index.
        """
        if not (0 <= self.current_index < len(self.playlist)):
            logger.error("Индекс текущей песни вне диапазона.")
            return

        song = self.playlist[self.current_index]
        source = await YTDLSource.from_song(song, loop=self.bot.loop, stream=True)
        if source is None:
            logger.error(f"Не удалось создать источник для песни: {song.title}")
            await self.next_song(auto=True)
            return

        # Проверяем наличие аудио URL
        if not source.url:
            logger.error(f"Отсутствует audio_url для песни: {song.title}")
            await self.next_song(auto=True)
            return

        # Останавливаем текущую песню, если она играет или на паузе
        if self.voice_client.is_playing() or self.voice_client.is_paused():
            self.voice_client.stop()

        # Запускаем воспроизведение
        self.voice_client.play(source, after=self.after_song)

        logger.info(f"Сейчас играет: {song.title}")

        # Создание или обновление 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)

    def after_song(self, error):
        """
        Обратный вызов после завершения песни.
        """
        if error:
            logger.error(f"Ошибка при воспроизведении песни: {error}")
            # Отправляем сообщение об ошибке в канал
            if self.current_text_channel:
                asyncio.run_coroutine_threadsafe(
                    self.current_text_channel.send(f"Ошибка при воспроизведении песни: {error}"),
                    self.bot.loop
                )
        # Планируем воспроизведение следующей песни
        asyncio.run_coroutine_threadsafe(self.next_song(auto=True), self.bot.loop)

    async def next_song(self, auto=False):
        """
        Воспроизводит следующую песню.
        Если auto=True, это вызов из after_song, иначе из взаимодействия пользователя.
        """
        async with self.play_lock:
            if self.loop_single:
                # Повторяем текущую песню
                await self.play_current_song()
                return

            self.current_index += 1

            if self.current_index >= len(self.playlist):
                if self.loop_album:
                    self.current_index = 0
                else:
                    await self.current_text_channel.send("Достигнут конец плейлиста.")
                    self.current_index = len(self.playlist)  # Устанавливаем индекс вне диапазона
                    return

            await self.play_current_song()

            if not auto:
                await self.current_text_channel.send(f"Следующая песня: {self.playlist[self.current_index].title}")

    async def previous_song(self):
        """
        Воспроизводит предыдущую песню.
        """
        async with self.play_lock:
            if self.current_index > 0:
                self.current_index -= 1
            else:
                if self.loop_album:
                    self.current_index = len(self.playlist) - 1
                else:
                    await self.current_text_channel.send("Это первая песня в плейлисте.")
                    return

            await self.play_current_song()
            await self.current_text_channel.send(f"Возвращаемся к: {self.playlist[self.current_index].title}")

    async def pause_resume_song(self):
        """
        Приостанавливает или возобновляет воспроизведение.
        """
        if self.voice_client.is_playing():
            self.voice_client.pause()
            await self.update_embed(content="Музыка на паузе.")
        elif self.voice_client.is_paused():
            self.voice_client.resume()
            await self.update_embed(content="Музыка продолжена.")
        else:
            await self.current_text_channel.send("Музыка не воспроизводится.")

    async def update_embed(self, content: str):
        """
        Обновляет embed-сообщение с дополнительным контентом.
        """
        if self.current_embed_message:
            try:
                await self.current_embed_message.edit(content=content)
            except disnake.NotFound:
                pass

    @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

        custom_id = inter.data.custom_id

        if custom_id == "pause":
            await self.pause_resume_song()
            await inter.response.defer()  # Отложенный ответ, так как мы уже обновили сообщение

        elif custom_id == "next":
            if self.voice_client.is_playing() or self.voice_client.is_paused():
                self.voice_client.stop()
                # Воспроизведение следующей песни будет вызвано `after_song`
                await inter.response.send_message("Следующая песня.", ephemeral=True)
            else:
                await inter.response.send_message("Музыка не воспроизводится.", ephemeral=True)

        elif custom_id == "previous":
            await self.previous_song()
            await inter.response.defer()

        elif 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 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 cog_unload(self):
        """
        Корректное завершение работы Cog.
        """
        if self.voice_client:
            await self.voice_client.disconnect()

    @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__":