WIP: additional radio stations
This commit is contained in:
@ -235,15 +235,18 @@ class ValidRadioNextRequest(BaseModel):
|
||||
"""
|
||||
- **key**: API Key
|
||||
- **skipTo**: UUID to skip to [optional]
|
||||
- **station**: Station (default: "main")
|
||||
"""
|
||||
|
||||
key: str
|
||||
skipTo: Optional[str] = None
|
||||
station: str = "main"
|
||||
|
||||
|
||||
class ValidRadioReshuffleRequest(ValidRadioNextRequest):
|
||||
"""
|
||||
- **key**: API Key
|
||||
- **station**: Station (default: "main")
|
||||
"""
|
||||
|
||||
|
||||
@ -252,11 +255,13 @@ class ValidRadioQueueRequest(BaseModel):
|
||||
- **draw**: DataTables draw count, default 1
|
||||
- **start**: paging start position, default 0
|
||||
- **search**: Optional search query
|
||||
- **station**: Station (default: "main")
|
||||
"""
|
||||
|
||||
draw: Optional[int] = 1
|
||||
start: Optional[int] = 0
|
||||
search: Optional[str] = None
|
||||
station: str = "main"
|
||||
|
||||
|
||||
class ValidRadioQueueShiftRequest(BaseModel):
|
||||
@ -264,18 +269,22 @@ class ValidRadioQueueShiftRequest(BaseModel):
|
||||
- **key**: API Key
|
||||
- **uuid**: UUID to shift
|
||||
- **next**: Play next if true, immediately if false, default False
|
||||
- **station**: Station (default: "main")
|
||||
"""
|
||||
|
||||
key: str
|
||||
uuid: str
|
||||
next: Optional[bool] = False
|
||||
station: str = "main"
|
||||
|
||||
|
||||
class ValidRadioQueueRemovalRequest(BaseModel):
|
||||
"""
|
||||
- **key**: API Key
|
||||
- **uuid**: UUID to remove
|
||||
- **station**: Station (default: "main")
|
||||
"""
|
||||
|
||||
key: str
|
||||
uuid: str
|
||||
station: str = "main"
|
||||
|
@ -71,14 +71,21 @@ class LyricSearch(FastAPI):
|
||||
)
|
||||
|
||||
for endpoint, handler in self.endpoints.items():
|
||||
rate_limit: tuple[int, int] = (2, 3) # Default; (Times, Seconds)
|
||||
_schema_include = endpoint in ["lyric/search"]
|
||||
|
||||
if endpoint == "typeahead/lyrics": # More permissive rate limiting for typeahead
|
||||
rate_limit = (20, 2)
|
||||
(times, seconds) = rate_limit
|
||||
|
||||
app.add_api_route(
|
||||
f"/{endpoint}",
|
||||
handler,
|
||||
methods=["POST"],
|
||||
include_in_schema=_schema_include,
|
||||
dependencies=[Depends(RateLimiter(times=2, seconds=3))],
|
||||
dependencies=[Depends(RateLimiter(times=times, seconds=seconds))] if not endpoint == "typeahead/lyrics" else None
|
||||
)
|
||||
|
||||
|
||||
async def typeahead_handler(self, data: ValidTypeAheadRequest) -> JSONResponse:
|
||||
"""
|
||||
|
@ -33,7 +33,7 @@ class Radio(FastAPI):
|
||||
self.constants = constants
|
||||
self.loop = loop
|
||||
self.radio_util = radio_util.RadioUtil(self.constants, self.loop)
|
||||
self.playlist_loaded: bool = False
|
||||
self.playlists_loaded: bool = False
|
||||
|
||||
self.endpoints: dict = {
|
||||
"radio/np": self.radio_now_playing,
|
||||
@ -65,8 +65,9 @@ class Radio(FastAPI):
|
||||
app.add_event_handler("startup", self.on_start)
|
||||
|
||||
async def on_start(self) -> None:
|
||||
logging.info("radio: Initializing")
|
||||
self.loop.run_in_executor(None, self.radio_util.load_playlist)
|
||||
stations = ", ".join(self.radio_util.db_queries.keys())
|
||||
logging.info("radio: Initializing stations:\n%s", stations)
|
||||
self.loop.run_in_executor(None, self.radio_util.load_playlists)
|
||||
|
||||
async def radio_skip(
|
||||
self, data: ValidRadioNextRequest, request: Request
|
||||
@ -89,7 +90,7 @@ class Radio(FastAPI):
|
||||
"errorText": "No such queue item.",
|
||||
},
|
||||
)
|
||||
self.radio_util.active_playlist = self.radio_util.active_playlist[
|
||||
self.radio_util.active_playlist[data.station] = self.radio_util.active_playlist[data.station][
|
||||
queue_item[0] :
|
||||
]
|
||||
# if not self.radio_util.active_playlist:
|
||||
@ -125,7 +126,7 @@ class Radio(FastAPI):
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
|
||||
random.shuffle(self.radio_util.active_playlist)
|
||||
random.shuffle(self.radio_util.active_playlist[data.station])
|
||||
return JSONResponse(content={"ok": True})
|
||||
|
||||
async def radio_get_queue(
|
||||
@ -146,7 +147,7 @@ class Radio(FastAPI):
|
||||
else:
|
||||
start: int = 0
|
||||
end: int = 20
|
||||
orig_queue: list[dict] = self.radio_util.active_playlist
|
||||
orig_queue: list[dict] = self.radio_util.active_playlist[data.station]
|
||||
if not search:
|
||||
queue_full: list = orig_queue
|
||||
else:
|
||||
@ -190,7 +191,7 @@ class Radio(FastAPI):
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid)
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid, data.station)
|
||||
if not queue_item:
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
@ -200,8 +201,8 @@ class Radio(FastAPI):
|
||||
},
|
||||
)
|
||||
(x, item) = queue_item
|
||||
self.radio_util.active_playlist.pop(x)
|
||||
self.radio_util.active_playlist.insert(0, item)
|
||||
self.radio_util.active_playlist[data.station].pop(x)
|
||||
self.radio_util.active_playlist[data.station].insert(0, item)
|
||||
if not data.next:
|
||||
await self.radio_util._ls_skip()
|
||||
return JSONResponse(
|
||||
@ -221,7 +222,7 @@ class Radio(FastAPI):
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid)
|
||||
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid, data.station)
|
||||
if not queue_item:
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
@ -230,7 +231,7 @@ class Radio(FastAPI):
|
||||
"errorText": "Queue item not found.",
|
||||
},
|
||||
)
|
||||
self.radio_util.active_playlist.pop(queue_item[0])
|
||||
self.radio_util.active_playlist[data.station].pop(queue_item[0])
|
||||
return JSONResponse(
|
||||
content={
|
||||
"ok": True,
|
||||
@ -238,7 +239,8 @@ class Radio(FastAPI):
|
||||
)
|
||||
|
||||
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"
|
||||
) -> Response:
|
||||
"""
|
||||
Get album art, optional parameter track_id may be specified.
|
||||
@ -247,7 +249,7 @@ class Radio(FastAPI):
|
||||
"""
|
||||
try:
|
||||
if not track_id:
|
||||
track_id = self.radio_util.now_playing.get("id")
|
||||
track_id = self.radio_util.now_playing[station].get("id")
|
||||
logging.debug("Seeking album art with trackId: %s", track_id)
|
||||
album_art: Optional[bytes] = self.radio_util.get_album_art(
|
||||
track_id=track_id
|
||||
@ -265,11 +267,13 @@ class Radio(FastAPI):
|
||||
url="https://codey.lol/images/radio_art_default.jpg", status_code=302
|
||||
)
|
||||
|
||||
async def radio_now_playing(self, request: Request) -> JSONResponse:
|
||||
async def radio_now_playing(self, request: Request,
|
||||
station: Optional[str] = "main") -> JSONResponse:
|
||||
"""
|
||||
Get currently playing track info
|
||||
"""
|
||||
ret_obj: dict = {**self.radio_util.now_playing}
|
||||
ret_obj: dict = {**self.radio_util.now_playing[station]}
|
||||
ret_obj["station"] = station
|
||||
try:
|
||||
ret_obj["elapsed"] = int(time.time()) - ret_obj["start"]
|
||||
except KeyError:
|
||||
@ -289,15 +293,17 @@ class Radio(FastAPI):
|
||||
(Track will be removed from the queue in the process.)
|
||||
- **key**: API key
|
||||
- **skipTo**: Optional UUID to skip to
|
||||
- **station**: Station (default: "main")
|
||||
"""
|
||||
logging.info("Radio get next")
|
||||
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
||||
raise HTTPException(status_code=403, detail="Unauthorized")
|
||||
if (
|
||||
not isinstance(self.radio_util.active_playlist, list)
|
||||
or not self.radio_util.active_playlist
|
||||
not isinstance(self.radio_util.active_playlist[data.station], list)
|
||||
or not self.radio_util.active_playlist[data.station]
|
||||
):
|
||||
if self.radio_util.playlist_loaded:
|
||||
self.radio_util.playlist_loaded = False
|
||||
if self.radio_util.playlists_loaded:
|
||||
self.radio_util.playlists_loaded = False
|
||||
await self.on_start()
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
@ -306,7 +312,7 @@ class Radio(FastAPI):
|
||||
"errorText": "General failure occurred, prompting playlist reload.",
|
||||
},
|
||||
)
|
||||
next = self.radio_util.active_playlist.pop(0)
|
||||
next = self.radio_util.active_playlist[data.station].pop(0)
|
||||
if not isinstance(next, dict):
|
||||
logging.critical("next is of type: %s, reloading playlist...", type(next))
|
||||
await self.on_start()
|
||||
@ -322,16 +328,17 @@ class Radio(FastAPI):
|
||||
time_started: int = int(time.time())
|
||||
time_ends: int = int(time_started + duration)
|
||||
|
||||
if len(self.radio_util.active_playlist) > 1:
|
||||
self.radio_util.active_playlist.append(next) # Push to end of playlist
|
||||
if len(self.radio_util.active_playlist[data.station]) > 1:
|
||||
self.radio_util.active_playlist[data.station].append(next) # Push to end of playlist
|
||||
else:
|
||||
self.loop.run_in_executor(None, self.radio_util.load_playlist)
|
||||
|
||||
self.radio_util.now_playing = next
|
||||
self.radio_util.now_playing[data.station] = next
|
||||
next["start"] = time_started
|
||||
next["end"] = time_ends
|
||||
try:
|
||||
background_tasks.add_task(self.radio_util.webhook_song_change, next)
|
||||
if data.station == "main":
|
||||
background_tasks.add_task(self.radio_util.webhook_song_change, next)
|
||||
except Exception as e:
|
||||
logging.info("radio_get_next Exception: %s", str(e))
|
||||
traceback.print_exc()
|
||||
@ -377,7 +384,7 @@ class Radio(FastAPI):
|
||||
},
|
||||
)
|
||||
|
||||
search: bool = self.radio_util.search_playlist(
|
||||
search: bool = self.radio_util.search_db(
|
||||
artistsong=artistsong, artist=artist, song=song
|
||||
)
|
||||
if data.alsoSkip:
|
||||
|
Reference in New Issue
Block a user