async def get_random_track_from_db(self, tries=0) -> Optional[MutableMapping]: """Get a random track from the local database and return it.""" track: Optional[MutableMapping] = {} try: query_data = {} date = datetime.datetime.now( datetime.timezone.utc) - datetime.timedelta(days=7) date_timestamp = int(date.timestamp()) query_data["day"] = date_timestamp max_age = await self.config.cache_age() maxage = datetime.datetime.now( tz=datetime.timezone.utc) - datetime.timedelta(days=max_age) maxage_int = int(time.mktime(maxage.timetuple())) query_data["maxage"] = maxage_int track = await self.local_cache_api.lavalink.fetch_random(query_data ) if track is not None: if track.get("loadType") == "V2_COMPACT": track["loadType"] = "V2_COMPAT" results = LoadResult(track) track = random.choice(list(results.tracks)) except Exception as exc: debug_exc_log(log, exc, "Failed to fetch a random track from database") track = {} if not track: return None return track
async def get_random_from_db(self): tracks = [] try: query_data = {} date = datetime.datetime.now( datetime.timezone.utc) - datetime.timedelta(days=7) date = int(date.timestamp()) query_data["day"] = date max_age = await self.config.cache_age() maxage = datetime.datetime.now( tz=datetime.timezone.utc) - datetime.timedelta(days=max_age) maxage_int = int(time.mktime(maxage.timetuple())) query_data["maxage"] = maxage_int vals = await self.database.fetch_all("lavalink", "data", query_data) recently_played = [ r.tracks for r in vals if r if isinstance(tracks, dict) ] if recently_played: track = random.choice(recently_played) if track.get("loadType") == "V2_COMPACT": track["loadType"] = "V2_COMPAT" results = LoadResult(track) tracks = list(results.tracks) except Exception: tracks = [] return tracks
async def _api_nuker(self, ctx: commands.Context, db_entries) -> None: tasks = [] for i, entry in enumerate(db_entries, start=1): query = entry.query data = entry.data _raw_query = audio_dataclasses.Query.process_input(query) results = LoadResult(json.loads(data)) with contextlib.suppress(Exception): if not _raw_query.is_local and not results.has_error and len( results.tracks) >= 1: global_task = dict(llresponse=results, query=_raw_query) tasks.append(global_task) if i % 20000 == 0: log.debug("Running pending writes to database") await asyncio.gather( *[ asyncio.ensure_future(self.update_global(**a)) for a in tasks ], loop=self.bot.loop, return_exceptions=True, ) tasks = [] log.debug("Pending writes to database have finished") if i % 20000 == 0: await ctx.send(f"20k-sleeping") await asyncio.sleep(5) await ctx.send( f"20k-Local Audio cache sent upstream, thanks for contributing")
def get_load_track(self, track: Track): load_track = { "loadType": LoadType.TRACK_LOADED, "playlistInfo": {}, "tracks": [self.track_creator(track)], } return LoadResult(load_track)
async def contribute_to_global( self, ctx: commands.Context, db_entries: List[LavalinkCacheFetchForGlobalResult]) -> None: tasks = [] async for i, entry in AsyncIter(db_entries).enumerate(start=1): query = entry.query data = entry.data _raw_query = Query.process_input( query, self.cog.local_folder_current_path) if data.get("loadType") == "V2_COMPACT": data["loadType"] = "V2_COMPAT" results = LoadResult(data) with contextlib.suppress(Exception): if not _raw_query.is_local and not results.has_error and len( results.tracks) >= 1: global_task = dict(llresponse=results, query=_raw_query) tasks.append(global_task) if i % 500 == 0: if IS_DEBUG: log.debug("Running pending writes to database") await asyncio.gather( *[ self.global_cache_api.update_global(**a) for a in tasks ], return_exceptions=True, ) tasks = [] if IS_DEBUG: log.debug("Pending writes to database have finished") await asyncio.sleep(5) with contextlib.suppress(Exception): if tasks: if IS_DEBUG: log.debug("Running pending writes to database") await asyncio.gather( *[self.global_cache_api.update_global(**a) for a in tasks], return_exceptions=True, ) if IS_DEBUG: log.debug("Pending writes to database have finished") await ctx.tick()
async def play_random(self): tracks = [] try: query_data = {} for i in range(1, 8): date = ("%" + str( (datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=i)).date()) + "%") query_data[f"day{i}"] = date vals = await self.fetch_all("lavalink", "data", query_data) recently_played = [r.data for r in vals if r] if recently_played: track = random.choice(recently_played) results = LoadResult(json.loads(track)) tracks = list(results.tracks) except Exception: tracks = [] return tracks
async def lavalink_query( self, ctx: commands.Context, player: lavalink.Player, query: audio_dataclasses.Query, forced: bool = False, ) -> Tuple[LoadResult, bool]: """A replacement for :code:`lavalink.Player.load_tracks`. This will try to get a valid cached entry first if not found or if in valid it will then call the lavalink API. Parameters ---------- ctx: commands.Context The context this method is being called under. player : lavalink.Player The player who's requesting the query. query: audio_dataclasses.Query The Query object for the query in question. forced:bool Whether or not to skip cache and call API first.. Returns ------- Tuple[lavalink.LoadResult, bool] Tuple with the Load result and whether or not the API was called. """ current_cache_level = CacheLevel(await self.config.cache_level()) cache_enabled = CacheLevel.set_lavalink().is_subset( current_cache_level) val = None _raw_query = audio_dataclasses.Query.process_input(query) query = str(_raw_query) if cache_enabled and not forced and not _raw_query.is_local: update = True with contextlib.suppress(SQLError): (val, update) = await self.database.fetch_one( "lavalink", "data", {"query": query}) if update: val = None if val and isinstance(val, dict): log.debug(f"Querying Local Database for {query}") task = ("update", ("lavalink", {"query": query})) self.append_task(ctx, *task) else: val = None if val and not forced and isinstance(val, dict): data = val data["query"] = query if data.get("loadType") == "V2_COMPACT": data["loadType"] = "V2_COMPAT" results = LoadResult(data) called_api = False if results.has_error: # If cached value has an invalid entry make a new call so that it gets updated return await self.lavalink_query(ctx, player, _raw_query, forced=True) else: called_api = True results = None try: results = await player.load_tracks(query) except KeyError: results = None except RuntimeError: raise TrackEnqueueError if results is None: results = LoadResult({ "loadType": "LOAD_FAILED", "playlistInfo": {}, "tracks": [] }) if (cache_enabled and results.load_type and not results.has_error and not _raw_query.is_local and results.tracks): with contextlib.suppress(SQLError): time_now = int( datetime.datetime.now( datetime.timezone.utc).timestamp()) data = json.dumps(results._raw) if all( k in data for k in ["loadType", "playlistInfo", "isSeekable", "isStream" ]): task = ( "insert", ( "lavalink", [{ "query": query, "data": data, "last_updated": time_now, "last_fetched": time_now, }], ), ) self.append_task(ctx, *task) return results, called_api
async def fetch_track( self, ctx: commands.Context, player: lavalink.Player, query: Query, forced: bool = False, lazy: bool = False, should_query_global: bool = True, ) -> Tuple[LoadResult, bool]: """A replacement for :code:`lavalink.Player.load_tracks`. This will try to get a valid cached entry first if not found or if in valid it will then call the lavalink API. Parameters ---------- ctx: commands.Context The context this method is being called under. player : lavalink.Player The player who's requesting the query. query: audio_dataclasses.Query The Query object for the query in question. forced:bool Whether or not to skip cache and call API first. lazy:bool If set to True, it will not call the api if a track is not found. should_query_global:bool If the method should query the global database. Returns ------- Tuple[lavalink.LoadResult, bool] Tuple with the Load result and whether or not the API was called. """ current_cache_level = CacheLevel(await self.config.cache_level()) cache_enabled = CacheLevel.set_lavalink().is_subset( current_cache_level) val = None query = Query.process_input(query, self.cog.local_folder_current_path) query_string = str(query) globaldb_toggle = await self.config.global_db_enabled() valid_global_entry = False results = None called_api = False prefer_lyrics = await self.cog.get_lyrics_status(ctx) if prefer_lyrics and query.is_youtube and query.is_search: query_string = f"{query} - lyrics" if cache_enabled and not forced and not query.is_local: try: (val, last_updated) = await self.local_cache_api.lavalink.fetch_one( {"query": query_string}) except Exception as exc: debug_exc_log( log, exc, f"Failed to fetch '{query_string}' from Lavalink table") if val and isinstance(val, dict): if IS_DEBUG: log.debug(f"Updating Local Database with {query_string}") task = ("update", ("lavalink", {"query": query_string})) self.append_task(ctx, *task) else: val = None if val and not forced and isinstance(val, dict): valid_global_entry = False called_api = False else: val = None if (globaldb_toggle and not val and should_query_global and not forced and not query.is_local and not query.is_spotify): valid_global_entry = False with contextlib.suppress(Exception): global_entry = await self.global_cache_api.get_call(query=query ) if global_entry.get("loadType") == "V2_COMPACT": global_entry["loadType"] = "V2_COMPAT" results = LoadResult(global_entry) if results.load_type in [ LoadType.PLAYLIST_LOADED, LoadType.TRACK_LOADED, LoadType.SEARCH_RESULT, LoadType.V2_COMPAT, ]: valid_global_entry = True if valid_global_entry: if IS_DEBUG: log.debug(f"Querying Global DB api for {query}") results, called_api = results, False if valid_global_entry: pass elif lazy is True: called_api = False elif val and not forced and isinstance(val, dict): data = val data["query"] = query_string if data.get("loadType") == "V2_COMPACT": data["loadType"] = "V2_COMPAT" results = LoadResult(data) called_api = False if results.has_error: # If cached value has an invalid entry make a new call so that it gets updated results, called_api = await self.fetch_track(ctx, player, query, forced=True) valid_global_entry = False else: if IS_DEBUG: log.debug(f"Querying Lavalink api for {query_string}") called_api = True try: results = await player.load_tracks(query_string) except KeyError: results = None except RuntimeError: raise TrackEnqueueError if results is None: results = LoadResult({ "loadType": "LOAD_FAILED", "playlistInfo": {}, "tracks": [] }) valid_global_entry = False update_global = (globaldb_toggle and not valid_global_entry and self.global_cache_api.has_api_key) with contextlib.suppress(Exception): if (update_global and not query.is_local and not results.has_error and len(results.tracks) >= 1): global_task = ("global", dict(llresponse=results, query=query)) self.append_task(ctx, *global_task) if (cache_enabled and results.load_type and not results.has_error and not query.is_local and results.tracks): try: time_now = int( datetime.datetime.now(datetime.timezone.utc).timestamp()) data = json.dumps(results._raw) if all(k in data for k in ["loadType", "playlistInfo", "isSeekable", "isStream"]): task = ( "insert", ( "lavalink", [{ "query": query_string, "data": data, "last_updated": time_now, "last_fetched": time_now, }], ), ) self.append_task(ctx, *task) except Exception as exc: debug_exc_log( log, exc, f"Failed to enqueue write task for '{query_string}' to Lavalink table", ) return results, called_api
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
async def spotify_enqueue( self, ctx: commands.Context, query_type: str, uri: str, enqueue: bool, player: lavalink.Player, lock: Callable, notifier: Optional[Notifier] = None, query_global=True, ) -> List[lavalink.Track]: track_list = [] has_not_allowed = False await self.audio_api._get_api_key() try: current_cache_level = (CacheLevel(await self.config.cache_level()) if HAS_SQL else CacheLevel.none()) 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) globaldb_toggle = await _config.enabled() 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 = str(datetime.datetime.now(datetime.timezone.utc)) 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 llresponse = None if youtube_cache: update = True with contextlib.suppress(SQLError): val, update = await self.fetch_one( "youtube", "youtube_url", {"track": track_info}) if update: val = None should_query_global = (globaldb_toggle and not update and query_global and val is None) if should_query_global: llresponse = await self.audio_api.get_spotify( track_name, artist_name) if llresponse: llresponse = LoadResult(llresponse) val = llresponse or 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 and llresponse is None: task = ("update", ("youtube", {"track": track_info})) self.append_task(ctx, *task) if llresponse: track_object = llresponse.tracks elif val: try: result, called_api = await self.lavalink_query( ctx, player, audio_dataclasses.Query.process_input(val), 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." ), ) 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 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: embed3 = discord.Embed( colour=await ctx.embed_colour(), title= _("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), ) await ctx.send(embed=embed3) 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
async def lavalink_query( self, ctx: commands.Context, player: lavalink.Player, query: audio_dataclasses.Query, forced: bool = False, lazy: bool = False, should_query_global: bool = True, ) -> Tuple[LoadResult, bool]: """A replacement for :code:`lavalink.Player.load_tracks`. This will try to get a valid cached entry first if not found or if in valid it will then call the lavalink API. Parameters ---------- ctx: commands.Context The context this method is being called under. player : lavalink.Player The player who's requesting the query. query: audio_dataclasses.Query The Query object for the query in question. forced:bool Whether or not to skip cache and call API first.. lazy:bool If set to True, it will not call the api if a track is not found. Returns ------- Tuple[lavalink.LoadResult, bool] Tuple with the Load result and whether or not the API was called. """ await self.audio_api._get_api_key() current_cache_level = (CacheLevel(await self.config.cache_level()) if HAS_SQL else CacheLevel.none()) cache_enabled = CacheLevel.set_lavalink().is_subset( current_cache_level) val = None _raw_query = audio_dataclasses.Query.process_input(query) query = str(_raw_query) valid_global_entry = True results = None globaldb_toggle = await _config.enabled() if cache_enabled and not forced and not _raw_query.is_local: update = True with contextlib.suppress(SQLError): val, update = await self.fetch_one("lavalink", "data", {"query": query}) if update: val = None if val: local_task = ("update", ("lavalink", {"query": query})) self.append_task(ctx, *local_task) valid_global_entry = False if (globaldb_toggle and not val and should_query_global and not forced and not _raw_query.is_local and not _raw_query.is_spotify): valid_global_entry = False with contextlib.suppress(Exception): global_entry = await self.audio_api.get_call(query=_raw_query) results = LoadResult(global_entry) if results.load_type in [ LoadType.PLAYLIST_LOADED, LoadType.TRACK_LOADED, LoadType.SEARCH_RESULT, LoadType.V2_COMPAT, ]: valid_global_entry = True if valid_global_entry: return results, False if lazy is True: called_api = False elif val and not forced: data = json.loads(val) data["query"] = query results = LoadResult(data) called_api = False if results.has_error: # If cached value has an invalid entry make a new call so that it gets updated results, called_api = await self.lavalink_query(ctx, player, _raw_query, forced=True) valid_global_entry = False else: called_api = True results = None try: results = await player.load_tracks(query) except KeyError: results = None if results is None: results = LoadResult({ "loadType": "LOAD_FAILED", "playlistInfo": {}, "tracks": [] }) valid_global_entry = False update_global = globaldb_toggle and not valid_global_entry and _WRITE_GLOBAL_API_ACCESS with contextlib.suppress(Exception): if (update_global and not _raw_query.is_local and not results.has_error and len(results.tracks) >= 1): global_task = ("global", dict(llresponse=results, query=_raw_query)) self.append_task(ctx, *global_task) if (cache_enabled and results.load_type and not results.has_error and not _raw_query.is_local and results.tracks): with contextlib.suppress(SQLError): time_now = str(datetime.datetime.now(datetime.timezone.utc)) local_task = ( "insert", ( "lavalink", [{ "query": query, "data": json.dumps(results._raw), "last_updated": time_now, "last_fetched": time_now, }], ), ) self.append_task(ctx, *local_task) return results, called_api