cache album art

This commit is contained in:
codey 2025-02-11 09:50:31 -05:00
parent 028cfc197b
commit 5ee99664f3

View File

@ -12,6 +12,7 @@ import regex
import music_tag
from . import radio_util
from uuid import uuid4 as uuid
from typing import Optional
from pydantic import BaseModel
from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException
from fastapi.responses import RedirectResponse
@ -19,6 +20,11 @@ from aiohttp import ClientSession, ClientTimeout
double_space = regex.compile(r'\s{2,}')
"""
TODO:
minor refactoring/type annotations/docstrings
"""
class RadioException(Exception):
pass
@ -95,6 +101,7 @@ class Radio(FastAPI):
'start': 0,
'end': 0,
'file_path': None,
'id': None,
}
self.endpoints = {
"radio/np": self.radio_now_playing,
@ -113,7 +120,7 @@ class Radio(FastAPI):
# 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=False)
include_in_schema=True)
asyncio.get_event_loop().run_until_complete(self.load_playlist())
@ -181,6 +188,7 @@ class Radio(FastAPI):
for x, item in enumerate(self.active_playlist[0:limit+1]):
queue_out.append({
'pos': x,
'id': item.get('id'),
'uuid': item.get('uuid'),
'artist': item.get('artist'),
'song': item.get('song'),
@ -281,6 +289,7 @@ class Radio(FastAPI):
results = await db_cursor.fetchall()
self.active_playlist = [{
'uuid': str(uuid().hex),
'id': r['id'],
'artist': double_space.sub(' ', r['artist']).strip(),
'song': double_space.sub(' ', r['song']).strip(),
'artistsong': double_space.sub(' ', r['artistdashsong']).strip(),
@ -292,20 +301,74 @@ class Radio(FastAPI):
except:
traceback.print_exc()
async def cache_album_art(self, track_id: int, album_art: bytes) -> None:
try:
logging.info("Attempting cache for %s", track_id)
async with sqlite3.connect(self.active_playlist_path,
timeout=2) as db_conn:
async with await db_conn.execute("UPDATE tracks SET album_art = ? WHERE id = ?",
(album_art, track_id,)) as db_cursor:
await db_conn.commit()
logging.info("Committed %s", track_id)
except:
traceback.print_exc()
async def get_album_art(self, track_id: Optional[int] = None,
file_path: Optional[str] = None) -> bytes:
try:
async with sqlite3.connect(self.active_playlist_path,
timeout=2) as db_conn:
db_conn.row_factory = sqlite3.Row
query = "SELECT album_art FROM tracks WHERE id = ?"
query_params = (track_id,)
if file_path:
query = "SELECT album_art FROM tracks WHERE file_path = ?"
query_params = (file_path,)
async with await db_conn.execute(query,
query_params) as db_cursor:
result = await db_cursor.fetchone()
if not result:
return
return result['album_art']
except:
traceback.print_exc()
return
async def _get_album_art(self, track_id: Optional[int] = None, file_path: Optional[str] = None) -> bytes|None:
try:
if not file_path:
file_path = self.now_playing.get('file_path')
if not file_path:
logging.info("_get_album_art:: No current file")
return
original_file_path = file_path
file_path = file_path.replace("/paul/toons/",
"/singer/gogs_toons/")
logging.info("Seeking %s", original_file_path)
cached_album_art = await self.get_album_art(file_path=original_file_path)
if cached_album_art:
logging.info("Returning from cache!")
return cached_album_art
# Not cached, read from file
tagged = music_tag.load_file(file_path)
album_art = tagged.get('artwork').first
logging.info("Returning from file read!")
return album_art.data
except:
traceback.print_exc()
# TODO: Optimize/cache
async def album_art_handler(self, request: Request) -> bytes:
try:
current_file = self.now_playing.get('file_path')
if not current_file:
logging.info("album_art_handler:: No current file")
album_art = await self._get_album_art()
if not album_art:
return RedirectResponse(url="https://codey.lol/images/radio_art_default.jpg",
status_code=302)
current_file = current_file.replace("/paul/toons/",
"/singer/gogs_toons/")
tagged = music_tag.load_file(current_file)
album_art = tagged.get('artwork').first
return Response(content=album_art.data,
media_type=album_art.mime)
return Response(content=album_art,
media_type="image/png")
except Exception as e:
traceback.print_exc()
return RedirectResponse(url="https://codey.lol/images/radio_art_default.jpg",
@ -366,6 +429,12 @@ class Radio(FastAPI):
background_tasks.add_task(self.radio_util.webhook_song_change, next)
except Exception as e:
traceback.print_exc()
try:
if not await self.get_album_art(file_path=next['file_path']):
album_art = await self._get_album_art(next['file_path'])
await self.cache_album_art(next['id'], album_art)
except:
traceback.print_exc()
return next
else:
return await self.radio_pop_track(request, recursion_type="not list: self.active_playlist")