Beispiel #1
0
    async def on_red_audio_track_auto_play(
        self,
        guild: discord.Guild,
        track: lavalink.Track,
        requester: discord.Member,
        player: lavalink.Player,
    ):
        notify_channel = self.bot.get_channel(player.fetch("channel"))
        tries = 0
        while not player._is_playing:
            await asyncio.sleep(0.1)
            if tries > 1000:
                return

        if notify_channel and not player.fetch("autoplay_notified", False):
            await self.send_embed_msg(notify_channel, title=_("Auto Play started."))
            player.store("autoplay_notified", True)
    async def _eq_check(self, ctx: commands.Context, player: lavalink.Player) -> None:
        eq = player.fetch("eq", Equalizer())

        config_bands = await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands()
        if not config_bands:
            config_bands = eq.bands
            await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands)

        if eq.bands != config_bands:
            band_num = list(range(0, eq.band_count))
            band_value = config_bands
            eq_dict = {}
            for k, v in zip(band_num, band_value):
                eq_dict[k] = v
            for band, value in eq_dict.items():
                eq.set_gain(band, value)
            player.store("eq", eq)
            await self._apply_gains(ctx.guild.id, config_bands)
Beispiel #3
0
    async def on_red_audio_track_auto_play(
        self,
        guild: discord.Guild,
        track: lavalink.Track,
        requester: discord.Member,
        player: lavalink.Player,
    ):
        notify_channel = self.bot.get_channel(player.fetch("channel"))
        tries = 0
        while not player._is_playing:
            await asyncio.sleep(0.1)
            if tries > 1000:
                return

        if notify_channel and not player.fetch("autoplay_notified", False):
            if (len(player.manager.players) < 10 or not player._last_resume
                    and player._last_resume + datetime.timedelta(seconds=60) >
                    datetime.datetime.now(tz=datetime.timezone.utc)):
                await self.send_embed_msg(notify_channel,
                                          title=_("Auto Play started."))
            player.store("autoplay_notified", True)
    async def lavalink_event_handler(self, player: lavalink.Player,
                                     event_type: lavalink.LavalinkEvents,
                                     extra):

        if event_type == lavalink.LavalinkEvents.TRACK_START:
            track = player.current
            if track:
                embed = Embed(
                    description="▶️ **Now playing** [{0.title}]({0.uri}) !".
                    format(track))
                message = await track.channel.send(embed=embed)
                track.start_message = message
                if not len(player.queue) > 1:
                    player.store("m_msg", message)

        if event_type == lavalink.LavalinkEvents.TRACK_END:
            msg = player.fetch("m_msg")
            if (extra == lavalink.TrackEndReason.FINISHED
                    or extra == lavalink.TrackEndReason.REPLACED):
                if msg:
                    await msg.delete()

            if len(player.queue):
                await player.stop()
Beispiel #5
0
    async def lavalink_event_handler(self, player: lavalink.Player,
                                     event_type: lavalink.LavalinkEvents,
                                     extra) -> None:
        current_track = player.current
        current_channel = player.channel
        guild = self.rgetattr(current_channel, "guild", None)
        if not (current_channel and guild):
            player.store("autoplay_notified", False)
            await player.stop()
            await player.disconnect()
            return
        if await self.bot.cog_disabled_in_guild(self, guild):
            await player.stop()
            await player.disconnect()
            if guild:
                await self.config.guild_from_id(
                    guild_id=guild.id).currently_auto_playing_in.set([])
            return
        guild_id = self.rgetattr(guild, "id", None)
        if not guild:
            return
        guild_data = await self.config.guild(guild).all()
        disconnect = guild_data["disconnect"]

        if event_type == lavalink.LavalinkEvents.WEBSOCKET_CLOSED:
            deafen = guild_data["auto_deafen"]
            await self._websocket_closed_handler(guild=guild,
                                                 player=player,
                                                 extra=extra,
                                                 deafen=deafen,
                                                 disconnect=disconnect)
            return

        await set_contextual_locales_from_guild(self.bot, guild)
        current_requester = self.rgetattr(current_track, "requester", None)
        current_stream = self.rgetattr(current_track, "is_stream", None)
        current_length = self.rgetattr(current_track, "length", None)
        current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
        current_id = self.rgetattr(current_track, "_info",
                                   {}).get("identifier")

        repeat = guild_data["repeat"]
        notify = guild_data["notify"]
        autoplay = guild_data["auto_play"]
        description = await self.get_track_description(
            current_track, self.local_folder_current_path)
        status = await self.config.status()
        log.debug(
            f"Received a new lavalink event for {guild_id}: {event_type}: {extra}"
        )
        prev_song: lavalink.Track = player.fetch("prev_song")
        await self.maybe_reset_error_counter(player)

        if event_type == lavalink.LavalinkEvents.TRACK_START:
            self.skip_votes[guild] = []
            playing_song = player.fetch("playing_song")
            requester = player.fetch("requester")
            player.store("prev_song", playing_song)
            player.store("prev_requester", requester)
            player.store("playing_song", current_track)
            player.store("requester", current_requester)
            self.bot.dispatch("red_audio_track_start", guild, current_track,
                              current_requester)
            if guild_id and current_track:
                await self.api_interface.persistent_queue_api.played(
                    guild_id=guild_id, track_id=current_track.track_identifier)
            notify_channel = player.fetch("channel")
            if notify_channel:
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set(
                        [notify_channel, player.channel.id])
            else:
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
        if event_type == lavalink.LavalinkEvents.TRACK_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_track_end", guild, prev_song,
                              prev_requester)
            player.store("resume_attempts", 0)
        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_queue_end", guild, prev_song,
                              prev_requester)
            if guild_id:
                await self.api_interface.persistent_queue_api.drop(guild_id)
            if player.is_auto_playing or (autoplay and not player.queue
                                          and player.fetch("playing_song")
                                          is not None
                                          and self.playlist_api is not None
                                          and self.api_interface is not None):
                notify_channel = player.fetch("channel")
                try:
                    await self.api_interface.autoplay(player,
                                                      self.playlist_api)
                except DatabaseError:
                    notify_channel = self.bot.get_channel(notify_channel)
                    if notify_channel:
                        await self.send_embed_msg(
                            notify_channel,
                            title=_("Couldn't get a valid track."))
                    return
                except TrackEnqueueError:
                    notify_channel = self.bot.get_channel(notify_channel)
                    if notify_channel:
                        await self.send_embed_msg(
                            notify_channel,
                            title=_("Unable to Get Track"),
                            description=_(
                                "I'm unable to get a track from Lavalink at the moment, try again in a few "
                                "minutes."),
                        )
                    return
        if event_type == lavalink.LavalinkEvents.TRACK_START and notify:
            notify_channel = player.fetch("channel")
            if notify_channel:
                notify_channel = self.bot.get_channel(notify_channel)
                if player.fetch("notify_message") is not None:
                    with contextlib.suppress(discord.HTTPException):
                        await player.fetch("notify_message").delete()
                if not (description and notify_channel):
                    return
                if current_stream:
                    dur = "LIVE"
                else:
                    dur = self.format_time(current_length)

                thumb = None
                if await self.config.guild(guild
                                           ).thumbnail() and current_thumbnail:
                    thumb = current_thumbnail

                notify_message = await self.send_embed_msg(
                    notify_channel,
                    description=description,
                    footer=_("Track length: {length} | Requested by: {user}").
                    format(length=dur, user=current_requester),
                    thumbnail=thumb,
                    author={
                        "name":
                        _("Now Playing"),
                        "url":
                        "https://cdn.discordapp.com/emojis/572861527049109515.gif",
                    },
                )
                player.store("notify_message", notify_message)
        if event_type == lavalink.LavalinkEvents.TRACK_START and status:
            player_check = await self.get_active_player_count()
            await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.TRACK_END and status:
            await asyncio.sleep(1)
            if not player.is_playing:
                player_check = await self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            if not autoplay:
                notify_channel = player.fetch("channel")
                if notify_channel and notify:
                    notify_channel = self.bot.get_channel(notify_channel)
                    await self.send_embed_msg(notify_channel,
                                              title=_("Queue ended."))
                if disconnect:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
                    await player.disconnect()
                    self._ll_guild_updates.discard(guild.id)
            if status:
                player_check = await self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type in [
                lavalink.LavalinkEvents.TRACK_EXCEPTION,
                lavalink.LavalinkEvents.TRACK_STUCK,
        ]:
            message_channel = player.fetch("channel")
            while True:
                if current_track in player.queue:
                    player.queue.remove(current_track)
                else:
                    break
            if repeat:
                player.current = None
            if not guild_id:
                return
            guild_id = int(guild_id)
            self._error_counter.setdefault(guild_id, 0)
            if guild_id not in self._error_counter:
                self._error_counter[guild_id] = 0
            early_exit = await self.increase_error_counter(player)
            if early_exit:
                self._disconnected_players[guild_id] = True
                self.play_lock[guild_id] = False
                eq = player.fetch("eq")
                player.queue = []
                player.store("playing_song", None)
                player.store("autoplay_notified", False)
                if eq:
                    await self.config.custom("EQUALIZER",
                                             guild_id).eq_bands.set(eq.bands)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
                self._ll_guild_updates.discard(guild_id)
                self.bot.dispatch("red_audio_audio_disconnect", guild)
            if message_channel:
                message_channel = self.bot.get_channel(message_channel)
                if early_exit:
                    embed = discord.Embed(
                        colour=await self.bot.get_embed_color(message_channel),
                        title=_("Multiple Errors Detected"),
                        description=_(
                            "Closing the audio player "
                            "due to multiple errors being detected. "
                            "If this persists, please inform the bot owner "
                            "as the Audio cog may be temporally unavailable."),
                    )
                    await message_channel.send(embed=embed)
                    return
                else:
                    description = description or ""
                    if event_type == lavalink.LavalinkEvents.TRACK_STUCK:
                        embed = discord.Embed(
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            title=_("Track Stuck"),
                            description=
                            _("Playback of the song has stopped due to an unexcepted error.\n{error}"
                              ).format(error=description),
                        )
                    else:
                        embed = discord.Embed(
                            title=_("Track Error"),
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            description="{}\n{}".format(
                                extra.replace("\n", ""), description),
                        )
                        if current_id:
                            asyncio.create_task(
                                self.api_interface.global_cache_api.
                                report_invalid(current_id))
                    await message_channel.send(embed=embed)
            await player.skip()
