This commit is contained in:
2025-08-21 15:35:10 -04:00
parent dd8d07b2f0
commit a8d089c0fe
5 changed files with 57 additions and 49 deletions

View File

@@ -1,6 +1,9 @@
from typing import Optional from typing import Optional
from typing import Literal
from pydantic import BaseModel from pydantic import BaseModel
Station = Literal["main", "rock", "rap", "electronic", "pop"]
""" """
LastFM LastFM
""" """
@@ -96,25 +99,6 @@ class ValidYTSearchRequest(BaseModel):
t: str = "rick astley - never gonna give you up" t: str = "rick astley - never gonna give you up"
"""
XC
"""
class ValidXCRequest(BaseModel):
"""
- **key**: valid XC API key
- **bid**: bot id
- **cmd**: bot command
- **data**: command data
"""
key: str
bid: int
cmd: str
data: Optional[dict]
""" """
Transcriptions Transcriptions
""" """
@@ -211,7 +195,7 @@ class ValidRadioSongRequest(BaseModel):
song: Optional[str] = None song: Optional[str] = None
artistsong: Optional[str] = None artistsong: Optional[str] = None
alsoSkip: Optional[bool] = False alsoSkip: Optional[bool] = False
station: str = "main" station: Station = "main"
class ValidRadioTypeaheadRequest(BaseModel): class ValidRadioTypeaheadRequest(BaseModel):
@@ -241,7 +225,7 @@ class ValidRadioNextRequest(BaseModel):
key: str key: str
skipTo: Optional[str] = None skipTo: Optional[str] = None
station: str = "main" station: Station = "main"
class ValidRadioReshuffleRequest(ValidRadioNextRequest): class ValidRadioReshuffleRequest(ValidRadioNextRequest):
@@ -262,7 +246,7 @@ class ValidRadioQueueRequest(BaseModel):
draw: Optional[int] = 1 draw: Optional[int] = 1
start: Optional[int] = 0 start: Optional[int] = 0
search: Optional[str] = None search: Optional[str] = None
station: str = "main" station: Station = "main"
class ValidRadioQueueShiftRequest(BaseModel): class ValidRadioQueueShiftRequest(BaseModel):
@@ -276,7 +260,7 @@ class ValidRadioQueueShiftRequest(BaseModel):
key: str key: str
uuid: str uuid: str
next: Optional[bool] = False next: Optional[bool] = False
station: str = "main" station: Station = "main"
class ValidRadioQueueRemovalRequest(BaseModel): class ValidRadioQueueRemovalRequest(BaseModel):
@@ -288,4 +272,4 @@ class ValidRadioQueueRemovalRequest(BaseModel):
key: str key: str
uuid: str uuid: str
station: str = "main" station: Station = "main"

View File

@@ -30,7 +30,7 @@ class Meme(FastAPI):
f"/{endpoint}", f"/{endpoint}",
handler, handler,
methods=["GET"], methods=["GET"],
include_in_schema=True, include_in_schema=False,
dependencies=dependencies, dependencies=dependencies,
) )

View File

@@ -10,6 +10,7 @@ from .constructors import (
ValidRadioSongRequest, ValidRadioSongRequest,
ValidRadioTypeaheadRequest, ValidRadioTypeaheadRequest,
ValidRadioQueueRequest, ValidRadioQueueRequest,
Station
) )
from utils import radio_util from utils import radio_util
from typing import Optional from typing import Optional
@@ -43,23 +44,19 @@ class Radio(FastAPI):
"radio/reshuffle": self.radio_reshuffle, "radio/reshuffle": self.radio_reshuffle,
"radio/queue_remove": self.radio_queue_remove, "radio/queue_remove": self.radio_queue_remove,
"radio/ls._next_": self.radio_get_next, "radio/ls._next_": self.radio_get_next,
"radio/album_art": self.album_art_handler,
} }
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
methods: list[str] = ["POST"]
if endpoint == "radio/album_art":
methods = ["GET"]
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["POST"], include_in_schema=True, f"/{endpoint}", handler, methods=methods, include_in_schema=False,
dependencies=[Depends( dependencies=[Depends(
RateLimiter(times=10, seconds=2))] if not endpoint == "radio/np" else None, RateLimiter(times=10, seconds=2))] if not endpoint == "radio/np" else None,
) )
# NOTE: Not in loop because method is GET for this endpoint
app.add_api_route(
"/radio/album_art",
self.album_art_handler,
methods=["GET"],
include_in_schema=True,
)
app.add_event_handler("startup", self.on_start) app.add_event_handler("startup", self.on_start)
async def on_start(self) -> None: async def on_start(self) -> None:
@@ -135,21 +132,35 @@ class Radio(FastAPI):
""" """
Get current play queue (paged, 20 results per page) Get current play queue (paged, 20 results per page)
""" """
if not (data and data.station):
return JSONResponse(status_code=500,
content={
"err": True,
"errorText": "Invalid request.",
})
search: Optional[str] = None search: Optional[str] = None
draw: int = 0 draw: int = 0
if isinstance(data, ValidRadioQueueRequest): if isinstance(data, ValidRadioQueueRequest):
search = data.search search = data.search
draw = data.draw draw = data.draw or 0
start: int = int(data.start) start: int = int(data.start or 0)
end: int = start + 20 end: int = start + 20
else: else:
start: int = 0 start: int = 0
end: int = 20 end: int = 20
orig_queue: list[dict] = self.radio_util.active_playlist[data.station] orig_queue: list[dict] = self.radio_util.active_playlist[data.station]
if not search: if not search:
queue_full: list = orig_queue queue_full: Optional[list] = orig_queue
else: else:
queue_full: list = self.radio_util.datatables_search(data.search, data.station) queue_full = self.radio_util.datatables_search(search, data.station)
if not queue_full:
return JSONResponse(
status_code=500,
content={
"err": True,
"errorText": "No queue found.",
}
)
queue: list = queue_full[start:end] queue: list = queue_full[start:end]
queue_out: list[dict] = [] queue_out: list[dict] = []
for x, item in enumerate(queue): for x, item in enumerate(queue):
@@ -240,7 +251,7 @@ class Radio(FastAPI):
async def album_art_handler( async def album_art_handler(
self, request: Request, track_id: Optional[int] = None, self, request: Request, track_id: Optional[int] = None,
station: Optional[str] = "main" station: Station = "main"
) -> Response: ) -> Response:
""" """
Get album art, optional parameter track_id may be specified. Get album art, optional parameter track_id may be specified.
@@ -251,6 +262,13 @@ class Radio(FastAPI):
try: try:
if not track_id: if not track_id:
track_id = self.radio_util.now_playing[station].get("id") track_id = self.radio_util.now_playing[station].get("id")
if not track_id:
# Still no track ID
return JSONResponse(status_code=500,
content={
"err": True,
"errorText": "Invalid request",
})
logging.debug("Seeking album art with trackId: %s", track_id) logging.debug("Seeking album art with trackId: %s", track_id)
album_art: Optional[bytes] = self.radio_util.get_album_art( album_art: Optional[bytes] = self.radio_util.get_album_art(
track_id=track_id track_id=track_id
@@ -269,7 +287,7 @@ class Radio(FastAPI):
) )
async def radio_now_playing(self, request: Request, async def radio_now_playing(self, request: Request,
station: Optional[str] = "main") -> JSONResponse: station: Station = "main") -> JSONResponse:
""" """
Get currently playing track info Get currently playing track info
- **station**: default "main" - **station**: default "main"

