Update to artist credit handling

This commit is contained in:
Connor Johnstone
2026-03-18 14:34:58 -04:00
parent a268ec4e56
commit 8b16859526
10 changed files with 181 additions and 60 deletions

View File

@@ -162,14 +162,38 @@ async fn add_album(
if body.artist.is_none() && body.album.is_none() && body.mbid.is_none() {
return Err(ApiError::BadRequest("provide artist+album or mbid".into()));
}
let summary = shanty_watch::add_album(
// Try adding with the given MBID first. If it fails (e.g., the MBID is a release-group,
// not a release), resolve it to an actual release MBID and retry.
let mut mbid = body.mbid.clone();
let result = shanty_watch::add_album(
state.db.conn(),
body.artist.as_deref(),
body.album.as_deref(),
body.mbid.as_deref(),
mbid.as_deref(),
&state.mb_client,
)
.await?;
.await;
let summary = match result {
Ok(s) => s,
Err(_) if mbid.is_some() => {
// MBID might be a release-group — resolve to the first release
let rg_mbid = mbid.as_deref().unwrap();
let release_mbid = resolve_release_from_group(&state, rg_mbid).await?;
mbid = Some(release_mbid);
shanty_watch::add_album(
state.db.conn(),
body.artist.as_deref(),
body.album.as_deref(),
mbid.as_deref(),
&state.mb_client,
)
.await?
}
Err(e) => return Err(e.into()),
};
Ok(HttpResponse::Ok().json(serde_json::json!({
"tracks_added": summary.tracks_added,
"tracks_already_owned": summary.tracks_already_owned,

View File

@@ -148,6 +148,7 @@ async fn get_cached_album_tracks(
rg_id: &str,
first_release_id: Option<&str>,
ttl_seconds: i64,
extend_ttl: bool,
) -> Result<CachedAlbumTracks, ApiError> {
let cache_key = format!("artist_rg_tracks:{rg_id}");
@@ -156,6 +157,10 @@ async fn get_cached_album_tracks(
.map_err(|e| ApiError::Internal(e.to_string()))?
{
if let Ok(cached) = serde_json::from_str::<CachedAlbumTracks>(&json) {
// Extend TTL if artist is now watched (upgrades 7-day browse cache to permanent)
if extend_ttl {
let _ = queries::cache::set(state.db.conn(), &cache_key, "musicbrainz", &json, ttl_seconds).await;
}
return Ok(cached);
}
}
@@ -353,13 +358,15 @@ async fn get_artist_full(
// If artist has any watched items, cache permanently (10 years);
// otherwise cache for 7 days (just browsing)
let cache_ttl = if artist_wanted.is_empty() { 7 * 86400 } else { 10 * 365 * 86400 };
let is_watched = !artist_wanted.is_empty();
let cache_ttl = if is_watched { 10 * 365 * 86400 } else { 7 * 86400 };
let cached = match get_cached_album_tracks(
&state,
&rg.id,
rg.first_release_id.as_deref(),
cache_ttl,
is_watched,
).await {
Ok(c) => c,
Err(e) => {

View File

@@ -87,17 +87,6 @@ async fn trigger_process(
let tid = task_id.clone();
tokio::spawn(async move {
// Count total pending for progress reporting
let total = shanty_db::queries::downloads::list(
state.db.conn(),
Some(shanty_db::entities::download_queue::DownloadStatus::Pending),
)
.await
.map(|v| v.len() as u64)
.unwrap_or(0);
state.tasks.update_progress(&tid, 0, total, "Starting downloads...");
let cookies = state.config.download.cookies_path.clone();
let format: shanty_dl::AudioFormat = state.config.download.format.parse().unwrap_or(shanty_dl::AudioFormat::Opus);
let source: shanty_dl::SearchSource = state.config.download.search_source.parse().unwrap_or(shanty_dl::SearchSource::YouTubeMusic);
@@ -108,8 +97,19 @@ async fn trigger_process(
format,
cookies_path: cookies,
};
match shanty_dl::run_queue(state.db.conn(), &backend, &backend_config, false).await {
Ok(stats) => state.tasks.complete(&tid, format!("{stats}")),
let task_state = state.clone();
let progress_tid = tid.clone();
let on_progress: shanty_dl::ProgressFn = Box::new(move |current, total, msg| {
task_state.tasks.update_progress(&progress_tid, current, total, msg);
});
match shanty_dl::run_queue_with_progress(state.db.conn(), &backend, &backend_config, false, Some(on_progress)).await {
Ok(stats) => {
// Invalidate cached artist totals so library/detail pages show fresh data
let _ = shanty_db::queries::cache::purge_prefix(state.db.conn(), "artist_totals:").await;
state.tasks.complete(&tid, format!("{stats}"));
}
Err(e) => state.tasks.fail(&tid, e.to_string()),
}
});

View File

@@ -21,7 +21,7 @@ pub struct AlbumTrackSearchParams {
limit: u32,
}
fn default_limit() -> u32 { 10 }
fn default_limit() -> u32 { 25 }
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(web::resource("/search/artist").route(web::get().to(search_artist)))

View File

@@ -126,6 +126,8 @@ async fn trigger_organize(
let promoted = queries::wanted::promote_downloaded_to_owned(state.db.conn())
.await
.unwrap_or(0);
// Invalidate cached artist totals so library/detail pages show fresh data
let _ = queries::cache::purge_prefix(state.db.conn(), "artist_totals:").await;
let msg = if promoted > 0 {
format!("{stats}{promoted} items marked as owned")
} else {