|
|
|
@@ -23,6 +23,7 @@ from fastapi import (
|
|
|
|
|
Depends)
|
|
|
|
|
from fastapi_throttle import RateLimiter
|
|
|
|
|
from fastapi.responses import RedirectResponse, JSONResponse, FileResponse
|
|
|
|
|
from auth.deps import get_current_user
|
|
|
|
|
|
|
|
|
|
class Radio(FastAPI):
|
|
|
|
|
"""Radio Endpoints"""
|
|
|
|
@@ -52,9 +53,9 @@ class Radio(FastAPI):
|
|
|
|
|
if endpoint == "radio/album_art":
|
|
|
|
|
methods = ["GET"]
|
|
|
|
|
app.add_api_route(
|
|
|
|
|
f"/{endpoint}", handler, methods=methods, include_in_schema=False,
|
|
|
|
|
f"/{endpoint}", handler, methods=methods, include_in_schema=True,
|
|
|
|
|
dependencies=[Depends(
|
|
|
|
|
RateLimiter(times=10, seconds=2))] if not endpoint == "radio/np" else None,
|
|
|
|
|
RateLimiter(times=25, seconds=2))] if not endpoint == "radio/np" else None,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
app.add_event_handler("startup", self.on_start)
|
|
|
|
@@ -65,21 +66,22 @@ class Radio(FastAPI):
|
|
|
|
|
await self.radio_util.load_playlists()
|
|
|
|
|
|
|
|
|
|
async def radio_skip(
|
|
|
|
|
self, data: ValidRadioNextRequest, request: Request
|
|
|
|
|
self, data: ValidRadioNextRequest, request: Request, user=Depends(get_current_user)
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Skip to the next track in the queue, or to the UUID specified in `skipTo` if provided.
|
|
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioNextRequest): Contains the API key, optional UUID to skip to, and station name.
|
|
|
|
|
- **data** (ValidRadioNextRequest): Contains optional UUID to skip to, and station name.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Indicates success or failure of the skip operation.
|
|
|
|
|
"""
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
try:
|
|
|
|
|
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Unauthorized")
|
|
|
|
|
if data.skipTo:
|
|
|
|
|
queue_item = self.radio_util.get_queue_item_by_uuid(data.skipTo, data.station)
|
|
|
|
|
if not queue_item:
|
|
|
|
@@ -115,21 +117,22 @@ class Radio(FastAPI):
|
|
|
|
|
raise e # Re-raise HTTPException
|
|
|
|
|
|
|
|
|
|
async def radio_reshuffle(
|
|
|
|
|
self, data: ValidRadioReshuffleRequest, request: Request
|
|
|
|
|
self, data: ValidRadioReshuffleRequest, request: Request, user=Depends(get_current_user)
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Reshuffle the play queue.
|
|
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioReshuffleRequest): Contains the API key and station name.
|
|
|
|
|
- **data** (ValidRadioReshuffleRequest): Contains the station name.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Indicates success of the reshuffle operation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Unauthorized")
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
|
|
|
|
|
random.shuffle(self.radio_util.active_playlist[data.station])
|
|
|
|
|
return JSONResponse(content={"ok": True})
|
|
|
|
@@ -205,21 +208,22 @@ class Radio(FastAPI):
|
|
|
|
|
return JSONResponse(content=out_json)
|
|
|
|
|
|
|
|
|
|
async def radio_queue_shift(
|
|
|
|
|
self, data: ValidRadioQueueShiftRequest, request: Request
|
|
|
|
|
self, data: ValidRadioQueueShiftRequest, request: Request, user=Depends(get_current_user)
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Shift the position of a UUID within the queue.
|
|
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioQueueShiftRequest): Contains the API key, UUID to shift, and station name.
|
|
|
|
|
- **data** (ValidRadioQueueShiftRequest): Contains the UUID to shift, and station name.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Indicates success of the shift operation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Unauthorized")
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
|
|
|
|
|
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid, data.station)
|
|
|
|
|
if not queue_item:
|
|
|
|
@@ -242,21 +246,22 @@ class Radio(FastAPI):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def radio_queue_remove(
|
|
|
|
|
self, data: ValidRadioQueueRemovalRequest, request: Request
|
|
|
|
|
self, data: ValidRadioQueueRemovalRequest, request: Request, user=Depends(get_current_user)
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Remove an item from the current play queue.
|
|
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioQueueRemovalRequest): Contains the API key, UUID of the item to remove, and station name.
|
|
|
|
|
- **data** (ValidRadioQueueRemovalRequest): Contains the UUID of the item to remove, and station name.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Indicates success of the removal operation.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Unauthorized")
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
|
|
|
|
|
queue_item = self.radio_util.get_queue_item_by_uuid(data.uuid, data.station)
|
|
|
|
|
if not queue_item:
|
|
|
|
@@ -343,24 +348,27 @@ class Radio(FastAPI):
|
|
|
|
|
data: ValidRadioNextRequest,
|
|
|
|
|
request: Request,
|
|
|
|
|
background_tasks: BackgroundTasks,
|
|
|
|
|
user=Depends(get_current_user),
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Get the next track in the queue. The track will be removed from the queue in the process.
|
|
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioNextRequest): Contains the API key, optional UUID to skip to, and station name.
|
|
|
|
|
- **data** (ValidRadioNextRequest): Contains optional UUID to skip to, and station name.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **background_tasks** (BackgroundTasks): Background tasks for webhook execution.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Contains the next track information.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
|
|
|
|
|
logging.info("Radio get next")
|
|
|
|
|
if data.station not in self.radio_util.active_playlist.keys():
|
|
|
|
|
raise HTTPException(status_code=500, detail="No such station/not ready")
|
|
|
|
|
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[data.station], list)
|
|
|
|
|
or not self.radio_util.active_playlist[data.station]
|
|
|
|
@@ -411,21 +419,23 @@ class Radio(FastAPI):
|
|
|
|
|
return JSONResponse(content=next)
|
|
|
|
|
|
|
|
|
|
async def radio_request(
|
|
|
|
|
self, data: ValidRadioSongRequest, request: Request
|
|
|
|
|
self, data: ValidRadioSongRequest, request: Request, user=Depends(get_current_user)
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Handle song requests.
|
|
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioSongRequest): Contains the API key, artist, song, and station name.
|
|
|
|
|
- **data** (ValidRadioSongRequest): Contains artist, song, and station name.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Indicates success or failure of the request.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Unauthorized")
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
|
|
|
|
|
artistsong: Optional[str] = data.artistsong
|
|
|
|
|
artist: Optional[str] = data.artist
|
|
|
|
|
song: Optional[str] = data.song
|
|
|
|
@@ -454,7 +464,7 @@ class Radio(FastAPI):
|
|
|
|
|
return JSONResponse(content={"result": search})
|
|
|
|
|
|
|
|
|
|
def radio_typeahead(
|
|
|
|
|
self, data: ValidRadioTypeaheadRequest, request: Request
|
|
|
|
|
self, data: ValidRadioTypeaheadRequest, request: Request, user=Depends(get_current_user)
|
|
|
|
|
) -> JSONResponse:
|
|
|
|
|
"""
|
|
|
|
|
Handle typeahead queries for the radio.
|
|
|
|
@@ -462,10 +472,14 @@ class Radio(FastAPI):
|
|
|
|
|
Parameters:
|
|
|
|
|
- **data** (ValidRadioTypeaheadRequest): Contains the typeahead query.
|
|
|
|
|
- **request** (Request): The HTTP request object.
|
|
|
|
|
- **user**: Current authenticated user.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
- **JSONResponse**: Contains the typeahead results.
|
|
|
|
|
"""
|
|
|
|
|
if "dj" not in user.get("roles", []):
|
|
|
|
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
|
|
|
|
|
|
|
|
if not isinstance(data.query, str):
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=500,
|
|
|
|
|