This commit is contained in:
codey 2025-01-15 20:17:49 -05:00
parent b2bb724826
commit c09f72803e
7 changed files with 128 additions and 25 deletions

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python3.12 #!/usr/bin/env python3.12
# pylint: disable=bare-except
import importlib import importlib
import traceback
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel from pydantic import BaseModel
@ -182,6 +184,7 @@ class LastFM(FastAPI):
/get_track_info/ /get_track_info/
Get track info from Last.FM given an artist/track Get track info from Last.FM given an artist/track
""" """
try:
artist = data.a artist = data.a
track = data.t track = data.t
@ -197,4 +200,9 @@ class LastFM(FastAPI):
'success': True, 'success': True,
'result': track_info_result 'result': track_info_result
} }
except:
traceback.print_exc()
return {
'err': True,
'errorText': 'General error',
}

View File

@ -194,6 +194,16 @@ class LyricSearch(FastAPI):
result = result.dict() result = result.dict()
result['lyrics'] = regex.sub(r'(\s/\s|\n)', '<br>', result['lyrics']).strip() result['lyrics'] = regex.sub(r'(\s/\s|\n)', '<br>', result['lyrics']).strip()
result['confidence'] = f'{float(result.get('confidence', 0)):.2f}' result['confidence'] = f'{float(result.get('confidence', 0)):.2f}'
result['time'] = f'{float(result['time']):.4f}'
if "cached" in result['src']:
result['from_cache'] = True
"""
REMOVE BELOW AFTER TESTING IS DONE
"""
# if not data.extra:
# result.pop('src')
return result return result

View File

@ -10,6 +10,7 @@ class LyricsResult:
src: str src: str
lyrics: str lyrics: str
confidence: float confidence: float
time: float = 0.00
def dict(self): def dict(self):
"""Return as dict""" """Return as dict"""

View File

@ -37,7 +37,6 @@ class Aggregate:
continue continue
search_result = await source.search(artist, song) search_result = await source.search(artist, song)
if search_result: if search_result:
return search_result break
logging.info("%s: NOT FOUND!", source.label) logging.info("%s: NOT FOUND!", source.label)
return search_result return search_result

View File

