significant refactor/cleanup

This commit is contained in:
2025-02-11 20:01:07 -05:00
parent 2c368aaf1a
commit 88d870ce8f
12 changed files with 440 additions and 467 deletions

View File

@ -7,27 +7,29 @@ import urllib.parse
import regex
import aiosqlite as sqlite3
from fastapi import FastAPI, HTTPException
from typing import LiteralString, Optional, Pattern
from .constructors import ValidTypeAheadRequest, ValidLyricRequest
from lyric_search.constructors import LyricsResult
from lyric_search.sources import aggregate
from lyric_search import notifier
class CacheUtils:
"""Lyrics Cache DB Utils"""
def __init__(self):
self.lyrics_db_path = os.path.join("/", "usr", "local", "share",
self.lyrics_db_path: LiteralString = os.path.join("/", "usr", "local", "share",
"sqlite_dbs", "cached_lyrics.db")
async def check_typeahead(self, s: str, pre_query: str | None = None):
async def check_typeahead(self, s: str, pre_query: str | None = None) -> Optional[list[dict]]:
"""Check s against artists stored - for typeahead"""
async with sqlite3.connect(self.lyrics_db_path,
timeout=2) as db_conn:
db_conn.row_factory = lambda c, r: dict([(col[0], r[idx]) for idx, col in enumerate(c.description)])
if not pre_query:
query = "SELECT distinct(artist) FROM lyrics WHERE artist LIKE ? LIMIT 15"
query_params = (f"%{s}%",)
query: str = "SELECT distinct(artist) FROM lyrics WHERE artist LIKE ? LIMIT 15"
query_params: tuple = (f"%{s}%",)
else:
query = "SELECT distinct(song) FROM lyrics WHERE artist LIKE ? AND song LIKE ? LIMIT 15"
query_params = (f"%{pre_query}%", f"%{s}%",)
query: str = "SELECT distinct(song) FROM lyrics WHERE artist LIKE ? AND song LIKE ? LIMIT 15"
query_params: tuple = (f"%{pre_query}%", f"%{s}%",)
async with await db_conn.execute(query, query_params) as db_cursor:
return await db_cursor.fetchall()
@ -43,14 +45,14 @@ class LyricSearch(FastAPI):
self.notifier = notifier.DiscordNotifier()
self.endpoints = {
self.endpoints: dict = {
"typeahead/artist": self.artist_typeahead_handler,
"typeahead/song": self.song_typeahead_handler,
"lyric_search": self.lyric_search_handler, # Preserving old endpoint path temporarily
"lyric/search": self.lyric_search_handler,
}
self.acceptable_request_sources = [
self.acceptable_request_sources: list = [
"WEB",
"WEB-RADIO",
"IRC-MS",
@ -62,25 +64,25 @@ class LyricSearch(FastAPI):
"LIMNORIA-SHARED",
]
self.lrc_regex = regex.compile(r'\[([0-9]{2}:[0-9]{2})\.[0-9]{1,3}\](\s(.*)){0,}')
self.lrc_regex: Pattern = regex.compile(r'\[([0-9]{2}:[0-9]{2})\.[0-9]{1,3}\](\s(.*)){0,}')
for endpoint, handler in self.endpoints.items():
_schema_include = endpoint in ["lyric/search"]
app.add_api_route(f"/{endpoint}", handler, methods=["POST"], include_in_schema=_schema_include)
async def artist_typeahead_handler(self, data: ValidTypeAheadRequest):
async def artist_typeahead_handler(self, data: ValidTypeAheadRequest) -> list[str]|dict:
"""Artist Type Ahead Handler"""
if not isinstance(data.query, str) or len(data.query) < 2:
return {
'err': True,
'errorText': 'Invalid request',
}
query = data.query
typeahead_result = await self.cache_utils.check_typeahead(query)
typeahead_list = [str(r.get('artist')) for r in typeahead_result]
query: str = data.query
typeahead_result: Optional[list[dict]] = await self.cache_utils.check_typeahead(query)
typeahead_list: list[str] = [str(r.get('artist')) for r in typeahead_result]
return typeahead_list
async def song_typeahead_handler(self, data: ValidTypeAheadRequest):
async def song_typeahead_handler(self, data: ValidTypeAheadRequest) -> list[str]|dict:
"""Song Type Ahead Handler"""
if not isinstance(data.pre_query, str)\
or not isinstance(data.query, str|None):
@ -88,13 +90,13 @@ class LyricSearch(FastAPI):
'err': True,
'errorText': 'Invalid request',
}
pre_query = data.pre_query
query = data.query
typeahead_result = await self.cache_utils.check_typeahead(query, pre_query)
typeahead_list = [str(r.get('song')) for r in typeahead_result]
pre_query: str = data.pre_query
query: str = data.query
typeahead_result: Optional[list[dict]] = await self.cache_utils.check_typeahead(query, pre_query)
typeahead_list: list[str] = [str(r.get('song')) for r in typeahead_result]
return typeahead_list
async def lyric_search_handler(self, data: ValidLyricRequest):
async def lyric_search_handler(self, data: ValidLyricRequest) -> dict:
"""
Search for lyrics
@ -129,15 +131,15 @@ class LyricSearch(FastAPI):
if search_artist and search_song:
search_artist = self.constants.DOUBLE_SPACE_REGEX.sub(" ", search_artist.strip())
search_song = self.constants.DOUBLE_SPACE_REGEX.sub(" ", search_song.strip())
search_artist = urllib.parse.unquote(search_artist)
search_song = urllib.parse.unquote(search_song)
search_artist: str = self.constants.DOUBLE_SPACE_REGEX.sub(" ", search_artist.strip())
search_song: str = self.constants.DOUBLE_SPACE_REGEX.sub(" ", search_song.strip())
search_artist: str = urllib.parse.unquote(search_artist)
search_song: str = urllib.parse.unquote(search_song)
excluded_sources = data.excluded_sources
excluded_sources: list = data.excluded_sources
aggregate_search = aggregate.Aggregate(exclude_methods=excluded_sources)
plain_lyrics = not data.lrc
result = await aggregate_search.search(search_artist, search_song, plain_lyrics)
plain_lyrics: bool = not data.lrc
result: Optional[LyricsResult] = await aggregate_search.search(search_artist, search_song, plain_lyrics)
if not result:
return {
@ -145,15 +147,15 @@ class LyricSearch(FastAPI):
'errorText': 'Sources exhausted, lyrics not located.',
}
result = result.todict()
result: dict = result.todict()
if data.sub and not data.lrc:
seeked_found_line = None
lyric_lines = result['lyrics'].strip().split(" / ")
seeked_found_line: Optional[int] = None
lyric_lines: list[str] = result['lyrics'].strip().split(" / ")
for i, line in enumerate(lyric_lines):
line = regex.sub(r'\u2064', '', line.strip())
line: str = regex.sub(r'\u2064', '', line.strip())
if data.sub.strip().lower() in line.strip().lower():
seeked_found_line = i
seeked_found_line: int = i
logging.debug("Found %s at %s, match for %s!",
line, seeked_found_line, data.sub) # REMOVEME: DEBUG
break