cache album art
This commit is contained in:
		| @@ -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") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user