This commit is contained in:
2025-02-14 16:07:24 -05:00
parent 00af36703a
commit 60416c493f
19 changed files with 204 additions and 308 deletions

View File

@ -7,7 +7,8 @@ import urllib.parse
import regex
import aiosqlite as sqlite3
from fastapi import FastAPI, HTTPException
from typing import LiteralString, Optional, Pattern
from typing import LiteralString, Optional, Callable
from regex import Pattern
from .constructors import ValidTypeAheadRequest, ValidLyricRequest
from lyric_search.constructors import LyricsResult
from lyric_search.sources import aggregate
@ -15,32 +16,31 @@ from lyric_search import notifier
class CacheUtils:
"""Lyrics Cache DB Utils"""
def __init__(self):
self.lyrics_db_path: LiteralString = os.path.join("/", "usr", "local", "share",
def __init__(self) -> None:
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) -> Optional[list[dict]]:
async def check_typeahead(self, s: str, pre_query: str | None = None) -> 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)])
db_conn.row_factory = sqlite3.Row
if not pre_query:
query: str = "SELECT distinct(artist) FROM lyrics WHERE artist LIKE ? LIMIT 15"
query_params: tuple = (f"%{s}%",)
else:
query: str = "SELECT distinct(song) FROM lyrics WHERE artist LIKE ? AND song LIKE ? LIMIT 15"
query_params: tuple = (f"%{pre_query}%", f"%{s}%",)
query = "SELECT distinct(song) FROM lyrics WHERE artist LIKE ? AND song LIKE ? LIMIT 15"
query_params = (f"%{pre_query}%", f"%{s}%",)
async with await db_conn.execute(query, query_params) as db_cursor:
return await db_cursor.fetchall()
class LyricSearch(FastAPI):
"""Lyric Search Endpoint"""
def __init__(self, app: FastAPI, util, constants, glob_state): # pylint: disable=super-init-not-called
def __init__(self, app: FastAPI, util, constants): # pylint: disable=super-init-not-called
self.app = app
self.util = util
self.constants = constants
self.glob_state = glob_state
self.cache_utils = CacheUtils()
self.notifier = notifier.DiscordNotifier()
@ -78,7 +78,7 @@ class LyricSearch(FastAPI):
'errorText': 'Invalid request',
}
query: str = data.query
typeahead_result: Optional[list[dict]] = await self.cache_utils.check_typeahead(query)
typeahead_result: 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
@ -92,7 +92,7 @@ class LyricSearch(FastAPI):
}
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_result: 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
@ -122,24 +122,30 @@ class LyricSearch(FastAPI):
}
if not data.t:
search_artist: str = data.a
search_song: str = data.s
search_artist: Optional[str] = data.a
search_song: Optional[str] = data.s
else:
t_split = data.t.split(" - ", maxsplit=1)
search_artist: str = t_split[0]
search_song: str = t_split[1]
search_artist = t_split[0]
search_song = t_split[1]
if search_artist and 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)
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 = urllib.parse.unquote(search_artist)
search_song = urllib.parse.unquote(search_song)
excluded_sources: list = data.excluded_sources
if not isinstance(search_artist, str) or not isinstance(search_song, str):
return {
'err': True,
'errorText': 'Invalid request',
}
excluded_sources: Optional[list] = data.excluded_sources
aggregate_search = aggregate.Aggregate(exclude_methods=excluded_sources)
plain_lyrics: bool = not data.lrc
result: Optional[LyricsResult] = await aggregate_search.search(search_artist, search_song, plain_lyrics)
result: Optional[LyricsResult|dict] = await aggregate_search.search(search_artist, search_song, plain_lyrics)
if not result:
return {
@ -147,15 +153,15 @@ class LyricSearch(FastAPI):
'errorText': 'Sources exhausted, lyrics not located.',
}
result: dict = result.todict()
result = vars(result)
if data.sub and not data.lrc:
seeked_found_line: Optional[int] = None
lyric_lines: list[str] = result['lyrics'].strip().split(" / ")
for i, line in enumerate(lyric_lines):
line: str = regex.sub(r'\u2064', '', line.strip())
line = regex.sub(r'\u2064', '', line.strip())
if data.sub.strip().lower() in line.strip().lower():
seeked_found_line: int = i
seeked_found_line = i
logging.debug("Found %s at %s, match for %s!",
line, seeked_found_line, data.sub) # REMOVEME: DEBUG
break