cache album art
This commit is contained in:
parent
028cfc197b
commit
5ee99664f3
@ -12,6 +12,7 @@ import regex
|
|||||||
import music_tag
|
import music_tag
|
||||||
from . import radio_util
|
from . import radio_util
|
||||||
from uuid import uuid4 as uuid
|
from uuid import uuid4 as uuid
|
||||||
|
from typing import Optional
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException
|
from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
@ -19,6 +20,11 @@ from aiohttp import ClientSession, ClientTimeout
|
|||||||
|
|
||||||
double_space = regex.compile(r'\s{2,}')
|
double_space = regex.compile(r'\s{2,}')
|
||||||
|
|
||||||
|
"""
|
||||||
|
TODO:
|
||||||
|
minor refactoring/type annotations/docstrings
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class RadioException(Exception):
|
class RadioException(Exception):
|
||||||
pass
|
pass
|
||||||
@ -95,6 +101,7 @@ class Radio(FastAPI):
|
|||||||
'start': 0,
|
'start': 0,
|
||||||
'end': 0,
|
'end': 0,
|
||||||
'file_path': None,
|
'file_path': None,
|
||||||
|
'id': None,
|
||||||
}
|
}
|
||||||
self.endpoints = {
|
self.endpoints = {
|
||||||
"radio/np": self.radio_now_playing,
|
"radio/np": self.radio_now_playing,
|
||||||
@ -113,7 +120,7 @@ class Radio(FastAPI):
|
|||||||
|
|
||||||
# NOTE: Not in loop because method is GET for this endpoint
|
# NOTE: Not in loop because method is GET for this endpoint
|
||||||
app.add_api_route("/radio/album_art", self.album_art_handler, methods=["GET"],
|
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())
|
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]):
|
for x, item in enumerate(self.active_playlist[0:limit+1]):
|
||||||
queue_out.append({
|
queue_out.append({
|
||||||
'pos': x,
|
'pos': x,
|
||||||
|
'id': item.get('id'),
|
||||||
'uuid': item.get('uuid'),
|
'uuid': item.get('uuid'),
|
||||||
'artist': item.get('artist'),
|
'artist': item.get('artist'),
|
||||||
'song': item.get('song'),
|
'song': item.get('song'),
|
||||||
@ -281,6 +289,7 @@ class Radio(FastAPI):
|
|||||||
results = await db_cursor.fetchall()
|
results = await db_cursor.fetchall()
|
||||||
self.active_playlist = [{
|
self.active_playlist = [{
|
||||||
'uuid': str(uuid().hex),
|
'uuid': str(uuid().hex),
|
||||||
|
'id': r['id'],
|
||||||
'artist': double_space.sub(' ', r['artist']).strip(),
|
'artist': double_space.sub(' ', r['artist']).strip(),
|
||||||
'song': double_space.sub(' ', r['song']).strip(),
|
'song': double_space.sub(' ', r['song']).strip(),
|
||||||
'artistsong': double_space.sub(' ', r['artistdashsong']).strip(),
|
'artistsong': double_space.sub(' ', r['artistdashsong']).strip(),
|
||||||
@ -291,21 +300,75 @@ class Radio(FastAPI):
|
|||||||
len(self.active_playlist))
|
len(self.active_playlist))
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
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
|
# TODO: Optimize/cache
|
||||||
async def album_art_handler(self, request: Request) -> bytes:
|
async def album_art_handler(self, request: Request) -> bytes:
|
||||||
try:
|
try:
|
||||||
current_file = self.now_playing.get('file_path')
|
album_art = await self._get_album_art()
|
||||||
if not current_file:
|
if not album_art:
|
||||||
logging.info("album_art_handler:: No current file")
|
|
||||||
return RedirectResponse(url="https://codey.lol/images/radio_art_default.jpg",
|
return RedirectResponse(url="https://codey.lol/images/radio_art_default.jpg",
|
||||||
status_code=302)
|
status_code=302)
|
||||||
current_file = current_file.replace("/paul/toons/",
|
return Response(content=album_art,
|
||||||
"/singer/gogs_toons/")
|
media_type="image/png")
|
||||||
tagged = music_tag.load_file(current_file)
|
|
||||||
album_art = tagged.get('artwork').first
|
|
||||||
return Response(content=album_art.data,
|
|
||||||
media_type=album_art.mime)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return RedirectResponse(url="https://codey.lol/images/radio_art_default.jpg",
|
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)
|
background_tasks.add_task(self.radio_util.webhook_song_change, next)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
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
|
return next
|
||||||
else:
|
else:
|
||||||
return await self.radio_pop_track(request, recursion_type="not list: self.active_playlist")
|
return await self.radio_pop_track(request, recursion_type="not list: self.active_playlist")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user