radio changes/progress

This commit is contained in:
2025-02-10 20:29:57 -05:00
parent 3ce937ac8e
commit 32009c9a99
4 changed files with 250 additions and 10 deletions

View File

@ -6,12 +6,14 @@ import traceback
import os
import aiosqlite as sqlite3
import time
import random
import asyncio
import regex
import music_tag
from . import radio_util
from uuid import uuid4 as uuid
from pydantic import BaseModel
from fastapi import FastAPI, Request, Response, HTTPException
from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException
from fastapi.responses import RedirectResponse
from aiohttp import ClientSession, ClientTimeout
@ -39,16 +41,45 @@ class ValidRadioSongRequest(BaseModel):
class ValidRadioNextRequest(BaseModel):
"""
- **key**: API Key
- **skipTo**: UUID to skip to [optional]
"""
key: str
skipTo: str|None = None
class ValidRadioReshuffleRequest(ValidRadioNextRequest):
"""
- **key**: API Key
"""
class ValidRadioQueueShiftRequest(BaseModel):
"""
- **key**: API Key
- **uuid**: UUID to shift
- **next**: Play next if true, immediately if false, default False
"""
key: str
uuid: str
next: bool = False
class ValidRadioQueueRemovalRequest(BaseModel):
"""
- **key**: API Key
- **uuid**: UUID to remove
"""
key: str
uuid: str
class Radio(FastAPI):
"""Radio Endpoints"""
def __init__(self, app: FastAPI, my_util, constants, glob_state): # pylint: disable=super-init-not-called
def __init__(self, app: FastAPI, my_util, constants, glob_state) -> None: # pylint: disable=super-init-not-called
self.app = app
self.util = my_util
self.constants = constants
self.radio_util = radio_util.RadioUtil(self.constants)
self.glob_state = glob_state
self.ls_uri = "http://10.10.10.101:29000"
self.sqlite_exts: list[str] = ['/home/singer/api/solibs/spellfix1.cpython-311-x86_64-linux-gnu.so']
@ -70,6 +101,9 @@ class Radio(FastAPI):
"radio/request": self.radio_request,
"radio/get_queue": self.radio_get_queue,
"radio/skip": self.radio_skip,
"radio/queue_shift": self.radio_queue_shift,
"radio/reshuffle": self.radio_reshuffle,
"radio/queue_remove": self.radio_queue_remove,
# "widget/sqlite": self.homepage_sqlite_widget,
# "widget/lyrics": self.homepage_lyrics_widget,
# "widget/radio": self.homepage_radio_widget,
@ -88,7 +122,7 @@ class Radio(FastAPI):
asyncio.get_event_loop().run_until_complete(self.load_playlist())
async def get_queue_item_by_uuid(self, uuid: str) -> tuple[int, dict] | None:
def get_queue_item_by_uuid(self, uuid: str) -> tuple[int, dict] | None:
"""
Get queue item by UUID
Args:
@ -99,7 +133,7 @@ class Radio(FastAPI):
for x, item in enumerate(self.active_playlist):
if item.get('uuid') == uuid:
return (x, item)
return False
return None
async def _ls_skip(self) -> bool:
async with ClientSession() as session:
@ -111,24 +145,40 @@ class Radio(FastAPI):
async def radio_skip(self, data: ValidRadioNextRequest, request: Request) -> bool:
"""
Skip to the next track in the queue
Skip to the next track in the queue, or to uuid specified in skipTo if provided
"""
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:
(x, _) = self.get_queue_item_by_uuid(data.skipTo)
self.active_playlist = self.active_playlist[x:]
if not self.active_playlist:
await self.load_playlist()
return await self._ls_skip()
except Exception as e:
traceback.print_exc()
return False
async def radio_reshuffle(self, data: ValidRadioReshuffleRequest, request: Request) -> dict:
"""
Reshuffle the play queue
"""
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.active_playlist)
return {
'ok': True
}
async def radio_get_queue(self, request: Request, limit: int = 100) -> dict:
async def radio_get_queue(self, request: Request, limit: int = 20_000) -> dict:
"""
Get current play queue, up to limit n [default: 100]
Get current play queue, up to limit n [default: 20k]
Args:
limit (int): Number of results to return (default 100)
limit (int): Number of results to return (default 20k)
Returns:
dict
"""
@ -146,6 +196,37 @@ class Radio(FastAPI):
'items': queue_out
}
async def radio_queue_shift(self, data: ValidRadioQueueShiftRequest, request: Request) -> dict:
"""Shift position of a UUID within the queue [currently limited to playing next or immediately]"""
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
raise HTTPException(status_code=403, detail="Unauthorized")
(x, item) = self.get_queue_item_by_uuid(data.uuid)
self.active_playlist.pop(x)
self.active_playlist.insert(0, item)
if not data.next:
await self._ls_skip()
return {
'ok': True,
}
async def radio_queue_remove(self, data: ValidRadioQueueRemovalRequest, request: Request) -> dict:
"""Remove an item from the current play queue"""
if not self.util.check_key(path=request.url.path, req_type=4, key=data.key):
raise HTTPException(status_code=403, detail="Unauthorized")
(x, found_item) = self.get_queue_item_by_uuid(data.uuid)
if not found_item:
return {
'ok': False,
'err': 'UUID not found in play queue',
}
self.active_playlist.pop(x)
return {
'ok': True,
}
async def search_playlist(self, artistsong: str|None = None, artist: str|None = None, song: str|None = None) -> bool:
if artistsong and (artist or song):
raise RadioException("Cannot search using combination provided")
@ -250,7 +331,8 @@ class Radio(FastAPI):
return ret_obj
async def radio_get_next(self, data: ValidRadioNextRequest, request: Request) -> dict:
async def radio_get_next(self, data: ValidRadioNextRequest, request: Request,
background_tasks: BackgroundTasks) -> dict:
"""
Get next track
Args:
@ -285,6 +367,10 @@ class Radio(FastAPI):
self.now_playing = next
next['start'] = time_started
next['end'] = time_ends
try:
background_tasks.add_task(self.radio_util.webhook_song_change, next)
except Exception as e:
traceback.print_exc()
return next
else:
return await self.radio_pop_track(request, recursion_type="not list: self.active_playlist")