minor
This commit is contained in:
@@ -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"
|
||||||
|
@@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -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"
|
||||||
|
@@ -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"""
|
||||||
|
@@ -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,
|
||||||
|
Reference in New Issue
Block a user