diff --git a/base.py b/base.py index 42a33bf..67bdc60 100644 --- a/base.py +++ b/base.py @@ -93,6 +93,7 @@ routes: dict = { app, util, constants, loop ), "mgr": importlib.import_module("endpoints.mgr.mgr_test").Mgr(app, util, constants), + "meme": importlib.import_module("endpoints.meme").Meme(app, util, constants), } # Misc endpoint depends on radio endpoint instance diff --git a/endpoints/karma.py b/endpoints/karma.py index 64cee80..a8dbabf 100644 --- a/endpoints/karma.py +++ b/endpoints/karma.py @@ -85,9 +85,7 @@ class KarmaDB: "INSERT INTO karma(keyword, score, last_change) VALUES(?, ?, ?)" ) friendly_flag: str = "++" if not flag else "--" - audit_message: str = ( - f"{granter} adjusted karma for {keyword} @ {datetime.datetime.now().isoformat()}: {friendly_flag}" - ) + audit_message: str = f"{granter} adjusted karma for {keyword} @ {datetime.datetime.now().isoformat()}: {friendly_flag}" audit_query: str = ( "INSERT INTO karma_audit(impacted_keyword, comment) VALUES(?, ?)" ) diff --git a/endpoints/lyric_search.py b/endpoints/lyric_search.py index c6e8f9b..003a51f 100644 --- a/endpoints/lyric_search.py +++ b/endpoints/lyric_search.py @@ -115,7 +115,7 @@ class LyricSearch(FastAPI): if data.src.upper() not in self.acceptable_request_sources: await self.notifier.send( - f"ERROR @ {__file__.rsplit("/", maxsplit=1)[-1]}", + f"ERROR @ {__file__.rsplit('/', maxsplit=1)[-1]}", f"Unknown request source: {data.src}", ) return JSONResponse( diff --git a/endpoints/meme.py b/endpoints/meme.py new file mode 100644 index 0000000..ad16059 --- /dev/null +++ b/endpoints/meme.py @@ -0,0 +1,37 @@ +import logging +from fastapi import FastAPI, Request, Response +from fastapi.responses import JSONResponse +from utils.meme_util import MemeUtil + + +class Meme(FastAPI): + """ + Misc Endpoints + """ + + def __init__(self, app: FastAPI, my_util, constants) -> None: + self.app: FastAPI = app + self.util = my_util + self.meme_util = MemeUtil(constants) + self.constants = constants + self.endpoints: dict = { + "memes/get_meme/{id:path}": self.get_meme_by_id, + "memes/list_memes": self.list_memes, + } + + for endpoint, handler in self.endpoints.items(): + app.add_api_route( + f"/{endpoint}", handler, methods=["GET"], include_in_schema=True + ) + + async def get_meme_by_id(self, id: int, request: Request) -> Response: + """Get meme (image) by id""" + meme_image = await self.meme_util.get_meme_by_id(id) + if not meme_image: + return Response(status_code=404, content="Not found") + return Response(content=meme_image, media_type="image/png") + + async def list_memes(self, page: int, request: Request) -> Response: + """Get meme (image) by id""" + meme_list = await self.meme_util.list_memes(page) + return JSONResponse(content={"memes": meme_list}) diff --git a/endpoints/transcriptions.py b/endpoints/transcriptions.py index 3ad1d72..86e9574 100644 --- a/endpoints/transcriptions.py +++ b/endpoints/transcriptions.py @@ -113,9 +113,7 @@ class Transcriptions(FastAPI): db_path: Union[str, LiteralString] = os.path.join( "/usr/local/share", "sqlite_dbs", "sp.db" ) - db_query: str = ( - """SELECT ("S" || Season || "E" || Episode || " " || Title), Character, Line FROM SP_DAT WHERE ID = ?""" - ) + db_query: str = """SELECT ("S" || Season || "E" || Episode || " " || Title), Character, Line FROM SP_DAT WHERE ID = ?""" case 1: db_path = os.path.join("/usr/local/share", "sqlite_dbs", "futur.db") db_query = """SELECT ("S" || EP_S || "E" || EP_EP || " " || EP_TITLE || "
Opener: " || EP_OPENER || ""), EP_LINE_SPEAKER, EP_LINE FROM clean_dialog WHERE EP_ID = ? ORDER BY LINE_ID ASC""" diff --git a/lyric_search/sources/cache.py b/lyric_search/sources/cache.py index d9d72c5..8a1955e 100644 --- a/lyric_search/sources/cache.py +++ b/lyric_search/sources/cache.py @@ -89,10 +89,8 @@ class Cache: logging.debug( "Checking whether %s is already stored", artistsong.replace("\n", " - ") ) - check_query: str = ( - 'SELECT id, artist, song FROM lyrics WHERE editdist3((lower(artist) || " " || lower(song)), (? || " " || ?))\ + check_query: str = 'SELECT id, artist, song FROM lyrics WHERE editdist3((lower(artist) || " " || lower(song)), (? || " " || ?))\ <= 410 ORDER BY editdist3((lower(artist) || " " || lower(song)), ?) ASC LIMIT 1' - ) artistsong_split = artistsong.split("\n", maxsplit=1) artist = artistsong_split[0].lower() song = artistsong_split[1].lower() @@ -213,8 +211,10 @@ class Cache: lyrics = regex.sub(r"(
|\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)\ + insert_query = ( + "INSERT INTO lyrics (src, date_retrieved, artist, song, artistsong, confidence, lyrics)\ VALUES(?, ?, ?, ?, ?, ?, ?)" + ) params = ( lyr_result.src, time.time(), @@ -258,10 +258,8 @@ class Cache: if artist == "!" and song == "!": random_search = True - search_query: str = ( - "SELECT id, artist, song, lyrics, src, confidence\ + search_query: str = "SELECT id, artist, song, lyrics, src, confidence\ FROM lyrics ORDER BY RANDOM() LIMIT 1" - ) logging.info("Searching %s - %s on %s", artist, song, self.label) @@ -320,11 +318,9 @@ class Cache: self.cache_pre_query ) as _db_cursor: if not random_search: - 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((lower(artist) || " " || lower(song)), (? || " " || ?))\ <= 410 ORDER BY editdist3((lower(artist) || " " || lower(song)), ?) ASC LIMIT 10' - ) search_params: tuple = ( artist.strip(), song.strip(), diff --git a/lyric_search/sources/genius.py b/lyric_search/sources/genius.py index 43c26fd..d1ed66c 100644 --- a/lyric_search/sources/genius.py +++ b/lyric_search/sources/genius.py @@ -8,9 +8,7 @@ import re from typing import Optional from aiohttp import ClientTimeout, ClientSession from bs4 import BeautifulSoup, ResultSet # type: ignore -from tenacity import ( - retry, stop_after_attempt, wait_fixed -) +from tenacity import retry, stop_after_attempt, wait_fixed import html as htm from . import private, common, cache, redis_cache from lyric_search import utils @@ -58,6 +56,7 @@ class Genius: f"{self.genius_search_url}{search_term}", timeout=self.timeout, headers=self.headers, + verify_ssl=False, ) as request: request.raise_for_status() text: Optional[str] = await request.text() @@ -109,7 +108,10 @@ class Genius: scrape_url: str = f"{self.genius_url}{scrape_stub[1:]}" async with client.get( - scrape_url, timeout=self.timeout, headers=self.headers + scrape_url, + timeout=self.timeout, + headers=self.headers, + verify_ssl=False, ) as scrape_request: scrape_request.raise_for_status() scrape_text: Optional[str] = await scrape_request.text() diff --git a/lyric_search/sources/lrclib.py b/lyric_search/sources/lrclib.py index b0b4b39..d3f0f81 100644 --- a/lyric_search/sources/lrclib.py +++ b/lyric_search/sources/lrclib.py @@ -6,9 +6,7 @@ import traceback import logging from typing import Optional, Union from aiohttp import ClientTimeout, ClientSession -from tenacity import ( - retry, stop_after_attempt, wait_fixed -) +from tenacity import retry, stop_after_attempt, wait_fixed from lyric_search import utils from lyric_search.constructors import LyricsResult from . import common, cache, redis_cache diff --git a/utils/meme_util.py b/utils/meme_util.py new file mode 100644 index 0000000..b2998de --- /dev/null +++ b/utils/meme_util.py @@ -0,0 +1,77 @@ +import os +import io +from typing import Optional +import aiosqlite as sqlite3 +from PIL import Image + + +class MemeUtil: + """ + Meme Utils + """ + + def __init__(self, constants) -> None: + self.constants = constants + self.meme_db_path = os.path.join("/usr/local/share", "sqlite_dbs", "meme.db") + + def convert_to_png(self, in_buffer: io.BytesIO) -> bytes: + in_buffer.seek(0) + with Image.open(in_buffer) as im: + if im.format == "PNG": + raise ValueError("Already a PNG") + out_buffer = io.BytesIO() + im.save(out_buffer, format="PNG") + out_buffer.seek(0) + return out_buffer.read() + + async def get_meme_by_id(self, meme_id: int) -> Optional[bytes]: + """ + Get meme by id + Args: + meme_id (int) + Returns: + Optional[bytes] + """ + ret_image: Optional[bytes] = None + buffer: Optional[io.BytesIO] = None + async with sqlite3.connect(self.meme_db_path, timeout=5) as db_conn: + db_conn.row_factory = sqlite3.Row + query: str = "SELECT image FROM memes WHERE id = ? LIMIT 1" + async with await db_conn.execute(query, (meme_id,)) as db_cursor: + result = await db_cursor.fetchone() + if not result: + return None + buffer = io.BytesIO(result["image"]) + with Image.open(buffer) as im: + if im.format != "PNG": + ret_image = self.convert_to_png(buffer) + else: + ret_image = result["image"] + return ret_image + + async def list_memes(self, page: int) -> Optional[list]: + """ + List memes (paginated) + Args: + page (id) + Returns: + list + """ + out_result: list = [] + async with sqlite3.connect(self.meme_db_path, timeout=5) as db_conn: + db_conn.row_factory = sqlite3.Row + rows_per_page: int = 10 + offset: int = (page - 1) * rows_per_page + query: str = "SELECT id, timestamp FROM memes ORDER BY timestamp DESC LIMIT 10 OFFSET ?" + async with await db_conn.execute(query, (offset,)) as db_cursor: + results = await db_cursor.fetchall() + for result in results: + result_id = result["id"] + result_timestamp = result["timestamp"] + out_result.append( + { + "id": result_id, + "timestamp": result_timestamp, + } + ) + return out_result diff --git a/utils/radio_util.py b/utils/radio_util.py index fbd2e34..94150a4 100644 --- a/utils/radio_util.py +++ b/utils/radio_util.py @@ -56,6 +56,7 @@ class RadioUtil: "deathcore", "edm", "electronic", + "hard rock", ] self.active_playlist: list[dict] = [] self.playlist_loaded: bool = False @@ -160,11 +161,9 @@ class RadioUtil: if not artistsong and (not artist or not song): raise RadioException("No query provided") try: - search_query: str = ( - 'SELECT id, artist, song, (artist || " - " || song) AS artistsong, album, file_path, duration FROM tracks\ + search_query: str = 'SELECT id, artist, song, (artist || " - " || song) AS artistsong, album, file_path, duration FROM tracks\ WHERE editdist3((lower(artist) || " " || lower(song)), (? || " " || ?))\ <= 410 ORDER BY editdist3((lower(artist) || " " || lower(song)), ?) ASC LIMIT 1' - ) if artistsong: artistsong_split: list = artistsong.split(" - ", maxsplit=1) (search_artist, search_song) = tuple(artistsong_split) @@ -256,9 +255,7 @@ class RadioUtil: for pair in pairs: try: artist, genre = pair - query: str = ( - "INSERT OR IGNORE INTO artist_genre (artist, genre) VALUES(?, ?)" - ) + query: str = "INSERT OR IGNORE INTO artist_genre (artist, genre) VALUES(?, ?)" params: tuple[str, str] = (artist, genre) res = _db.execute(query, params) if isinstance(res.lastrowid, int): @@ -376,7 +373,7 @@ class RadioUtil: dedupe_processed.append(artistsongabc) logging.info( - "Duplicates removed." "New playlist size: %s", + "Duplicates removed.New playlist size: %s", len(self.active_playlist), )