mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-23 11:13:51 -05:00
[3.2][Audio] Part 6 (Last? maybe?) (#3244)
* Removes `MAX_BALANCE` from bank, user `bank.get_max_balance()` now `[p]bankset maxbal` can be used to set the maximum bank balance Signed-off-by: Guy <guyreis96@gmail.com> * Initial Commit Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * I need to make sure I keep aika on her toes. Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Fixes a few missing kwargs and case consistency Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Fixes a few missing kwargs and case consistency v2 and typos Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Reset cooldowns + add changelogs Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Add 3 extra file formats. Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * IRDUMB - fix capitalization. Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Fix a silent error, and some incorrect messages. Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Remove unnecessary emojis from queue when they are not needed Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Remove duplicated call in `[p]playlist update` Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Remove duplicated call in `[p]playlist update` Signed-off-by: guyre <27962761+drapersniper@users.noreply.github.com> * Resolve conflicts Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Bring all files up to date + Black Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Facepalm Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * *Sigh* Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * *Sigh* 2.0 Signed-off-by: Draper <27962761+Drapersniper@users.noreply.github.com> * Merge branch 'V3/develop' of https://github.com/Cog-Creators/Red-DiscordBot into audio-misc-pt1 Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> # Resolve Conflicts: # redbot/cogs/audio/audio.py # redbot/cogs/audio/utils.py * Import missing Typecheck Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Fix Broken docstrings Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Sort Local Tracks Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * 🤦 Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Reorder the sorting of local tracks, `alphanumerical lower then alphanumerical upper` `a comes before A, but B comes after A` Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black formatting Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Make the local file sorting case insensitive Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Add global blacklist/whitelist + fix some issues with original server based whitelist/blacklist Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Remove the pre-commit yaml Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Nottin to see Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Further improvement to the blacklists Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Further improvement to the blacklists Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Fix the __str__ method on LocalTracks Object * Rename LocalTracks.to_string_hidden() to LocalTracks.to_string_user() To keep it inline with the Query object * Remove encoding pragmas + a few typo fixes * Update some typehints + fix some typos * Remove this duplicate call * Black * fix capitalization * Address preda's review Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Remove the API from the audio cog - Is in direct conflict with goals stated in #2804 - Features this was intended to enable can be enabled in other more appropriate ways later on * changelog * Address Aika's review * Black * *sigh* dont use github web ui * Fuck windows Long live linux... *sigh* no lets ensure windows users can still use local tracks * Merge branch 'V3/develop' of https://github.com/Cog-Creators/Red-DiscordBot into refactoring Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> # Conflicts: # redbot/cogs/audio/audio.py * 👀 + chore Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * facepalm Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * facepalm... again y u h8 me bruh Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fuk this fuk u tube fuck python fuck all Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * awyehfqwajefhnqeffawefqa eqewarfqaesf qwef qaf qwfr Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fuck everything Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * oh lord saviour resus i love you just make this work Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Change logic to be no errors within last 10 seconds... this should be a valid work around discord ratelimits caused by the spam * Remove auto deletion Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * See I did a ting Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * irdumb Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * black Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Add an is_url attribute to Query objects * chore Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black * Address Aikas review Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Hyperlink Playlist names Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Make shit bold Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * why was this here Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * why was this here Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Initial commit * Workinnng Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Improve SQL Statements + migrate from SQL Alchemy + Databases to APSW Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * apsw tested and working Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * chose Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Migrate Playlist to DB 3 TODO 1 Migrate Config to Schema 3 without playlists and update get_playlist methods Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Revert "Migrate Playlist to DB 3 TODO 1 Migrate Config to Schema 3 without playlists and update get_playlist methods" This reverts commit4af33cffSigned-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Implement schema migration * Lets not touch the deps since #3192 is already adding them * chore * *sigh* Black * Follow the existing logic and always default Playlist to guild scope * wghqjegqf black * Update usage of last_fetched and last_updated to be Ints... However column migration still pending * Some bug fixes * Update usage of last_fetched and last_updated to be Ints... However column migration still pending * working Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * partial match Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * better partial match Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * black Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * I thought i done this before * Delete 3195.misc.1.rst Wrong PR * Thanks Sinbad Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Thanks Sinbad Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Log Errors in init ... Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Update error logs. Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Create index Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * :Drapersweat: Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Chore Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Revert "Chore" This reverts commitedcc9a9fUGHHHH Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Allow removing tracks from queue by URL Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Words matter Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * chore Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * arghhh CONFLICTS Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Review sinbads latest comment .. ToDo.. Nuke existing playlist - check version and set version Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * migrate the DB schema to v3 (to keep in line with the schema visioning of Config * Add a Todo * *sigh* conflicts and black * *sigh* black * Passively delete playlist deletion mechanism * Delete Old entries on startup * Since we are dropping the table mightaware make these into JSON for future proofing * Don't Dump strings in JSON field ? :think: * Move some things around to make easier to use 1 connection to the Audio DB * Move some things around to make easier to use 1 connection to the Audio DB * *sigh* * Clean up api * *sigh* black * Red + reorder some variables * 🤦 * how could i forget this ....... * Black Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * #automagically Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * FINAFUCKINGLY Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * FINAFUCKINGLY Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Remove unused config default Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Remove the API from the audio Cog (Properly) Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Missed these changes Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * ARGHHH Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Some fixes I've noticed while running through the code line by line Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Translation + UX (show playlist author ID if can't find user) Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* missed this one Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * this is no longer needed .... Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * 🤦 Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fix new lines in error messages Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black * Sinbads Review Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Sinbads Review Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* copy paste Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * imrpove backups Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Im a fucking idiot Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Fix #3238 Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * chore Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * humans Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * humans Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * add play alias to playlists Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Im dumb ... Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Im dumb ... Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fix new line Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fix new line Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * show playlist count on playlist picker Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * DJ/Vote system fixes Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * DJ/Vote system fixes Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* fix currency check Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * show playlist count on playlist picker Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * DJ/Vote system fixes Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * DJ/Vote system fixes Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* fix currency check Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Fix duplicate messages on timeout Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fix SQL Statement logic Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * fix SQL Statement logic Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Markdown escape Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Markdown escape Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Markdown escape fix Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Markdown escape fix Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * clean up local cache more frequently Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * clean up db more frequently Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Await in hell Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* im dumb Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* im dumb Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black cuz I hate red Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Black cuz I hate red Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * StringIO to ByteIO Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * StringIO to ByteIO Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* im dumb Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * :Facepalm: the whole purpose of this is so its offline so this can be backed up without being blocking Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Run write queries on ThreadPoolExecutor Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * Backup Audio.db Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* im dumb Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * blaaaack Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * *sigh* Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * formatting Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * remove duplicated string of code Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> * ffs awaits Signed-off-by: Drapersniper <27962761+drapersniper@users.noreply.github.com> Co-authored-by: Michael H <michael@michaelhall.tech>
This commit is contained in:
@@ -4,25 +4,10 @@ import contextlib
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
import traceback
|
||||
from collections import namedtuple
|
||||
from typing import Callable, Dict, List, Mapping, Optional, Tuple, Union
|
||||
|
||||
try:
|
||||
from sqlite3 import Error as SQLError
|
||||
from databases import Database
|
||||
|
||||
HAS_SQL = True
|
||||
_ERROR = None
|
||||
except ImportError as err:
|
||||
_ERROR = "".join(traceback.format_exception_only(type(err), err)).strip()
|
||||
HAS_SQL = False
|
||||
SQLError = err.__class__
|
||||
Database = None
|
||||
|
||||
from typing import Callable, List, MutableMapping, Optional, TYPE_CHECKING, Tuple, Union, NoReturn
|
||||
|
||||
import aiohttp
|
||||
import discord
|
||||
@@ -32,129 +17,38 @@ from lavalink.rest_api import LoadResult
|
||||
from redbot.core import Config, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
|
||||
from . import audio_dataclasses
|
||||
from .errors import InvalidTableError, SpotifyFetchError, YouTubeApiError, DatabaseError
|
||||
from .databases import CacheInterface, SQLError
|
||||
from .errors import DatabaseError, SpotifyFetchError, YouTubeApiError, TrackEnqueueError
|
||||
from .playlists import get_playlist
|
||||
from .utils import CacheLevel, Notifier, is_allowed, queue_duration, track_limit
|
||||
|
||||
log = logging.getLogger("red.audio.cache")
|
||||
_ = Translator("Audio", __file__)
|
||||
|
||||
_DROP_YOUTUBE_TABLE = "DROP TABLE youtube;"
|
||||
|
||||
_CREATE_YOUTUBE_TABLE = """
|
||||
CREATE TABLE IF NOT EXISTS youtube(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
track_info TEXT,
|
||||
youtube_url TEXT,
|
||||
last_updated TEXT,
|
||||
last_fetched TEXT
|
||||
);
|
||||
"""
|
||||
|
||||
_CREATE_UNIQUE_INDEX_YOUTUBE_TABLE = (
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_youtube_url ON youtube (track_info, youtube_url);"
|
||||
)
|
||||
|
||||
_INSERT_YOUTUBE_TABLE = """
|
||||
INSERT OR REPLACE INTO
|
||||
youtube(track_info, youtube_url, last_updated, last_fetched)
|
||||
VALUES (:track_info, :track_url, :last_updated, :last_fetched);
|
||||
"""
|
||||
_QUERY_YOUTUBE_TABLE = "SELECT * FROM youtube WHERE track_info=:track;"
|
||||
_UPDATE_YOUTUBE_TABLE = """UPDATE youtube
|
||||
SET last_fetched=:last_fetched
|
||||
WHERE track_info=:track;"""
|
||||
|
||||
_DROP_SPOTIFY_TABLE = "DROP TABLE spotify;"
|
||||
|
||||
_CREATE_UNIQUE_INDEX_SPOTIFY_TABLE = (
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_spotify_uri ON spotify (id, type, uri);"
|
||||
)
|
||||
|
||||
_CREATE_SPOTIFY_TABLE = """
|
||||
CREATE TABLE IF NOT EXISTS spotify(
|
||||
id TEXT,
|
||||
type TEXT,
|
||||
uri TEXT,
|
||||
track_name TEXT,
|
||||
artist_name TEXT,
|
||||
song_url TEXT,
|
||||
track_info TEXT,
|
||||
last_updated TEXT,
|
||||
last_fetched TEXT
|
||||
);
|
||||
"""
|
||||
|
||||
_INSERT_SPOTIFY_TABLE = """
|
||||
INSERT OR REPLACE INTO
|
||||
spotify(id, type, uri, track_name, artist_name,
|
||||
song_url, track_info, last_updated, last_fetched)
|
||||
VALUES (:id, :type, :uri, :track_name, :artist_name,
|
||||
:song_url, :track_info, :last_updated, :last_fetched);
|
||||
"""
|
||||
_QUERY_SPOTIFY_TABLE = "SELECT * FROM spotify WHERE uri=:uri;"
|
||||
_UPDATE_SPOTIFY_TABLE = """UPDATE spotify
|
||||
SET last_fetched=:last_fetched
|
||||
WHERE uri=:uri;"""
|
||||
|
||||
_DROP_LAVALINK_TABLE = "DROP TABLE lavalink;"
|
||||
|
||||
_CREATE_LAVALINK_TABLE = """
|
||||
CREATE TABLE IF NOT EXISTS lavalink(
|
||||
query TEXT,
|
||||
data BLOB,
|
||||
last_updated TEXT,
|
||||
last_fetched TEXT
|
||||
|
||||
);
|
||||
"""
|
||||
|
||||
_CREATE_UNIQUE_INDEX_LAVALINK_TABLE = (
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_lavalink_query ON lavalink (query);"
|
||||
)
|
||||
|
||||
_INSERT_LAVALINK_TABLE = """
|
||||
INSERT OR REPLACE INTO
|
||||
lavalink(query, data, last_updated, last_fetched)
|
||||
VALUES (:query, :data, :last_updated, :last_fetched);
|
||||
"""
|
||||
_QUERY_LAVALINK_TABLE = "SELECT * FROM lavalink WHERE query=:query;"
|
||||
_QUERY_LAST_FETCHED_LAVALINK_TABLE = (
|
||||
"SELECT * FROM lavalink "
|
||||
"WHERE last_fetched LIKE :day1"
|
||||
" OR last_fetched LIKE :day2"
|
||||
" OR last_fetched LIKE :day3"
|
||||
" OR last_fetched LIKE :day4"
|
||||
" OR last_fetched LIKE :day5"
|
||||
" OR last_fetched LIKE :day6"
|
||||
" OR last_fetched LIKE :day7;"
|
||||
)
|
||||
_UPDATE_LAVALINK_TABLE = """UPDATE lavalink
|
||||
SET last_fetched=:last_fetched
|
||||
WHERE query=:query;"""
|
||||
|
||||
_PARSER = {
|
||||
"youtube": {
|
||||
"insert": _INSERT_YOUTUBE_TABLE,
|
||||
"youtube_url": {"query": _QUERY_YOUTUBE_TABLE},
|
||||
"update": _UPDATE_YOUTUBE_TABLE,
|
||||
},
|
||||
"spotify": {
|
||||
"insert": _INSERT_SPOTIFY_TABLE,
|
||||
"track_info": {"query": _QUERY_SPOTIFY_TABLE},
|
||||
"update": _UPDATE_SPOTIFY_TABLE,
|
||||
},
|
||||
"lavalink": {
|
||||
"insert": _INSERT_LAVALINK_TABLE,
|
||||
"data": {"query": _QUERY_LAVALINK_TABLE, "played": _QUERY_LAST_FETCHED_LAVALINK_TABLE},
|
||||
"update": _UPDATE_LAVALINK_TABLE,
|
||||
},
|
||||
}
|
||||
|
||||
_TOP_100_GLOBALS = "https://www.youtube.com/playlist?list=PL4fGSI1pDJn6puJdseH2Rt9sMvt9E2M4i"
|
||||
_TOP_100_US = "https://www.youtube.com/playlist?list=PL4fGSI1pDJn5rWitrRWFKdm-ulaFiIyoK"
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_database: CacheInterface
|
||||
_bot: Red
|
||||
_config: Config
|
||||
else:
|
||||
_database = None
|
||||
_bot = None
|
||||
_config = None
|
||||
|
||||
|
||||
def _pass_config_to_apis(config: Config, bot: Red):
|
||||
global _database, _config, _bot
|
||||
if _config is None:
|
||||
_config = config
|
||||
if _bot is None:
|
||||
_bot = bot
|
||||
if _database is None:
|
||||
_database = CacheInterface()
|
||||
|
||||
|
||||
class SpotifyAPI:
|
||||
"""Wrapper for the Spotify API."""
|
||||
@@ -162,17 +56,19 @@ class SpotifyAPI:
|
||||
def __init__(self, bot: Red, session: aiohttp.ClientSession):
|
||||
self.bot = bot
|
||||
self.session = session
|
||||
self.spotify_token = None
|
||||
self.spotify_token: Optional[MutableMapping[str, Union[str, int]]] = None
|
||||
self.client_id = None
|
||||
self.client_secret = None
|
||||
|
||||
@staticmethod
|
||||
async def _check_token(token: dict):
|
||||
async def _check_token(token: MutableMapping):
|
||||
now = int(time.time())
|
||||
return token["expires_at"] - now < 60
|
||||
|
||||
@staticmethod
|
||||
def _make_token_auth(client_id: Optional[str], client_secret: Optional[str]) -> dict:
|
||||
def _make_token_auth(
|
||||
client_id: Optional[str], client_secret: Optional[str]
|
||||
) -> MutableMapping[str, Union[str, int]]:
|
||||
if client_id is None:
|
||||
client_id = ""
|
||||
if client_secret is None:
|
||||
@@ -181,7 +77,9 @@ class SpotifyAPI:
|
||||
auth_header = base64.b64encode((client_id + ":" + client_secret).encode("ascii"))
|
||||
return {"Authorization": "Basic %s" % auth_header.decode("ascii")}
|
||||
|
||||
async def _make_get(self, url: str, headers: dict = None, params: dict = None) -> dict:
|
||||
async def _make_get(
|
||||
self, url: str, headers: MutableMapping = None, params: MutableMapping = None
|
||||
) -> MutableMapping[str, str]:
|
||||
if params is None:
|
||||
params = {}
|
||||
async with self.session.request("GET", url, params=params, headers=headers) as r:
|
||||
@@ -193,13 +91,12 @@ class SpotifyAPI:
|
||||
)
|
||||
return await r.json()
|
||||
|
||||
async def _get_auth(self):
|
||||
if self.client_id is None or self.client_secret is None:
|
||||
tokens = await self.bot.get_shared_api_tokens("spotify")
|
||||
self.client_id = tokens.get("client_id", "")
|
||||
self.client_secret = tokens.get("client_secret", "")
|
||||
async def _get_auth(self) -> NoReturn:
|
||||
tokens = await self.bot.get_shared_api_tokens("spotify")
|
||||
self.client_id = tokens.get("client_id", "")
|
||||
self.client_secret = tokens.get("client_secret", "")
|
||||
|
||||
async def _request_token(self) -> dict:
|
||||
async def _request_token(self) -> MutableMapping[str, Union[str, int]]:
|
||||
await self._get_auth()
|
||||
|
||||
payload = {"grant_type": "client_credentials"}
|
||||
@@ -223,7 +120,9 @@ class SpotifyAPI:
|
||||
log.debug("Created a new access token for Spotify: {0}".format(token))
|
||||
return self.spotify_token["access_token"]
|
||||
|
||||
async def post_call(self, url: str, payload: dict, headers: dict = None) -> dict:
|
||||
async def post_call(
|
||||
self, url: str, payload: MutableMapping, headers: MutableMapping = None
|
||||
) -> MutableMapping[str, Union[str, int]]:
|
||||
async with self.session.post(url, data=payload, headers=headers) as r:
|
||||
if r.status != 200:
|
||||
log.debug(
|
||||
@@ -233,13 +132,15 @@ class SpotifyAPI:
|
||||
)
|
||||
return await r.json()
|
||||
|
||||
async def get_call(self, url: str, params: dict) -> dict:
|
||||
async def get_call(
|
||||
self, url: str, params: MutableMapping
|
||||
) -> MutableMapping[str, Union[str, int]]:
|
||||
token = await self._get_spotify_token()
|
||||
return await self._make_get(
|
||||
url, params=params, headers={"Authorization": "Bearer {0}".format(token)}
|
||||
)
|
||||
|
||||
async def get_categories(self) -> List[Dict[str, str]]:
|
||||
async def get_categories(self) -> List[MutableMapping]:
|
||||
url = "https://api.spotify.com/v1/browse/categories"
|
||||
params = {}
|
||||
result = await self.get_call(url, params=params)
|
||||
@@ -278,10 +179,9 @@ class YouTubeAPI:
|
||||
self.session = session
|
||||
self.api_key = None
|
||||
|
||||
async def _get_api_key(self,) -> Optional[str]:
|
||||
if self.api_key is None:
|
||||
tokens = await self.bot.get_shared_api_tokens("youtube")
|
||||
self.api_key = tokens.get("api_key", "")
|
||||
async def _get_api_key(self,) -> str:
|
||||
tokens = await self.bot.get_shared_api_tokens("youtube")
|
||||
self.api_key = tokens.get("api_key", "")
|
||||
return self.api_key
|
||||
|
||||
async def get_call(self, query: str) -> Optional[str]:
|
||||
@@ -310,122 +210,39 @@ class YouTubeAPI:
|
||||
|
||||
@cog_i18n(_)
|
||||
class MusicCache:
|
||||
"""
|
||||
Handles music queries to the Spotify and Youtube Data API.
|
||||
"""Handles music queries to the Spotify and Youtube Data API.
|
||||
|
||||
Always tries the Cache first.
|
||||
"""
|
||||
|
||||
def __init__(self, bot: Red, session: aiohttp.ClientSession, path: str):
|
||||
def __init__(self, bot: Red, session: aiohttp.ClientSession):
|
||||
self.bot = bot
|
||||
self.spotify_api: SpotifyAPI = SpotifyAPI(bot, session)
|
||||
self.youtube_api: YouTubeAPI = YouTubeAPI(bot, session)
|
||||
self._session: aiohttp.ClientSession = session
|
||||
if HAS_SQL:
|
||||
self.database: Database = Database(
|
||||
f'sqlite:///{os.path.abspath(str(os.path.join(path, "cache.db")))}'
|
||||
)
|
||||
else:
|
||||
self.database = None
|
||||
self.database = _database
|
||||
|
||||
self._tasks: dict = {}
|
||||
self._tasks: MutableMapping = {}
|
||||
self._lock: asyncio.Lock = asyncio.Lock()
|
||||
self.config: Optional[Config] = None
|
||||
|
||||
async def initialize(self, config: Config):
|
||||
if HAS_SQL:
|
||||
await self.database.connect()
|
||||
|
||||
await self.database.execute(query="PRAGMA temp_store = 2;")
|
||||
await self.database.execute(query="PRAGMA journal_mode = wal;")
|
||||
await self.database.execute(query="PRAGMA wal_autocheckpoint;")
|
||||
await self.database.execute(query="PRAGMA read_uncommitted = 1;")
|
||||
|
||||
await self.database.execute(query=_CREATE_LAVALINK_TABLE)
|
||||
await self.database.execute(query=_CREATE_UNIQUE_INDEX_LAVALINK_TABLE)
|
||||
await self.database.execute(query=_CREATE_YOUTUBE_TABLE)
|
||||
await self.database.execute(query=_CREATE_UNIQUE_INDEX_YOUTUBE_TABLE)
|
||||
await self.database.execute(query=_CREATE_SPOTIFY_TABLE)
|
||||
await self.database.execute(query=_CREATE_UNIQUE_INDEX_SPOTIFY_TABLE)
|
||||
self.config = config
|
||||
|
||||
async def close(self):
|
||||
if HAS_SQL:
|
||||
await self.database.execute(query="PRAGMA optimize;")
|
||||
await self.database.disconnect()
|
||||
|
||||
async def insert(self, table: str, values: List[dict]):
|
||||
# if table == "spotify":
|
||||
# return
|
||||
if HAS_SQL:
|
||||
query = _PARSER.get(table, {}).get("insert")
|
||||
if query is None:
|
||||
raise InvalidTableError(f"{table} is not a valid table in the database.")
|
||||
|
||||
await self.database.execute_many(query=query, values=values)
|
||||
|
||||
async def update(self, table: str, values: Dict[str, str]):
|
||||
# if table == "spotify":
|
||||
# return
|
||||
if HAS_SQL:
|
||||
table = _PARSER.get(table, {})
|
||||
sql_query = table.get("update")
|
||||
time_now = str(datetime.datetime.now(datetime.timezone.utc))
|
||||
values["last_fetched"] = time_now
|
||||
if not table:
|
||||
raise InvalidTableError(f"{table} is not a valid table in the database.")
|
||||
await self.database.fetch_one(query=sql_query, values=values)
|
||||
|
||||
async def fetch_one(
|
||||
self, table: str, query: str, values: Dict[str, str]
|
||||
) -> Tuple[Optional[str], bool]:
|
||||
table = _PARSER.get(table, {})
|
||||
sql_query = table.get(query, {}).get("query")
|
||||
if HAS_SQL:
|
||||
if not table:
|
||||
raise InvalidTableError(f"{table} is not a valid table in the database.")
|
||||
|
||||
row = await self.database.fetch_one(query=sql_query, values=values)
|
||||
last_updated = getattr(row, "last_updated", None)
|
||||
need_update = True
|
||||
with contextlib.suppress(TypeError):
|
||||
if last_updated:
|
||||
last_update = datetime.datetime.fromisoformat(
|
||||
last_updated
|
||||
) + datetime.timedelta(days=await self.config.cache_age())
|
||||
last_update.replace(tzinfo=datetime.timezone.utc)
|
||||
|
||||
need_update = last_update < datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
return getattr(row, query, None), need_update if table != "spotify" else True
|
||||
else:
|
||||
return None, True
|
||||
|
||||
# TODO: Create a task to remove entries
|
||||
# from DB that haven't been fetched in x days ... customizable by Owner
|
||||
|
||||
async def fetch_all(self, table: str, query: str, values: Dict[str, str]) -> List[Mapping]:
|
||||
if HAS_SQL:
|
||||
table = _PARSER.get(table, {})
|
||||
sql_query = table.get(query, {}).get("played")
|
||||
if not table:
|
||||
raise InvalidTableError(f"{table} is not a valid table in the database.")
|
||||
|
||||
return await self.database.fetch_all(query=sql_query, values=values)
|
||||
return []
|
||||
await _database.init()
|
||||
|
||||
@staticmethod
|
||||
def _spotify_format_call(qtype: str, key: str) -> Tuple[str, dict]:
|
||||
def _spotify_format_call(qtype: str, key: str) -> Tuple[str, MutableMapping]:
|
||||
params = {}
|
||||
if qtype == "album":
|
||||
query = "https://api.spotify.com/v1/albums/{0}/tracks".format(key)
|
||||
query = f"https://api.spotify.com/v1/albums/{key}/tracks"
|
||||
elif qtype == "track":
|
||||
query = "https://api.spotify.com/v1/tracks/{0}".format(key)
|
||||
query = f"https://api.spotify.com/v1/tracks/{key}"
|
||||
else:
|
||||
query = "https://api.spotify.com/v1/playlists/{0}/tracks".format(key)
|
||||
query = f"https://api.spotify.com/v1/playlists/{key}/tracks"
|
||||
return query, params
|
||||
|
||||
@staticmethod
|
||||
def _get_spotify_track_info(track_data: dict) -> Tuple[str, ...]:
|
||||
def _get_spotify_track_info(track_data: MutableMapping) -> Tuple[str, ...]:
|
||||
artist_name = track_data["artists"][0]["name"]
|
||||
track_name = track_data["name"]
|
||||
track_info = f"{track_name} {artist_name}"
|
||||
@@ -451,7 +268,7 @@ class MusicCache:
|
||||
total_tracks = len(tracks)
|
||||
database_entries = []
|
||||
track_count = 0
|
||||
time_now = str(datetime.datetime.now(datetime.timezone.utc))
|
||||
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
||||
youtube_cache = CacheLevel.set_youtube().is_subset(current_cache_level)
|
||||
for track in tracks:
|
||||
if track.get("error", {}).get("message") == "invalid id":
|
||||
@@ -484,7 +301,7 @@ class MusicCache:
|
||||
if youtube_cache:
|
||||
update = True
|
||||
with contextlib.suppress(SQLError):
|
||||
val, update = await self.fetch_one(
|
||||
(val, update) = await self.database.fetch_one(
|
||||
"youtube", "youtube_url", {"track": track_info}
|
||||
)
|
||||
if update:
|
||||
@@ -517,7 +334,7 @@ class MusicCache:
|
||||
) -> str:
|
||||
track_url = await self.youtube_api.get_call(track_info)
|
||||
if CacheLevel.set_youtube().is_subset(current_cache_level) and track_url:
|
||||
time_now = str(datetime.datetime.now(datetime.timezone.utc))
|
||||
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
||||
task = (
|
||||
"insert",
|
||||
(
|
||||
@@ -540,12 +357,12 @@ class MusicCache:
|
||||
query_type: str,
|
||||
uri: str,
|
||||
recursive: Union[str, bool] = False,
|
||||
params=None,
|
||||
params: MutableMapping = None,
|
||||
notifier: Optional[Notifier] = None,
|
||||
) -> Union[dict, List[str]]:
|
||||
) -> Union[MutableMapping, List[str]]:
|
||||
|
||||
if recursive is False:
|
||||
call, params = self._spotify_format_call(query_type, uri)
|
||||
(call, params) = self._spotify_format_call(query_type, uri)
|
||||
results = await self.spotify_api.get_call(call, params)
|
||||
else:
|
||||
results = await self.spotify_api.get_call(recursive, params)
|
||||
@@ -608,8 +425,7 @@ class MusicCache:
|
||||
skip_youtube: bool = False,
|
||||
notifier: Optional[Notifier] = None,
|
||||
) -> List[str]:
|
||||
"""
|
||||
Queries the Database then falls back to Spotify and YouTube APIs.
|
||||
"""Queries the Database then falls back to Spotify and YouTube APIs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -628,14 +444,12 @@ class MusicCache:
|
||||
List[str]
|
||||
List of Youtube URLs.
|
||||
"""
|
||||
current_cache_level = (
|
||||
CacheLevel(await self.config.cache_level()) if HAS_SQL else CacheLevel.none()
|
||||
)
|
||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||
cache_enabled = CacheLevel.set_spotify().is_subset(current_cache_level)
|
||||
if query_type == "track" and cache_enabled:
|
||||
update = True
|
||||
with contextlib.suppress(SQLError):
|
||||
val, update = await self.fetch_one(
|
||||
(val, update) = await self.database.fetch_one(
|
||||
"spotify", "track_info", {"uri": f"spotify:track:{uri}"}
|
||||
)
|
||||
if update:
|
||||
@@ -673,9 +487,7 @@ class MusicCache:
|
||||
track_list = []
|
||||
has_not_allowed = False
|
||||
try:
|
||||
current_cache_level = (
|
||||
CacheLevel(await self.config.cache_level()) if HAS_SQL else CacheLevel.none()
|
||||
)
|
||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||
guild_data = await self.config.guild(ctx.guild).all()
|
||||
|
||||
# now = int(time.time())
|
||||
@@ -698,7 +510,7 @@ class MusicCache:
|
||||
|
||||
return track_list
|
||||
database_entries = []
|
||||
time_now = str(datetime.datetime.now(datetime.timezone.utc))
|
||||
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)
|
||||
@@ -730,7 +542,7 @@ class MusicCache:
|
||||
if youtube_cache:
|
||||
update = True
|
||||
with contextlib.suppress(SQLError):
|
||||
val, update = await self.fetch_one(
|
||||
(val, update) = await self.database.fetch_one(
|
||||
"youtube", "youtube_url", {"track": track_info}
|
||||
)
|
||||
if update:
|
||||
@@ -745,7 +557,7 @@ class MusicCache:
|
||||
|
||||
if val:
|
||||
try:
|
||||
result, called_api = await self.lavalink_query(
|
||||
(result, called_api) = await self.lavalink_query(
|
||||
ctx, player, audio_dataclasses.Query.process_input(val)
|
||||
)
|
||||
except (RuntimeError, aiohttp.ServerDisconnectedError):
|
||||
@@ -760,7 +572,7 @@ class MusicCache:
|
||||
lock(ctx, False)
|
||||
error_embed = discord.Embed(
|
||||
colour=await ctx.embed_colour(),
|
||||
title=_("Player timedout, skipping remaning tracks."),
|
||||
title=_("Player timeout, skipping remaining tracks."),
|
||||
)
|
||||
await notifier.update_embed(error_embed)
|
||||
break
|
||||
@@ -771,16 +583,6 @@ class MusicCache:
|
||||
key = "lavalink"
|
||||
seconds = "???"
|
||||
second_key = None
|
||||
# if track_count == 2:
|
||||
# five_time = int(time.time()) - now
|
||||
# if track_count >= 2:
|
||||
# remain_tracks = total_tracks - track_count
|
||||
# time_remain = (remain_tracks / 2) * five_time
|
||||
# if track_count < total_tracks:
|
||||
# seconds = dynamic_time(int(time_remain))
|
||||
# if track_count == total_tracks:
|
||||
# seconds = "0s"
|
||||
# second_key = "lavalink_time"
|
||||
await notifier.notify_user(
|
||||
current=track_count,
|
||||
total=total_tracks,
|
||||
@@ -837,16 +639,14 @@ class MusicCache:
|
||||
await player.play()
|
||||
if len(track_list) == 0:
|
||||
if not has_not_allowed:
|
||||
embed3 = discord.Embed(
|
||||
colour=await ctx.embed_colour(),
|
||||
title=_(
|
||||
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),
|
||||
).format(prefix=ctx.prefix)
|
||||
)
|
||||
await ctx.send(embed=embed3)
|
||||
player.maybe_shuffle()
|
||||
if enqueue and tracks_from_spotify:
|
||||
if total_tracks > enqueued_tracks:
|
||||
@@ -885,15 +685,15 @@ class MusicCache:
|
||||
return track_list
|
||||
|
||||
async def youtube_query(self, ctx: commands.Context, track_info: str) -> str:
|
||||
current_cache_level = (
|
||||
CacheLevel(await self.config.cache_level()) if HAS_SQL else CacheLevel.none()
|
||||
)
|
||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||
cache_enabled = CacheLevel.set_youtube().is_subset(current_cache_level)
|
||||
val = None
|
||||
if cache_enabled:
|
||||
update = True
|
||||
with contextlib.suppress(SQLError):
|
||||
val, update = await self.fetch_one("youtube", "youtube_url", {"track": track_info})
|
||||
(val, update) = await self.database.fetch_one(
|
||||
"youtube", "youtube_url", {"track": track_info}
|
||||
)
|
||||
if update:
|
||||
val = None
|
||||
if val is None:
|
||||
@@ -914,10 +714,8 @@ class MusicCache:
|
||||
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.
|
||||
"""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
|
||||
----------
|
||||
@@ -934,9 +732,7 @@ class MusicCache:
|
||||
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()) if HAS_SQL else CacheLevel.none()
|
||||
)
|
||||
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)
|
||||
@@ -944,14 +740,15 @@ class MusicCache:
|
||||
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})
|
||||
(val, update) = await self.database.fetch_one("lavalink", "data", {"query": query})
|
||||
if update:
|
||||
val = None
|
||||
if val:
|
||||
if val and not isinstance(val, str):
|
||||
log.debug(f"Querying Local Database for {query}")
|
||||
task = ("update", ("lavalink", {"query": query}))
|
||||
self.append_task(ctx, *task)
|
||||
if val and not forced:
|
||||
data = json.loads(val)
|
||||
data = val
|
||||
data["query"] = query
|
||||
results = LoadResult(data)
|
||||
called_api = False
|
||||
@@ -965,6 +762,8 @@ class MusicCache:
|
||||
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 (
|
||||
@@ -975,7 +774,7 @@ class MusicCache:
|
||||
and results.tracks
|
||||
):
|
||||
with contextlib.suppress(SQLError):
|
||||
time_now = str(datetime.datetime.now(datetime.timezone.utc))
|
||||
time_now = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
||||
task = (
|
||||
"insert",
|
||||
(
|
||||
@@ -1003,10 +802,12 @@ class MusicCache:
|
||||
tasks = self._tasks[ctx.message.id]
|
||||
del self._tasks[ctx.message.id]
|
||||
await asyncio.gather(
|
||||
*[self.insert(*a) for a in tasks["insert"]], return_exceptions=True
|
||||
*[self.database.insert(*a) for a in tasks["insert"]],
|
||||
return_exceptions=True,
|
||||
)
|
||||
await asyncio.gather(
|
||||
*[self.update(*a) for a in tasks["update"]], return_exceptions=True
|
||||
*[self.database.update(*a) for a in tasks["update"]],
|
||||
return_exceptions=True,
|
||||
)
|
||||
log.debug(f"Completed database writes for {lock_id} " f"({lock_author})")
|
||||
|
||||
@@ -1015,16 +816,16 @@ class MusicCache:
|
||||
log.debug("Running pending writes to database")
|
||||
with contextlib.suppress(Exception):
|
||||
tasks = {"update": [], "insert": []}
|
||||
for k, task in self._tasks.items():
|
||||
for (k, task) in self._tasks.items():
|
||||
for t, args in task.items():
|
||||
tasks[t].append(args)
|
||||
self._tasks = {}
|
||||
|
||||
await asyncio.gather(
|
||||
*[self.insert(*a) for a in tasks["insert"]], return_exceptions=True
|
||||
*[self.database.insert(*a) for a in tasks["insert"]], return_exceptions=True
|
||||
)
|
||||
await asyncio.gather(
|
||||
*[self.update(*a) for a in tasks["update"]], return_exceptions=True
|
||||
*[self.database.update(*a) for a in tasks["update"]], return_exceptions=True
|
||||
)
|
||||
log.debug("Completed pending writes to database have finished")
|
||||
|
||||
@@ -1034,29 +835,26 @@ class MusicCache:
|
||||
self._tasks[lock_id] = {"update": [], "insert": []}
|
||||
self._tasks[lock_id][event].append(task)
|
||||
|
||||
async def play_random(self):
|
||||
async def get_random_from_db(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
|
||||
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.fetch_all("lavalink", "data", query_data)
|
||||
recently_played = [r.data for r in vals if r]
|
||||
vals = await self.database.fetch_all("lavalink", "data", query_data)
|
||||
recently_played = [r.tracks for r in vals if r]
|
||||
|
||||
if recently_played:
|
||||
track = random.choice(recently_played)
|
||||
results = LoadResult(json.loads(track))
|
||||
results = LoadResult(track)
|
||||
tracks = list(results.tracks)
|
||||
except Exception:
|
||||
tracks = []
|
||||
@@ -1065,9 +863,7 @@ class MusicCache:
|
||||
|
||||
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()) if HAS_SQL else CacheLevel.none()
|
||||
)
|
||||
current_cache_level = CacheLevel(await self.config.cache_level())
|
||||
cache_enabled = CacheLevel.set_lavalink().is_subset(current_cache_level)
|
||||
playlist = None
|
||||
tracks = None
|
||||
@@ -1084,10 +880,10 @@ class MusicCache:
|
||||
|
||||
if not tracks or not getattr(playlist, "tracks", None):
|
||||
if cache_enabled:
|
||||
tracks = await self.play_random()
|
||||
tracks = await self.get_random_from_db()
|
||||
if not tracks:
|
||||
ctx = namedtuple("Context", "message")
|
||||
results, called_api = await self.lavalink_query(
|
||||
(results, called_api) = await self.lavalink_query(
|
||||
ctx(player.channel.guild),
|
||||
player,
|
||||
audio_dataclasses.Query.process_input(_TOP_100_US),
|
||||
@@ -1124,7 +920,7 @@ class MusicCache:
|
||||
continue
|
||||
valid = True
|
||||
|
||||
track.extras = {"autoplay": 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
|
||||
|
||||
Reference in New Issue
Block a user