Beispiel #6
0
    async def _websocket_closed_handler(
        self,
        guild: discord.Guild,
        player: lavalink.Player,
        extra: Dict,
        deafen: bool,
        disconnect: bool,
    ) -> None:
        guild_id = guild.id
        node = player.node
        voice_ws: DiscordWebSocket = node.get_voice_ws(guild_id)
        code = extra.get("code")
        by_remote = extra.get("byRemote", "")
        reason = extra.get("reason", "").strip()
        if self._ws_resume[guild_id].is_set():
            ws_audio_log.debug(
                f"WS EVENT | Discarding WS Closed event for guild {guild_id} -> "
                f"Socket Closed {voice_ws.socket._closing or voice_ws.socket.closed}.  "
                f"Code: {code} -- Remote: {by_remote} -- {reason}")
            return
        self._ws_resume[guild_id].set()
        if player.channel:
            current_perms = player.channel.permissions_for(
                player.channel.guild.me)
            has_perm = current_perms.speak and current_perms.connect
        else:
            has_perm = False
        channel_id = player.channel.id
        if voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
            if player._con_delay:
                delay = player._con_delay.delay()
            else:
                player._con_delay = ExponentialBackoff(base=1)
                delay = player._con_delay.delay()
            ws_audio_log.warning(
                "YOU CAN IGNORE THIS UNLESS IT'S CONSISTENTLY REPEATING FOR THE SAME GUILD - "
                f"Voice websocket closed for guild {guild_id} -> "
                f"Socket Closed {voice_ws.socket._closing or voice_ws.socket.closed}.  "
                f"Code: {code} -- Remote: {by_remote} -- {reason}")
            ws_audio_log.debug(
                f"Reconnecting to channel {channel_id} in guild: {guild_id} | {delay:.2f}s"
            )
            await asyncio.sleep(delay)
            while voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
                voice_ws = node.get_voice_ws(guild_id)
                await asyncio.sleep(0.1)

            if has_perm and player.current and player.is_playing:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.resume(player.current, start=player.position)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Currently playing.")
            elif has_perm and player.paused and player.current:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.pause(pause=True)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Currently Paused.")
            elif has_perm and (not disconnect) and (not player.is_playing):
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Not playing, but auto disconnect disabled."
                )
                self._ll_guild_updates.discard(guild_id)
            elif not has_perm:
                self.bot.dispatch("red_audio_audio_disconnect", guild)
                ws_audio_log.info(
                    "Voice websocket disconnected "
                    f"from channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Missing permissions.")
                self._ll_guild_updates.discard(guild_id)
                player.store("autoplay_notified", False)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
            else:
                self.bot.dispatch("red_audio_audio_disconnect", guild)
                ws_audio_log.info(
                    "Voice websocket disconnected "
                    f"from channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Unknown.")
                self._ll_guild_updates.discard(guild_id)
                player.store("autoplay_notified", False)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
        elif code in (42069, ):
            player.store("resumes", player.fetch("resumes", 0) + 1)
            await player.connect(deafen=deafen)
            await player.resume(player.current, start=player.position)
            ws_audio_log.info(
                f"Player resumed in channel {channel_id} in guild: {guild_id} | "
                f"Reason: Error code {code} & {reason}.")
        elif code in (4015, 4014, 4009, 4006, 1006):
            if (code == 4006 and has_perm and player._last_resume
                    and player._last_resume + datetime.timedelta(seconds=5) >
                    datetime.datetime.now(tz=datetime.timezone.utc)):
                attempts = player.fetch("resume_attempts", 0)
                player.store("resume_attempts", attempts + 1)
                channel = self.bot.get_channel(player.channel.id)
                should_skip = not channel.members or all(
                    m.bot for m in channel.members)
                if should_skip:
                    ws_audio_log.info(
                        "Voice websocket reconnected skipped "
                        f"for channel {channel_id} in guild: {guild_id} | "
                        f"Reason: Error code {code} & "
                        "Player resumed too recently and no human members connected."
                    )
                    return

            if player._con_delay:
                delay = player._con_delay.delay()
            else:
                player._con_delay = ExponentialBackoff(base=1)
                delay = player._con_delay.delay()
            ws_audio_log.debug(
                f"Reconnecting to channel {channel_id} in guild: {guild_id} | {delay:.2f}s"
            )
            await asyncio.sleep(delay)
            if has_perm and player.current and player.is_playing:
                await player.connect(deafen=deafen)
                await player.resume(player.current, start=player.position)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Player is active.")
            elif has_perm and player.paused and player.current:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.pause(pause=True)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Player is paused.")
            elif has_perm and (not disconnect) and (not player.is_playing):
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                ws_audio_log.info(
                    "Voice websocket reconnected "
                    f"to channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Not playing.")
                self._ll_guild_updates.discard(guild_id)
            elif not has_perm:
                self.bot.dispatch("red_audio_audio_disconnect", guild)
                ws_audio_log.info(
                    "Voice websocket disconnected "
                    f"from channel {channel_id} in guild: {guild_id} | "
                    f"Reason: Error code {code} & Missing permissions.")
                self._ll_guild_updates.discard(guild_id)
                player.store("autoplay_notified", False)
                await player.stop()
                await player.disconnect()
                await self.config.guild_from_id(
                    guild_id=guild_id).currently_auto_playing_in.set([])
        else:
            ws_audio_log.info(
                "WS EVENT - IGNORED (Healthy Socket) | "
                f"Voice websocket closed event for guild {guild_id} -> "
                f"Code: {code} -- Remote: {by_remote} -- {reason}")
        self._ws_resume[guild_id].clear()
