cache album art
This commit is contained in:
parent
028cfc197b
commit
5ee99664f3
@ -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")
|
||||
|
Loading…
x
Reference in New Issue
Block a user