@ -2,6 +2,8 @@
# pylint: disable=wrong-import-order, wrong-import-position bare-except, broad-exception-caught # pylint: disable=wrong-import-order, wrong-import-position bare-except, broad-exception-caught
import os import os
import time
import regex
import logging import logging
import sys import sys
import traceback import traceback
@ -40,8 +42,66 @@ class Cache:
confidence=confidence) confidence=confidence)
return None return None
async def check_existence(self, artistsong: str) -> Optional[bool]:
"""
Check whether lyrics are already stored for track
@artistsong: artist and song in artist\nsong format
"""
logging.debug("Checking whether %s is already stored",
artistsong.replace("\n", " - "))
check_query = "SELECT id FROM lyrics WHERE artistsong LIKE ? LIMIT 1"
params = (artistsong,)
async with sqlite3.connect(self.cache_db, timeout=2) as db_conn:
async with db_conn.execute(check_query, params) as db_cursor:
result = await db_cursor.fetchone()
if result:
logging.debug("%s is already stored.",
artistsong.replace("\n", " - "))
return True
logging.debug("%s cleared to be stored.",
artistsong)
return False
async def store(self, lyr_result: LyricsResult) -> None:
"""
store
@lyr_result: the returned lyrics to cache
"""
logging.info("Storing %s",
f"{lyr_result.artist} - {lyr_result.song}")
if lyr_result.src.lower() == "cache":
logging.info("Skipping cache storage - returned LyricsResult originated from cache")
return
artistsong = f"{lyr_result.artist}\n{lyr_result.song}"
if await self.check_existence(artistsong):
logging.info("Skipping cache storage - %s is already stored.",
artistsong.replace("\n", " - "))
return
try:
lyrics = regex.sub(r'(<br>|\n|\r\n)', ' / ', lyr_result.lyrics.strip())
lyrics = regex.sub(r'\s{2,}', ' ', lyrics)
insert_query = "INSERT INTO lyrics (src, date_retrieved, artist, song, artistsong, confidence, lyrics) VALUES(?, ?, ?, ?, ?, ?, ?)"
params = (lyr_result.src, time.time(), lyr_result.artist,
lyr_result.song, artistsong, lyr_result.confidence, lyrics)
async with sqlite3.connect(self.cache_db, timeout=2) as db_conn:
async with db_conn.execute(insert_query, params) as _cursor:
await db_conn.commit()
logging.info("Stored %s!", artistsong.replace("\n", " - "))
except:
logging.critical("Cache storage error!")
traceback.print_exc()
async def search(self, artist: str, song: str) -> Optional[LyricsResult]: async def search(self, artist: str, song: str) -> Optional[LyricsResult]:
""" """
search
@artist: the artist to search @artist: the artist to search
@song: the song to search @song: the song to search
Returns: Returns:
@ -52,6 +112,8 @@ class Cache:
song: str = song.strip().lower() song: str = song.strip().lower()
search_params: Optional[tuple] = None search_params: Optional[tuple] = None
random_search: bool = False random_search: bool = False
time_start: float = time.time()
logging.info("Searching %s - %s on %s", logging.info("Searching %s - %s on %s",
artist, song, self.label) artist, song, self.label)
async with sqlite3.connect(self.cache_db, timeout=2) as db_conn: async with sqlite3.connect(self.cache_db, timeout=2) as db_conn:
@ -60,8 +122,8 @@ class Cache:
await db_conn.load_extension(ext) await db_conn.load_extension(ext)
async with await db_conn.executescript(self.cache_pre_query) as _db_cursor: async with await db_conn.executescript(self.cache_pre_query) as _db_cursor:
search_query: str = 'SELECT id, artist, song, lyrics, src, confidence FROM lyrics\ search_query: str = 'SELECT id, artist, song, lyrics, src, confidence FROM lyrics\
WHERE editdist3((artist || " " || song), (? || " " || ?))\ WHERE editdist3((lower(artist) || " " || lower(song)), (? || " " || ?))\
<= 410 ORDER BY editdist3((artist || " " || song), ?) ASC LIMIT 10' <= 410 ORDER BY editdist3((lower(artist) || " " || lower(song)), ?) ASC LIMIT 10'
search_params: tuple = (artist.strip(), song.strip(), search_params: tuple = (artist.strip(), song.strip(),
f"{artist.strip()} {song.strip()}") f"{artist.strip()} {song.strip()}")
if artist == "!" and song == "!": if artist == "!" and song == "!":
@ -85,9 +147,13 @@ class Cache:
return None return None
(candidate, confidence) = best_match (candidate, confidence) = best_match
logging.info("Result found on %s", self.label) logging.info("Result found on %s", self.label)
return self.get_matched(sqlite_rows=results, matched = self.get_matched(sqlite_rows=results,
matched_candidate=candidate, matched_candidate=candidate,
confidence=confidence) confidence=confidence)
time_end: float = time.time()
time_diff: float = time_end - time_start
matched.time = time_diff
return matched
except: except:
if log_level == "DEBUG": if log_level == "DEBUG":
traceback.print_exc() traceback.print_exc()

View File

