From a11748775e013c64dd1f45314119ab6227851088 Mon Sep 17 00:00:00 2001 From: codey Date: Sat, 23 Aug 2025 08:20:32 -0400 Subject: [PATCH] TRip: capitalize RQ job statuses in related endpoints, order job list, other: minor/typing --- endpoints/misc.py | 44 ++++++++++++++++++++++++++++++----- endpoints/rip.py | 14 +++++++---- lyric_search/sources/cache.py | 2 ++ utils/rip_background.py | 18 +++++++------- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/endpoints/misc.py b/endpoints/misc.py index 23c5376..67656bb 100644 --- a/endpoints/misc.py +++ b/endpoints/misc.py @@ -3,11 +3,18 @@ import time import os import json import random -from typing import Optional, Annotated +from typing import Any, Optional, Annotated from fastapi import FastAPI, Request, UploadFile, Response, HTTPException, Form, Depends from fastapi_throttle import RateLimiter from fastapi.responses import JSONResponse -import redis.asyncio as redis +import redis +from rq import Queue +from rq.registry import ( + StartedJobRegistry, + FinishedJobRegistry, + FailedJobRegistry, + DeferredJobRegistry +) from lyric_search.sources import private, cache as LyricsCache, redis_cache @@ -22,7 +29,7 @@ class Misc(FastAPI): self.constants = constants self.lyr_cache = LyricsCache.Cache() self.redis_cache = redis_cache.RedisCache() - self.redis_client = redis.Redis(password=private.REDIS_PW) + self.redis_client: Any = redis.Redis(password=private.REDIS_PW) self.radio = radio self.activity_image: Optional[bytes] = None self.nos_json_path: str = os.path.join( @@ -35,6 +42,7 @@ class Misc(FastAPI): "widget/sqlite": self.homepage_sqlite_widget, "widget/lyrics": self.homepage_lyrics_widget, "widget/radio": self.homepage_radio_widget, + "widget/rq": self.homepage_rq_widget, "misc/get_activity_image": self.get_activity_image, "misc/no": self.no, } @@ -141,14 +149,14 @@ class Misc(FastAPI): """ # Measure response time w/ test lyric search time_start: float = time.time() # Start time for response_time - test_lyrics_result = await self.redis_client.ft().search( # noqa: F841 + test_lyrics_result = self.redis_client.ft().search( # noqa: F841 "@artist: test @song: test" ) time_end: float = time.time() # End response time test - total_keys = await self.redis_client.dbsize() + total_keys = self.redis_client.dbsize() response_time: float = time_end - time_start - index_info = await self.redis_client.ft().info() + index_info = self.redis_client.ft().info() indexed_lyrics: int = index_info.get("num_docs") return JSONResponse( content={ @@ -158,6 +166,30 @@ class Misc(FastAPI): "sessions": -1, } ) + + async def homepage_rq_widget(self) -> JSONResponse: + """ + Homepage RQ Widget Handler + """ + queue_name = "dls" + queue = Queue(queue_name, self.redis_client) + queued = queue.count + started = StartedJobRegistry(queue_name, connection=self.redis_client).count + failed = FailedJobRegistry(queue_name, connection=self.redis_client).count + finished = FinishedJobRegistry(queue_name, connection=self.redis_client).count + deferred = DeferredJobRegistry(queue_name, connection=self.redis_client).count + + return JSONResponse( + content={ + queue_name: { + "queued": queued, + "started": started, + "failed": failed, + "finished": finished, + "deferred": deferred, + } + } + ) async def homepage_sqlite_widget(self) -> JSONResponse: """ diff --git a/endpoints/rip.py b/endpoints/rip.py index d1abf93..5ce2f77 100644 --- a/endpoints/rip.py +++ b/endpoints/rip.py @@ -76,14 +76,14 @@ class RIP(FastAPI): job_status: str | JobStatus = job.get_status() progress = job.meta.get("progress", 0) if progress == 100 and not job.meta.get("tarball"): - job_status = "compressing" + job_status = "Compressing" tracks_in = job.meta.get("tracks_in") tracks_out = len(job.meta.get("tracks", [])) return { "id": job.id, - "status": job_status, + "status": job_status.title(), "result": job.result, "tarball": job.meta.get("tarball"), "enqueued_at": job.enqueued_at, @@ -179,7 +179,7 @@ class RIP(FastAPI): retry=Retry(max=1, interval=[30]), meta={ "progress": 0, - "status": "queued", + "status": "Queued", "target": target, "tracks_in": len(track_ids), "quality": data.quality, @@ -189,7 +189,7 @@ class RIP(FastAPI): return JSONResponse( content={ "job_id": job.id, - "status": "queued", + "status": "Queued", "target": job.meta.get("target", None), "quality": job.meta.get("quality", "Unknown"), } @@ -267,4 +267,10 @@ class RIP(FastAPI): except Exception: continue + # ---- Sort newest first ---- + def job_sort_key(job): + return job.get("ended_at") or job.get("started_at") or job.get("enqueued_at") or 0 + + jobs_info.sort(key=job_sort_key, reverse=True) + return {"jobs": jobs_info} diff --git a/lyric_search/sources/cache.py b/lyric_search/sources/cache.py index b612a8b..c21df54 100644 --- a/lyric_search/sources/cache.py +++ b/lyric_search/sources/cache.py @@ -66,6 +66,8 @@ class Cache: confidence=row["confidence"], ) else: + if not sqlite_rows: + return None for row in sqlite_rows: if row[0] == matched_id: (_id, artist, song, lyrics, original_src) = row[:-1] diff --git a/utils/rip_background.py b/utils/rip_background.py index 5f8b4a2..7b98467 100644 --- a/utils/rip_background.py +++ b/utils/rip_background.py @@ -101,7 +101,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): job.meta["tracks"] = [] # will hold per-track dicts job.meta["progress"] = 0 job.meta["tarball"] = None - job.meta["status"] = "started" + job.meta["status"] = "Started" job.save_meta() except Exception as e: logging.warning("Failed to init job.meta: %s", e) @@ -123,7 +123,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): for i, track_id in enumerate(track_list or []): track_info = { "track_id": str(track_id), - "status": "pending", # pending | success | failed + "status": "Pending", # Pending | Success | Failed "file_path": None, # str | None "error": None, # str | None "attempts": 0, # int @@ -177,7 +177,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): tmp_file = None # consumed # Track success - track_info["status"] = "success" + track_info["status"] = "Success" track_info["file_path"] = str(final_file) track_info["error"] = None all_final_files.append(final_file) @@ -193,7 +193,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): ) track_info["error"] = str(e) if attempt >= MAX_RETRIES: - track_info["status"] = "failed" + track_info["status"] = "Failed" # small backoff before next attempt (or next track) await asyncio.sleep(random.uniform(THROTTLE_MIN, THROTTLE_MAX)) finally: @@ -223,7 +223,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): if job: try: job.meta["tarball"] = None - job.meta["status"] = "failed" + job.meta["status"] = "Failed" job.save_meta() except Exception: pass @@ -232,7 +232,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): # Pick artist with the most tracks artist_counts: dict[str, int] = {} for t in per_track_meta: - if t["status"] == "success" and t.get("file_path"): + if t["status"] == "Success" and t.get("file_path"): try: artist = Path(t["file_path"]).relative_to(ROOT_DIR).parts[0] except Exception: @@ -256,7 +256,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): if job: try: - job.meta["status"] = "compressing" + job.meta["status"] = "Compressing" job.save_meta() except Exception: pass @@ -303,7 +303,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): if job: job.meta["tarball"] = str(final_tarball) job.meta["progress"] = 100 - job.meta["status"] = "completed" + job.meta["status"] = "Completed" job.save_meta() return [str(final_tarball)] @@ -315,7 +315,7 @@ def bulk_download(track_list: list, quality: str = "FLAC"): return loop.run_until_complete(process_tracks()) except Exception as e: if job: - job.meta["status"] = "failed" + job.meta["status"] = "Failed" job.save_meta() logging.critical("Exception: %s", str(e)) finally: