diff --git a/endpoints/constructors.py b/endpoints/constructors.py index f721107..920fea4 100644 --- a/endpoints/constructors.py +++ b/endpoints/constructors.py @@ -45,13 +45,15 @@ class ValidArtistSearchRequest(BaseModel): a: str - class Config: - schema_extra = { - "example": { - "a": "eminem" - } - } - + model_config = { + "json_schema_extra": { + "examples": [ + { + "a": "eminem", + }] + } + } + class ValidAlbumDetailRequest(BaseModel): """ - **a**: artist name @@ -61,13 +63,15 @@ class ValidAlbumDetailRequest(BaseModel): a: str release: str - class Config: - schema_extra = { - "example": { - "a": "eminem", - "release": "houdini" - } - } + model_config = { + "json_schema_extra": { + "examples": [ + { + "a": "eminem", + "release": "houdini", + }] + } + } class ValidTrackInfoRequest(BaseModel): """ @@ -77,15 +81,17 @@ class ValidTrackInfoRequest(BaseModel): a: str t: str - - class Config: - schema_extra = { - "example": { - "a": "eminem", - "t": "rap god" - } - } - + + model_config = { + "json_schema_extra": { + "examples": [ + { + "a": "eminem", + "t": "rap god", + }] + } + } + """ Rand Msg """ diff --git a/endpoints/karma.py b/endpoints/karma.py index f161b0a..e95d2b1 100644 --- a/endpoints/karma.py +++ b/endpoints/karma.py @@ -7,8 +7,8 @@ import aiosqlite as sqlite3 from typing import LiteralString, Optional, Union from fastapi import FastAPI, Request, HTTPException from fastapi.responses import JSONResponse -from .constructors import ValidTopKarmaRequest, ValidKarmaRetrievalRequest,\ - ValidKarmaUpdateRequest +from .constructors import (ValidTopKarmaRequest, ValidKarmaRetrievalRequest, + ValidKarmaUpdateRequest) class KarmaDB: """Karma DB Util""" diff --git a/endpoints/lastfm.py b/endpoints/lastfm.py index 94445a0..6925c9a 100644 --- a/endpoints/lastfm.py +++ b/endpoints/lastfm.py @@ -3,8 +3,8 @@ import traceback from typing import Optional, Union from fastapi import FastAPI from fastapi.responses import JSONResponse -from .constructors import ValidArtistSearchRequest, ValidAlbumDetailRequest,\ - ValidTrackInfoRequest, LastFMException +from .constructors import (ValidArtistSearchRequest, ValidAlbumDetailRequest, + ValidTrackInfoRequest, LastFMException) class LastFM(FastAPI): """Last.FM Endpoints""" @@ -41,7 +41,8 @@ class LastFM(FastAPI): }) artist_result = await self.lastfm.search_artist(artist=artist) - if not artist_result or "err" in artist_result.keys(): + if not artist_result or not artist_result.get('bio')\ + or "err" in artist_result.keys(): return JSONResponse(status_code=500, content={ 'err': True, 'errorText': 'Search failed (no results?)', diff --git a/endpoints/misc.py b/endpoints/misc.py index e437138..905eedc 100644 --- a/endpoints/misc.py +++ b/endpoints/misc.py @@ -1,7 +1,11 @@ import logging import time -from typing import Optional -from fastapi import FastAPI +import os +from typing import Optional, Annotated +from fastapi import ( + FastAPI, Request, UploadFile, + Response, HTTPException, Form +) from fastapi.responses import JSONResponse import redis.asyncio as redis from lyric_search.sources import private, cache as LyricsCache, redis_cache @@ -19,17 +23,52 @@ class Misc(FastAPI): self.redis_cache = redis_cache.RedisCache() self.redis_client = redis.Redis(password=private.REDIS_PW) self.radio = radio + self.activity_image: Optional[bytes] = None self.endpoints: dict = { "widget/redis": self.homepage_redis_widget, "widget/sqlite": self.homepage_sqlite_widget, "widget/lyrics": self.homepage_lyrics_widget, "widget/radio": self.homepage_radio_widget, + "misc/get_activity_image": self.get_activity_image, } for endpoint, handler in self.endpoints.items(): app.add_api_route(f"/{endpoint}", handler, methods=["GET"], - include_in_schema=False) + include_in_schema=True) + app.add_api_route("/misc/upload_activity_image", + self.upload_activity_image, methods=["POST"]) + + async def upload_activity_image(self, + image: UploadFile, + key: Annotated[str, Form()], request: Request) -> Response: + if not key or not isinstance(key, str)\ + or not self.util.check_key(path=request.url.path, req_type=2, key=key): + raise HTTPException(status_code=403, detail="Unauthorized") + if not image: + return JSONResponse(status_code=500, content={ + 'err': True, + 'errorText': 'Invalid request', + }) + self.activity_image = await image.read() + return JSONResponse(content={ + 'success': True, + }) + + async def get_activity_image(self, request: Request) -> Response: + if isinstance(self.activity_image, bytes): + return Response(content=self.activity_image, + media_type="image/png") + + + # Fallback + fallback_path = os.path.join("/var/www/codey.lol/public", + "images", "plex_placeholder.png") + + with open(fallback_path, 'rb') as f: + return Response(content=f.read(), + media_type="image/png") + async def get_radio_np(self) -> str: """ Get radio now playing diff --git a/endpoints/radio.py b/endpoints/radio.py index 3ffbe4b..608cc44 100644 --- a/endpoints/radio.py +++ b/endpoints/radio.py @@ -3,13 +3,16 @@ import traceback import time import random import asyncio -from . import radio_util -from .constructors import ValidRadioNextRequest, ValidRadioReshuffleRequest, ValidRadioQueueShiftRequest,\ - ValidRadioQueueRemovalRequest, ValidRadioSongRequest,\ - ValidRadioTypeaheadRequest, RadioException +from utils import radio_util +from .constructors import (ValidRadioNextRequest, ValidRadioReshuffleRequest, + ValidRadioQueueShiftRequest, ValidRadioQueueRemovalRequest, + ValidRadioSongRequest, ValidRadioTypeaheadRequest, + RadioException) + from uuid import uuid4 as uuid from typing import Optional -from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException +from fastapi import (FastAPI, BackgroundTasks, Request, + Response, HTTPException) from fastapi.responses import RedirectResponse, JSONResponse """ diff --git a/endpoints/radio_util.py b/utils/radio_util.py similarity index 94% rename from endpoints/radio_util.py rename to utils/radio_util.py index 170a876..c5cb86e 100644 --- a/endpoints/radio_util.py +++ b/utils/radio_util.py @@ -10,7 +10,7 @@ from aiohttp import ClientSession, ClientTimeout import aiosqlite as sqlite3 from typing import Union, Optional, LiteralString, Iterable from uuid import uuid4 as uuid -from .constructors import RadioException +from endpoints.constructors import RadioException double_space: Pattern = regex.compile(r'\s{2,}') @@ -31,6 +31,7 @@ class RadioUtil: self.now_playing: dict = { 'artist': 'N/A', 'song': 'N/A', + 'album': 'N/A', 'genre': 'N/A', 'artistsong': 'N/A - N/A', 'duration': 0, @@ -94,7 +95,7 @@ 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, genre, file_path, duration FROM tracks\ + search_query: str = 'SELECT id, artist, song, (artist || " - " || song) AS artistsong, album, genre, file_path, duration FROM tracks\ WHERE editdist3((lower(artist) || " " || lower(song)), (? || " " || ?))\ <= 410 ORDER BY editdist3((lower(artist) || " " || lower(song)), ?) ASC LIMIT 1' if artistsong: @@ -138,14 +139,14 @@ class RadioUtil: try: logging.info(f"Loading playlist...") self.active_playlist.clear() - # db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, genre, file_path, duration FROM tracks\ + # db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\ # GROUP BY artistdashsong ORDER BY RANDOM()' """ LIMITED GENRES """ - db_query: str = """SELECT distinct(LOWER(TRIM(artist)) || " - " || LOWER(TRIM(song))), (TRIM(artist) || " - " || TRIM(song)) AS artistdashsong, id, artist, song, genre, file_path, duration FROM tracks\ + 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 "%rock%"\ OR genre LIKE "%pop punk%"\ @@ -178,16 +179,22 @@ class RadioUtil: OR genre LIKE "%synthwave%"\ OR genre LIKE "%trap%"\ OR genre LIKE "%indie pop%"\ - OR genre LIKE "%dnb%"\ - OR genre LIKE "untagged")\ + OR genre LIKE "%dnb%")\ GROUP BY artistdashsong ORDER BY RANDOM()""" - + """ - LIMITED TO ONE ARTIST... + LIMITED TO ONE/SMALL SUBSET OF GENRES """ - # db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, genre, file_path, duration FROM tracks\ - # WHERE artist LIKE "%wage war%" GROUP BY artistdashsong ORDER BY id DESC' + # db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\ + # WHERE genre like "%edm%" OR genre LIKE "%electronic%"' + + """ + LIMITED TO ONE/SOME ARTISTS... + """ + + # db_query = 'SELECT distinct(artist || " - " || song) AS artistdashsong, id, artist, song, album, genre, file_path, duration FROM tracks\ + # WHERE artist LIKE "%sullivan king%" GROUP BY artistdashsong ORDER BY artist ASC, album ASC, id DESC' async with sqlite3.connect(self.active_playlist_path, timeout=2) as db_conn: @@ -199,6 +206,7 @@ class RadioUtil: 'id': r['id'], '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', 'artistsong': double_space.sub(' ', r['artistdashsong']).strip(), 'file_path': r['file_path'],