Beispiel #7
0
    async def _websocket_closed_handler(
        self,
        guild: discord.Guild,
        player: lavalink.Player,
        extra: Dict,
        deafen: bool,
        disconnect: bool,
    ) -> None:
        guild_id = guild.id
        event_channel_id = extra.get("channelID")
        try:
            if not self._ws_resume[guild_id].is_set():
                await self._ws_resume[guild_id].wait()
            else:
                self._ws_resume[guild_id].clear()
            node = player.node
            voice_ws: DiscordWebSocket = node.get_voice_ws(guild_id)
            code = extra.get("code")
            by_remote = extra.get("byRemote", "")
            reason = extra.get("reason", "No Specified Reason").strip()
            channel_id = player.channel.id
            try:
                event_channel_id, to_handle_code = await self._ws_op_codes[
                    guild_id].get()
            except asyncio.QueueEmpty:
                log.debug("Empty queue - Resuming Processor - Early exit")
                return

            if code != to_handle_code:
                code = to_handle_code
                if player.channel.id != event_channel_id:
                    code = 4014
            if event_channel_id != channel_id:
                ws_audio_log.info(
                    "Received an op code for a channel that is no longer valid; %d "
                    "Reason: Error code %d & %s, %r",
                    event_channel_id,
                    code,
                    reason,
                    player,
                )
                self._ws_op_codes[guild_id]._init(
                    self._ws_op_codes[guild_id]._maxsize)
                return
            if player.channel:
                has_perm = self.can_join_and_speak(player.channel)
            else:
                has_perm = False
            if code in (
                    1000,
            ) and has_perm and player.current and player.is_playing:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.resume(player.current,
                                    start=player.position,
                                    replace=True)
                ws_audio_log.info(
                    "Player resumed | Reason: Error code %d & %s, %r", code,
                    reason, player)
                self._ws_op_codes[guild_id]._init(
                    self._ws_op_codes[guild_id]._maxsize)
                return

            if voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
                if player._con_delay:
                    delay = player._con_delay.delay()
                else:
                    player._con_delay = ExponentialBackoff(base=1)
                    delay = player._con_delay.delay()
                ws_audio_log.warning(
                    "YOU CAN IGNORE THIS UNLESS IT'S CONSISTENTLY REPEATING FOR THE SAME GUILD - "
                    "Voice websocket closed for guild %d -> "
                    "Socket Closed %s.  "
                    "Code: %d -- Remote: %s -- %s, %r",
                    guild_id,
                    voice_ws.socket._closing or voice_ws.socket.closed,
                    code,
                    by_remote,
                    reason,
                    player,
                )
                ws_audio_log.debug(
                    "Reconnecting to channel %d in guild: %d | %.2fs",
                    channel_id,
                    guild_id,
                    delay,
                )
                await asyncio.sleep(delay)
                while voice_ws.socket._closing or voice_ws.socket.closed or not voice_ws.open:
                    voice_ws = node.get_voice_ws(guild_id)
                    await asyncio.sleep(0.1)

                if has_perm and player.current and player.is_playing:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Currently playing, %r",
                        code,
                        player,
                    )
                elif has_perm and player.paused and player.current:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True,
                                        pause=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Currently Paused, %r",
                        code,
                        player,
                    )
                elif has_perm and (not disconnect) and (not player.is_playing):
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Not playing, but auto disconnect disabled, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                elif not has_perm:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    ws_audio_log.info(
                        "Voice websocket disconnected "
                        "Reason: Error code %d & Missing permissions, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                    player.store("autoplay_notified", False)
                    await player.stop()
                    await player.disconnect()
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
                else:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    ws_audio_log.info(
                        "Voice websocket disconnected Reason: Error code %d & Unknown, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                    player.store("autoplay_notified", False)
                    await player.stop()
                    await player.disconnect()
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
            elif code in (
                    42069,
            ) and has_perm and player.current and player.is_playing:
                player.store("resumes", player.fetch("resumes", 0) + 1)
                await player.connect(deafen=deafen)
                await player.resume(player.current,
                                    start=player.position,
                                    replace=True)
                ws_audio_log.info(
                    "Player resumed - Reason: Error code %d & %s, %r", code,
                    reason, player)
            elif code in (4015, 4009, 4006, 4000, 1006):
                if player._con_delay:
                    delay = player._con_delay.delay()
                else:
                    player._con_delay = ExponentialBackoff(base=1)
                    delay = player._con_delay.delay()
                ws_audio_log.debug(
                    "Reconnecting to channel %d in guild: %d | %.2fs",
                    channel_id, guild_id, delay)
                await asyncio.sleep(delay)
                if has_perm and player.current and player.is_playing:
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Player is active, %r",
                        code,
                        player,
                    )
                elif has_perm and player.paused and player.current:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True,
                                        pause=True)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "Reason: Error code %d & Player is paused, %r",
                        code,
                        player,
                    )
                elif has_perm and (not disconnect) and (not player.is_playing):
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.connect(deafen=deafen)
                    ws_audio_log.info(
                        "Voice websocket reconnected "
                        "to channel %d in guild: %d | "
                        "Reason: Error code %d & Not playing, %r",
                        channel_id,
                        guild_id,
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                elif not has_perm:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    ws_audio_log.info(
                        "Voice websocket disconnected "
                        "Reason: Error code %d & Missing permissions, %r",
                        code,
                        player,
                    )
                    self._ll_guild_updates.discard(guild_id)
                    player.store("autoplay_notified", False)
                    await player.stop()
                    await player.disconnect()
                    await self.config.guild_from_id(
                        guild_id=guild_id).currently_auto_playing_in.set([])
            else:
                if not player.paused and player.current:
                    player.store("resumes", player.fetch("resumes", 0) + 1)
                    await player.resume(player.current,
                                        start=player.position,
                                        replace=True)
                    ws_audio_log.info(
                        "WS EVENT - SIMPLE RESUME (Healthy Socket) | "
                        "Voice websocket closed event "
                        "Code: %d -- Remote: %s -- %s, %r",
                        code,
                        by_remote,
                        reason,
                        player,
                    )
                else:
                    ws_audio_log.info(
                        "WS EVENT - IGNORED (Healthy Socket) | "
                        "Voice websocket closed event "
                        "Code: %d -- Remote: %s -- %s, %r",
                        code,
                        by_remote,
                        reason,
                        player,
                    )
        except Exception:
            log.exception("Error in task")
        finally:
            self._ws_op_codes[guild_id]._init(
                self._ws_op_codes[guild_id]._maxsize)
            self._ws_resume[guild_id].set()
Beispiel #8
0
    async def autoplay(self, player: lavalink.Player):
        autoplaylist = await self.config.guild(player.channel.guild
                                               ).autoplaylist()
        current_cache_level = CacheLevel(await self.config.cache_level())
        cache_enabled = CacheLevel.set_lavalink().is_subset(
            current_cache_level)
        playlist = None
        tracks = None
        if autoplaylist["enabled"]:
            with contextlib.suppress(Exception):
                playlist = await get_playlist(
                    autoplaylist["id"],
                    autoplaylist["scope"],
                    self.bot,
                    player.channel.guild,
                    player.channel.guild.me,
                )
                tracks = playlist.tracks_obj

        if not tracks or not getattr(playlist, "tracks", None):
            if cache_enabled:
                tracks = await self.get_random_from_db()
            if not tracks:
                ctx = namedtuple("Context", "message")
                (results, called_api) = await self.lavalink_query(
                    ctx(player.channel.guild),
                    player,
                    audio_dataclasses.Query.process_input(_TOP_100_US),
                )
                tracks = list(results.tracks)
        if tracks:
            multiple = len(tracks) > 1
            track = tracks[0]

            valid = not multiple
            tries = len(tracks)
            while valid is False and multiple:
                tries -= 1
                if tries <= 0:
                    raise DatabaseError("No valid entry found")
                track = random.choice(tracks)
                query = audio_dataclasses.Query.process_input(track)
                await asyncio.sleep(0.001)
                if not query.valid:
                    continue
                if query.is_local and not query.track.exists():
                    continue
                if not await is_allowed(
                        player.channel.guild,
                    (f"{track.title} {track.author} {track.uri} "
                     f"{str(audio_dataclasses.Query.process_input(track))}"),
                ):
                    log.debug(
                        "Query is not allowed in "
                        f"{player.channel.guild} ({player.channel.guild.id})")
                    continue
                valid = True

            track.extras["autoplay"] = True
            player.add(player.channel.guild.me, track)
            self.bot.dispatch("red_audio_track_auto_play",
                              player.channel.guild, track,
                              player.channel.guild.me)
            if not player.current:
                await player.play()
Beispiel #9
0
    async def spotify_enqueue(
        self,
        ctx: commands.Context,
        query_type: str,
        uri: str,
        enqueue: bool,
        player: lavalink.Player,
        lock: Callable,
        notifier: Optional[Notifier] = None,
    ) -> List[lavalink.Track]:
        track_list = []
        has_not_allowed = False
        try:
            current_cache_level = CacheLevel(await self.config.cache_level())
            guild_data = await self.config.guild(ctx.guild).all()

            # now = int(time.time())
            enqueued_tracks = 0
            consecutive_fails = 0
            queue_dur = await queue_duration(ctx)
            queue_total_duration = lavalink.utils.format_time(queue_dur)
            before_queue_length = len(player.queue)
            tracks_from_spotify = await self._spotify_fetch_tracks(
                query_type, uri, params=None, notifier=notifier)
            total_tracks = len(tracks_from_spotify)
            if total_tracks < 1:
                lock(ctx, False)
                embed3 = discord.Embed(
                    colour=await ctx.embed_colour(),
                    title=
                    _("This doesn't seem to be a supported Spotify URL or code."
                      ),
                )
                await notifier.update_embed(embed3)

                return track_list
            database_entries = []
            time_now = int(
                datetime.datetime.now(datetime.timezone.utc).timestamp())

            youtube_cache = CacheLevel.set_youtube().is_subset(
                current_cache_level)
            spotify_cache = CacheLevel.set_spotify().is_subset(
                current_cache_level)
            for track_count, track in enumerate(tracks_from_spotify):
                (
                    song_url,
                    track_info,
                    uri,
                    artist_name,
                    track_name,
                    _id,
                    _type,
                ) = self._get_spotify_track_info(track)

                database_entries.append({
                    "id": _id,
                    "type": _type,
                    "uri": uri,
                    "track_name": track_name,
                    "artist_name": artist_name,
                    "song_url": song_url,
                    "track_info": track_info,
                    "last_updated": time_now,
                    "last_fetched": time_now,
                })
                val = None
                if youtube_cache:
                    update = True
                    with contextlib.suppress(SQLError):
                        (val, update) = await self.database.fetch_one(
                            "youtube", "youtube_url", {"track": track_info})
                    if update:
                        val = None
                if val is None:
                    val = await self._youtube_first_time_query(
                        ctx,
                        track_info,
                        current_cache_level=current_cache_level)
                if youtube_cache and val:
                    task = ("update", ("youtube", {"track": track_info}))
                    self.append_task(ctx, *task)

                if val:
                    try:
                        (result, called_api) = await self.lavalink_query(
                            ctx, player,
                            audio_dataclasses.Query.process_input(val))
                    except (RuntimeError, aiohttp.ServerDisconnectedError):
                        lock(ctx, False)
                        error_embed = discord.Embed(
                            colour=await ctx.embed_colour(),
                            title=
                            _("The connection was reset while loading the playlist."
                              ),
                        )
                        await notifier.update_embed(error_embed)
                        break
                    except asyncio.TimeoutError:
                        lock(ctx, False)
                        error_embed = discord.Embed(
                            colour=await ctx.embed_colour(),
                            title=_(
                                "Player timeout, skipping remaining tracks."),
                        )
                        await notifier.update_embed(error_embed)
                        break
                    track_object = result.tracks
                else:
                    track_object = []
                if (track_count % 2 == 0) or (track_count == total_tracks):
                    key = "lavalink"
                    seconds = "???"
                    second_key = None
                    await notifier.notify_user(
                        current=track_count,
                        total=total_tracks,
                        key=key,
                        seconds_key=second_key,
                        seconds=seconds,
                    )

                if consecutive_fails >= 10:
                    error_embed = discord.Embed(
                        colour=await ctx.embed_colour(),
                        title=_("Failing to get tracks, skipping remaining."),
                    )
                    await notifier.update_embed(error_embed)
                    break
                if not track_object:
                    consecutive_fails += 1
                    continue
                consecutive_fails = 0
                single_track = track_object[0]
                if not await is_allowed(
                        ctx.guild,
                    (f"{single_track.title} {single_track.author} {single_track.uri} "
                     f"{str(audio_dataclasses.Query.process_input(single_track))}"
                     ),
                ):
                    has_not_allowed = True
                    log.debug(
                        f"Query is not allowed in {ctx.guild} ({ctx.guild.id})"
                    )
                    continue
                track_list.append(single_track)
                if enqueue:
                    if len(player.queue) >= 10000:
                        continue
                    if guild_data["maxlength"] > 0:
                        if track_limit(single_track, guild_data["maxlength"]):
                            enqueued_tracks += 1
                            player.add(ctx.author, single_track)
                            self.bot.dispatch(
                                "red_audio_track_enqueue",
                                player.channel.guild,
                                single_track,
                                ctx.author,
                            )
                    else:
                        enqueued_tracks += 1
                        player.add(ctx.author, single_track)
                        self.bot.dispatch(
                            "red_audio_track_enqueue",
                            player.channel.guild,
                            single_track,
                            ctx.author,
                        )

                    if not player.current:
                        await player.play()
            if len(track_list) == 0:
                if not has_not_allowed:
                    raise SpotifyFetchError(message=_(
                        "Nothing found.\nThe YouTube API key may be invalid "
                        "or you may be rate limited on YouTube's search service.\n"
                        "Check the YouTube API key again and follow the instructions "
                        "at `{prefix}audioset youtubeapi`.").format(
                            prefix=ctx.prefix))
            player.maybe_shuffle()
            if enqueue and tracks_from_spotify:
                if total_tracks > enqueued_tracks:
                    maxlength_msg = " {bad_tracks} tracks cannot be queued.".format(
                        bad_tracks=(total_tracks - enqueued_tracks))
                else:
                    maxlength_msg = ""

                embed = discord.Embed(
                    colour=await ctx.embed_colour(),
                    title=_("Playlist Enqueued"),
                    description=_(
                        "Added {num} tracks to the queue.{maxlength_msg}").
                    format(num=enqueued_tracks, maxlength_msg=maxlength_msg),
                )
                if not guild_data["shuffle"] and queue_dur > 0:
                    embed.set_footer(
                        text=_("{time} until start of playlist"
                               " playback: starts at #{position} in queue"
                               ).format(time=queue_total_duration,
                                        position=before_queue_length + 1))

                await notifier.update_embed(embed)
            lock(ctx, False)

            if spotify_cache:
                task = ("insert", ("spotify", database_entries))
                self.append_task(ctx, *task)
        except Exception as e:
            lock(ctx, False)
            raise e
        finally:
            lock(ctx, False)
        return track_list
Beispiel #10
0
    async def _eq_interact(
        self,
        ctx: commands.Context,
        player: lavalink.Player,
        eq: Equalizer,
        message: discord.Message,
        selected: int,
    ) -> None:
        player.store("eq", eq)
        emoji = {
            "far_left": "\N{BLACK LEFT-POINTING TRIANGLE}",
            "one_left": "\N{LEFTWARDS BLACK ARROW}",
            "max_output": "\N{BLACK UP-POINTING DOUBLE TRIANGLE}",
            "output_up": "\N{UP-POINTING SMALL RED TRIANGLE}",
            "output_down": "\N{DOWN-POINTING SMALL RED TRIANGLE}",
            "min_output": "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}",
            "one_right": "\N{BLACK RIGHTWARDS ARROW}",
            "far_right": "\N{BLACK RIGHT-POINTING TRIANGLE}",
            "reset": "\N{BLACK CIRCLE FOR RECORD}",
            "info": "\N{INFORMATION SOURCE}",
        }
        selector = f'{" " * 8}{"   " * selected}^^'
        try:
            await message.edit(
                content=box(f"{eq.visualise()}\n{selector}", lang="ini"))
        except discord.errors.NotFound:
            return
        try:
            (react_emoji,
             react_user) = await self._get_eq_reaction(ctx, message, emoji)
        except TypeError:
            return

        if not react_emoji:
            await self.config.custom("EQUALIZER",
                                     ctx.guild.id).eq_bands.set(eq.bands)
            await self._clear_react(message, emoji)

        if react_emoji == "\N{LEFTWARDS BLACK ARROW}":
            await self.remove_react(message, react_emoji, react_user)
            await self._eq_interact(ctx, player, eq, message,
                                    max(selected - 1, 0))

        if react_emoji == "\N{BLACK RIGHTWARDS ARROW}":
            await self.remove_react(message, react_emoji, react_user)
            await self._eq_interact(ctx, player, eq, message,
                                    min(selected + 1, 14))

        if react_emoji == "\N{UP-POINTING SMALL RED TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _max = float("{:.2f}".format(min(eq.get_gain(selected) + 0.1,
                                             1.0)))
            eq.set_gain(selected, _max)
            await self._apply_gain(ctx.guild.id, selected, _max)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{DOWN-POINTING SMALL RED TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _min = float("{:.2f}".format(
                max(eq.get_gain(selected) - 0.1, -0.25)))
            eq.set_gain(selected, _min)
            await self._apply_gain(ctx.guild.id, selected, _min)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK UP-POINTING DOUBLE TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _max = 1.0
            eq.set_gain(selected, _max)
            await self._apply_gain(ctx.guild.id, selected, _max)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            _min = -0.25
            eq.set_gain(selected, _min)
            await self._apply_gain(ctx.guild.id, selected, _min)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK LEFT-POINTING TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            selected = 0
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK RIGHT-POINTING TRIANGLE}":
            await self.remove_react(message, react_emoji, react_user)
            selected = 14
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{BLACK CIRCLE FOR RECORD}":
            await self.remove_react(message, react_emoji, react_user)
            for band in range(eq.band_count):
                eq.set_gain(band, 0.0)
            await self._apply_gains(ctx.guild.id, eq.bands)
            await self._eq_interact(ctx, player, eq, message, selected)

        if react_emoji == "\N{INFORMATION SOURCE}":
            await self.remove_react(message, react_emoji, react_user)
            await ctx.send_help(self.command_equalizer)
            await self._eq_interact(ctx, player, eq, message, selected)
