diff --git a/lrx_cli/cache.py b/lrx_cli/cache.py index b7bb767..ef2e79d 100644 --- a/lrx_cli/cache.py +++ b/lrx_cli/cache.py @@ -127,15 +127,13 @@ class CacheEngine: f"[{status_str}, ttl={remaining}s]" ) status = CacheStatus(status_str) - if confidence is None and status in ( - CacheStatus.SUCCESS_SYNCED, - CacheStatus.SUCCESS_UNSYNCED, - ): - confidence = ( - LEGACY_CONFIDENCE_SYNCED - if status == CacheStatus.SUCCESS_SYNCED - else LEGACY_CONFIDENCE_UNSYNCED - ) + if confidence is None: + if status == CacheStatus.SUCCESS_SYNCED: + confidence = LEGACY_CONFIDENCE_SYNCED + elif status == CacheStatus.SUCCESS_UNSYNCED: + confidence = LEGACY_CONFIDENCE_UNSYNCED + else: + confidence = 100.0 # negative statuses: value irrelevant return LyricResult( status=status, @@ -163,13 +161,14 @@ class CacheEngine: continue if best is None: best = cached - else: - cached_conf = ( - cached.confidence if cached.confidence is not None else 100.0 - ) - best_conf = best.confidence if best.confidence is not None else 100.0 - if cached_conf > best_conf: - best = cached + elif cached.confidence > best.confidence: + best = cached + elif ( + cached.confidence == best.confidence + and cached.status == CacheStatus.SUCCESS_SYNCED + and best.status != CacheStatus.SUCCESS_SYNCED + ): + best = cached return best # Write @@ -298,12 +297,15 @@ class CacheEngine: f"SELECT status, lyrics, source, confidence FROM cache WHERE {where} " "ORDER BY COALESCE(confidence, " " CASE status WHEN ? THEN ? ELSE ? END" - ") DESC, created_at DESC LIMIT 1", + ") DESC, " + "CASE status WHEN ? THEN 0 ELSE 1 END, " + "created_at DESC LIMIT 1", params + [ CacheStatus.SUCCESS_SYNCED.value, LEGACY_CONFIDENCE_SYNCED, LEGACY_CONFIDENCE_UNSYNCED, + CacheStatus.SUCCESS_SYNCED.value, ], ).fetchall() @@ -389,6 +391,7 @@ class CacheEngine: key=lambda x: ( x[0], -(x[1].get("confidence") or 0), + x[1].get("status") != CacheStatus.SUCCESS_SYNCED.value, -(x[1].get("created_at") or 0), ) ) diff --git a/lrx_cli/cli.py b/lrx_cli/cli.py index 8568ee0..ff1ee9e 100644 --- a/lrx_cli/cli.py +++ b/lrx_cli/cli.py @@ -453,6 +453,8 @@ def _print_cache_row(row: dict, indent: str = "") -> None: print(f"{indent} Lyrics : {line_count} lines") if confidence is not None: print(f"{indent} Confidence: {confidence:.0f}") + else: + print(f"{indent} Confidence: (legacy)") def run(): diff --git a/lrx_cli/core.py b/lrx_cli/core.py index 6ef5182..d8559de 100644 --- a/lrx_cli/core.py +++ b/lrx_cli/core.py @@ -40,15 +40,14 @@ _STATUS_TTL: dict[CacheStatus, Optional[int]] = { def _is_better(new: LyricResult, old: LyricResult) -> bool: - """Compare two results by confidence only. - - Synced/unsynced preference is already baked into the confidence score - (synced bonus in scoring weights), so we don't need a separate tier. - None confidence = trusted = 100. - """ - new_conf = new.confidence if new.confidence is not None else 100.0 - old_conf = old.confidence if old.confidence is not None else 100.0 - return new_conf > old_conf + """Compare two results: higher confidence wins; synced breaks ties.""" + if new.confidence != old.confidence: + return new.confidence > old.confidence + # Equal confidence — prefer synced as tiebreaker + return ( + new.status == CacheStatus.SUCCESS_SYNCED + and old.status != CacheStatus.SUCCESS_SYNCED + ) class LrcManager: @@ -121,13 +120,10 @@ class LrcManager: # Positive cache hit — apply the same confidence evaluation # as fresh fetches so that low-confidence cached results # don't block better results from later fetchers. - is_trusted = ( - cached.confidence is None - or cached.confidence >= HIGH_CONFIDENCE - ) + is_trusted = cached.confidence >= HIGH_CONFIDENCE logger.info( f"[{source}] cache hit: {cached.status.value}" - f" (confidence={'trusted' if cached.confidence is None else f'{cached.confidence:.0f}'})" + f" (confidence={cached.confidence:.0f})" ) if cached.status == CacheStatus.SUCCESS_SYNCED and is_trusted: return cached @@ -155,12 +151,10 @@ class LrcManager: CacheStatus.SUCCESS_SYNCED, CacheStatus.SUCCESS_UNSYNCED, ): - is_trusted = ( - result.confidence is None or result.confidence >= HIGH_CONFIDENCE - ) + is_trusted = result.confidence >= HIGH_CONFIDENCE logger.info( f"[{source}] got {result.status.value} lyrics" - f" (confidence={'trusted' if result.confidence is None else f'{result.confidence:.0f}'})" + f" (confidence={result.confidence:.0f})" ) # Trusted synced → return immediately if result.status == CacheStatus.SUCCESS_SYNCED and is_trusted: diff --git a/lrx_cli/models.py b/lrx_cli/models.py index b172144..02721ac 100644 --- a/lrx_cli/models.py +++ b/lrx_cli/models.py @@ -62,6 +62,4 @@ class LyricResult: lyrics: Optional[LRCData] = None source: Optional[str] = None # Which fetcher produced this result ttl: Optional[int] = None # Hint for cache TTL (seconds) - confidence: Optional[float] = ( - None # 0-100 selection confidence (None = exact/trusted) - ) + confidence: float = 100.0 # 0-100 selection confidence (100 = trusted/exact) diff --git a/pyproject.toml b/pyproject.toml index 12d8d0a..4406265 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "lrx-cli" -version = "0.2.1" +version = "0.3.0" description = "Fetch line-synced lyrics for your music player." readme = "README.md" requires-python = ">=3.13" diff --git a/uv.lock b/uv.lock index 9414823..0d0e99d 100644 --- a/uv.lock +++ b/uv.lock @@ -153,7 +153,7 @@ wheels = [ [[package]] name = "lrx-cli" -version = "0.2.1" +version = "0.3.0" source = { editable = "." } dependencies = [ { name = "cyclopts" },