From 5fa0fcb2c44a6b30c221eeb20a5a836df51382bf Mon Sep 17 00:00:00 2001 From: Tsiry Sandratraina Date: Sun, 21 Sep 2025 09:21:48 +0300 Subject: [PATCH] refactor: enhance error handling and retry logic in submit_listens function --- .../scrobbler/src/listenbrainz/core/submit.rs | 46 ++++++++++++++++++- crates/scrobbler/src/scrobbler.rs | 7 ++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/crates/scrobbler/src/listenbrainz/core/submit.rs b/crates/scrobbler/src/listenbrainz/core/submit.rs index c178f593..49eeab3f 100644 --- a/crates/scrobbler/src/listenbrainz/core/submit.rs +++ b/crates/scrobbler/src/listenbrainz/core/submit.rs @@ -1,5 +1,5 @@ use actix_web::HttpResponse; -use anyhow::Error; +use anyhow::{Context, Error}; use owo_colors::OwoColorize; use serde_json::json; use std::sync::Arc; @@ -24,7 +24,48 @@ pub async fn submit_listens( }, }))); } - match scrobble_listenbrainz(pool, cache, payload, token).await { + + const RETRIES: usize = 5; + for attempt in 1..=RETRIES { + match scrobble_listenbrainz(pool, cache, &payload, token).await.with_context( + || format!("Attempt {}/{}: Error submitting listens", attempt, RETRIES), + ) { + Ok(_) => { + return Ok(HttpResponse::Ok().json(json!({ + "status": "ok", + "payload": { + "submitted_listens": 1, + "ignored_listens": 0 + }, + }))); + } + Err(e) => { + if !e.to_string().contains("error decoding response body") { + println!("Non-retryable error: {}", e.to_string().red()); + println!("{:#?}", payload); + return Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": 4, + "message": format!("Failed to parse listens: {}", e) + }))); + } + println!("Retryable error on attempt {}/{}: {}", attempt, RETRIES, e.to_string().yellow()); + println!("{:#?}", payload); + + if attempt == RETRIES { + return Ok(HttpResponse::BadRequest().json(serde_json::json!({ + "error": 4, + "message": format!("Failed to parse listens after {} attempts: {}", RETRIES, e) + }))); + } + + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + } + } + + unreachable!(); + + /* match scrobble_listenbrainz(pool, cache, payload, token).await { Ok(_) => Ok(HttpResponse::Ok().json(json!({ "status": "ok", "payload": { @@ -40,4 +81,5 @@ pub async fn submit_listens( }))) } } + */ } diff --git a/crates/scrobbler/src/scrobbler.rs b/crates/scrobbler/src/scrobbler.rs index 31d058c2..09501817 100644 --- a/crates/scrobbler/src/scrobbler.rs +++ b/crates/scrobbler/src/scrobbler.rs @@ -432,7 +432,7 @@ pub async fn scrobble_v1( pub async fn scrobble_listenbrainz( pool: &Pool, cache: &Cache, - req: SubmitListensRequest, + req: &SubmitListensRequest, token: &str, ) -> Result<(), Error> { println!("Listenbrainz\n{:#?}", req); @@ -656,6 +656,10 @@ pub async fn scrobble_listenbrainz( return Ok(()); } + // Temporary disable Musicbrainz search to reduce rate limiting issues + // and because it often returns wrong results + // we can re-enable it later with a retry mechanism + /* let query = format!( r#"recording:"{}" AND artist:"{}""#, scrobble.track, scrobble.artist @@ -670,6 +674,7 @@ pub async fn scrobble_listenbrainz( tokio::time::sleep(std::time::Duration::from_secs(1)).await; return Ok(()); } + */ println!( "{} {} - {}, skipping", -- 2.51.2