Beispiel #11
0
    async def autoplay(self, player: lavalink.Player,
                       playlist_api: PlaylistWrapper):
        """Enqueue a random track."""
        autoplaylist = await self.config.guild(player.channel.guild
                                               ).autoplaylist()
        current_cache_level = CacheLevel(await self.config.cache_level())
        cache_enabled = CacheLevel.set_lavalink().is_subset(
            current_cache_level)
        playlist = None
        tracks = None
        if autoplaylist["enabled"]:
            try:
                playlist = await get_playlist(
                    autoplaylist["id"],
                    autoplaylist["scope"],
                    self.bot,
                    playlist_api,
                    player.channel.guild,
                    player.channel.guild.me,
                )
                tracks = playlist.tracks_obj
            except Exception as exc:
                debug_exc_log(log, exc,
                              "Failed to fetch playlist for autoplay")

        if not tracks or not getattr(playlist, "tracks", None):
            if cache_enabled:
                track = await self.get_random_track_from_db()
                tracks = [] if not track else [track]
            if not tracks:
                ctx = namedtuple("Context", "message guild cog")
                (results, called_api) = await self.fetch_track(
                    cast(
                        commands.Context,
                        ctx(player.channel.guild, player.channel.guild,
                            self.cog)),
                    player,
                    Query.process_input(_TOP_100_US,
                                        self.cog.local_folder_current_path),
                )
                tracks = list(results.tracks)
        if tracks:
            multiple = len(tracks) > 1
            valid = not multiple
            tries = len(tracks)
            track = tracks[0]
            while valid is False and multiple:
                tries -= 1
                if tries <= 0:
                    raise DatabaseError("No valid entry found")
                track = random.choice(tracks)
                query = Query.process_input(track,
                                            self.cog.local_folder_current_path)
                await asyncio.sleep(0.001)
                if (not query.valid) or (
                        query.is_local and query.local_track_path is not None
                        and not query.local_track_path.exists()):
                    continue
                notify_channel = self.bot.get_channel(player.fetch("channel"))
                if not await self.cog.is_query_allowed(
                        self.config,
                        notify_channel,
                        f"{track.title} {track.author} {track.uri} {query}",
                        query_obj=query,
                ):
                    if IS_DEBUG:
                        log.debug(
                            "Query is not allowed in "
                            f"{player.channel.guild} ({player.channel.guild.id})"
                        )
                    continue
                valid = True
            track.extras.update({
                "autoplay": True,
                "enqueue_time": int(time.time()),
                "vc": player.channel.id,
                "requester": player.channel.guild.me.id,
            })
            player.add(player.channel.guild.me, track)
            self.bot.dispatch("red_audio_track_auto_play",
                              player.channel.guild, track,
                              player.channel.guild.me)
            if not player.current:
                await player.play()
