INFO:__main__:Thumbnail URL: https://i.ytimg.com/vi/T2aNrTrnFAw/maxresdefault.jpg
INFO:__main__:Audio URL: https://rr5---sn-4g5ednld.googlevideo.com/videoplayback?expire=1727295340&ei=DBv0Zt6gDaSC6dsP6Pvx4QU&ip=79.137.194.235&id=o-AHr_rDsgkHltMabEfBjEWn1q1kqwzKg_Dtil1tekD04w&itag=251&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=Jf&mm=31%2C26&mn=sn-4g5ednld%2Csn-hpa7knll&ms=au%2Conr&mv=m&mvi=5&pl=23&initcwndbps=1345000&vprv=1&svpuc=1&mime=audio%2Fwebm&rqh=1&gir=yes&clen=2834988&dur=165.701&lmt=1691446277363904&mt=1727273350&fvip=3&keepalive=yes&fexp=51299151&c=IOS&txp=5532434&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRAIgZi9wCkkJIgYNMPhoTC6zFfqhx4eFnzMdIxMeEs1KrScCID7W-D2E8elL3nVR45XRbKuigPlwes8SjD9TmVyft7Rc&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRAIgcSxilZOaSRCTvZ3AtmQGzJsA5_-Uj-zTdcnw41pYD6MCIDLnmH62QjlSSCc98QUg9-gHUU21qzF1WSITZ-BoxXn8
INFO:__main__:Сейчас играет: я поднимаю свою голову вверх
INFO:disnake.client:Got a request to RESUME the websocket.
INFO:disnake.voice_client:Disconnecting from voice normally, close code 1000.
INFO:disnake.player:Preparing to terminate ffmpeg process 779322.
INFO:disnake.player:ffmpeg process 779322 has not terminated. Waiting to terminate...
INFO:__main__:Извлечение информации о треке: Серега пират - В этой траве
INFO:disnake.player:ffmpeg process 779322 should have terminated with a return code of -9.
INFO:disnake.voice_client:The voice handshake is being terminated for Channel ID 1205987745890050150 (Guild ID 1021683363242848266)
Ignoring exception in on_disconnect
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/disnake/client.py", line 703, in _run_event
    await coro(*args, **kwargs)
  File "/root/seregapirat.py", line 454, in on_disconnect
    await self.voice_client.disconnect()
  File "/usr/local/lib/python3.10/dist-packages/disnake/voice_client.py", line 487, in disconnect
    await self.voice_disconnect()
  File "/usr/local/lib/python3.10/dist-packages/disnake/voice_client.py", line 319, in voice_disconnect
    await self.channel.guild.change_voice_state(channel=None)
  File "/usr/local/lib/python3.10/dist-packages/disnake/guild.py", line 4434, in change_voice_state
    await ws.voice_state(self.id, channel_id, self_mute, self_deaf)
  File "/usr/local/lib/python3.10/dist-packages/disnake/gateway.py", line 838, in voice_state
    await self.send_as_json(payload)
  File "/usr/local/lib/python3.10/dist-packages/disnake/gateway.py", line 747, in send_as_json
    await self.send(utils._to_json(data))
  File "/usr/local/lib/python3.10/dist-packages/disnake/gateway.py", line 743, in send
    await self.socket.send_str(data)
  File "/usr/local/lib/python3.10/dist-packages/aiohttp/client_ws.py", line 225, in send_str
    await self._writer.send(data, binary=False, compress=compress)
  File "/usr/local/lib/python3.10/dist-packages/aiohttp/http_websocket.py", line 726, in send
    await self._send_frame(message, WSMsgType.TEXT, compress)
  File "/usr/local/lib/python3.10/dist-packages/aiohttp/http_websocket.py", line 626, in _send_frame
    raise ConnectionResetError("Cannot write to closing transport")
ConnectionResetError: Cannot write to closing transport
INFO:disnake.gateway:Shard ID None has sent the RESUME payload.
INFO:disnake.gateway:Shard ID None has successfully RESUMED session 5556a1c4e8caae3eb24f2ee64bcea577 under trace ["gateway-prd-us-east1-c-ndd6",{"micros":585,"calls":["id_created",{"micros":0,"calls":[]},"session_lookup_time",{"micros":356,"calls":[]},"session_lookup_finished",{"micros":13,"calls":[]},"discord-sessions-prd-1-146",{"micros":28}]}].
INFO:__main__:Извлечено видео: Серега пират - В этой траве
INFO:__main__:Thumbnail URL: https://i.ytimg.com/vi/3e1fUvqozAM/maxresdefault.jpg
INFO:__main__:Audio URL: https://rr4---sn-4g5ednsz.googlevideo.com/videoplayback?expire=1727295371&ei=Kxv0ZuWADpGP6dsP5Mi20Ac&ip=79.137.194.235&id=o-ABDfWPEALbrM8RKhPOwL-J1ae8hMdxuftMmkYnaWMKYJ&itag=251&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=kc&mm=31%2C29&mn=sn-4g5ednsz%2Csn-4g5e6nsz&ms=au%2Crdu&mv=m&mvi=4&pl=23&initcwndbps=1345000&bui=AXLXGFRSYsxJpS-oz2V8-q4rbxpkbPHzkWLJ242WXwKQWDeVYqsy7WOPusF1ESOx1sIq5_MgGOX2tMU0&spc=54MbxePRjDcUQt43hR64S3xG2q_9y5l685QeWimuzlv6t0M3tA&vprv=1&svpuc=1&mime=audio%2Fwebm&ns=ARvLfN9-yQePKQezmegYmwMQ&rqh=1&gir=yes&clen=2232834&dur=131.681&lmt=1722504337487349&mt=1727273350&fvip=3&keepalive=yes&fexp=51299151%2C51300760&c=WEB_CREATOR&sefc=1&txp=5532434&n=W5d7yUUc4j_OOg&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRQIgbGTB-P_4ya8UPXZtxssCeMy-eh_AlC45_4ub0fN2vRoCIQDtZ7ldKQbyNQjEt1B2WCUXspt4M7sKyH8YVPDFBqw22g%3D%3D&sig=AJfQdSswRAIgNFwvmGk_k9LzRcr9XT4YLsfgSE76jJJ47ZMykcbtQoICIEUKUhM04fZNcGGC7hDRkKGdTmMYabf_Y7eHOw39usIl
INFO:disnake.player:Preparing to terminate ffmpeg process 779347.
INFO:disnake.player:ffmpeg process 779347 has not terminated. Waiting to terminate...
INFO:disnake.player:ffmpeg process 779347 should have terminated with a return code of -9.
INFO:disnake.client:Got a request to RESUME the websocket.
INFO:disnake.gateway:Shard ID None has sent the RESUME payload.
INFO:disnake.gateway:Shard ID None has successfully RESUMED session 5556a1c4e8caae3eb24f2ee64bcea577 under trace ["gateway-prd-us-east1-c-bp2j",{"micros":6818,"calls":["id_created",{"micros":0,"calls":[]},"session_lookup_time",{"micros":6522,"calls":[]},"session_lookup_finished",{"micros":15,"calls":[]},"discord-sessions-prd-1-146",{"micros":23}]}].








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