misc / tRIP - beginnings/work in progress
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ constants.py
|
|||||||
tests.py
|
tests.py
|
||||||
db_migrate.py
|
db_migrate.py
|
||||||
notifier.py
|
notifier.py
|
||||||
|
test_hifi.py
|
||||||
youtube*
|
youtube*
|
||||||
playlist_creator.py
|
playlist_creator.py
|
||||||
artist_genre_tag.py
|
artist_genre_tag.py
|
||||||
|
3
base.py
3
base.py
@@ -98,6 +98,9 @@ routes: dict = {
|
|||||||
app, util, constants, loop
|
app, util, constants, loop
|
||||||
),
|
),
|
||||||
"meme": importlib.import_module("endpoints.meme").Meme(app, util, constants),
|
"meme": importlib.import_module("endpoints.meme").Meme(app, util, constants),
|
||||||
|
"trip": importlib.import_module("endpoints.rip").RIP(
|
||||||
|
app, util, constants
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Misc endpoint depends on radio endpoint instance
|
# Misc endpoint depends on radio endpoint instance
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import logging
|
|
||||||
from fastapi import FastAPI, Request, Response, Depends
|
from fastapi import FastAPI, Request, Response, Depends
|
||||||
from fastapi_throttle import RateLimiter
|
from fastapi_throttle import RateLimiter
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
@@ -22,12 +21,15 @@ class Meme(FastAPI):
|
|||||||
}
|
}
|
||||||
|
|
||||||
for endpoint, handler in self.endpoints.items():
|
for endpoint, handler in self.endpoints.items():
|
||||||
|
dependencies = None
|
||||||
|
if endpoint == "memes/list_memes":
|
||||||
|
dependencies = [Depends(RateLimiter(times=10, seconds=2))] # Do not rate limit image retrievals (cached)
|
||||||
app.add_api_route(
|
app.add_api_route(
|
||||||
f"/{endpoint}",
|
f"/{endpoint}",
|
||||||
handler,
|
handler,
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
include_in_schema=True,
|
include_in_schema=True,
|
||||||
dependencies=[Depends(RateLimiter(times=10, seconds=1))],
|
dependencies=dependencies,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_meme_by_id(self, id: int, request: Request) -> Response:
|
async def get_meme_by_id(self, id: int, request: Request) -> Response:
|
||||||
|
@@ -332,10 +332,7 @@ class Radio(FastAPI):
|
|||||||
time_started: int = int(time.time())
|
time_started: int = int(time.time())
|
||||||
time_ends: int = int(time_started + duration)
|
time_ends: int = int(time_started + duration)
|
||||||
|
|
||||||
if len(self.radio_util.active_playlist[data.station]) > 1:
|
|
||||||
self.radio_util.active_playlist[data.station].append(next) # Push to end of playlist
|
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[data.station] = next
|
self.radio_util.now_playing[data.station] = next
|
||||||
next["start"] = time_started
|
next["start"] = time_started
|
||||||
|
71
endpoints/rip.py
Normal file
71
endpoints/rip.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import logging
|
||||||
|
from fastapi import FastAPI, Request, Response, Depends
|
||||||
|
from fastapi_throttle import RateLimiter
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from utils.hifi_wrapper import HifiUtil
|
||||||
|
|
||||||
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
|
|
||||||
|
class RIP(FastAPI):
|
||||||
|
"""
|
||||||
|
Ripping Endpoints
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app: FastAPI, my_util, constants) -> None:
|
||||||
|
self.app: FastAPI = app
|
||||||
|
self.util = my_util
|
||||||
|
self.trip_util = HifiUtil()
|
||||||
|
self.constants = constants
|
||||||
|
self.endpoints: dict = {
|
||||||
|
"trip/get_artists_by_name": self.artists_by_name_handler,
|
||||||
|
"trip/get_albums_by_artist_id/{artist_id:path}": self.albums_by_artist_id_handler,
|
||||||
|
"trip/get_tracks_by_artist_song": self.tracks_by_artist_song_handler,
|
||||||
|
"trip/get_tracks_by_album_id/{album_id:path}": self.tracks_by_album_id_handler,
|
||||||
|
"trip/get_track_by_id/{track_id:path}": self.track_by_id_handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
for endpoint, handler in self.endpoints.items():
|
||||||
|
dependencies = [Depends(RateLimiter(times=8, seconds=2))] # Do not rate limit image retrievals (cached)
|
||||||
|
app.add_api_route(
|
||||||
|
f"/{endpoint}",
|
||||||
|
handler,
|
||||||
|
methods=["GET"],
|
||||||
|
include_in_schema=True,
|
||||||
|
dependencies=dependencies,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def artists_by_name_handler(self, artist: str, request: Request) -> Response:
|
||||||
|
"""Get artists by name"""
|
||||||
|
artists = await self.trip_util.get_artists_by_name(artist)
|
||||||
|
if not artists:
|
||||||
|
return Response(status_code=404, content="Not found")
|
||||||
|
return JSONResponse(content=artists)
|
||||||
|
|
||||||
|
async def albums_by_artist_id_handler(self, artist_id: int, request: Request) -> Response:
|
||||||
|
"""Get albums by artist ID"""
|
||||||
|
albums = await self.trip_util.get_albums_by_artist_id(artist_id)
|
||||||
|
if not albums:
|
||||||
|
return Response(status_code=404, content="Not found")
|
||||||
|
return JSONResponse(content=albums)
|
||||||
|
|
||||||
|
async def tracks_by_album_id_handler(self, album_id: int, request: Request) -> Response:
|
||||||
|
"""Get tracks by album id"""
|
||||||
|
tracks = await self.trip_util.get_tracks_by_album_id(album_id)
|
||||||
|
if not tracks:
|
||||||
|
return Response(status_code=404, content="Not Found")
|
||||||
|
return JSONResponse(content=tracks)
|
||||||
|
|
||||||
|
async def tracks_by_artist_song_handler(self, artist: str, song: str, request: Request) -> Response:
|
||||||
|
"""Get tracks by artist and song name"""
|
||||||
|
logging.critical("Searching for tracks by artist: %s, song: %s", artist, song)
|
||||||
|
tracks = await self.trip_util.get_tracks_by_artist_song(artist, song)
|
||||||
|
if not tracks:
|
||||||
|
return Response(status_code=404, content="Not found")
|
||||||
|
return JSONResponse(content=tracks)
|
||||||
|
|
||||||
|
async def track_by_id_handler(self, track_id: int, request: Request) -> Response:
|
||||||
|
"""Get track by ID"""
|
||||||
|
track = await self.trip_util.get_stream_url_by_track_id(track_id)
|
||||||
|
if not track:
|
||||||
|
return Response(status_code=404, content="Not found")
|
||||||
|
return JSONResponse(content={"stream_url": track})
|
@@ -111,8 +111,19 @@ class DataUtils:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.lrc_regex = regex.compile(
|
self.lrc_regex = regex.compile( # capture mm:ss and optional .xxx, then the lyric text
|
||||||
r"\[([0-9]{2}:[0-9]{2})\.[0-9]{1,3}\](\s(.*)){0,}"
|
r"""
|
||||||
|
\[ # literal “[”
|
||||||
|
( # 1st (and only) capture group:
|
||||||
|
[0-9]{2} # two-digit minutes
|
||||||
|
:[0-9]{2} # colon + two-digit seconds
|
||||||
|
(?:\.[0-9]{1,3})? # optional decimal part, e.g. .123
|
||||||
|
)
|
||||||
|
\] # literal “]”
|
||||||
|
\s* # optional whitespace
|
||||||
|
(.*) # capture the rest of the line as words
|
||||||
|
""",
|
||||||
|
regex.VERBOSE,
|
||||||
)
|
)
|
||||||
self.scrub_regex_1: Pattern = regex.compile(r"(\[.*?\])(\s){0,}(\:){0,1}")
|
self.scrub_regex_1: Pattern = regex.compile(r"(\[.*?\])(\s){0,}(\:){0,1}")
|
||||||
self.scrub_regex_2: Pattern = regex.compile(
|
self.scrub_regex_2: Pattern = regex.compile(
|
||||||
@@ -161,7 +172,7 @@ class DataUtils:
|
|||||||
)
|
)
|
||||||
_timetag = reg_helper[0]
|
_timetag = reg_helper[0]
|
||||||
if not reg_helper[1].strip():
|
if not reg_helper[1].strip():
|
||||||
_words = "♪"
|
continue
|
||||||
else:
|
else:
|
||||||
_words = reg_helper[1].strip()
|
_words = reg_helper[1].strip()
|
||||||
lrc_out.append(
|
lrc_out.append(
|
||||||
|
247
utils/hifi_wrapper.py
Normal file
247
utils/hifi_wrapper.py
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
from aiohttp import ClientSession, ClientTimeout
|
||||||
|
from typing import Optional
|
||||||
|
from urllib.parse import urlencode, quote
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class HifiUtil:
|
||||||
|
"""
|
||||||
|
HiFi API Utility Class
|
||||||
|
"""
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""Initialize HiFi API utility with base URLs."""
|
||||||
|
self.hifi_api_url: str = 'http://127.0.0.1:8000'
|
||||||
|
self.hifi_search_url: str = f"{self.hifi_api_url}/search"
|
||||||
|
|
||||||
|
def dedupe_by_key(self,
|
||||||
|
key: str,
|
||||||
|
entries: list[dict]) -> list[dict]:
|
||||||
|
deduped = {}
|
||||||
|
for entry in entries:
|
||||||
|
norm = entry[key].strip().lower()
|
||||||
|
if norm not in deduped:
|
||||||
|
deduped[norm] = entry
|
||||||
|
return list(deduped.values())
|
||||||
|
|
||||||
|
def format_duration(self, seconds):
|
||||||
|
if not seconds:
|
||||||
|
return None
|
||||||
|
m, s = divmod(seconds, 60)
|
||||||
|
return f"{m}:{s:02}"
|
||||||
|
|
||||||
|
async def search(self,
|
||||||
|
artist: str,
|
||||||
|
song: str = "",
|
||||||
|
album: str = "",
|
||||||
|
video_name: str = "",
|
||||||
|
playlist_name: str = "") -> Optional[list[dict]]:
|
||||||
|
"""Search HiFi API
|
||||||
|
Args:
|
||||||
|
artist (str, required)
|
||||||
|
song (str, optional)
|
||||||
|
album (str, optional)
|
||||||
|
video_name (str, optional)
|
||||||
|
playlist_name (str, optional)
|
||||||
|
Returns:
|
||||||
|
Optional[dict]: Returns the first result from the HiFi API search or None if no results found.
|
||||||
|
"""
|
||||||
|
async with ClientSession(timeout=ClientTimeout(total=30)) as session:
|
||||||
|
params: dict = {
|
||||||
|
"a": artist,
|
||||||
|
"s": song,
|
||||||
|
"al": album,
|
||||||
|
"v": video_name,
|
||||||
|
"p": playlist_name,
|
||||||
|
}
|
||||||
|
query: str = urlencode(params, quote_via=quote)
|
||||||
|
built_url: str = f"{self.hifi_search_url}?{query}"
|
||||||
|
async with session.get(built_url) as response:
|
||||||
|
json_response: dict = await response.json()
|
||||||
|
if isinstance(json_response, list):
|
||||||
|
json_response = json_response[0]
|
||||||
|
key = next(iter(json_response), None)
|
||||||
|
if not key:
|
||||||
|
logging.error("No matching key found in JSON response.")
|
||||||
|
return None
|
||||||
|
logging.info("Key: %s", key)
|
||||||
|
if key not in ["limit"]:
|
||||||
|
json_response = json_response[key]
|
||||||
|
items: list[dict] = json_response.get("items", [])
|
||||||
|
if not items:
|
||||||
|
logging.info("No results found.")
|
||||||
|
return None
|
||||||
|
return items
|
||||||
|
|
||||||
|
async def _retrieve(self,
|
||||||
|
req_type: str,
|
||||||
|
id: int,
|
||||||
|
quality: str = "LOSSLESS") -> Optional[list|dict]:
|
||||||
|
"""Retrieve a specific item by type and ID from the HiFi API.
|
||||||
|
Args:
|
||||||
|
type (str): The type of item (e.g., 'song', 'album').
|
||||||
|
id (int): The ID of the item.
|
||||||
|
quality (str): The quality of the item, default is "LOSSLESS". Other options: HIGH, LOW
|
||||||
|
Returns:
|
||||||
|
Optional[dict]: The item details or None if not found.
|
||||||
|
"""
|
||||||
|
async with ClientSession(timeout=ClientTimeout(total=10)) as session:
|
||||||
|
params: dict = {
|
||||||
|
'id': id,
|
||||||
|
'quality': quality,
|
||||||
|
}
|
||||||
|
if req_type not in ["track", "artist", "album", "playlist", "video"]:
|
||||||
|
logging.error("Invalid type: %s", type)
|
||||||
|
return None
|
||||||
|
if req_type in ["artist"]:
|
||||||
|
params.pop('id')
|
||||||
|
params['f'] = id # For non-track types, use 'f' instead of 'id' for full API output
|
||||||
|
query: str = urlencode(params, quote_via=quote)
|
||||||
|
built_url: str = f"{self.hifi_api_url}/{req_type}/?{query}"
|
||||||
|
logging.info("Built URL: %s", built_url)
|
||||||
|
async with session.get(built_url) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
logging.warning("Item not found: %s %s", req_type, id)
|
||||||
|
return None
|
||||||
|
response_json: list|dict = await response.json()
|
||||||
|
match req_type:
|
||||||
|
case "artist":
|
||||||
|
response_json = response_json[0]
|
||||||
|
if not isinstance(response_json, dict):
|
||||||
|
logging.error("Expected a dict but got: %s", type(response_json))
|
||||||
|
return None
|
||||||
|
response_json_rows = response_json.get('rows')
|
||||||
|
if not isinstance(response_json_rows, list):
|
||||||
|
logging.error("Expected a list but got: %s", type(response_json_rows))
|
||||||
|
return None
|
||||||
|
response_json = response_json_rows[0].get('modules')[0].get('pagedList')
|
||||||
|
case "album":
|
||||||
|
return response_json[1].get('items', [])
|
||||||
|
case "track":
|
||||||
|
return response_json
|
||||||
|
if not isinstance(response_json, dict):
|
||||||
|
logging.error("Expected a list but got: %s", type(response_json))
|
||||||
|
return None
|
||||||
|
return response_json.get('items')
|
||||||
|
|
||||||
|
async def get_artists_by_name(self,
|
||||||
|
artist_name: str) -> Optional[list]:
|
||||||
|
"""Get artist(s) by name from HiFi API.
|
||||||
|
Args:
|
||||||
|
artist_name (str): The name of the artist.
|
||||||
|
Returns:
|
||||||
|
Optional[dict]: The artist details or None if not found.
|
||||||
|
"""
|
||||||
|
artists_out: list[dict] = []
|
||||||
|
artists = await self.search(artist=artist_name)
|
||||||
|
if not artists:
|
||||||
|
logging.warning("No artist found for name: %s", artist_name)
|
||||||
|
return None
|
||||||
|
artists_out = [
|
||||||
|
{
|
||||||
|
'artist': res['name'],
|
||||||
|
'id': res['id'],
|
||||||
|
} for res in artists if 'name' in res and 'id' in res
|
||||||
|
]
|
||||||
|
artists_out = self.dedupe_by_key('artist', artists_out) # Remove duplicates
|
||||||
|
return artists_out
|
||||||
|
|
||||||
|
async def get_albums_by_artist_id(self,
|
||||||
|
artist_id: int) -> Optional[list|dict]:
|
||||||
|
"""Get albums by artist ID from HiFi API.
|
||||||
|
Args:
|
||||||
|
artist_id (int): The ID of the artist.
|
||||||
|
Returns:
|
||||||
|
Optional[list[dict]]: List of albums or None if not found.
|
||||||
|
"""
|
||||||
|
albums_out: list[dict] = []
|
||||||
|
albums = await self._retrieve("artist", artist_id)
|
||||||
|
if not albums:
|
||||||
|
logging.warning("No albums found for artist ID: %s", artist_id)
|
||||||
|
return None
|
||||||
|
albums_out = [
|
||||||
|
{
|
||||||
|
'artist': ", ".join(artist['name'] for artist in album['artists']),
|
||||||
|
'album': album['title'],
|
||||||
|
'id': album['id'],
|
||||||
|
'release_date': album.get('releaseDate', 'Unknown')
|
||||||
|
} for album in albums if 'title' in album and 'id' in album and 'artists' in album
|
||||||
|
]
|
||||||
|
|
||||||
|
logging.info("Retrieved albums: %s", albums_out)
|
||||||
|
return albums_out
|
||||||
|
|
||||||
|
async def get_tracks_by_album_id(self,
|
||||||
|
album_id: int) -> Optional[list|dict]:
|
||||||
|
"""Get tracks by album ID from HiFi API.
|
||||||
|
Args:
|
||||||
|
album_id (int): The ID of the album.
|
||||||
|
Returns:
|
||||||
|
Optional[list[dict]]: List of tracks or None if not found.
|
||||||
|
"""
|
||||||
|
track_list = await self._retrieve("album", album_id)
|
||||||
|
if not track_list:
|
||||||
|
logging.warning("No tracks found for album ID: %s", album_id)
|
||||||
|
return None
|
||||||
|
tracks_out: list[dict] = [
|
||||||
|
{
|
||||||
|
'id': track.get('item').get('id'),
|
||||||
|
'artist': track.get('item').get('artist').get('name'),
|
||||||
|
'title': track.get('item').get('title'),
|
||||||
|
'duration': self.format_duration(track.get('item').get('duration', 0)),
|
||||||
|
'version': track.get('item').get('version'),
|
||||||
|
'audioQuality': track.get('item').get('audioQuality'),
|
||||||
|
} for track in track_list
|
||||||
|
]
|
||||||
|
return tracks_out
|
||||||
|
|
||||||
|
|
||||||
|
async def get_tracks_by_artist_song(self,
|
||||||
|
artist: str,
|
||||||
|
song: str) -> Optional[list]:
|
||||||
|
"""Get track by artist and song name from HiFi API.
|
||||||
|
Args:
|
||||||
|
artist (str): The name of the artist.
|
||||||
|
song (str): The name of the song.
|
||||||
|
Returns:
|
||||||
|
Optional[dict]: The track details or None if not found.
|
||||||
|
"""
|
||||||
|
tracks_out: list[dict] = []
|
||||||
|
tracks = await self.search(artist=artist, song=song)
|
||||||
|
if not tracks:
|
||||||
|
logging.warning("No track found for artist: %s, song: %s", artist, song)
|
||||||
|
return None
|
||||||
|
tracks_out = [
|
||||||
|
{
|
||||||
|
'artist': ", ".join(artist['name'] for artist in track['artists']),
|
||||||
|
'song': track['title'],
|
||||||
|
'id': track['id'],
|
||||||
|
'album': track.get('album', {}).get('title', 'Unknown'),
|
||||||
|
'duration': track.get('duration', 0)
|
||||||
|
} for track in tracks if 'title' in track and 'id' in track and 'artists' in track
|
||||||
|
]
|
||||||
|
if not tracks_out:
|
||||||
|
logging.warning("No valid tracks found after processing.")
|
||||||
|
return None
|
||||||
|
logging.info("Retrieved tracks: %s", tracks_out)
|
||||||
|
return tracks_out
|
||||||
|
|
||||||
|
async def get_stream_url_by_track_id(self,
|
||||||
|
track_id: int,
|
||||||
|
quality: str = "LOSSLESS") -> Optional[str]:
|
||||||
|
"""Get stream URL by track ID from HiFi API.
|
||||||
|
Args:
|
||||||
|
track_id (int): The ID of the track.
|
||||||
|
quality (str): The quality of the stream, default is "LOSSLESS". Other options: HIGH, LOW
|
||||||
|
Returns:
|
||||||
|
Optional[str]: The stream URL or None if not found.
|
||||||
|
"""
|
||||||
|
track = await self._retrieve("track", track_id, quality)
|
||||||
|
if not isinstance(track, list) :
|
||||||
|
logging.warning("No track found for ID: %s", track_id)
|
||||||
|
return None
|
||||||
|
stream_url = track[2].get('OriginalTrackUrl')
|
||||||
|
if not stream_url:
|
||||||
|
logging.warning("No stream URL found for track ID: %s", track_id)
|
||||||
|
return None
|
||||||
|
logging.info("Retrieved stream URL: %s", stream_url)
|
||||||
|
return stream_url
|
||||||
|
|
@@ -3,6 +3,7 @@ import traceback
|
|||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
from uuid import uuid4 as uuid
|
from uuid import uuid4 as uuid
|
||||||
from typing import Union, Optional, Iterable
|
from typing import Union, Optional, Iterable
|
||||||
from aiohttp import ClientSession, ClientTimeout
|
from aiohttp import ClientSession, ClientTimeout
|
||||||
@@ -59,11 +60,13 @@ class RadioUtil:
|
|||||||
# # "hip hop",
|
# # "hip hop",
|
||||||
# "metalcore",
|
# "metalcore",
|
||||||
# "deathcore",
|
# "deathcore",
|
||||||
# # "edm",
|
# "edm",
|
||||||
# "electronic",
|
# "electronic",
|
||||||
# "hard rock",
|
# "post-hardcore",
|
||||||
# "rock",
|
# "post hardcore",
|
||||||
# # "ska",
|
# # "hard rock",
|
||||||
|
# # "rock",
|
||||||
|
# # # "ska",
|
||||||
# # "post punk",
|
# # "post punk",
|
||||||
# # "post-punk",
|
# # "post-punk",
|
||||||
# # "pop punk",
|
# # "pop punk",
|
||||||
@@ -96,9 +99,11 @@ class RadioUtil:
|
|||||||
self.webhooks: dict = {
|
self.webhooks: dict = {
|
||||||
"gpt": {
|
"gpt": {
|
||||||
"hook": self.constants.GPT_WEBHOOK,
|
"hook": self.constants.GPT_WEBHOOK,
|
||||||
|
"lastRun": None,
|
||||||
},
|
},
|
||||||
"sfm": {
|
"sfm": {
|
||||||
"hook": self.constants.SFM_WEBHOOK,
|
"hook": self.constants.SFM_WEBHOOK,
|
||||||
|
"lastRun": None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,8 +363,8 @@ class RadioUtil:
|
|||||||
query: str = (
|
query: str = (
|
||||||
"SELECT genre FROM artist_genre WHERE artist LIKE ? COLLATE NOCASE"
|
"SELECT genre FROM artist_genre WHERE artist LIKE ? COLLATE NOCASE"
|
||||||
)
|
)
|
||||||
params: tuple[str] = (f"%%{artist}%%",)
|
params: tuple[str] = (artist,)
|
||||||
with sqlite3.connect(self.artist_genre_db_path, timeout=2) as _db:
|
with sqlite3.connect(self.playback_db_path, timeout=2) as _db:
|
||||||
_db.row_factory = sqlite3.Row
|
_db.row_factory = sqlite3.Row
|
||||||
_cursor = _db.execute(query, params)
|
_cursor = _db.execute(query, params)
|
||||||
res = _cursor.fetchone()
|
res = _cursor.fetchone()
|
||||||
@@ -386,6 +391,8 @@ class RadioUtil:
|
|||||||
_playlist = await self.redis_client.json().get(playlist_redis_key)
|
_playlist = await self.redis_client.json().get(playlist_redis_key)
|
||||||
if playlist not in self.active_playlist.keys():
|
if playlist not in self.active_playlist.keys():
|
||||||
self.active_playlist[playlist] = []
|
self.active_playlist[playlist] = []
|
||||||
|
if not playlist == "rock":
|
||||||
|
random.shuffle(_playlist) # Temp/for Cocteau Twins
|
||||||
self.active_playlist[playlist] = [
|
self.active_playlist[playlist] = [
|
||||||
{
|
{
|
||||||
"uuid": str(uuid().hex),
|
"uuid": str(uuid().hex),
|
||||||
@@ -542,7 +549,7 @@ class RadioUtil:
|
|||||||
text: Optional[str] = await request.text()
|
text: Optional[str] = await request.text()
|
||||||
return isinstance(text, str) and text.startswith("OK")
|
return isinstance(text, str) and text.startswith("OK")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug("Skip failed: %s", str(e))
|
logging.critical("Skip failed: %s", str(e))
|
||||||
|
|
||||||
return False # failsafe
|
return False # failsafe
|
||||||
|
|
||||||
@@ -572,7 +579,10 @@ class RadioUtil:
|
|||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return None # disabled temporarily (needs rate limiting)
|
"""TEMP - ONLY MAIN"""
|
||||||
|
if not station == "main":
|
||||||
|
return
|
||||||
|
return # Temp disable global
|
||||||
# First, send track info
|
# First, send track info
|
||||||
"""
|
"""
|
||||||
TODO:
|
TODO:
|
||||||
@@ -630,7 +640,15 @@ class RadioUtil:
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
sfm_hook: str = self.webhooks["sfm"].get("hook")
|
now: float = time.time()
|
||||||
|
_sfm: dict = self.webhooks["sfm"]
|
||||||
|
if _sfm:
|
||||||
|
sfm_hook: str = _sfm.get("hook", "")
|
||||||
|
sfm_hook_lastRun: Optional[float] = _sfm.get("lastRun", 0.0)
|
||||||
|
|
||||||
|
if sfm_hook_lastRun and ((now - sfm_hook_lastRun) < 5):
|
||||||
|
logging.info("SFM Webhook: Throttled!")
|
||||||
|
return
|
||||||
async with ClientSession() as session:
|
async with ClientSession() as session:
|
||||||
async with await session.post(
|
async with await session.post(
|
||||||
sfm_hook,
|
sfm_hook,
|
||||||
@@ -643,36 +661,39 @@ class RadioUtil:
|
|||||||
request.raise_for_status()
|
request.raise_for_status()
|
||||||
|
|
||||||
# Next, AI feedback (for main stream only)
|
# Next, AI feedback (for main stream only)
|
||||||
|
"""
|
||||||
|
TEMP. DISABLED
|
||||||
|
"""
|
||||||
|
|
||||||
if station == "main":
|
# if station == "main":
|
||||||
ai_response: Optional[str] = await self.get_ai_song_info(
|
# ai_response: Optional[str] = await self.get_ai_song_info(
|
||||||
track["artist"], track["song"]
|
# track["artist"], track["song"]
|
||||||
)
|
# )
|
||||||
if not ai_response:
|
# if not ai_response:
|
||||||
return
|
# return
|
||||||
|
|
||||||
hook_data = {
|
# hook_data = {
|
||||||
"username": "GPT",
|
# "username": "GPT",
|
||||||
"embeds": [
|
# "embeds": [
|
||||||
{
|
# {
|
||||||
"title": "AI Feedback",
|
# "title": "AI Feedback",
|
||||||
"color": 0x35D0FF,
|
# "color": 0x35D0FF,
|
||||||
"description": ai_response.strip(),
|
# "description": ai_response.strip(),
|
||||||
}
|
# }
|
||||||
],
|
# ],
|
||||||
}
|
# }
|
||||||
|
|
||||||
ai_hook: str = self.webhooks["gpt"].get("hook")
|
# ai_hook: str = self.webhooks["gpt"].get("hook")
|
||||||
async with ClientSession() as session:
|
# async with ClientSession() as session:
|
||||||
async with await session.post(
|
# async with await session.post(
|
||||||
ai_hook,
|
# ai_hook,
|
||||||
json=hook_data,
|
# json=hook_data,
|
||||||
timeout=ClientTimeout(connect=5, sock_read=5),
|
# timeout=ClientTimeout(connect=5, sock_read=5),
|
||||||
headers={
|
# headers={
|
||||||
"content-type": "application/json; charset=utf-8",
|
# "content-type": "application/json; charset=utf-8",
|
||||||
},
|
# },
|
||||||
) as request:
|
# ) as request:
|
||||||
request.raise_for_status()
|
# request.raise_for_status()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info("Webhook error occurred: %s", str(e))
|
logging.info("Webhook error occurred: %s", str(e))
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
Reference in New Issue
Block a user