A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz

fix: improve artist matching logic in scrobble functions to prevent mismatches

+62 -49
+46 -32
crates/scrobbler/src/scrobbler.rs
··· 378 378 .await?; 379 379 380 380 if let Some(track) = result.tracks.items.first() { 381 - tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Spotify (track)"); 382 - scrobble.album = Some(track.album.name.clone()); 383 - let mut track = track.clone(); 381 + let artists = track 382 + .artists 383 + .iter() 384 + .map(|a| a.name.to_lowercase().clone()) 385 + .collect::<Vec<_>>() 386 + .join(", ") 387 + .to_lowercase(); 384 388 385 - if let Some(album) = spotify_client.get_album(&track.album.id).await? { 386 - track.album = album; 387 - } 389 + // check if artists don't contain the scrobble artist (to avoid wrong matches) 390 + if !artists.contains(&scrobble.artist.to_lowercase()) { 391 + tracing::warn!(artist = %artist, track = ?track, "Artist mismatch, skipping"); 392 + return Ok(()); 393 + } else { 394 + tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Spotify (track)"); 395 + scrobble.album = Some(track.album.name.clone()); 396 + let mut track = track.clone(); 388 397 389 - if let Some(artist) = spotify_client 390 - .get_artist(&track.album.artists[0].id) 391 - .await? 392 - { 393 - track.album.artists[0] = artist; 394 - } 398 + if let Some(album) = spotify_client.get_album(&track.album.id).await? { 399 + track.album = album; 400 + } 395 401 396 - rocksky::scrobble(cache, &did, track.into(), scrobble.timestamp).await?; 397 - tokio::time::sleep(std::time::Duration::from_secs(1)).await; 398 - return Ok(()); 402 + if let Some(artist) = spotify_client 403 + .get_artist(&track.album.artists[0].id) 404 + .await? 405 + { 406 + track.album.artists[0] = artist; 407 + } 408 + 409 + rocksky::scrobble(cache, &did, track.into(), scrobble.timestamp).await?; 410 + tokio::time::sleep(std::time::Duration::from_secs(1)).await; 411 + return Ok(()); 412 + } 399 413 } 400 414 401 415 let query = format!( ··· 616 630 let artists = track 617 631 .artists 618 632 .iter() 619 - .map(|a| a.name.clone()) 633 + .map(|a| a.name.to_lowercase().clone()) 620 634 .collect::<Vec<_>>() 621 635 .join(", ") 622 636 .to_lowercase(); ··· 625 639 if !artists.contains(&scrobble.artist.to_lowercase()) { 626 640 tracing::warn!(artist = %artist, track = ?track, "Artist mismatch, skipping"); 627 641 return Ok(()); 628 - } 642 + } else { 643 + tracing::info!("Spotify (track)"); 644 + scrobble.album = Some(track.album.name.clone()); 645 + let mut track = track.clone(); 629 646 630 - tracing::info!("Spotify (track)"); 631 - scrobble.album = Some(track.album.name.clone()); 632 - let mut track = track.clone(); 647 + if let Some(album) = spotify_client.get_album(&track.album.id).await? { 648 + track.album = album; 649 + } 633 650 634 - if let Some(album) = spotify_client.get_album(&track.album.id).await? { 635 - track.album = album; 636 - } 651 + if let Some(artist) = spotify_client 652 + .get_artist(&track.album.artists[0].id) 653 + .await? 654 + { 655 + track.album.artists[0] = artist; 656 + } 637 657 638 - if let Some(artist) = spotify_client 639 - .get_artist(&track.album.artists[0].id) 640 - .await? 641 - { 642 - track.album.artists[0] = artist; 658 + rocksky::scrobble(cache, &did, track.into(), scrobble.timestamp).await?; 659 + tokio::time::sleep(std::time::Duration::from_secs(1)).await; 660 + return Ok(()); 643 661 } 644 - 645 - rocksky::scrobble(cache, &did, track.into(), scrobble.timestamp).await?; 646 - tokio::time::sleep(std::time::Duration::from_secs(1)).await; 647 - return Ok(()); 648 662 } 649 663 650 664 let query = format!(
+16 -17
crates/webscrobbler/src/scrobbler.rs
··· 131 131 let artists = track 132 132 .artists 133 133 .iter() 134 - .map(|a| a.name.clone()) 134 + .map(|a| a.name.to_lowercase().clone()) 135 135 .collect::<Vec<_>>() 136 136 .join(", ") 137 137 .to_lowercase(); ··· 139 139 // check if artists don't contain the scrobble artist (to avoid wrong matches) 140 140 if !artists.contains(&scrobble.data.song.parsed.artist.trim().to_lowercase()) { 141 141 tracing::warn!(artist = %artist, track = ?track, "Artist mismatch, skipping"); 142 - return Ok(()); 143 - } 142 + } else { 143 + tracing::info!("Spotify (track)"); 144 + let mut track = track.clone(); 144 145 145 - tracing::info!("Spotify (track)"); 146 - let mut track = track.clone(); 146 + if let Some(album) = spotify_client.get_album(&track.album.id).await? { 147 + track.album = album; 148 + } 147 149 148 - if let Some(album) = spotify_client.get_album(&track.album.id).await? { 149 - track.album = album; 150 - } 150 + if let Some(artist) = spotify_client 151 + .get_artist(&track.album.artists[0].id) 152 + .await? 153 + { 154 + track.album.artists[0] = artist; 155 + } 151 156 152 - if let Some(artist) = spotify_client 153 - .get_artist(&track.album.artists[0].id) 154 - .await? 155 - { 156 - track.album.artists[0] = artist; 157 + rocksky::scrobble(cache, &did, track.into(), scrobble.time).await?; 158 + tokio::time::sleep(std::time::Duration::from_secs(1)).await; 159 + return Ok(()); 157 160 } 158 - 159 - rocksky::scrobble(cache, &did, track.into(), scrobble.time).await?; 160 - tokio::time::sleep(std::time::Duration::from_secs(1)).await; 161 - return Ok(()); 162 161 } 163 162 164 163 let query = format!(