Beispiel #12
0
    async def spotify_enqueue(
        self,
        ctx: commands.Context,
        query_type: str,
        uri: str,
        enqueue: bool,
        player: lavalink.Player,
        lock: Callable,
        notifier: Optional[Notifier] = None,
        forced: bool = False,
        query_global: bool = True,
    ) -> List[lavalink.Track]:
        """Queries the Database then falls back to Spotify and YouTube APIs then Enqueued matched
        tracks.

        Parameters
        ----------
        ctx: commands.Context
            The context this method is being called under.
        query_type : str
            Type of query to perform (Pl
        uri: str
            Spotify URL ID.
        enqueue:bool
            Whether or not to enqueue the tracks
        player: lavalink.Player
            The current Player.
        notifier: Notifier
            A Notifier object to handle the user UI notifications while tracks are loaded.
        lock: Callable
            A callable handling the Track enqueue lock while spotify tracks are being added.
        query_global: bool
            Whether or not to query the global API.
        forced: bool
            Ignore Cache and make a fetch from API.
        Returns
        -------
        List[str]
            List of Youtube URLs.
        """
        await self.global_cache_api._get_api_key()
        globaldb_toggle = await self.config.global_db_enabled()
        global_entry = globaldb_toggle and query_global
        track_list: List = []
        has_not_allowed = False
        youtube_api_error = None
        try:
            current_cache_level = CacheLevel(await self.config.cache_level())
            guild_data = await self.config.guild(ctx.guild).all()
            enqueued_tracks = 0
            consecutive_fails = 0
            queue_dur = await self.cog.queue_duration(ctx)
            queue_total_duration = self.cog.format_time(queue_dur)
            before_queue_length = len(player.queue)
            tracks_from_spotify = await self.fetch_from_spotify_api(
                query_type, uri, params=None, notifier=notifier)
            total_tracks = len(tracks_from_spotify)
            if total_tracks < 1 and notifier is not None:
                lock(ctx, False)
                embed3 = discord.Embed(
                    colour=await ctx.embed_colour(),
                    title=
                    _("This doesn't seem to be a supported Spotify URL or code."
                      ),
                )
                await notifier.update_embed(embed3)

                return track_list
            database_entries = []
            time_now = int(
                datetime.datetime.now(datetime.timezone.utc).timestamp())
            youtube_cache = CacheLevel.set_youtube().is_subset(
                current_cache_level)
            spotify_cache = CacheLevel.set_spotify().is_subset(
                current_cache_level)
            async for track_count, track in AsyncIter(
                    tracks_from_spotify).enumerate(start=1):
                (
                    song_url,
                    track_info,
                    uri,
                    artist_name,
                    track_name,
                    _id,
                    _type,
                ) = await self.spotify_api.get_spotify_track_info(track, ctx)

                database_entries.append({
                    "id": _id,
                    "type": _type,
                    "uri": uri,
                    "track_name": track_name,
                    "artist_name": artist_name,
                    "song_url": song_url,
                    "track_info": track_info,
                    "last_updated": time_now,
                    "last_fetched": time_now,
                })
                val = None
                llresponse = None
                if youtube_cache:
                    try:
                        (val, last_updated
                         ) = await self.local_cache_api.youtube.fetch_one(
                             {"track": track_info})
                    except Exception as exc:
                        debug_exc_log(
                            log, exc,
                            f"Failed to fetch {track_info} from YouTube table")
                should_query_global = globaldb_toggle and query_global and val is None
                if should_query_global:
                    llresponse = await self.global_cache_api.get_spotify(
                        track_name, artist_name)
                    if llresponse:
                        if llresponse.get("loadType") == "V2_COMPACT":
                            llresponse["loadType"] = "V2_COMPAT"
                        llresponse = LoadResult(llresponse)
                    val = llresponse or None
                if val is None:
                    try:
                        val = await self.fetch_youtube_query(
                            ctx,
                            track_info,
                            current_cache_level=current_cache_level)
                    except YouTubeApiError as err:
                        val = None
                        youtube_api_error = err.message
                if not youtube_api_error:
                    if youtube_cache and val and llresponse is None:
                        task = ("update", ("youtube", {"track": track_info}))
                        self.append_task(ctx, *task)

                    if isinstance(llresponse, LoadResult):
                        track_object = llresponse.tracks
                    elif val:
                        result = None
                        if should_query_global:
                            llresponse = await self.global_cache_api.get_call(
                                val)
                            if llresponse:
                                if llresponse.get("loadType") == "V2_COMPACT":
                                    llresponse["loadType"] = "V2_COMPAT"
                                llresponse = LoadResult(llresponse)
                            result = llresponse or None
                        if not result:
                            try:
                                (result, called_api) = await self.fetch_track(
                                    ctx,
                                    player,
                                    Query.process_input(
                                        val,
                                        self.cog.local_folder_current_path),
                                    forced=forced,
                                    should_query_global=not should_query_global,
                                )
                            except (RuntimeError,
                                    aiohttp.ServerDisconnectedError):
                                lock(ctx, False)
                                error_embed = discord.Embed(
                                    colour=await ctx.embed_colour(),
                                    title=
                                    _("The connection was reset while loading the playlist."
                                      ),
                                )
                                if notifier is not None:
                                    await notifier.update_embed(error_embed)
                                break
                            except asyncio.TimeoutError:
                                lock(ctx, False)
                                error_embed = discord.Embed(
                                    colour=await ctx.embed_colour(),
                                    title=
                                    _("Player timeout, skipping remaining tracks."
                                      ),
                                )
                                if notifier is not None:
                                    await notifier.update_embed(error_embed)
                                break
                        track_object = result.tracks
                    else:
                        track_object = []
                else:
                    track_object = []
                if (track_count % 2 == 0) or (track_count == total_tracks):
                    key = "lavalink"
                    seconds = "???"
                    second_key = None
                    if notifier is not None:
                        await notifier.notify_user(
                            current=track_count,
                            total=total_tracks,
                            key=key,
                            seconds_key=second_key,
                            seconds=seconds,
                        )

                if youtube_api_error or consecutive_fails >= (
                        20 if global_entry else 10):
                    error_embed = discord.Embed(
                        colour=await ctx.embed_colour(),
                        title=_("Failing to get tracks, skipping remaining."),
                    )
                    if notifier is not None:
                        await notifier.update_embed(error_embed)
                    if youtube_api_error:
                        lock(ctx, False)
                        raise SpotifyFetchError(message=youtube_api_error)
                    break
                if not track_object:
                    consecutive_fails += 1
                    continue
                consecutive_fails = 0
                single_track = track_object[0]
                query = Query.process_input(single_track,
                                            self.cog.local_folder_current_path)
                if not await self.cog.is_query_allowed(
                        self.config,
                        ctx,
                        f"{single_track.title} {single_track.author} {single_track.uri} {query}",
                        query_obj=query,
                ):
                    has_not_allowed = True
                    if IS_DEBUG:
                        log.debug(
                            f"Query is not allowed in {ctx.guild} ({ctx.guild.id})"
                        )
                    continue
                track_list.append(single_track)
                if enqueue:
                    if len(player.queue) >= 10000:
                        continue
                    if guild_data["maxlength"] > 0:
                        if self.cog.is_track_length_allowed(
                                single_track, guild_data["maxlength"]):
                            enqueued_tracks += 1
                            single_track.extras.update({
                                "enqueue_time":
                                int(time.time()),
                                "vc":
                                player.channel.id,
                                "requester":
                                ctx.author.id,
                            })
                            player.add(ctx.author, single_track)
                            self.bot.dispatch(
                                "red_audio_track_enqueue",
                                player.channel.guild,
                                single_track,
                                ctx.author,
                            )
                    else:
                        enqueued_tracks += 1
                        single_track.extras.update({
                            "enqueue_time":
                            int(time.time()),
                            "vc":
                            player.channel.id,
                            "requester":
                            ctx.author.id,
                        })
                        player.add(ctx.author, single_track)
                        self.bot.dispatch(
                            "red_audio_track_enqueue",
                            player.channel.guild,
                            single_track,
                            ctx.author,
                        )

                    if not player.current:
                        await player.play()
            if enqueue and tracks_from_spotify:
                if total_tracks > enqueued_tracks:
                    maxlength_msg = _(
                        " {bad_tracks} tracks cannot be queued.").format(
                            bad_tracks=(total_tracks - enqueued_tracks))
                else:
                    maxlength_msg = ""

                embed = discord.Embed(
                    colour=await ctx.embed_colour(),
                    title=_("Playlist Enqueued"),
                    description=_(
                        "Added {num} tracks to the queue.{maxlength_msg}").
                    format(num=enqueued_tracks, maxlength_msg=maxlength_msg),
                )
                if not guild_data["shuffle"] and queue_dur > 0:
                    embed.set_footer(
                        text=_("{time} until start of playlist"
                               " playback: starts at #{position} in queue"
                               ).format(time=queue_total_duration,
                                        position=before_queue_length + 1))

                if notifier is not None:
                    await notifier.update_embed(embed)
            lock(ctx, False)
            if not track_list and not has_not_allowed:
                raise SpotifyFetchError(message=_(
                    "Nothing found.\nThe YouTube API key may be invalid "
                    "or you may be rate limited on YouTube's search service.\n"
                    "Check the YouTube API key again and follow the instructions "
                    "at `{prefix}audioset youtubeapi`."))
            player.maybe_shuffle()

            if spotify_cache:
                task = ("insert", ("spotify", database_entries))
                self.append_task(ctx, *task)
        except Exception as exc:
            lock(ctx, False)
            raise exc
        finally:
            lock(ctx, False)
        return track_list
