refactor(v2): inline lyrics delegates and delete legacy lyrics commands
Inlines the bodies of `lyrics_get` and `lyrics_get_cache_stats` from crate::lyrics::commands::* directly into commands_v2/legacy_compat.rs, mapping the legacy String error type to RuntimeError::Internal at the return boundary for v2_lyrics_get. Renames LyricsCacheStats to V2LyricsCacheStats, hosted next to the inlined v2_lyrics_get_cache_stats. Deletes lyrics/commands.rs entirely (also drops the dead lyrics_clear_cache helper which had no callers; v2_lyrics_clear_cache in commands_v2/library.rs already inlines its own logic). The v2 functions retain their existing names, parameter shapes, and Tauri::State signatures; only the body changes. No frontend invoke changes required. Step 6 of the legacy cleanup plan, group A (lyrics). cc
This commit is contained in:
3 files changed
+138
-166
No files matched your search
@@ -324,9 +324,123 @@ pub async fn v2_lyrics_get(
|
|||||||
durationSecs: Option<u64>,
|
durationSecs: Option<u64>,
|
||||||
state: State<'_, LyricsState>,
|
state: State<'_, LyricsState>,
|
||||||
) -> Result<Option<crate::lyrics::LyricsPayload>, RuntimeError> {
|
) -> Result<Option<crate::lyrics::LyricsPayload>, RuntimeError> {
|
||||||
crate::lyrics::commands::lyrics_get(trackId, title, artist, album, durationSecs, state)
|
use crate::lyrics::providers::{fetch_lrclib, fetch_lyrics_ovh};
|
||||||
.await
|
use crate::lyrics::{build_cache_key, LyricsPayload};
|
||||||
.map_err(RuntimeError::Internal)
|
|
||||||
|
let track_id = trackId;
|
||||||
|
let duration_secs = durationSecs;
|
||||||
|
|
||||||
|
let title_trimmed = title.trim();
|
||||||
|
let artist_trimmed = artist.trim();
|
||||||
|
|
||||||
|
if title_trimmed.is_empty() || artist_trimmed.is_empty() {
|
||||||
|
return Err(RuntimeError::Internal(
|
||||||
|
"Lyrics lookup requires title and artist".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cache_key = build_cache_key(title_trimmed, artist_trimmed, duration_secs);
|
||||||
|
|
||||||
|
// Try cache by track_id first, then by key.
|
||||||
|
// If cached entry has plain but no synced lyrics, treat as miss and re-fetch
|
||||||
|
// (search-first strategy is likely to find synced now).
|
||||||
|
{
|
||||||
|
let db_opt__ = state.db.lock().await;
|
||||||
|
let db = db_opt__
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| RuntimeError::Internal("No active session - please log in".to_string()))?;
|
||||||
|
|
||||||
|
let cached = if let Some(id) = track_id {
|
||||||
|
db.get_by_track_id(id).ok().flatten()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.or_else(|| db.get_by_cache_key(&cache_key).ok().flatten());
|
||||||
|
|
||||||
|
if let Some(payload) = cached {
|
||||||
|
let has_synced = payload
|
||||||
|
.synced_lrc
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| !s.trim().is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if has_synced {
|
||||||
|
return Ok(Some(payload));
|
||||||
|
}
|
||||||
|
// plain-only cache: fall through to re-fetch for synced
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider chain: LRCLIB (with 1 retry on network error) -> lyrics.ovh
|
||||||
|
let lrclib_data = match fetch_lrclib(title_trimmed, artist_trimmed, duration_secs).await {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(e) => {
|
||||||
|
// Network error — retry once
|
||||||
|
eprintln!("[Lyrics] LRCLIB attempt 1 failed: {}, retrying…", e);
|
||||||
|
match fetch_lrclib(title_trimmed, artist_trimmed, duration_secs).await {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(e2) => {
|
||||||
|
eprintln!(
|
||||||
|
"[Lyrics] LRCLIB attempt 2 failed: {}, falling back to lyrics.ovh",
|
||||||
|
e2
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(data) = lrclib_data {
|
||||||
|
let payload = LyricsPayload {
|
||||||
|
track_id,
|
||||||
|
title: title_trimmed.to_string(),
|
||||||
|
artist: artist_trimmed.to_string(),
|
||||||
|
album: album.clone(),
|
||||||
|
duration_secs,
|
||||||
|
plain: data.plain,
|
||||||
|
synced_lrc: data.synced_lrc,
|
||||||
|
provider: data.provider,
|
||||||
|
cached: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let db_opt__ = state.db.lock().await;
|
||||||
|
let db = db_opt__
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| RuntimeError::Internal("No active session - please log in".to_string()))?;
|
||||||
|
db.upsert(&cache_key, &payload)
|
||||||
|
.map_err(RuntimeError::Internal)?;
|
||||||
|
return Ok(Some(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(data) = fetch_lyrics_ovh(title_trimmed, artist_trimmed).await {
|
||||||
|
let payload = LyricsPayload {
|
||||||
|
track_id,
|
||||||
|
title: title_trimmed.to_string(),
|
||||||
|
artist: artist_trimmed.to_string(),
|
||||||
|
album,
|
||||||
|
duration_secs,
|
||||||
|
plain: data.plain,
|
||||||
|
synced_lrc: data.synced_lrc,
|
||||||
|
provider: data.provider,
|
||||||
|
cached: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let db_opt__ = state.db.lock().await;
|
||||||
|
let db = db_opt__
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| RuntimeError::Internal("No active session - please log in".to_string()))?;
|
||||||
|
db.upsert(&cache_key, &payload)
|
||||||
|
.map_err(RuntimeError::Internal)?;
|
||||||
|
return Ok(Some(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct V2LyricsCacheStats {
|
||||||
|
pub entries: u64,
|
||||||
|
pub size_bytes: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1424,8 +1538,27 @@ pub fn v2_get_download_settings(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn v2_lyrics_get_cache_stats(
|
pub async fn v2_lyrics_get_cache_stats(
|
||||||
state: State<'_, crate::lyrics::LyricsState>,
|
state: State<'_, crate::lyrics::LyricsState>,
|
||||||
) -> Result<crate::lyrics::commands::LyricsCacheStats, String> {
|
) -> Result<V2LyricsCacheStats, String> {
|
||||||
crate::lyrics::commands::lyrics_get_cache_stats(state).await
|
let entries = {
|
||||||
|
let db_opt__ = state.db.lock().await;
|
||||||
|
let db = db_opt__
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("No active session - please log in")?;
|
||||||
|
db.count_entries()?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Approximate on-disk usage as the SQLite DB file size.
|
||||||
|
let db_path = dirs::cache_dir()
|
||||||
|
.ok_or("Could not determine cache directory")?
|
||||||
|
.join("qbz")
|
||||||
|
.join("lyrics")
|
||||||
|
.join("lyrics.db");
|
||||||
|
let size_bytes = std::fs::metadata(&db_path).map(|m| m.len()).unwrap_or(0);
|
||||||
|
|
||||||
|
Ok(V2LyricsCacheStats {
|
||||||
|
entries,
|
||||||
|
size_bytes,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
//! Tauri commands for lyrics
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use tauri::State;
|
|
||||||
|
|
||||||
use super::providers::{fetch_lrclib, fetch_lyrics_ovh};
|
|
||||||
use super::{build_cache_key, LyricsPayload, LyricsState};
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn lyrics_get(
|
|
||||||
track_id: Option<u64>,
|
|
||||||
title: String,
|
|
||||||
artist: String,
|
|
||||||
album: Option<String>,
|
|
||||||
duration_secs: Option<u64>,
|
|
||||||
state: State<'_, LyricsState>,
|
|
||||||
) -> Result<Option<LyricsPayload>, String> {
|
|
||||||
let title_trimmed = title.trim();
|
|
||||||
let artist_trimmed = artist.trim();
|
|
||||||
|
|
||||||
if title_trimmed.is_empty() || artist_trimmed.is_empty() {
|
|
||||||
return Err("Lyrics lookup requires title and artist".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let cache_key = build_cache_key(title_trimmed, artist_trimmed, duration_secs);
|
|
||||||
|
|
||||||
// Try cache by track_id first, then by key.
|
|
||||||
// If cached entry has plain but no synced lyrics, treat as miss and re-fetch
|
|
||||||
// (search-first strategy is likely to find synced now).
|
|
||||||
{
|
|
||||||
let db_opt__ = state.db.lock().await;
|
|
||||||
let db = db_opt__
|
|
||||||
.as_ref()
|
|
||||||
.ok_or("No active session - please log in")?;
|
|
||||||
|
|
||||||
let cached = if let Some(id) = track_id {
|
|
||||||
db.get_by_track_id(id).ok().flatten()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
.or_else(|| db.get_by_cache_key(&cache_key).ok().flatten());
|
|
||||||
|
|
||||||
if let Some(payload) = cached {
|
|
||||||
let has_synced = payload
|
|
||||||
.synced_lrc
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| !s.trim().is_empty())
|
|
||||||
.unwrap_or(false);
|
|
||||||
if has_synced {
|
|
||||||
return Ok(Some(payload));
|
|
||||||
}
|
|
||||||
// plain-only cache: fall through to re-fetch for synced
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provider chain: LRCLIB (with 1 retry on network error) -> lyrics.ovh
|
|
||||||
let lrclib_data = match fetch_lrclib(title_trimmed, artist_trimmed, duration_secs).await {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(e) => {
|
|
||||||
// Network error — retry once
|
|
||||||
eprintln!("[Lyrics] LRCLIB attempt 1 failed: {}, retrying…", e);
|
|
||||||
match fetch_lrclib(title_trimmed, artist_trimmed, duration_secs).await {
|
|
||||||
Ok(data) => data,
|
|
||||||
Err(e2) => {
|
|
||||||
eprintln!(
|
|
||||||
"[Lyrics] LRCLIB attempt 2 failed: {}, falling back to lyrics.ovh",
|
|
||||||
e2
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(data) = lrclib_data {
|
|
||||||
let payload = LyricsPayload {
|
|
||||||
track_id,
|
|
||||||
title: title_trimmed.to_string(),
|
|
||||||
artist: artist_trimmed.to_string(),
|
|
||||||
album: album.clone(),
|
|
||||||
duration_secs,
|
|
||||||
plain: data.plain,
|
|
||||||
synced_lrc: data.synced_lrc,
|
|
||||||
provider: data.provider,
|
|
||||||
cached: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let db_opt__ = state.db.lock().await;
|
|
||||||
let db = db_opt__
|
|
||||||
.as_ref()
|
|
||||||
.ok_or("No active session - please log in")?;
|
|
||||||
db.upsert(&cache_key, &payload)?;
|
|
||||||
return Ok(Some(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(data) = fetch_lyrics_ovh(title_trimmed, artist_trimmed).await {
|
|
||||||
let payload = LyricsPayload {
|
|
||||||
track_id,
|
|
||||||
title: title_trimmed.to_string(),
|
|
||||||
artist: artist_trimmed.to_string(),
|
|
||||||
album,
|
|
||||||
duration_secs,
|
|
||||||
plain: data.plain,
|
|
||||||
synced_lrc: data.synced_lrc,
|
|
||||||
provider: data.provider,
|
|
||||||
cached: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let db_opt__ = state.db.lock().await;
|
|
||||||
let db = db_opt__
|
|
||||||
.as_ref()
|
|
||||||
.ok_or("No active session - please log in")?;
|
|
||||||
db.upsert(&cache_key, &payload)?;
|
|
||||||
return Ok(Some(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn lyrics_clear_cache(state: State<'_, LyricsState>) -> Result<(), String> {
|
|
||||||
let db_opt__ = state.db.lock().await;
|
|
||||||
let db = db_opt__
|
|
||||||
.as_ref()
|
|
||||||
.ok_or("No active session - please log in")?;
|
|
||||||
db.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct LyricsCacheStats {
|
|
||||||
pub entries: u64,
|
|
||||||
pub size_bytes: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn lyrics_get_cache_stats(
|
|
||||||
state: State<'_, LyricsState>,
|
|
||||||
) -> Result<LyricsCacheStats, String> {
|
|
||||||
let entries = {
|
|
||||||
let db_opt__ = state.db.lock().await;
|
|
||||||
let db = db_opt__
|
|
||||||
.as_ref()
|
|
||||||
.ok_or("No active session - please log in")?;
|
|
||||||
db.count_entries()?
|
|
||||||
};
|
|
||||||
|
|
||||||
// Approximate on-disk usage as the SQLite DB file size.
|
|
||||||
let db_path = dirs::cache_dir()
|
|
||||||
.ok_or("Could not determine cache directory")?
|
|
||||||
.join("qbz")
|
|
||||||
.join("lyrics")
|
|
||||||
.join("lyrics.db");
|
|
||||||
let size_bytes = std::fs::metadata(&db_path).map(|m| m.len()).unwrap_or(0);
|
|
||||||
|
|
||||||
Ok(LyricsCacheStats {
|
|
||||||
entries,
|
|
||||||
size_bytes,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
//! Fetches and caches lyrics from public providers.
|
//! Fetches and caches lyrics from public providers.
|
||||||
|
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod commands;
|
|
||||||
pub mod providers;
|
pub mod providers;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
Reference in new issue
Block a user