View File

@@ -24,7 +24,7 @@ from pydantic import BaseModel
class ValidBulkFetchRequest(BaseModel): class ValidBulkFetchRequest(BaseModel):
track_ids: list[int] track_ids: list[int]
target: str target: str
quality: Literal["FLAC", "Lossy"] quality: Literal["FLAC", "Lossy"] = "FLAC"
class RIP(FastAPI): class RIP(FastAPI):
@@ -120,7 +120,7 @@ class RIP(FastAPI):
album_id: int, album_id: int,
request: Request, request: Request,
user=Depends(get_current_user), user=Depends(get_current_user),
quality: str = "FLAC", quality: Literal["FLAC", "Lossy"] = "FLAC"
) -> Response: ) -> Response:
"""Get tracks by album id""" """Get tracks by album id"""
tracks = await self.trip_util.get_tracks_by_album_id(album_id, quality) tracks = await self.trip_util.get_tracks_by_album_id(album_id, quality)
@@ -141,8 +141,8 @@ class RIP(FastAPI):
async def track_by_id_handler( async def track_by_id_handler(
self, self,
track_id: int, track_id: int,
quality: str,
request: Request, request: Request,
quality: Literal["FLAC", "Lossy"] = "FLAC",
user=Depends(get_current_user), user=Depends(get_current_user),
) -> Response: ) -> Response:
"""Get track by ID""" """Get track by ID"""

View File

@@ -1,9 +1,9 @@
import os import os
import aiosqlite as sqlite3 import aiosqlite as sqlite3
from fastapi import FastAPI, Depends from fastapi import FastAPI, Depends, Response
from fastapi_throttle import RateLimiter from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from typing import Optional, LiteralString, Union from typing import Optional, LiteralString, Union, Iterable, cast
from .constructors import ValidShowEpisodeLineRequest, ValidShowEpisodeListRequest from .constructors import ValidShowEpisodeLineRequest, ValidShowEpisodeListRequest
@@ -88,7 +88,7 @@ class Transcriptions(FastAPI):
async with sqlite3.connect(database=db_path, timeout=1) as _db: async with sqlite3.connect(database=db_path, timeout=1) as _db:
async with await _db.execute(db_query) as _cursor: async with await _db.execute(db_query) as _cursor:
result: list[tuple] = await _cursor.fetchall() result: Iterable[sqlite3.Row] = await _cursor.fetchall()
return JSONResponse( return JSONResponse(
content={ content={
"show_title": show_title, "show_title": show_title,
@@ -104,7 +104,7 @@ class Transcriptions(FastAPI):
async def get_episode_lines_handler( async def get_episode_lines_handler(
self, data: ValidShowEpisodeLineRequest self, data: ValidShowEpisodeLineRequest
) -> JSONResponse: ) -> Response:
""" """
Get lines for a particular episode Get lines for a particular episode
- **s**: Show ID to query - **s**: Show ID to query
@@ -138,8 +138,14 @@ class Transcriptions(FastAPI):
async with sqlite3.connect(database=db_path, timeout=1) as _db: async with sqlite3.connect(database=db_path, timeout=1) as _db:
params: tuple = (episode_id,) params: tuple = (episode_id,)
async with await _db.execute(db_query, params) as _cursor: async with await _db.execute(db_query, params) as _cursor:
result: list[tuple] = await _cursor.fetchall() result: Iterable[sqlite3.Row] = await _cursor.fetchall()
first_result: tuple = result[0] result_list = cast(list[sqlite3.Row], result)
if not result_list:
return Response(
status_code=404,
content="Not found",
)
first_result: sqlite3.Row = result_list[0]
return JSONResponse( return JSONResponse(
content={ content={
"episode_id": episode_id, "episode_id": episode_id,