radio_util: db restructuring related changes, misc refactoring, add todos
This commit is contained in:
parent
4e3940e297
commit
f946a6f81c
@ -1,19 +1,28 @@
|
||||
import logging
|
||||
import traceback
|
||||
import time
|
||||
import regex
|
||||
from regex import Pattern
|
||||
import datetime
|
||||
import os
|
||||
import gpt
|
||||
from aiohttp import ClientSession, ClientTimeout
|
||||
import aiosqlite as sqlite3
|
||||
from typing import Union, Optional, LiteralString, Iterable
|
||||
from uuid import uuid4 as uuid
|
||||
from typing import Union, Optional, Iterable
|
||||
from aiohttp import ClientSession, ClientTimeout
|
||||
import regex
|
||||
from regex import Pattern
|
||||
import aiosqlite as sqlite3
|
||||
import gpt
|
||||
from endpoints.constructors import RadioException
|
||||
|
||||
double_space: Pattern = regex.compile(r"\s{2,}")
|
||||
|
||||
"""
|
||||
TODO:
|
||||
- Album art rework
|
||||
- Allow tracks to be queried again based on genre; unable to query tracks based on genre presently,
|
||||
as genre was moved outside track_file_map to artist_genre_map
|
||||
- Ask GPT when we encounter an untagged (no genre defined) artist, automation is needed for this tedious task
|
||||
- etc..
|
||||
"""
|
||||
|
||||
|
||||
class RadioUtil:
|
||||
"""
|
||||
@ -27,9 +36,12 @@ class RadioUtil:
|
||||
self.sqlite_exts: list[str] = [
|
||||
"/home/api/api/solibs/spellfix1.cpython-311-x86_64-linux-gnu.so"
|
||||
]
|
||||
self.active_playlist_path: Union[str, LiteralString] = os.path.join(
|
||||
self.active_playlist_path: str = os.path.join(
|
||||
"/usr/local/share", "sqlite_dbs", "track_file_map.db"
|
||||
)
|
||||
self.artist_genre_db_path: str = os.path.join(
|
||||
"/usr/local/share", "sqlite_dbs", "artist_genre_map.db"
|
||||
)
|
||||
self.active_playlist_name = "default" # not used
|
||||
self.active_playlist: list[dict] = []
|
||||
self.now_playing: dict = {
|
||||
@ -64,6 +76,13 @@ class RadioUtil:
|
||||
return str(datetime.timedelta(seconds=s)).split(".", maxsplit=1)[0]
|
||||
|
||||
async def trackdb_typeahead(self, query: str) -> Optional[list[str]]:
|
||||
"""
|
||||
Query track db for typeahead
|
||||
Args:
|
||||
query (str): The search query
|
||||
Returns:
|
||||
Optional[list[str]]
|
||||
"""
|
||||
if not query:
|
||||
return None
|
||||
async with sqlite3.connect(self.active_playlist_path, timeout=1) as _db:
|
||||
@ -126,7 +145,7 @@ class RadioUtil:
|
||||
result: Optional[sqlite3.Row | bool] = await db_cursor.fetchone()
|
||||
if not result or not isinstance(result, sqlite3.Row):
|
||||
return False
|
||||
pushObj: dict = {
|
||||
push_obj: dict = {
|
||||
"id": result["id"],
|
||||
"uuid": str(uuid().hex),
|
||||
"artist": result["artist"].strip(),
|
||||
@ -136,17 +155,43 @@ class RadioUtil:
|
||||
"file_path": result["file_path"],
|
||||
"duration": result["duration"],
|
||||
}
|
||||
self.active_playlist.insert(0, pushObj)
|
||||
self.active_playlist.insert(0, push_obj)
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.critical("search_playlist:: Search error occurred: %s", str(e))
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
async def get_genre(self, artist: str) -> str:
|
||||
"""
|
||||
Retrieve Genre for given Artist
|
||||
Args:
|
||||
artist (str): The artist to query
|
||||
Returns:
|
||||
str
|
||||
"""
|
||||
try:
|
||||
artist = artist.strip()
|
||||
query = "SELECT genre FROM artist_genre WHERE artist LIKE ?"
|
||||
params = (f"%{artist}",)
|
||||
async with sqlite3.connect(self.artist_genre_db_path, timeout=2) as _db:
|
||||
_db.row_factory = sqlite3.Row
|
||||
async with await _db.execute(query, params) as _cursor:
|
||||
res = await _cursor.fetchone()
|
||||
if not res:
|
||||
raise RadioException(
|
||||
f"Could not locate {artist} in artist_genre_map db."
|
||||
)
|
||||
return res["genre"]
|
||||
except Exception as e:
|
||||
logging.info("Failed to look up genre for artist: %s (%s)", artist, str(e))
|
||||
traceback.print_exc()
|
||||
return "Not Found"
|
||||
|
||||
async def load_playlist(self) -> None:
|
||||
"""Load Playlist"""
|
||||
try:
|
||||
logging.info(f"Loading playlist...")
|
||||
logging.info("Loading playlist...")
|
||||
self.active_playlist.clear()
|
||||
# db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\
|
||||
# GROUP BY artistdashsong ORDER BY RANDOM()'
|
||||
@ -155,36 +200,10 @@ class RadioUtil:
|
||||
LIMITED GENRES
|
||||
"""
|
||||
|
||||
# db_query: str = """SELECT distinct(LOWER(TRIM(artist)) || " - " || LOWER(TRIM(song))), (TRIM(artist) || " - " || TRIM(song)) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\
|
||||
# WHERE (genre LIKE "%metalcore%"\
|
||||
# OR genre LIKE "%math rock%"\
|
||||
# OR genre LIKE "%punk rock%"\
|
||||
# OR genre LIKE "%metal%"\
|
||||
# OR genre LIKE "%punk%"\
|
||||
# OR genre LIKE "%electronic%"\
|
||||
# OR genre LIKE "%nu metal%"\
|
||||
# OR genre LIKE "%EDM%"\
|
||||
# OR genre LIKE "%post-hardcore%"\
|
||||
# OR genre LIKE "%pop rock%"\
|
||||
# OR genre LIKE "%experimental%"\
|
||||
# OR genre LIKE "%post-punk%"\
|
||||
# OR genre LIKE "%death metal%"\
|
||||
# OR genre LIKE "%electronicore%"\
|
||||
# OR genre LIKE "%hard rock%"\
|
||||
# OR genre LIKE "%psychedelic rock%"\
|
||||
# OR genre LIKE "%grunge%"\
|
||||
# OR genre LIKE "%house%"\
|
||||
# OR genre LIKE "%dubstep%"\
|
||||
# OR genre LIKE "%hardcore%"\
|
||||
# OR genre LIKE "%hair metal%"\
|
||||
# OR genre LIKE "%horror punk%"\
|
||||
# OR genre LIKE "%breakcore%"\
|
||||
# OR genre LIKE "%post-rock%"\
|
||||
# OR genre LIKE "%deathcore%"\
|
||||
# OR genre LIKE "%hardcore punk%"\
|
||||
# OR genre LIKE "%indie pop%"\
|
||||
# OR genre LIKE "%dnb%")\
|
||||
# GROUP BY artistdashsong ORDER BY RANDOM()"""
|
||||
db_query: str = (
|
||||
'SELECT distinct(LOWER(TRIM(artist)) || " - " || LOWER(TRIM(song))), (TRIM(artist) || " - " || TRIM(song))'
|
||||
"AS artistdashsong, id, artist, song, album, file_path, duration FROM tracks GROUP BY artistdashsong ORDER BY RANDOM()"
|
||||
)
|
||||
|
||||
"""
|
||||
LIMITED TO ONE/SMALL SUBSET OF GENRES
|
||||
@ -200,14 +219,14 @@ class RadioUtil:
|
||||
# db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\
|
||||
# WHERE (artist LIKE "%rise against%" OR artist LIKE "%i prevail%" OR artist LIKE "%volumes%" OR artist LIKE "%movements%" OR artist LIKE "%woe%" OR artist LIKE "%smittyztop%" OR artist LIKE "%chunk! no,%" OR artist LIKE "%fame on fire%" OR artist LIKE "%our last night%" OR artist LIKE "%animal in me%") AND (NOT song LIKE "%%stripped%%" AND NOT song LIKE "%(2022)%" AND NOT song LIKE "%(live%%" AND NOT song LIKE "%%acoustic%%" AND NOT song LIKE "%%instrumental%%" AND NOT song LIKE "%%remix%%" AND NOT song LIKE "%%reimagined%%" AND NOT song LIKE "%%alternative%%" AND NOT song LIKE "%%unzipped%%") GROUP BY artistdashsong ORDER BY RANDOM()'# ORDER BY album ASC, id ASC'
|
||||
|
||||
db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\
|
||||
WHERE (artist LIKE "%sullivan king%" OR artist LIKE "%kayzo%" OR artist LIKE "%adventure club%") AND (NOT song LIKE "%%stripped%%" AND NOT song LIKE "%(2022)%" AND NOT song LIKE "%(live%%" AND NOT song LIKE "%%acoustic%%" AND NOT song LIKE "%%instrumental%%" AND NOT song LIKE "%%remix%%" AND NOT song LIKE "%%reimagined%%" AND NOT song LIKE "%%alternative%%" AND NOT song LIKE "%%unzipped%%") GROUP BY artistdashsong ORDER BY RANDOM()'# ORDER BY album ASC, id ASC'
|
||||
# db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\
|
||||
# WHERE (artist LIKE "%sullivan king%" OR artist LIKE "%kayzo%" OR artist LIKE "%adventure club%") AND (NOT song LIKE "%%stripped%%" AND NOT song LIKE "%(2022)%" AND NOT song LIKE "%(live%%" AND NOT song LIKE "%%acoustic%%" AND NOT song LIKE "%%instrumental%%" AND NOT song LIKE "%%remix%%" AND NOT song LIKE "%%reimagined%%" AND NOT song LIKE "%%alternative%%" AND NOT song LIKE "%%unzipped%%") GROUP BY artistdashsong ORDER BY RANDOM()'# ORDER BY album ASC, id ASC'
|
||||
|
||||
# db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\
|
||||
# WHERE (artist LIKE "%akira the don%") AND (NOT song LIKE "%%stripped%%" AND NOT song LIKE "%(2022)%" AND NOT song LIKE "%(live%%" AND NOT song LIKE "%%acoustic%%" AND NOT song LIKE "%%instrumental%%" AND NOT song LIKE "%%remix%%" AND NOT song LIKE "%%reimagined%%" AND NOT song LIKE "%%alternative%%" AND NOT song LIKE "%%unzipped%%") GROUP BY artistdashsong ORDER BY RANDOM()'# ORDER BY album ASC, id ASC'
|
||||
|
||||
async with sqlite3.connect(
|
||||
f"file:{self.active_playlist_path}?mode=ro", uri=True, timeout=2
|
||||
f"file:{self.active_playlist_path}?mode=ro", uri=True, timeout=15
|
||||
) as db_conn:
|
||||
db_conn.row_factory = sqlite3.Row
|
||||
async with await db_conn.execute(db_query) as db_cursor:
|
||||
@ -219,7 +238,9 @@ class RadioUtil:
|
||||
"artist": double_space.sub(" ", r["artist"]).strip(),
|
||||
"song": double_space.sub(" ", r["song"]).strip(),
|
||||
"album": double_space.sub(" ", r["album"]).strip(),
|
||||
"genre": r["genre"] if r["genre"] else "Unknown",
|
||||
"genre": await self.get_genre(
|
||||
double_space.sub(" ", r["artist"]).strip()
|
||||
),
|
||||
"artistsong": double_space.sub(
|
||||
" ", r["artistdashsong"]
|
||||
).strip(),
|
||||
@ -232,7 +253,8 @@ class RadioUtil:
|
||||
"Populated active playlists with %s items",
|
||||
len(self.active_playlist),
|
||||
)
|
||||
except:
|
||||
except Exception as e:
|
||||
logging.info("Playlist load failed: %s", str(e))
|
||||
traceback.print_exc()
|
||||
|
||||
async def cache_album_art(self, track_id: int, album_art: bytes) -> None:
|
||||
@ -244,18 +266,19 @@ class RadioUtil:
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
async with sqlite3.connect(self.active_playlist_path, timeout=2) as db_conn:
|
||||
async with await db_conn.execute(
|
||||
"UPDATE tracks SET album_art = ? WHERE id = ?",
|
||||
(
|
||||
album_art,
|
||||
track_id,
|
||||
),
|
||||
) as db_cursor:
|
||||
await db_conn.commit()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return None # TODO: Album art is being reworked, temporarily return None
|
||||
# try:
|
||||
# async with sqlite3.connect(self.active_playlist_path, timeout=2) as db_conn:
|
||||
# async with await db_conn.execute(
|
||||
# "UPDATE tracks SET album_art = ? WHERE id = ?",
|
||||
# (
|
||||
# album_art,
|
||||
# track_id,
|
||||
# ),
|
||||
# ) as db_cursor:
|
||||
# await db_conn.commit()
|
||||
# except:
|
||||
# traceback.print_exc()
|
||||
|
||||
async def get_album_art(
|
||||
self, track_id: Optional[int] = None, file_path: Optional[str] = None
|
||||
@ -268,28 +291,29 @@ class RadioUtil:
|
||||
Returns:
|
||||
bytes
|
||||
"""
|
||||
try:
|
||||
async with sqlite3.connect(self.active_playlist_path, timeout=2) as db_conn:
|
||||
db_conn.row_factory = sqlite3.Row
|
||||
query: str = "SELECT album_art FROM tracks WHERE id = ?"
|
||||
query_params: tuple = (track_id,)
|
||||
return None # TODO: Album art is being reworked, temporarily return None
|
||||
# try:
|
||||
# async with sqlite3.connect(self.active_playlist_path, timeout=2) as db_conn:
|
||||
# db_conn.row_factory = sqlite3.Row
|
||||
# query: str = "SELECT album_art FROM tracks WHERE id = ?"
|
||||
# query_params: tuple = (track_id,)
|
||||
|
||||
if file_path and not track_id:
|
||||
query = "SELECT album_art FROM tracks WHERE file_path = ?"
|
||||
query_params = (file_path,)
|
||||
# if file_path and not track_id:
|
||||
# query = "SELECT album_art FROM tracks WHERE file_path = ?"
|
||||
# query_params = (file_path,)
|
||||
|
||||
async with await db_conn.execute(query, query_params) as db_cursor:
|
||||
result: Optional[Union[sqlite3.Row, bool]] = (
|
||||
await db_cursor.fetchone()
|
||||
)
|
||||
if not result or not isinstance(result, sqlite3.Row):
|
||||
return None
|
||||
return result["album_art"]
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
# async with await db_conn.execute(query, query_params) as db_cursor:
|
||||
# result: Optional[Union[sqlite3.Row, bool]] = (
|
||||
# await db_cursor.fetchone()
|
||||
# )
|
||||
# if not result or not isinstance(result, sqlite3.Row):
|
||||
# return None
|
||||
# return result["album_art"]
|
||||
# except:
|
||||
# traceback.print_exc()
|
||||
# return None
|
||||
|
||||
def get_queue_item_by_uuid(self, uuid: str) -> Optional[tuple[int, dict]]:
|
||||
def get_queue_item_by_uuid(self, _uuid: str) -> Optional[tuple[int, dict]]:
|
||||
"""
|
||||
Get queue item by UUID
|
||||
Args:
|
||||
@ -298,7 +322,7 @@ class RadioUtil:
|
||||
Optional[tuple[int, dict]]
|
||||
"""
|
||||
for x, item in enumerate(self.active_playlist):
|
||||
if item.get("uuid") == uuid:
|
||||
if item.get("uuid") == _uuid:
|
||||
return (x, item)
|
||||
return None
|
||||
|
||||
@ -340,22 +364,25 @@ class RadioUtil:
|
||||
return response
|
||||
|
||||
async def webhook_song_change(self, track: dict) -> None:
|
||||
"""
|
||||
Handles Song Change Outbounds (Webhooks)
|
||||
Args:
|
||||
track (dict)
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
try:
|
||||
"""
|
||||
Handles Song Change Outbounds (Webhooks)
|
||||
Args:
|
||||
track (dict)
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# First, send track info
|
||||
friendly_track_start: str = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(track["start"])
|
||||
)
|
||||
friendly_track_end: str = time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(track["end"])
|
||||
)
|
||||
"""
|
||||
TODO:
|
||||
Review friendly_track_start and friendly_track_end, not currently in use
|
||||
"""
|
||||
# friendly_track_start: str = time.strftime(
|
||||
# "%Y-%m-%d %H:%M:%S", time.localtime(track["start"])
|
||||
# )
|
||||
# friendly_track_end: str = time.strftime(
|
||||
# "%Y-%m-%d %H:%M:%S", time.localtime(track["end"])
|
||||
# )
|
||||
hook_data: dict = {
|
||||
"username": "serious.FM",
|
||||
"embeds": [
|
||||
@ -443,4 +470,5 @@ class RadioUtil:
|
||||
) as request:
|
||||
request.raise_for_status()
|
||||
except Exception as e:
|
||||
logging.info("Webhook error occurred: %s", str(e))
|
||||
traceback.print_exc()
|
||||
|
Loading…
x
Reference in New Issue
Block a user