Beispiel #13
0
    async def lavalink_event_handler(self, player: lavalink.Player,
                                     event_type: lavalink.LavalinkEvents,
                                     extra) -> None:
        current_track = player.current
        current_channel = player.channel
        guild = self.rgetattr(current_channel, "guild", None)
        guild_id = self.rgetattr(guild, "id", None)
        current_requester = self.rgetattr(current_track, "requester", None)
        current_stream = self.rgetattr(current_track, "is_stream", None)
        current_length = self.rgetattr(current_track, "length", None)
        current_thumbnail = self.rgetattr(current_track, "thumbnail", None)
        current_extras = self.rgetattr(current_track, "extras", {})
        guild_data = await self.config.guild(guild).all()
        repeat = guild_data["repeat"]
        notify = guild_data["notify"]
        disconnect = guild_data["disconnect"]
        autoplay = guild_data["auto_play"]
        description = self.get_track_description(
            current_track, self.local_folder_current_path)
        status = await self.config.status()
        log.debug(
            f"Received a new lavalink event for {guild_id}: {event_type}: {extra}"
        )
        prev_song: lavalink.Track = player.fetch("prev_song")
        await self.maybe_reset_error_counter(player)

        if event_type == lavalink.LavalinkEvents.TRACK_START:
            self.skip_votes[guild] = []
            playing_song = player.fetch("playing_song")
            requester = player.fetch("requester")
            player.store("prev_song", playing_song)
            player.store("prev_requester", requester)
            player.store("playing_song", current_track)
            player.store("requester", current_requester)
            self.bot.dispatch("red_audio_track_start", guild, current_track,
                              current_requester)
        if event_type == lavalink.LavalinkEvents.TRACK_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_track_end", guild, prev_song,
                              prev_requester)
        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            prev_requester = player.fetch("prev_requester")
            self.bot.dispatch("red_audio_queue_end", guild, prev_song,
                              prev_requester)
            if (autoplay and not player.queue
                    and player.fetch("playing_song") is not None
                    and self.playlist_api is not None
                    and self.api_interface is not None):
                try:
                    await self.api_interface.autoplay(player,
                                                      self.playlist_api)
                except DatabaseError:
                    notify_channel = player.fetch("channel")
                    if notify_channel:
                        notify_channel = self.bot.get_channel(notify_channel)
                        await self.send_embed_msg(
                            notify_channel,
                            title=_("Couldn't get a valid track."))
                    return
        if event_type == lavalink.LavalinkEvents.TRACK_START and notify:
            notify_channel = player.fetch("channel")
            if notify_channel:
                notify_channel = self.bot.get_channel(notify_channel)
                if player.fetch("notify_message") is not None:
                    with contextlib.suppress(discord.HTTPException):
                        await player.fetch("notify_message").delete()

                if (autoplay and current_extras.get("autoplay")
                        and (prev_song is None or
                             (hasattr(prev_song, "extras")
                              and not prev_song.extras.get("autoplay")))):
                    await self.send_embed_msg(notify_channel,
                                              title=_("Auto Play started."))

                if not description:
                    return
                if current_stream:
                    dur = "LIVE"
                else:
                    dur = self.format_time(current_length)

                thumb = None
                if await self.config.guild(guild
                                           ).thumbnail() and current_thumbnail:
                    thumb = current_thumbnail

                notify_message = await self.send_embed_msg(
                    notify_channel,
                    title=_("Now Playing"),
                    description=description,
                    footer=_("Track length: {length} | Requested by: {user}").
                    format(length=dur, user=current_requester),
                    thumbnail=thumb,
                )
                player.store("notify_message", notify_message)
        if event_type == lavalink.LavalinkEvents.TRACK_START and status:
            player_check = self.get_active_player_count()
            await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.TRACK_END and status:
            await asyncio.sleep(1)
            if not player.is_playing:
                player_check = self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type == lavalink.LavalinkEvents.QUEUE_END:
            if not autoplay:
                notify_channel = player.fetch("channel")
                if notify_channel and notify:
                    notify_channel = self.bot.get_channel(notify_channel)
                    await self.send_embed_msg(notify_channel,
                                              title=_("Queue ended."))
                if disconnect:
                    self.bot.dispatch("red_audio_audio_disconnect", guild)
                    await player.disconnect()
            if status:
                player_check = self.get_active_player_count()
                await self.update_bot_presence(*player_check)

        if event_type in [
                lavalink.LavalinkEvents.TRACK_EXCEPTION,
                lavalink.LavalinkEvents.TRACK_STUCK,
        ]:
            message_channel = player.fetch("channel")
            while True:
                if current_track in player.queue:
                    player.queue.remove(current_track)
                else:
                    break
            if repeat:
                player.current = None
            if not guild_id:
                return
            self._error_counter.setdefault(guild_id, 0)
            if guild_id not in self._error_counter:
                self._error_counter[guild_id] = 0
            early_exit = await self.increase_error_counter(player)
            if early_exit:
                self._disconnected_players[guild_id] = True
                self.play_lock[guild_id] = False
                eq = player.fetch("eq")
                player.queue = []
                player.store("playing_song", None)
                if eq:
                    await self.config.custom("EQUALIZER",
                                             guild_id).eq_bands.set(eq.bands)
                await player.stop()
                await player.disconnect()
                self.bot.dispatch("red_audio_audio_disconnect", guild)
            if message_channel:
                message_channel = self.bot.get_channel(message_channel)
                if early_exit:
                    embed = discord.Embed(
                        colour=await self.bot.get_embed_color(message_channel),
                        title=_("Multiple Errors Detected"),
                        description=_(
                            "Closing the audio player "
                            "due to multiple errors being detected. "
                            "If this persists, please inform the bot owner "
                            "as the Audio cog may be temporally unavailable."),
                    )
                    await message_channel.send(embed=embed)
                    return
                else:
                    description = description or ""
                    if event_type == lavalink.LavalinkEvents.TRACK_STUCK:
                        embed = discord.Embed(
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            title=_("Track Stuck"),
                            description="{}".format(description),
                        )
                    else:
                        embed = discord.Embed(
                            title=_("Track Error"),
                            colour=await
                            self.bot.get_embed_color(message_channel),
                            description="{}\n{}".format(
                                extra.replace("\n", ""), description),
                        )
                    await message_channel.send(embed=embed)
            await player.skip()