@ -5,15 +5,18 @@ import sys
sys.path.insert(1,'..') sys.path.insert(1,'..')
import traceback import traceback
import logging import logging
import time
from typing import Optional from typing import Optional
from aiohttp import ClientTimeout, ClientSession from aiohttp import ClientTimeout, ClientSession
from bs4 import BeautifulSoup, ResultSet from bs4 import BeautifulSoup, ResultSet
import html as htm import html as htm
from . import private from . import private
from . import common from . import common
from . import cache
from lyric_search_new import utils from lyric_search_new import utils
from lyric_search_new.constructors import LyricsResult from lyric_search_new.constructors import LyricsResult
logger = logging.getLogger() logger = logging.getLogger()
log_level = logging.getLevelName(logger.level) log_level = logging.getLevelName(logger.level)
@ -32,6 +35,7 @@ class Genius:
self.timeout = ClientTimeout(connect=2, sock_read=4) self.timeout = ClientTimeout(connect=2, sock_read=4)
self.datautils = utils.DataUtils() self.datautils = utils.DataUtils()
self.matcher = utils.TrackMatcher() self.matcher = utils.TrackMatcher()
self.cache = cache.Cache()
async def search(self, artist: str, song: str) -> Optional[LyricsResult]: async def search(self, artist: str, song: str) -> Optional[LyricsResult]:
""" """
@ -41,6 +45,7 @@ class Genius:
try: try:
artist: str = artist.strip().lower() artist: str = artist.strip().lower()
song: str = song.strip().lower() song: str = song.strip().lower()
time_start: float = time.time()
logging.info("Searching %s - %s on %s", logging.info("Searching %s - %s on %s",
artist, song, self.label) artist, song, self.label)
search_term: str = f'{artist}%20{song}' search_term: str = f'{artist}%20{song}'
@ -104,14 +109,19 @@ class Genius:
artist: str = track.split(" - ", maxsplit=1)[0] artist: str = track.split(" - ", maxsplit=1)[0]
song: str = track.split(" - ", maxsplit=1)[1] song: str = track.split(" - ", maxsplit=1)[1]
logging.info("Result found on %s", self.label) logging.info("Result found on %s", self.label)
return LyricsResult(artist=artist, time_end: float = time.time()
time_diff: float = time_end - time_start
matched = LyricsResult(artist=artist,
song=song, song=song,
src=self.label, src=self.label,
lyrics=returned_lyrics, lyrics=returned_lyrics,
confidence=confidence) confidence=confidence,
time=time_diff)
await self.cache.store(matched)
return matched
except: except:
if log_level == "DEBUG": # if log_level == "DEBUG":
traceback.print_exc() traceback.print_exc()
return return

View File

@ -2,6 +2,7 @@
# pylint: disable=bare-except, broad-exception-caught, wrong-import-position # pylint: disable=bare-except, broad-exception-caught, wrong-import-position
import sys import sys
import time
sys.path.insert(1,'..') sys.path.insert(1,'..')
import traceback import traceback
import logging import logging
@ -10,6 +11,7 @@ from aiohttp import ClientTimeout, ClientSession
from lyric_search_new import utils from lyric_search_new import utils
from lyric_search_new.constructors import LyricsResult from lyric_search_new.constructors import LyricsResult
from . import common from . import common
from . import cache
logger = logging.getLogger() logger = logging.getLogger()
log_level = logging.getLevelName(logger.level) log_level = logging.getLevelName(logger.level)
@ -28,6 +30,7 @@ class LRCLib:
self.timeout = ClientTimeout(connect=2, sock_read=4) self.timeout = ClientTimeout(connect=2, sock_read=4)
self.datautils = utils.DataUtils() self.datautils = utils.DataUtils()
self.matcher = utils.TrackMatcher() self.matcher = utils.TrackMatcher()
self.cache = cache.Cache()
async def search(self, artist: str, song: str) -> Optional[LyricsResult]: async def search(self, artist: str, song: str) -> Optional[LyricsResult]:
""" """
@ -37,6 +40,7 @@ class LRCLib:
try: try:
artist: str = artist.strip().lower() artist: str = artist.strip().lower()
song: str = song.strip().lower() song: str = song.strip().lower()
time_start: float = time.time()
logging.info("Searching %s - %s on %s", logging.info("Searching %s - %s on %s",
artist, song, self.label) artist, song, self.label)
@ -91,11 +95,16 @@ class LRCLib:
if not confidence: if not confidence:
return # No suitable match found return # No suitable match found
logging.info("Result found on %s", self.label) logging.info("Result found on %s", self.label)
return LyricsResult(artist=returned_artist, time_end: float = time.time()
time_diff: float = time_end - time_start
matched = LyricsResult(artist=returned_artist,
song=returned_song, song=returned_song,
src=self.label, src=self.label,
lyrics=returned_lyrics, lyrics=returned_lyrics,
confidence=confidence) confidence=confidence,
time=time_diff)
await self.cache.store(matched)
return matched
except: except:
if log_level == "DEBUG": if log_level == "DEBUG":
traceback.print_exc() traceback.print_exc()