feat: able to set confidence for certain source via cli
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
# LRX-CLI
|
# LRX-CLI
|
||||||
|
|
||||||
A CLI tool for fetching LRC lyrics on Linux. Automatically detects the currently playing track via MPRIS/DBus and retrieves synced (or plain with all time tags set to `[00:00.00]` if failed to find any synced) lyrics from multiple sources.
|
A CLI tool for fetching LRC lyrics on Linux. Automatically detects the currently playing track via MPRIS/DBus and retrieves the best-matching lyrics from multiple sources, ranked by confidence scoring.
|
||||||
|
|
||||||
## Sources
|
## Sources
|
||||||
|
|
||||||
Lyrics are fetched using a fallback pipeline (first synced result wins):
|
Sources are queried in order. High-confidence results (exact match or manual insert) terminate the pipeline early; otherwise all sources are tried and the highest-confidence result wins.
|
||||||
|
|
||||||
1. **Local** — sidecar `.lrc` files or embedded audio metadata (FLAC, MP3)
|
1. **Local** — sidecar `.lrc` files or embedded audio metadata (FLAC, MP3)
|
||||||
2. **Cache Search** — fuzzy cross-album lookup in local cache
|
2. **Cache Search** — fuzzy cross-album lookup in local cache
|
||||||
@@ -43,25 +43,22 @@ See `lrx --help` for full command reference. Common use cases:
|
|||||||
lrx search --path "/path/to/Westlife - My Love.flac"
|
lrx search --path "/path/to/Westlife - My Love.flac"
|
||||||
```
|
```
|
||||||
|
|
||||||
- Export to sidecar `.lrc` file:
|
- Export to sidecar `.lrc` file (or `.txt` with `--plain`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lrx export
|
lrx export
|
||||||
```
|
lrx export --plain
|
||||||
|
|
||||||
or to a custom path:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
lrx export --output /path/to/lyrics.lrc
|
lrx export --output /path/to/lyrics.lrc
|
||||||
```
|
```
|
||||||
|
|
||||||
- Cache management:
|
- Cache management:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lrx cache stats # show cache statistics
|
lrx cache stats # statistics with source×status table and confidence distribution
|
||||||
lrx cache query # query cache for current track
|
lrx cache query # inspect cache entries for current track
|
||||||
lrx cache clear # clears cache of current track
|
lrx cache clear # clear cache of current track
|
||||||
lrx cache clear --all # clears entire cache
|
lrx cache clear --all # clear entire cache
|
||||||
|
lrx cache confidence spotify 100 # manually set confidence for a source
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|||||||
+27
-1
@@ -133,7 +133,7 @@ class CacheEngine:
|
|||||||
elif status == CacheStatus.SUCCESS_UNSYNCED:
|
elif status == CacheStatus.SUCCESS_UNSYNCED:
|
||||||
confidence = LEGACY_CONFIDENCE_UNSYNCED
|
confidence = LEGACY_CONFIDENCE_UNSYNCED
|
||||||
else:
|
else:
|
||||||
confidence = 100.0 # negative statuses: value irrelevant
|
confidence = 0.0 # negative statuses: no confidence
|
||||||
|
|
||||||
return LyricResult(
|
return LyricResult(
|
||||||
status=status,
|
status=status,
|
||||||
@@ -399,6 +399,32 @@ class CacheEngine:
|
|||||||
|
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
# Update
|
||||||
|
|
||||||
|
def update_confidence(
|
||||||
|
self,
|
||||||
|
track: TrackMeta,
|
||||||
|
confidence: float,
|
||||||
|
source: str,
|
||||||
|
) -> int:
|
||||||
|
"""Update confidence for a specific source's cache entry matching *track*.
|
||||||
|
|
||||||
|
Returns the number of rows updated.
|
||||||
|
"""
|
||||||
|
conditions, params = self._track_where(track)
|
||||||
|
if not conditions:
|
||||||
|
return 0
|
||||||
|
conditions.append("source = ?")
|
||||||
|
params.append(source)
|
||||||
|
where = " AND ".join(conditions)
|
||||||
|
with sqlite3.connect(self.db_path) as conn:
|
||||||
|
cur = conn.execute(
|
||||||
|
f"UPDATE cache SET confidence = ? WHERE {where}",
|
||||||
|
[confidence] + params,
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
return cur.rowcount
|
||||||
|
|
||||||
# Query / inspect
|
# Query / inspect
|
||||||
|
|
||||||
def query_track(self, track: TrackMeta) -> list[dict]:
|
def query_track(self, track: TrackMeta) -> list[dict]:
|
||||||
|
|||||||
@@ -401,6 +401,30 @@ def stats():
|
|||||||
print(f" {label:>{label_w}} : {count}")
|
print(f" {label:>{label_w}} : {count}")
|
||||||
|
|
||||||
|
|
||||||
|
@cache_app.command
|
||||||
|
def confidence(
|
||||||
|
source: Annotated[
|
||||||
|
str, cyclopts.Parameter(help="Source to update (e.g. spotify, netease).")
|
||||||
|
],
|
||||||
|
score: Annotated[float, cyclopts.Parameter(help="Confidence score (0-100).")],
|
||||||
|
):
|
||||||
|
"""Set confidence score for the current track's cache entry from a specific source."""
|
||||||
|
if not 0 <= score <= 100:
|
||||||
|
logger.error("Score must be between 0 and 100.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
track = get_current_track(_player)
|
||||||
|
if not track:
|
||||||
|
logger.error("No active playing track found.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
updated = manager.cache.update_confidence(track, score, source=source)
|
||||||
|
if updated:
|
||||||
|
print(f"Updated [{source}] confidence to {score:.0f}.")
|
||||||
|
else:
|
||||||
|
print(f"No cache entry found for [{source}].")
|
||||||
|
|
||||||
|
|
||||||
@cache_app.command
|
@cache_app.command
|
||||||
def insert(
|
def insert(
|
||||||
*,
|
*,
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "lrx-cli"
|
name = "lrx-cli"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
description = "Fetch line-synced lyrics for your music player."
|
description = "Fetch line-synced lyrics for your music player."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
|
|||||||
@@ -305,6 +305,35 @@ def test_search_by_meta_fuzzy_rules_and_duration_sorting(cache_db: CacheEngine)
|
|||||||
assert sources[-1] == "unknown-len"
|
assert sources[-1] == "unknown-len"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_confidence_targets_specific_source(cache_db: CacheEngine) -> None:
|
||||||
|
track = _track(artist="A", title="T", album="AL")
|
||||||
|
cache_db.set(track, "s1", _result(CacheStatus.SUCCESS_SYNCED, "x", "s1"))
|
||||||
|
cache_db.set(track, "s2", _result(CacheStatus.SUCCESS_UNSYNCED, "y", "s2"))
|
||||||
|
|
||||||
|
updated = cache_db.update_confidence(track, 75.0, "s1")
|
||||||
|
|
||||||
|
assert updated == 1
|
||||||
|
rows = {r["source"]: r for r in cache_db.query_track(track)}
|
||||||
|
assert rows["s1"]["confidence"] == 75.0
|
||||||
|
assert rows["s2"]["confidence"] == 100.0 # unchanged
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_confidence_returns_zero_for_missing_source(
|
||||||
|
cache_db: CacheEngine,
|
||||||
|
) -> None:
|
||||||
|
track = _track(artist="A", title="T", album="AL")
|
||||||
|
cache_db.set(track, "s1", _result(CacheStatus.SUCCESS_SYNCED, "x", "s1"))
|
||||||
|
|
||||||
|
assert cache_db.update_confidence(track, 50.0, "nonexistent") == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_confidence_returns_zero_for_empty_track(
|
||||||
|
cache_db: CacheEngine,
|
||||||
|
) -> None:
|
||||||
|
empty = _track(artist=None, title=None, album=None, length=None)
|
||||||
|
assert cache_db.update_confidence(empty, 50.0, "s1") == 0
|
||||||
|
|
||||||
|
|
||||||
def test_query_track_and_stats_return_expected_aggregates(
|
def test_query_track_and_stats_return_expected_aggregates(
|
||||||
cache_db: CacheEngine,
|
cache_db: CacheEngine,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user