diff --git a/endpoints/constructors.py b/endpoints/constructors.py index 7151f05..7f568b1 100644 --- a/endpoints/constructors.py +++ b/endpoints/constructors.py @@ -218,6 +218,13 @@ class ValidRadioSongRequest(BaseModel): artistsong: Optional[str] = None alsoSkip: Optional[bool] = False +class ValidRadioTypeaheadRequest(BaseModel): + """ + - **query**: Typeahead query + """ + query: str + + class ValidRadioQueueGetRequest(BaseModel): """ - **key**: API key (optional, needed if specifying a non-default limit) diff --git a/endpoints/radio.py b/endpoints/radio.py index 737e5b9..58f8f70 100644 --- a/endpoints/radio.py +++ b/endpoints/radio.py @@ -7,7 +7,8 @@ import random import asyncio from . import radio_util from .constructors import ValidRadioNextRequest, ValidRadioReshuffleRequest, ValidRadioQueueShiftRequest,\ - ValidRadioQueueRemovalRequest, ValidRadioSongRequest, RadioException + ValidRadioQueueRemovalRequest, ValidRadioSongRequest,\ + ValidRadioTypeaheadRequest, RadioException from uuid import uuid4 as uuid from typing import Optional from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException @@ -30,6 +31,7 @@ class Radio(FastAPI): self.endpoints: dict = { "radio/np": self.radio_now_playing, "radio/request": self.radio_request, + "radio/typeahead": self.radio_typeahead, "radio/get_queue": self.radio_get_queue, "radio/skip": self.radio_skip, "radio/queue_shift": self.radio_queue_shift, @@ -289,4 +291,20 @@ class Radio(FastAPI): await self.radio_util._ls_skip() return JSONResponse(content={ 'result': search - }) \ No newline at end of file + }) + + async def radio_typeahead(self, data: ValidRadioTypeaheadRequest, + request: Request) -> JSONResponse: + """ + Radio typehead handler + - **query**: Typeahead query + """ + if not isinstance(data.query, str): + return JSONResponse(status_code=500, content={ + 'err': True, + 'errorText': 'Invalid request.', + }) + typeahead = await self.radio_util.trackdb_typeahead(data.query) + if not typeahead: + return JSONResponse(content=[]) + return JSONResponse(content=typeahead) \ No newline at end of file diff --git a/endpoints/radio_util.py b/endpoints/radio_util.py index 5b2bca2..adb7de4 100644 --- a/endpoints/radio_util.py +++ b/endpoints/radio_util.py @@ -14,7 +14,7 @@ import os import gpt from aiohttp import ClientSession, ClientTimeout import aiosqlite as sqlite3 -from typing import Union, Optional, LiteralString +from typing import Union, Optional, LiteralString, Iterable from uuid import uuid4 as uuid from .constructors import RadioException @@ -65,6 +65,22 @@ class RadioUtil: """ return str(datetime.timedelta(seconds=s)).split(".", maxsplit=1)[0] + async def trackdb_typeahead(self, query: str) -> Optional[list[str]]: + if not query: + return None + async with sqlite3.connect(self.active_playlist_path, + timeout=1) as _db: + _db.row_factory = sqlite3.Row + db_query: str = 'SELECT (artist || " - " || song) as artistsong FROM tracks WHERE\ + artistsong LIKE ? LIMIT 15' + db_params: tuple[str] = (f"%{query}%",) + async with _db.execute(db_query, db_params) as _cursor: + result: Iterable[sqlite3.Row] = await _cursor.fetchall() + out_result = [ + str(r['artistsong']) for r in result + ] + return out_result + async def search_playlist(self, artistsong: Optional[str] = None, artist: Optional[str] = None, song: Optional[str] = None) -> bool: @@ -138,7 +154,8 @@ class RadioUtil: "metal", "punk", "electronic", "nu metal", "EDM",\ "post-hardcore", "pop rock", "experimental", "post-punk", "death metal", "electronicore", "hard rock", "psychedelic rock",\ "grunge", "house", "dubstep", "hardcore", "hair metal", "horror punk", "folk punk", "breakcore",\ - "post-rock", "deathcore", "hardcore punk", "synthwave", "trap") GROUP BY artistdashsong ORDER BY RANDOM()' + "post-rock", "deathcore", "hardcore punk", "synthwave",\ + "trap", "indie pop") GROUP BY artistdashsong ORDER BY RANDOM()' """ LIMITED TO ONE ARTIST...