Пример #1
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
Пример #2
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