A decentralized music tracking and discovery platform built on AT Protocol 🎵

Refactor logging to use `tracing` instead of `println` and `eprintln`

- Replaced all instances of `println!` and `eprintln!` with `tracing::info!`, `tracing::warn!`, and `tracing::error!` for better structured logging.
- Updated log messages to include relevant context and structured fields for improved traceability.
- Ensured consistent logging practices across the `jetstream`, `scrobbler`, and `webscrobbler` crates.

+260 -426
+44 -42
crates/analytics/src/subscriber/mod.rs
··· 42 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 43 match serde_json::from_str::<ScrobblePayload>(&data) { 44 Ok(payload) => match save_scrobble(conn.clone(), payload.clone()).await { 45 - Ok(_) => println!( 46 - "Scrobble saved successfully for {}", 47 - payload.scrobble.uri.cyan() 48 ), 49 - Err(e) => eprintln!("Error saving scrobble: {}", e), 50 }, 51 Err(e) => { 52 - eprintln!("Error parsing payload: {}", e); 53 - println!("{}", data); 54 } 55 } 56 } ··· 77 match serde_json::from_str::<NewTrackPayload>(&data) { 78 Ok(payload) => match save_track(conn.clone(), payload.clone()).await { 79 Ok(_) => { 80 - println!("Song saved successfully for {}", payload.track.title.cyan()) 81 } 82 - Err(e) => eprintln!("Error saving song: {}", e), 83 }, 84 Err(e) => { 85 - eprintln!("Error parsing payload: {}", e); 86 - println!("{}", data); 87 } 88 } 89 } ··· 109 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 110 match serde_json::from_str::<LikePayload>(&data) { 111 Ok(payload) => match like(conn.clone(), payload.clone()).await { 112 - Ok(_) => println!( 113 - "Like saved successfully for {}", 114 - payload.track_id.xata_id.cyan() 115 ), 116 - Err(e) => eprintln!("Error saving like: {}", e), 117 }, 118 Err(e) => { 119 - eprintln!("Error parsing payload: {}", e); 120 - println!("{}", data); 121 } 122 } 123 } ··· 143 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 144 match serde_json::from_str::<UnlikePayload>(&data) { 145 Ok(payload) => match unlike(conn.clone(), payload.clone()).await { 146 - Ok(_) => println!( 147 - "Unlike saved successfully for {}", 148 - payload.track_id.xata_id.cyan() 149 ), 150 - Err(e) => eprintln!("Error saving unlike: {}", e), 151 }, 152 Err(e) => { 153 - eprintln!("Error parsing payload: {}", e); 154 - println!("{}", data); 155 } 156 } 157 } ··· 177 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 178 match serde_json::from_str::<UserPayload>(&data) { 179 Ok(payload) => match save_user(conn.clone(), payload.clone()).await { 180 - Ok(_) => println!( 181 - "User saved successfully for {}{}", 182 - "@".cyan(), 183 - payload.handle.cyan() 184 ), 185 - Err(e) => eprintln!("Error saving user: {}", e), 186 }, 187 Err(e) => { 188 - eprintln!("Error parsing payload: {}", e); 189 - println!("{}", data); 190 } 191 } 192 } ··· 253 Ok(_) => (), 254 Err(e) => { 255 if !e.to_string().contains("violates primary key constraint") { 256 - println!("[artists] error: {}", e); 257 return Err(e.into()); 258 } 259 } ··· 308 Ok(_) => (), 309 Err(e) => { 310 if !e.to_string().contains("violates primary key constraint") { 311 - println!("[albums] error: {}", e); 312 return Err(e.into()); 313 } 314 } ··· 371 Ok(_) => (), 372 Err(e) => { 373 if !e.to_string().contains("violates primary key constraint") { 374 - println!("[tracks] error: {}", e); 375 return Err(e.into()); 376 } 377 } ··· 394 Ok(_) => (), 395 Err(e) => { 396 if !e.to_string().contains("violates primary key constraint") { 397 - println!("[album_tracks] error: {}", e); 398 return Err(e.into()); 399 } 400 } ··· 412 Ok(_) => (), 413 Err(e) => { 414 if !e.to_string().contains("violates primary key constraint") { 415 - println!("[artist_tracks] error: {}", e); 416 return Err(e.into()); 417 } 418 } ··· 430 Ok(_) => (), 431 Err(e) => { 432 if !e.to_string().contains("violates primary key constraint") { 433 - println!("[artist_albums] error: {}", e); 434 return Err(e.into()); 435 } 436 } ··· 448 Ok(_) => (), 449 Err(e) => { 450 if !e.to_string().contains("violates primary key constraint") { 451 - println!("[user_albums] error: {}", e); 452 return Err(e.into()); 453 } 454 } ··· 466 Ok(_) => (), 467 Err(e) => { 468 if !e.to_string().contains("violates primary key constraint") { 469 - println!("[user_artists] error: {}", e); 470 return Err(e.into()); 471 } 472 } ··· 484 Ok(_) => (), 485 Err(e) => { 486 if !e.to_string().contains("violates primary key constraint") { 487 - println!("[user_tracks] error: {}", e); 488 return Err(e.into()); 489 } 490 } ··· 521 Ok(_) => (), 522 Err(e) => { 523 if !e.to_string().contains("violates primary key constraint") { 524 - println!("[scrobbles] error: {}", e); 525 return Err(e.into()); 526 } 527 } ··· 593 Ok(_) => (), 594 Err(e) => { 595 if !e.to_string().contains("violates primary key constraint") { 596 - println!("[tracks] error: {}", e); 597 return Err(e.into()); 598 } 599 } ··· 616 Ok(_) => (), 617 Err(e) => { 618 if !e.to_string().contains("violates primary key constraint") { 619 - println!("[album_tracks] error: {}", e); 620 return Err(e.into()); 621 } 622 } ··· 634 Ok(_) => (), 635 Err(e) => { 636 if !e.to_string().contains("violates primary key constraint") { 637 - println!("[artist_tracks] error: {}", e); 638 return Err(e.into()); 639 } 640 }
··· 42 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 43 match serde_json::from_str::<ScrobblePayload>(&data) { 44 Ok(payload) => match save_scrobble(conn.clone(), payload.clone()).await { 45 + Ok(_) => tracing::info!( 46 + uri = %payload.scrobble.uri.cyan(), 47 + "Scrobble saved successfully", 48 ), 49 + Err(e) => tracing::error!("Error saving scrobble: {}", e), 50 }, 51 Err(e) => { 52 + tracing::error!("Error parsing payload: {}", e); 53 + tracing::debug!("{}", data); 54 } 55 } 56 } ··· 77 match serde_json::from_str::<NewTrackPayload>(&data) { 78 Ok(payload) => match save_track(conn.clone(), payload.clone()).await { 79 Ok(_) => { 80 + tracing::info!( 81 + title = %payload.track.title.cyan(), 82 + "Track saved successfully", 83 + ) 84 } 85 + Err(e) => tracing::error!("Error saving track: {}", e), 86 }, 87 Err(e) => { 88 + tracing::error!("Error parsing payload: {}", e); 89 + tracing::debug!("{}", data); 90 } 91 } 92 } ··· 112 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 113 match serde_json::from_str::<LikePayload>(&data) { 114 Ok(payload) => match like(conn.clone(), payload.clone()).await { 115 + Ok(_) => tracing::info!( 116 + track_id = %payload.track_id.xata_id.cyan(), 117 + "Like saved successfully", 118 ), 119 + Err(e) => tracing::error!("Error saving like: {}", e), 120 }, 121 Err(e) => { 122 + tracing::error!("Error parsing payload: {}", e); 123 + tracing::debug!("{}", data); 124 } 125 } 126 } ··· 146 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 147 match serde_json::from_str::<UnlikePayload>(&data) { 148 Ok(payload) => match unlike(conn.clone(), payload.clone()).await { 149 + Ok(_) => tracing::info!( 150 + track_id = %payload.track_id.xata_id.cyan(), 151 + "Unlike saved successfully", 152 ), 153 + Err(e) => tracing::error!("Error saving unlike: {}", e), 154 }, 155 Err(e) => { 156 + tracing::error!("Error parsing payload: {}", e); 157 + tracing::debug!("{}", data); 158 } 159 } 160 } ··· 180 let data = String::from_utf8(msg.payload.to_vec()).unwrap(); 181 match serde_json::from_str::<UserPayload>(&data) { 182 Ok(payload) => match save_user(conn.clone(), payload.clone()).await { 183 + Ok(_) => tracing::info!( 184 + handle = %payload.handle.cyan(), 185 + "User saved successfully", 186 ), 187 + Err(e) => tracing::error!("Error saving user: {}", e), 188 }, 189 Err(e) => { 190 + tracing::error!("Error parsing payload: {}", e); 191 + tracing::debug!("{}", data); 192 } 193 } 194 } ··· 255 Ok(_) => (), 256 Err(e) => { 257 if !e.to_string().contains("violates primary key constraint") { 258 + tracing::error!("[artists] error: {}", e); 259 return Err(e.into()); 260 } 261 } ··· 310 Ok(_) => (), 311 Err(e) => { 312 if !e.to_string().contains("violates primary key constraint") { 313 + tracing::error!("[albums] error: {}", e); 314 return Err(e.into()); 315 } 316 } ··· 373 Ok(_) => (), 374 Err(e) => { 375 if !e.to_string().contains("violates primary key constraint") { 376 + tracing::error!("[tracks] error: {}", e); 377 return Err(e.into()); 378 } 379 } ··· 396 Ok(_) => (), 397 Err(e) => { 398 if !e.to_string().contains("violates primary key constraint") { 399 + tracing::error!("[album_tracks] error: {}", e); 400 return Err(e.into()); 401 } 402 } ··· 414 Ok(_) => (), 415 Err(e) => { 416 if !e.to_string().contains("violates primary key constraint") { 417 + tracing::error!("[artist_tracks] error: {}", e); 418 return Err(e.into()); 419 } 420 } ··· 432 Ok(_) => (), 433 Err(e) => { 434 if !e.to_string().contains("violates primary key constraint") { 435 + tracing::error!("[artist_albums] error: {}", e); 436 return Err(e.into()); 437 } 438 } ··· 450 Ok(_) => (), 451 Err(e) => { 452 if !e.to_string().contains("violates primary key constraint") { 453 + tracing::error!("[user_albums] error: {}", e); 454 return Err(e.into()); 455 } 456 } ··· 468 Ok(_) => (), 469 Err(e) => { 470 if !e.to_string().contains("violates primary key constraint") { 471 + tracing::error!("[user_artists] error: {}", e); 472 return Err(e.into()); 473 } 474 } ··· 486 Ok(_) => (), 487 Err(e) => { 488 if !e.to_string().contains("violates primary key constraint") { 489 + tracing::error!("[user_tracks] error: {}", e); 490 return Err(e.into()); 491 } 492 } ··· 523 Ok(_) => (), 524 Err(e) => { 525 if !e.to_string().contains("violates primary key constraint") { 526 + tracing::error!("[scrobbles] error: {}", e); 527 return Err(e.into()); 528 } 529 } ··· 595 Ok(_) => (), 596 Err(e) => { 597 if !e.to_string().contains("violates primary key constraint") { 598 + tracing::error!("[tracks] error: {}", e); 599 return Err(e.into()); 600 } 601 } ··· 618 Ok(_) => (), 619 Err(e) => { 620 if !e.to_string().contains("violates primary key constraint") { 621 + tracing::error!("[album_tracks] error: {}", e); 622 return Err(e.into()); 623 } 624 } ··· 636 Ok(_) => (), 637 Err(e) => { 638 if !e.to_string().contains("violates primary key constraint") { 639 + tracing::error!("[artist_tracks] error: {}", e); 640 return Err(e.into()); 641 } 642 }
+2 -2
crates/dropbox/src/cmd/serve.rs
··· 27 req: HttpRequest, 28 ) -> Result<impl Responder, actix_web::Error> { 29 let method = req.match_info().get("method").unwrap_or("unknown"); 30 - println!("Method: {}", method.bright_green()); 31 32 let conn = data.get_ref().clone(); 33 handle(method, &mut payload, &req, conn) ··· 41 let addr = format!("{}:{}", host, port); 42 43 let url = format!("http://{}", addr); 44 - println!("Listening on {}", url.bright_green()); 45 46 let pool = PgPoolOptions::new() 47 .max_connections(5)
··· 27 req: HttpRequest, 28 ) -> Result<impl Responder, actix_web::Error> { 29 let method = req.match_info().get("method").unwrap_or("unknown"); 30 + tracing::info!(method = %method.bright_green(), "API call"); 31 32 let conn = data.get_ref().clone(); 33 handle(method, &mut payload, &req, conn) ··· 41 let addr = format!("{}:{}", host, port); 42 43 let url = format!("http://{}", addr); 44 + tracing::info!(url = %url.bright_green(), "Listening on"); 45 46 let pool = PgPoolOptions::new() 47 .max_connections(5)
+8
crates/dropbox/src/handlers/files.rs
··· 2 3 use actix_web::{web, HttpRequest, HttpResponse}; 4 use anyhow::Error; 5 use sqlx::{Pool, Postgres}; 6 use tokio_stream::StreamExt; 7 ··· 24 let body = read_payload!(payload); 25 let params = serde_json::from_slice::<GetFilesParams>(&body)?; 26 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 27 28 if refresh_token.is_none() { 29 return Ok(HttpResponse::Unauthorized().finish()); ··· 48 let body = read_payload!(payload); 49 let params = serde_json::from_slice::<GetFilesParams>(&body)?; 50 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 51 52 if refresh_token.is_none() { 53 return Ok(HttpResponse::Unauthorized().finish()); ··· 72 let body = read_payload!(payload); 73 let params = serde_json::from_slice::<GetFilesAtParams>(&body)?; 74 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 75 76 if refresh_token.is_none() { 77 return Ok(HttpResponse::Unauthorized().finish()); ··· 96 let body = read_payload!(payload); 97 let params = serde_json::from_slice::<DownloadFileParams>(&body)?; 98 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 99 100 if refresh_token.is_none() { 101 return Ok(HttpResponse::Unauthorized().finish()); ··· 118 let body = read_payload!(payload); 119 let params = serde_json::from_slice::<DownloadFileParams>(&body)?; 120 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 121 122 if refresh_token.is_none() { 123 return Ok(HttpResponse::Unauthorized().finish()); ··· 142 let body = read_payload!(payload); 143 let params = serde_json::from_slice::<DownloadFileParams>(&body)?; 144 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 145 146 if refresh_token.is_none() { 147 return Ok(HttpResponse::Unauthorized().finish()); ··· 165 ) -> Result<HttpResponse, Error> { 166 let body = read_payload!(payload); 167 let params = serde_json::from_slice::<ScanFolderParams>(&body)?; 168 169 let pool = pool.clone(); 170 thread::spawn(move || {
··· 2 3 use actix_web::{web, HttpRequest, HttpResponse}; 4 use anyhow::Error; 5 + use owo_colors::OwoColorize; 6 use sqlx::{Pool, Postgres}; 7 use tokio_stream::StreamExt; 8 ··· 25 let body = read_payload!(payload); 26 let params = serde_json::from_slice::<GetFilesParams>(&body)?; 27 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 28 + tracing::info!(did = %params.did.bright_green(), "dropbox.getFiles"); 29 30 if refresh_token.is_none() { 31 return Ok(HttpResponse::Unauthorized().finish()); ··· 50 let body = read_payload!(payload); 51 let params = serde_json::from_slice::<GetFilesParams>(&body)?; 52 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 53 + tracing::info!(did = %params.did.bright_green(), "dropbox.createMusicFolder"); 54 55 if refresh_token.is_none() { 56 return Ok(HttpResponse::Unauthorized().finish()); ··· 75 let body = read_payload!(payload); 76 let params = serde_json::from_slice::<GetFilesAtParams>(&body)?; 77 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 78 + tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.getFilesAt"); 79 80 if refresh_token.is_none() { 81 return Ok(HttpResponse::Unauthorized().finish()); ··· 100 let body = read_payload!(payload); 101 let params = serde_json::from_slice::<DownloadFileParams>(&body)?; 102 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 103 + tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.downloadFile"); 104 105 if refresh_token.is_none() { 106 return Ok(HttpResponse::Unauthorized().finish()); ··· 123 let body = read_payload!(payload); 124 let params = serde_json::from_slice::<DownloadFileParams>(&body)?; 125 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 126 + tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.getTemporaryLink"); 127 128 if refresh_token.is_none() { 129 return Ok(HttpResponse::Unauthorized().finish()); ··· 148 let body = read_payload!(payload); 149 let params = serde_json::from_slice::<DownloadFileParams>(&body)?; 150 let refresh_token = find_dropbox_refresh_token(&pool.clone(), &params.did).await?; 151 + tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.getMetadata"); 152 153 if refresh_token.is_none() { 154 return Ok(HttpResponse::Unauthorized().finish()); ··· 172 ) -> Result<HttpResponse, Error> { 173 let body = read_payload!(payload); 174 let params = serde_json::from_slice::<ScanFolderParams>(&body)?; 175 + tracing::info!(did = %params.did.bright_green(), path = %params.path.bright_green(), "dropbox.scanFolder"); 176 177 let pool = pool.clone(); 178 thread::spawn(move || {
+60 -74
crates/dropbox/src/scan.rs
··· 91 .await?; 92 93 if res.status().as_u16() == 400 || res.status().as_u16() == 409 { 94 - println!("Path not found: {}", path.bright_red()); 95 return Ok(()); 96 } 97 98 let entry = res.json::<Entry>().await?; 99 100 if entry.tag.clone().unwrap().as_str() == "folder" { 101 - println!("Scanning folder: {}", path.bright_green()); 102 103 let parent_path = Path::new(&path) 104 .parent() ··· 160 161 let client = Client::new(); 162 163 - println!("Downloading file: {}", path.bright_green()); 164 165 let res = client 166 .post(&format!("{}/files/download", CONTENT_URL)) ··· 176 let mut tmpfile = File::create(&tmppath)?; 177 tmpfile.write_all(&bytes)?; 178 179 - println!( 180 - "Reading file: {}", 181 - &tmppath.clone().display().to_string().bright_green() 182 - ); 183 184 let tagged_file = match Probe::open(&tmppath)?.read() { 185 Ok(tagged_file) => tagged_file, 186 Err(e) => { 187 - println!("Error opening file: {}", e); 188 return Ok(()); 189 } 190 }; ··· 193 let tag = match primary_tag { 194 Some(tag) => tag, 195 None => { 196 - println!("No tag found in file"); 197 return Ok(()); 198 } 199 }; 200 201 let pictures = tag.pictures(); 202 203 - println!( 204 - "Title: {}", 205 - tag.get_string(&lofty::tag::ItemKey::TrackTitle) 206 - .unwrap_or_default() 207 - .bright_green() 208 ); 209 - println!( 210 - "Artist: {}", 211 - tag.get_string(&lofty::tag::ItemKey::TrackArtist) 212 - .unwrap_or_default() 213 - .bright_green() 214 ); 215 - println!( 216 - "Album Artist: {}", 217 - tag.get_string(&lofty::tag::ItemKey::AlbumArtist) 218 - .unwrap_or_default() 219 - .bright_green() 220 ); 221 - println!( 222 - "Album: {}", 223 - tag.get_string(&lofty::tag::ItemKey::AlbumTitle) 224 - .unwrap_or_default() 225 - .bright_green() 226 ); 227 - println!( 228 - "Lyrics: {}", 229 - tag.get_string(&lofty::tag::ItemKey::Lyrics) 230 - .unwrap_or_default() 231 - .bright_green() 232 - ); 233 - println!("Year: {}", tag.year().unwrap_or_default().bright_green()); 234 - println!( 235 - "Track Number: {}", 236 - tag.track().unwrap_or_default().bright_green() 237 - ); 238 - println!( 239 - "Track Total: {}", 240 - tag.track_total().unwrap_or_default().bright_green() 241 ); 242 - println!( 243 - "Release Date: {:?}", 244 - tag.get_string(&lofty::tag::ItemKey::OriginalReleaseDate) 245 - .unwrap_or_default() 246 - .bright_green() 247 ); 248 - println!( 249 - "Recording Date: {:?}", 250 - tag.get_string(&lofty::tag::ItemKey::RecordingDate) 251 - .unwrap_or_default() 252 - .bright_green() 253 ); 254 - println!( 255 - "Copyright Message: {}", 256 - tag.get_string(&lofty::tag::ItemKey::CopyrightMessage) 257 - .unwrap_or_default() 258 - .bright_green() 259 ); 260 - println!("Pictures: {:?}", pictures); 261 262 let title = tag 263 .get_string(&lofty::tag::ItemKey::TrackTitle) ··· 290 291 match track { 292 Some(track) => { 293 - println!("Track exists: {}", title.bright_green()); 294 let parent_path = Path::new(&path) 295 .parent() 296 .map(|p| p.to_string_lossy().to_string()); 297 let status = 298 create_dropbox_path(&pool, &entry, &track, &dropbox_id, parent_path).await; 299 - println!("status: {:?}", status); 300 301 // TODO: publish file metadata to nats 302 } 303 None => { 304 - println!("Creating track: {}", title.bright_green()); 305 let album_art = 306 upload_album_cover(albumart_id.into(), pictures, &access_token).await?; 307 let client = Client::new(); ··· 338 })) 339 .send() 340 .await?; 341 - println!("Track Saved: {} {}", title, response.status()); 342 tokio::time::sleep(std::time::Duration::from_secs(3)).await; 343 344 let track = get_track_by_hash(&pool, &hash).await?; ··· 353 return Ok(()); 354 } 355 356 - println!("Failed to create track: {}", title.bright_green()); 357 } 358 } 359 ··· 413 .send() 414 .await?; 415 416 - println!("Cover uploaded: {}", response.status()); 417 418 Ok(Some(name)) 419 } ··· 433 let meta_opts = MetadataOptions::default(); 434 let format_opts = FormatOptions::default(); 435 436 - let probed = 437 - match symphonia::default::get_probe().format(&hint, media_source, &format_opts, &meta_opts) 438 - { 439 - Ok(probed) => probed, 440 - Err(_) => { 441 - println!("Error probing file"); 442 - return Ok(duration); 443 - } 444 - }; 445 446 if let Some(track) = probed.format.tracks().first() { 447 if let Some(duration) = track.codec_params.n_frames {
··· 91 .await?; 92 93 if res.status().as_u16() == 400 || res.status().as_u16() == 409 { 94 + tracing::error!(path = %path.bright_red(), "Path not found"); 95 return Ok(()); 96 } 97 98 let entry = res.json::<Entry>().await?; 99 100 if entry.tag.clone().unwrap().as_str() == "folder" { 101 + tracing::info!(path = %path.bright_green(), "Scanning folder"); 102 103 let parent_path = Path::new(&path) 104 .parent() ··· 160 161 let client = Client::new(); 162 163 + tracing::info!(path = %path.bright_green(), "Downloading file"); 164 165 let res = client 166 .post(&format!("{}/files/download", CONTENT_URL)) ··· 176 let mut tmpfile = File::create(&tmppath)?; 177 tmpfile.write_all(&bytes)?; 178 179 + tracing::info!(path = %tmppath.clone().display().to_string().bright_green(), "Reading file"); 180 181 let tagged_file = match Probe::open(&tmppath)?.read() { 182 Ok(tagged_file) => tagged_file, 183 Err(e) => { 184 + tracing::error!(path = %tmppath.clone().display().to_string().bright_red(), "Error reading file: {}", e); 185 return Ok(()); 186 } 187 }; ··· 190 let tag = match primary_tag { 191 Some(tag) => tag, 192 None => { 193 + tracing::error!(path = %tmppath.clone().display().to_string().bright_red(), "No tag found in file"); 194 return Ok(()); 195 } 196 }; 197 198 let pictures = tag.pictures(); 199 200 + tracing::info!( 201 + title = %tag 202 + .get_string(&lofty::tag::ItemKey::TrackTitle) 203 + .unwrap_or_default(), 204 ); 205 + tracing::info!( 206 + artist = %tag 207 + .get_string(&lofty::tag::ItemKey::TrackArtist) 208 + .unwrap_or_default(), 209 ); 210 + tracing::info!( 211 + album = %tag 212 + .get_string(&lofty::tag::ItemKey::AlbumTitle) 213 + .unwrap_or_default(), 214 ); 215 + tracing::info!( 216 + album_artist = %tag 217 + .get_string(&lofty::tag::ItemKey::AlbumArtist) 218 + .unwrap_or_default(), 219 ); 220 + tracing::info!( 221 + lyrics = %tag 222 + .get_string(&lofty::tag::ItemKey::Lyrics) 223 + .unwrap_or_default(), 224 ); 225 + tracing::info!(year = %tag.year().unwrap_or_default()); 226 + tracing::info!(track_number = %tag.track().unwrap_or_default()); 227 + tracing::info!(track_total = %tag.track_total().unwrap_or_default()); 228 + tracing::info!( 229 + release_date = %tag 230 + .get_string(&lofty::tag::ItemKey::OriginalReleaseDate) 231 + .unwrap_or_default(), 232 ); 233 + tracing::info!( 234 + recording_date = %tag 235 + .get_string(&lofty::tag::ItemKey::RecordingDate) 236 + .unwrap_or_default(), 237 ); 238 + tracing::info!( 239 + copyright_message = %tag 240 + .get_string(&lofty::tag::ItemKey::CopyrightMessage) 241 + .unwrap_or_default(), 242 ); 243 + tracing::info!(pictures = ?pictures); 244 245 let title = tag 246 .get_string(&lofty::tag::ItemKey::TrackTitle) ··· 273 274 match track { 275 Some(track) => { 276 + tracing::info!(title = %title.bright_green(), "Track exists"); 277 let parent_path = Path::new(&path) 278 .parent() 279 .map(|p| p.to_string_lossy().to_string()); 280 let status = 281 create_dropbox_path(&pool, &entry, &track, &dropbox_id, parent_path).await; 282 + tracing::info!(status = ?status); 283 284 // TODO: publish file metadata to nats 285 } 286 None => { 287 + tracing::info!(title = %title.bright_green(), "Creating track"); 288 let album_art = 289 upload_album_cover(albumart_id.into(), pictures, &access_token).await?; 290 let client = Client::new(); ··· 321 })) 322 .send() 323 .await?; 324 + tracing::info!(title = title, status = %response.status(), "Track saved"); 325 tokio::time::sleep(std::time::Duration::from_secs(3)).await; 326 327 let track = get_track_by_hash(&pool, &hash).await?; ··· 336 return Ok(()); 337 } 338 339 + tracing::error!(title = %title.bright_red(), "Failed to create track"); 340 } 341 } 342 ··· 396 .send() 397 .await?; 398 399 + tracing::info!(status = %response.status(), "Cover uploaded"); 400 401 Ok(Some(name)) 402 } ··· 416 let meta_opts = MetadataOptions::default(); 417 let format_opts = FormatOptions::default(); 418 419 + let probed = match symphonia::default::get_probe().format( 420 + &hint, 421 + media_source, 422 + &format_opts, 423 + &meta_opts, 424 + ) { 425 + Ok(probed) => probed, 426 + Err(e) => { 427 + tracing::error!(path = %path.display().to_string().bright_red(), "Error probing file: {}", e); 428 + return Ok(duration); 429 + } 430 + }; 431 432 if let Some(track) = probed.format.tracks().first() { 433 if let Some(duration) = track.codec_params.n_frames {
+2 -2
crates/googledrive/src/cmd/serve.rs
··· 27 req: HttpRequest, 28 ) -> Result<impl Responder, actix_web::Error> { 29 let method = req.match_info().get("method").unwrap_or("unknown"); 30 - println!("Method: {}", method.bright_green()); 31 32 let conn = data.get_ref().clone(); 33 handle(method, &mut payload, &req, conn) ··· 41 let addr = format!("{}:{}", host, port); 42 43 let url = format!("http://{}", addr); 44 - println!("Listening on {}", url.bright_green()); 45 46 let pool = PgPoolOptions::new() 47 .max_connections(5)
··· 27 req: HttpRequest, 28 ) -> Result<impl Responder, actix_web::Error> { 29 let method = req.match_info().get("method").unwrap_or("unknown"); 30 + tracing::info!(method = %method.bright_green(), "API call"); 31 32 let conn = data.get_ref().clone(); 33 handle(method, &mut payload, &req, conn) ··· 41 let addr = format!("{}:{}", host, port); 42 43 let url = format!("http://{}", addr); 44 + tracing::info!(url = %url.bright_green(), "Listening on"); 45 46 let pool = PgPoolOptions::new() 47 .max_connections(5)
+6 -1
crates/googledrive/src/repo/google_drive_path.rs
··· 1 use sqlx::{Pool, Postgres}; 2 3 use crate::{ ··· 47 .execute(pool) 48 .await?; 49 50 - println!("{:?}", result); 51 52 sqlx::query( 53 r#"
··· 1 + use owo_colors::OwoColorize; 2 use sqlx::{Pool, Postgres}; 3 4 use crate::{ ··· 48 .execute(pool) 49 .await?; 50 51 + tracing::info!( 52 + file_id = %file.id.bright_green(), 53 + rows_affected = %result.rows_affected(), 54 + "Google Drive path created" 55 + ); 56 57 sqlx::query( 58 r#"
+27 -79
crates/googledrive/src/scan.rs
··· 104 let file = res.json::<File>().await?; 105 106 if file.mime_type == "application/vnd.google-apps.folder" { 107 - println!("Scanning folder: {}", file.name.bright_green()); 108 109 create_google_drive_directory( 110 &pool, ··· 172 return Ok(()); 173 } 174 175 - println!("Downloading file: {}", file.name.bright_green()); 176 177 let client = Client::new(); 178 ··· 191 let mut tmpfile = std::fs::File::create(&tmppath)?; 192 tmpfile.write_all(&bytes)?; 193 194 - println!( 195 - "Reading file: {}", 196 - &tmppath.clone().display().to_string().bright_green() 197 - ); 198 199 let tagged_file = match Probe::open(&tmppath)?.read() { 200 Ok(tagged_file) => tagged_file, 201 Err(e) => { 202 - println!("Error opening file: {}", e); 203 return Ok(()); 204 } 205 }; ··· 208 let tag = match primary_tag { 209 Some(tag) => tag, 210 None => { 211 - println!("No tag found in file"); 212 return Ok(()); 213 } 214 }; 215 216 let pictures = tag.pictures(); 217 218 - println!( 219 - "Title: {}", 220 - tag.get_string(&lofty::tag::ItemKey::TrackTitle) 221 - .unwrap_or_default() 222 - .bright_green() 223 - ); 224 - println!( 225 - "Artist: {}", 226 - tag.get_string(&lofty::tag::ItemKey::TrackArtist) 227 - .unwrap_or_default() 228 - .bright_green() 229 - ); 230 - println!( 231 - "Album Artist: {}", 232 - tag.get_string(&lofty::tag::ItemKey::AlbumArtist) 233 - .unwrap_or_default() 234 - .bright_green() 235 - ); 236 - println!( 237 - "Album: {}", 238 - tag.get_string(&lofty::tag::ItemKey::AlbumTitle) 239 - .unwrap_or_default() 240 - .bright_green() 241 - ); 242 - println!( 243 - "Lyrics: {}", 244 - tag.get_string(&lofty::tag::ItemKey::Lyrics) 245 - .unwrap_or_default() 246 - .bright_green() 247 - ); 248 - println!("Year: {}", tag.year().unwrap_or_default().bright_green()); 249 - println!( 250 - "Track Number: {}", 251 - tag.track().unwrap_or_default().bright_green() 252 - ); 253 - println!( 254 - "Track Total: {}", 255 - tag.track_total().unwrap_or_default().bright_green() 256 - ); 257 - println!( 258 - "Release Date: {:?}", 259 - tag.get_string(&lofty::tag::ItemKey::OriginalReleaseDate) 260 - .unwrap_or_default() 261 - .bright_green() 262 - ); 263 - println!( 264 - "Recording Date: {:?}", 265 - tag.get_string(&lofty::tag::ItemKey::RecordingDate) 266 - .unwrap_or_default() 267 - .bright_green() 268 - ); 269 - println!( 270 - "Copyright Message: {}", 271 - tag.get_string(&lofty::tag::ItemKey::CopyrightMessage) 272 - .unwrap_or_default() 273 - .bright_green() 274 - ); 275 - println!("Pictures: {:?}", pictures); 276 277 let title = tag 278 .get_string(&lofty::tag::ItemKey::TrackTitle) ··· 304 305 match track { 306 Some(track) => { 307 - println!("Track exists: {}", title.bright_green()); 308 let parent_drive_id = parent_drive_file_id.as_deref(); 309 - let status = create_google_drive_path( 310 &pool, 311 &file, 312 &track, ··· 315 ) 316 .await?; 317 318 - println!("status: {:?}", status); 319 // TODO: publish file metadata to nats 320 } 321 None => { 322 - println!("Creating track: {}", title.bright_green()); 323 324 let albumart = 325 upload_album_cover(albumart_id.into(), pictures, &access_token).await?; ··· 358 })) 359 .send() 360 .await?; 361 - println!("Track Saved: {} {}", title, response.status()); 362 tokio::time::sleep(std::time::Duration::from_secs(3)).await; 363 364 let track = get_track_by_hash(&pool, &hash).await?; 365 if let Some(track) = track { 366 let parent_drive_id = parent_drive_file_id.as_deref(); 367 - let status = create_google_drive_path( 368 &pool, 369 &file, 370 &track, 371 &google_drive_id, 372 parent_drive_id.unwrap_or(""), 373 ) 374 - .await; 375 - 376 - println!("status: {:?}", status); 377 378 // TODO: publish file metadata to nats 379 ··· 382 return Ok(()); 383 } 384 385 - println!("Failed to create track: {}", title.bright_green()); 386 } 387 } 388 ··· 442 .send() 443 .await?; 444 445 - println!("Cover uploaded: {}", response.status()); 446 447 Ok(Some(name)) 448 } ··· 466 match symphonia::default::get_probe().format(&hint, media_source, &format_opts, &meta_opts) 467 { 468 Ok(probed) => probed, 469 - Err(_) => { 470 - println!("Error probing file"); 471 return Ok(duration); 472 } 473 };
··· 104 let file = res.json::<File>().await?; 105 106 if file.mime_type == "application/vnd.google-apps.folder" { 107 + tracing::info!(folder = %file.name.bright_green(), "Scanning folder"); 108 109 create_google_drive_directory( 110 &pool, ··· 172 return Ok(()); 173 } 174 175 + tracing::info!(file = %file.name.bright_green(), "Downloading file"); 176 177 let client = Client::new(); 178 ··· 191 let mut tmpfile = std::fs::File::create(&tmppath)?; 192 tmpfile.write_all(&bytes)?; 193 194 + tracing::info!(path = %tmppath.display(), "Reading file"); 195 196 let tagged_file = match Probe::open(&tmppath)?.read() { 197 Ok(tagged_file) => tagged_file, 198 Err(e) => { 199 + tracing::warn!(file = %file.name.bright_green(), error = %e, "Failed to open file with lofty"); 200 return Ok(()); 201 } 202 }; ··· 205 let tag = match primary_tag { 206 Some(tag) => tag, 207 None => { 208 + tracing::warn!(file = %file.name.bright_green(), "No tag found in file"); 209 return Ok(()); 210 } 211 }; 212 213 let pictures = tag.pictures(); 214 215 + tracing::info!(title = %tag.get_string(&lofty::tag::ItemKey::TrackTitle).unwrap_or_default(), "Title"); 216 + tracing::info!(artist = %tag.get_string(&lofty::tag::ItemKey::TrackArtist).unwrap_or_default(), "Artist"); 217 + tracing::info!(album_artist = %tag.get_string(&lofty::tag::ItemKey::AlbumArtist).unwrap_or_default(), "Album artist"); 218 + tracing::info!(album = %tag.get_string(&lofty::tag::ItemKey::AlbumTitle).unwrap_or_default(), "Album"); 219 + tracing::info!(lyrics = %tag.get_string(&lofty::tag::ItemKey::Lyrics).unwrap_or_default(), "Lyrics"); 220 + tracing::info!(year = %tag.year().unwrap_or_default(), "Year"); 221 + tracing::info!(track_number = %tag.track().unwrap_or_default(), "Track number"); 222 + tracing::info!(track_total = %tag.track_total().unwrap_or_default(), "Track total"); 223 + tracing::info!(release_date = %tag.get_string(&lofty::tag::ItemKey::OriginalReleaseDate).unwrap_or_default(), "Release date"); 224 + tracing::info!(recording_date = %tag.get_string(&lofty::tag::ItemKey::RecordingDate).unwrap_or_default(), "Recording date"); 225 + tracing::info!(copyright = %tag.get_string(&lofty::tag::ItemKey::CopyrightMessage).unwrap_or_default(), "Copyright message"); 226 + tracing::info!(pictures = %pictures.len(), "Pictures found"); 227 228 let title = tag 229 .get_string(&lofty::tag::ItemKey::TrackTitle) ··· 255 256 match track { 257 Some(track) => { 258 + tracing::info!(title = %title.bright_green(), "Track exists"); 259 let parent_drive_id = parent_drive_file_id.as_deref(); 260 + create_google_drive_path( 261 &pool, 262 &file, 263 &track, ··· 266 ) 267 .await?; 268 269 // TODO: publish file metadata to nats 270 } 271 None => { 272 + tracing::info!(title = %title.bright_green(), "Creating track"); 273 274 let albumart = 275 upload_album_cover(albumart_id.into(), pictures, &access_token).await?; ··· 308 })) 309 .send() 310 .await?; 311 + tracing::info!(status = %response.status(), "Track saved"); 312 tokio::time::sleep(std::time::Duration::from_secs(3)).await; 313 314 let track = get_track_by_hash(&pool, &hash).await?; 315 if let Some(track) = track { 316 let parent_drive_id = parent_drive_file_id.as_deref(); 317 + create_google_drive_path( 318 &pool, 319 &file, 320 &track, 321 &google_drive_id, 322 parent_drive_id.unwrap_or(""), 323 ) 324 + .await?; 325 326 // TODO: publish file metadata to nats 327 ··· 330 return Ok(()); 331 } 332 333 + tracing::warn!(title = %title.bright_green(), "Failed to create track"); 334 } 335 } 336 ··· 390 .send() 391 .await?; 392 393 + tracing::info!(status = %response.status(), "Cover uploaded"); 394 395 Ok(Some(name)) 396 } ··· 414 match symphonia::default::get_probe().format(&hint, media_source, &format_opts, &meta_opts) 415 { 416 Ok(probed) => probed, 417 + Err(e) => { 418 + tracing::warn!(path = %path.display(), error = %e, "Failed to probe media"); 419 return Ok(duration); 420 } 421 };
+35 -71
crates/jetstream/src/repo.rs
··· 16 }, 17 webhook_worker::{push_to_queue, AppState}, 18 xata::{ 19 - album::Album, album_track::AlbumTrack, artist::Artist, artist_album::ArtistAlbum, 20 - artist_track::ArtistTrack, track::Track, user::User, user_album::UserAlbum, 21 - user_artist::UserArtist, user_track::UserTrack, 22 }, 23 }; 24 ··· 56 57 let user_id = save_user(&mut tx, did).await?; 58 59 - println!( 60 - "Saving scrobble: {} ", 61 - format!( 62 - "{} - {} - {}", 63 - scrobble_record.title, scrobble_record.artist, scrobble_record.album 64 - ) 65 - .magenta() 66 - ); 67 68 sqlx::query( 69 r#" ··· 144 { 145 Ok(_) => {} 146 Err(e) => { 147 - eprintln!("Failed to push to webhook queue: {}", e); 148 } 149 } 150 } ··· 188 } 189 } 190 _ => { 191 - println!("Unsupported operation: {}", commit.operation); 192 } 193 } 194 Ok(()) ··· 341 .await?; 342 343 if !albums.is_empty() { 344 - println!("Album already exists: {}", albums[0].title.magenta()); 345 return Ok(albums[0].xata_id.clone()); 346 } 347 348 - println!("Saving album: {}", scrobble_record.album.magenta()); 349 350 let uri: Option<String> = None; 351 let artist_uri: Option<String> = None; ··· 402 .await?; 403 404 if !artists.is_empty() { 405 - println!("Artist already exists: {}", artists[0].name.magenta()); 406 return Ok(artists[0].xata_id.clone()); 407 } 408 409 - println!("Saving artist: {}", scrobble_record.album_artist.magenta()); 410 411 let uri: Option<String> = None; 412 let picture = ""; ··· 450 .await?; 451 452 if !album_tracks.is_empty() { 453 - println!( 454 - "Album track already exists: {}", 455 - format!("{} - {}", album_id, track_id).magenta() 456 - ); 457 return Ok(()); 458 } 459 460 - println!( 461 - "Saving album track: {}", 462 - format!("{} - {}", album_id, track_id).magenta() 463 - ); 464 465 sqlx::query( 466 r#" ··· 492 .await?; 493 494 if !artist_tracks.is_empty() { 495 - println!( 496 - "Artist track already exists: {}", 497 - format!("{} - {}", artist_id, track_id).magenta() 498 - ); 499 return Ok(()); 500 } 501 502 - println!( 503 - "Saving artist track: {}", 504 - format!("{} - {}", artist_id, track_id).magenta() 505 - ); 506 507 sqlx::query( 508 r#" ··· 534 .await?; 535 536 if !artist_albums.is_empty() { 537 - println!( 538 - "Artist album already exists: {}", 539 - format!("{} - {}", artist_id, album_id).magenta() 540 - ); 541 return Ok(()); 542 } 543 544 - println!( 545 - "Saving artist album: {}", 546 - format!("{} - {}", artist_id, album_id).magenta() 547 - ); 548 549 sqlx::query( 550 r#" ··· 585 586 match artists.is_empty() { 587 true => { 588 - println!("Saving artist: {}", record.name.magenta()); 589 let did = users[0].did.clone(); 590 sqlx::query( 591 r#" ··· 632 .await?; 633 634 if !user_artists.is_empty() { 635 - println!( 636 - "User artist already exists: {}", 637 - format!("{} - {}", user_id, artist_id).magenta() 638 - ); 639 sqlx::query( 640 r#" 641 UPDATE user_artists ··· 652 return Ok(()); 653 } 654 655 - println!( 656 - "Saving user artist: {}", 657 - format!("{} - {}", user_id, artist_id).magenta() 658 - ); 659 660 sqlx::query( 661 r#" ··· 699 700 match albums.is_empty() { 701 true => { 702 - println!("Saving album: {}", record.title.magenta()); 703 let did = users[0].did.clone(); 704 sqlx::query( 705 r#" ··· 752 .await?; 753 754 if !user_albums.is_empty() { 755 - println!( 756 - "User album already exists: {}", 757 - format!("{} - {}", user_id, album_id).magenta() 758 - ); 759 sqlx::query( 760 r#" 761 UPDATE user_albums ··· 772 return Ok(()); 773 } 774 775 - println!( 776 - "Saving user album: {}", 777 - format!("{} - {}", user_id, album_id).magenta() 778 - ); 779 780 sqlx::query( 781 r#" ··· 822 823 match tracks.is_empty() { 824 true => { 825 - println!("Saving track: {}", record.title.magenta()); 826 let did = users[0].did.clone(); 827 sqlx::query( 828 r#" ··· 894 .await?; 895 896 if !user_tracks.is_empty() { 897 - println!( 898 - "User track already exists: {}", 899 - format!("{} - {}", user_id, track_id).magenta() 900 - ); 901 sqlx::query( 902 r#" 903 UPDATE user_tracks ··· 914 return Ok(()); 915 } 916 917 - println!( 918 - "Saving user track: {}", 919 - format!("{} - {}", user_id, track_id).magenta() 920 - ); 921 922 sqlx::query( 923 r#" ··· 954 .await?; 955 956 if artists.is_empty() { 957 - println!("Artist not found: {}", record.name.magenta()); 958 return Ok(()); 959 } 960 ··· 1023 .fetch_all(&mut **tx) 1024 .await?; 1025 if albums.is_empty() { 1026 - println!("Album not found: {}", record.title.magenta()); 1027 return Ok(()); 1028 } 1029 let album_id = &albums[0].xata_id; ··· 1082 .await?; 1083 1084 if tracks.is_empty() { 1085 - println!("Track not found: {}", record.title.magenta()); 1086 return Ok(()); 1087 } 1088
··· 16 }, 17 webhook_worker::{push_to_queue, AppState}, 18 xata::{ 19 + album::Album, 20 + album_track::AlbumTrack, 21 + artist::Artist, 22 + artist_album::ArtistAlbum, 23 + artist_track::ArtistTrack, 24 + track::Track, 25 + user::{self, User}, 26 + user_album::UserAlbum, 27 + user_artist::UserArtist, 28 + user_track::UserTrack, 29 }, 30 }; 31 ··· 63 64 let user_id = save_user(&mut tx, did).await?; 65 66 + tracing::info!(title = %scrobble_record.title.magenta(), artist = %scrobble_record.artist.magenta(), album = %scrobble_record.album.magenta(), "Saving scrobble"); 67 68 sqlx::query( 69 r#" ··· 144 { 145 Ok(_) => {} 146 Err(e) => { 147 + tracing::error!(error = %e, "Failed to push to webhook queue"); 148 } 149 } 150 } ··· 188 } 189 } 190 _ => { 191 + tracing::warn!(operation = %commit.operation, "Unsupported operation"); 192 } 193 } 194 Ok(()) ··· 341 .await?; 342 343 if !albums.is_empty() { 344 + tracing::info!(name = %albums[0].title.magenta(), "Album already exists"); 345 return Ok(albums[0].xata_id.clone()); 346 } 347 348 + tracing::info!(name = %scrobble_record.album, "Saving new album"); 349 350 let uri: Option<String> = None; 351 let artist_uri: Option<String> = None; ··· 402 .await?; 403 404 if !artists.is_empty() { 405 + tracing::info!(name = %scrobble_record.album_artist, "Artist already exists"); 406 return Ok(artists[0].xata_id.clone()); 407 } 408 409 + tracing::info!(name = %scrobble_record.album_artist, "Saving new artist"); 410 411 let uri: Option<String> = None; 412 let picture = ""; ··· 450 .await?; 451 452 if !album_tracks.is_empty() { 453 + tracing::info!(album_id = %album_id, track_id = %track_id, "Album track already exists"); 454 return Ok(()); 455 } 456 457 + tracing::info!(album_id = %album_id, track_id = %track_id, "Saving album track"); 458 459 sqlx::query( 460 r#" ··· 486 .await?; 487 488 if !artist_tracks.is_empty() { 489 + tracing::info!(artist_id = %artist_id, track_id = %track_id, "Artist track already exists"); 490 return Ok(()); 491 } 492 493 + tracing::info!(artist_id = %artist_id, track_id = %track_id, "Saving artist track"); 494 495 sqlx::query( 496 r#" ··· 522 .await?; 523 524 if !artist_albums.is_empty() { 525 + tracing::info!(artist_id = %artist_id, album_id = %album_id, "Artist album already exists"); 526 return Ok(()); 527 } 528 529 + tracing::info!(artist_id = %artist_id, album_id = %album_id, "Saving artist album"); 530 531 sqlx::query( 532 r#" ··· 567 568 match artists.is_empty() { 569 true => { 570 + tracing::info!(name = %record.name, "Artist not found in database, inserting new artist"); 571 let did = users[0].did.clone(); 572 sqlx::query( 573 r#" ··· 614 .await?; 615 616 if !user_artists.is_empty() { 617 + tracing::info!(user_id = %user_id, artist_id = %artist_id, "Updating user artist"); 618 sqlx::query( 619 r#" 620 UPDATE user_artists ··· 631 return Ok(()); 632 } 633 634 + tracing::info!(user_id = %user_id, artist_id = %artist_id, "Inserting user artist"); 635 636 sqlx::query( 637 r#" ··· 675 676 match albums.is_empty() { 677 true => { 678 + tracing::info!(title = %record.title, artist = %record.artist, "Album not found in database, inserting new album"); 679 let did = users[0].did.clone(); 680 sqlx::query( 681 r#" ··· 728 .await?; 729 730 if !user_albums.is_empty() { 731 + tracing::info!(user_id = %user_id, album_id = %album_id, "Updating user album"); 732 sqlx::query( 733 r#" 734 UPDATE user_albums ··· 745 return Ok(()); 746 } 747 748 + tracing::info!(user_id = %user_id, album_id = %album_id, "Inserting user album"); 749 750 sqlx::query( 751 r#" ··· 792 793 match tracks.is_empty() { 794 true => { 795 + tracing::info!(title = %record.title, artist = %record.artist, album = %record.album, "Track not found in database, inserting new track"); 796 let did = users[0].did.clone(); 797 sqlx::query( 798 r#" ··· 864 .await?; 865 866 if !user_tracks.is_empty() { 867 + tracing::info!(user_id = %user_id, track_id = %track_id, "Updating user track"); 868 sqlx::query( 869 r#" 870 UPDATE user_tracks ··· 881 return Ok(()); 882 } 883 884 + tracing::info!(user_id = %user_id, track_id = %track_id, "Inserting user track"); 885 886 sqlx::query( 887 r#" ··· 918 .await?; 919 920 if artists.is_empty() { 921 + tracing::warn!(name = %record.name, "Artist not found in database"); 922 return Ok(()); 923 } 924 ··· 987 .fetch_all(&mut **tx) 988 .await?; 989 if albums.is_empty() { 990 + tracing::warn!(title = %record.title, "Album not found in database"); 991 return Ok(()); 992 } 993 let album_id = &albums[0].xata_id; ··· 1046 .await?; 1047 1048 if tracks.is_empty() { 1049 + tracing::warn!(title = %record.title, "Track not found in database"); 1050 return Ok(()); 1051 } 1052
+6 -9
crates/jetstream/src/subscriber.rs
··· 40 let pool = Arc::new(Mutex::new(pool)); 41 42 let (mut ws_stream, _) = connect_async(&self.service_url).await?; 43 - println!( 44 - "Connected to jetstream at {}", 45 - self.service_url.bright_green() 46 - ); 47 48 while let Some(msg) = ws_stream.next().await { 49 match msg { 50 Ok(msg) => { 51 if let Err(e) = handle_message(state.clone(), pool.clone(), msg).await { 52 - eprintln!("Error handling message: {}", e); 53 } 54 } 55 Err(e) => { 56 - eprintln!("WebSocket error: {}", e); 57 break; 58 } 59 } ··· 76 return Ok::<(), Error>(()); 77 } 78 79 - println!("Received message: {:#?}", message); 80 if let Some(commit) = message.commit { 81 match save_scrobble(state, pool, &message.did, commit).await { 82 Ok(_) => { 83 - println!("Scrobble saved successfully"); 84 } 85 Err(e) => { 86 - eprintln!("Error saving scrobble: {}", e); 87 } 88 } 89 }
··· 40 let pool = Arc::new(Mutex::new(pool)); 41 42 let (mut ws_stream, _) = connect_async(&self.service_url).await?; 43 + tracing::info!(url = %self.service_url.bright_green(), "Connected to jetstream at"); 44 45 while let Some(msg) = ws_stream.next().await { 46 match msg { 47 Ok(msg) => { 48 if let Err(e) = handle_message(state.clone(), pool.clone(), msg).await { 49 + tracing::error!(error = %e, "Error handling message"); 50 } 51 } 52 Err(e) => { 53 + tracing::error!(error = %e, "WebSocket error"); 54 break; 55 } 56 } ··· 73 return Ok::<(), Error>(()); 74 } 75 76 + tracing::info!(message = %text.bright_green(), "Received message"); 77 if let Some(commit) = message.commit { 78 match save_scrobble(state, pool, &message.did, commit).await { 79 Ok(_) => { 80 + tracing::info!(user_id = %message.did.bright_green(), "Scrobble saved successfully"); 81 } 82 Err(e) => { 83 + tracing::error!(error = %e, "Error saving scrobble"); 84 } 85 } 86 }
+2 -2
crates/jetstream/src/webhook/discord/mod.rs
··· 37 embeds: Vec<DiscordEmbed>, 38 ) -> reqwest::Result<()> { 39 if discord_webhook_url.is_empty() { 40 - println!("DISCORD_WEBHOOK_URL is not set, skipping webhook post"); 41 return Ok(()); 42 } 43 ··· 48 let res = http.post(discord_webhook_url).json(&body).send().await?; 49 if !res.status().is_success() { 50 let text = res.text().await.unwrap_or_default(); 51 - eprintln!("Failed to post to Discord webhook: {}", text); 52 } 53 Ok(()) 54 }
··· 37 embeds: Vec<DiscordEmbed>, 38 ) -> reqwest::Result<()> { 39 if discord_webhook_url.is_empty() { 40 + tracing::warn!("DISCORD_WEBHOOK_URL is not set, skipping webhook post"); 41 return Ok(()); 42 } 43 ··· 48 let res = http.post(discord_webhook_url).json(&body).send().await?; 49 if !res.status().is_success() { 50 let text = res.text().await.unwrap_or_default(); 51 + tracing::error!(error = %text, "Failed to post to Discord webhook"); 52 } 53 Ok(()) 54 }
+2 -2
crates/jetstream/src/webhook_worker.rs
··· 79 } 80 Ok(None) => break, 81 Err(e) => { 82 - eprintln!("Failed to pop from Redis: {}", e); 83 break; 84 } 85 } ··· 93 tokens -= 1; 94 95 if let Err(e) = discord::post_embeds(&http, &discord_webhook_url, embeds).await { 96 - eprintln!("Failed to post to Discord webhook: {}", e); 97 } 98 } 99 }
··· 79 } 80 Ok(None) => break, 81 Err(e) => { 82 + tracing::error!(error = %e, "Failed to pop from Redis"); 83 break; 84 } 85 } ··· 93 tokens -= 1; 94 95 if let Err(e) = discord::post_embeds(&http, &discord_webhook_url, embeds).await { 96 + tracing::error!(error = %e, "Failed to post to Discord webhook"); 97 } 98 } 99 }
+1 -1
crates/scrobbler/src/auth.rs
··· 37 let expected_password = md5::compute(expected_password); 38 let expected_password = format!("{:x}", expected_password); 39 if expected_password != password_md5 { 40 - println!("{} != {}", expected_password, password_md5); 41 return Err(Error::msg("Invalid password")); 42 } 43 Ok(())
··· 37 let expected_password = md5::compute(expected_password); 38 let expected_password = format!("{:x}", expected_password); 39 if expected_password != password_md5 { 40 + tracing::error!(expected = %expected_password, provided = %password_md5, "Invalid password"); 41 return Err(Error::msg("Invalid password")); 42 } 43 Ok(())
+1 -1
crates/scrobbler/src/handlers/v1/nowplaying.rs
··· 18 let a = form.get("a").unwrap().to_string(); 19 let t = form.get("t").unwrap().to_string(); 20 21 - println!("Now playing: {} - {} {}", a, t, s.cyan()); 22 23 let user_id = verify_session_id(cache, &s); 24 if let Err(e) = user_id {
··· 18 let a = form.get("a").unwrap().to_string(); 19 let t = form.get("t").unwrap().to_string(); 20 21 + tracing::info!(artist = %a, track = %t, user = %s.cyan(), "Now playing"); 22 23 let user_id = verify_session_id(cache, &s); 24 if let Err(e) = user_id {
+1 -1
crates/scrobbler/src/handlers/v1/submission.rs
··· 29 } 30 31 let user_id = user_id.unwrap(); 32 - println!("Submission: {} - {} {} {} {}", a, t, i, user_id, s.cyan()); 33 34 match scrobble_v1(pool, cache, &form).await { 35 Ok(_) => Ok(HttpResponse::Ok().body("OK\n")),
··· 29 } 30 31 let user_id = user_id.unwrap(); 32 + tracing::info!(artist = %a, track = %t, timestamp = %i, user_id = %user_id, "Submission"); 33 34 match scrobble_v1(pool, cache, &form).await { 35 Ok(_) => Ok(HttpResponse::Ok().body("OK\n")),
+1 -4
crates/scrobbler/src/lib.rs
··· 56 .parse::<u16>() 57 .unwrap_or(7882); 58 59 - println!( 60 - "Starting Scrobble server @ {}", 61 - format!("{}:{}", host, port).green() 62 - ); 63 64 let limiter = web::Data::new( 65 Limiter::builder("redis://127.0.0.1")
··· 56 .parse::<u16>() 57 .unwrap_or(7882); 58 59 + tracing::info!(url = %format!("http://{}:{}", host, port).bright_green(), "Starting Scrobble server @"); 60 61 let limiter = web::Data::new( 62 Limiter::builder("redis://127.0.0.1")
+2 -8
crates/scrobbler/src/listenbrainz/core/submit.rs
··· 17 token: &str, 18 ) -> Result<HttpResponse, Error> { 19 if payload.listen_type != "playing_now" { 20 - println!("skipping listen type: {}", payload.listen_type.cyan()); 21 return Ok(HttpResponse::Ok().json(json!({ 22 "status": "ok", 23 "payload": { ··· 62 63 cache.del(&format!("listenbrainz:cache:{}:{}:{}", artist, track, did))?; 64 65 - println!( 66 - "Retryable error on attempt {}/{}: {}", 67 - attempt, 68 - RETRIES, 69 - e.to_string().yellow() 70 - ); 71 - println!("{:#?}", payload); 72 73 if attempt == RETRIES { 74 return Ok(HttpResponse::BadRequest().json(serde_json::json!({
··· 17 token: &str, 18 ) -> Result<HttpResponse, Error> { 19 if payload.listen_type != "playing_now" { 20 + tracing::info!(listen_type = %payload.listen_type.cyan(), "Skipping listen type"); 21 return Ok(HttpResponse::Ok().json(json!({ 22 "status": "ok", 23 "payload": { ··· 62 63 cache.del(&format!("listenbrainz:cache:{}:{}:{}", artist, track, did))?; 64 65 + tracing::error!(error = %e, attempt = attempt, "Retryable error submitting listens for {} - {} (attempt {}/{})", artist, track, attempt, RETRIES); 66 67 if attempt == RETRIES { 68 return Ok(HttpResponse::BadRequest().json(serde_json::json!({
+1 -1
crates/scrobbler/src/listenbrainz/core/validate_token.rs
··· 12 }, 13 }))), 14 Err(e) => { 15 - println!("Error validating token: {}", e); 16 Ok(HttpResponse::BadRequest().json(serde_json::json!({ 17 "error": 4, 18 "message": format!("Failed to validate token: {}", e)
··· 12 }, 13 }))), 14 Err(e) => { 15 + tracing::error!(error = %e, "Failed to validate token"); 16 Ok(HttpResponse::BadRequest().json(serde_json::json!({ 17 "error": 4, 18 "message": format!("Failed to validate token: {}", e)
+12 -13
crates/scrobbler/src/listenbrainz/handlers.rs
··· 58 let body = String::from_utf8_lossy(&payload); 59 let req = serde_json::from_str::<SubmitListensRequest>(&body) 60 .map_err(|e| { 61 - println!("{}", body); 62 - println!("Error parsing request body: {}", e); 63 e 64 }) 65 .map_err(actix_web::error::ErrorBadRequest)?; ··· 116 })); 117 } 118 Err(e) => { 119 - println!("Error validating token: {}", e); 120 return HttpResponse::InternalServerError().finish(); 121 } 122 } ··· 127 query: web::Query<String>, 128 data: web::Data<Arc<Pool<Postgres>>>, 129 ) -> impl Responder { 130 - let pool = data.get_ref(); 131 let query = query.into_inner(); 132 133 match search_users(&query).await { 134 Ok(users) => HttpResponse::Ok().json(users), 135 Err(e) => { 136 - println!("Error searching users: {}", e); 137 HttpResponse::InternalServerError().finish() 138 } 139 } ··· 145 match get_listens(&user_name).await { 146 Ok(listens) => HttpResponse::Ok().json(listens), 147 Err(e) => { 148 - println!("Error getting listens for user {}: {}", user_name, e); 149 HttpResponse::InternalServerError().finish() 150 } 151 } ··· 157 match get_listen_count(&user_name).await { 158 Ok(count) => HttpResponse::Ok().json(count), 159 Err(e) => { 160 - println!("Error getting listen count for user {}: {}", user_name, e); 161 HttpResponse::InternalServerError().finish() 162 } 163 } ··· 169 match get_playing_now(&user_name).await { 170 Ok(playing_now) => HttpResponse::Ok().json(playing_now), 171 Err(e) => { 172 - println!("Error getting playing now for user {}: {}", user_name, e); 173 HttpResponse::InternalServerError().finish() 174 } 175 } ··· 181 match get_top_artists(&user_name).await { 182 Ok(artists) => HttpResponse::Ok().json(artists), 183 Err(e) => { 184 - println!("Error getting top artists: {}", e); 185 HttpResponse::InternalServerError().finish() 186 } 187 } ··· 193 match get_top_releases(&user_name).await { 194 Ok(releases) => HttpResponse::Ok().json(releases), 195 Err(e) => { 196 - println!("Error getting top releases: {}", e); 197 HttpResponse::InternalServerError().finish() 198 } 199 } ··· 205 match get_top_recordings(&user_name).await { 206 Ok(recordings) => HttpResponse::Ok().json(recordings), 207 Err(e) => { 208 - println!("Error getting sitewide recordings: {}", e); 209 HttpResponse::InternalServerError().finish() 210 } 211 } ··· 217 match get_top_release_groups(&user_name).await { 218 Ok(release_groups) => HttpResponse::Ok().json(release_groups), 219 Err(e) => { 220 - println!("Error getting top release groups: {}", e); 221 HttpResponse::InternalServerError().finish() 222 } 223 } ··· 229 match get_top_recordings(&user_name).await { 230 Ok(recordings) => HttpResponse::Ok().json(recordings), 231 Err(e) => { 232 - println!("Error getting top recordings: {}", e); 233 HttpResponse::InternalServerError().finish() 234 } 235 }
··· 58 let body = String::from_utf8_lossy(&payload); 59 let req = serde_json::from_str::<SubmitListensRequest>(&body) 60 .map_err(|e| { 61 + tracing::error!(body = %body, error = %e, "Error parsing request body"); 62 e 63 }) 64 .map_err(actix_web::error::ErrorBadRequest)?; ··· 115 })); 116 } 117 Err(e) => { 118 + tracing::error!(error = %e, "Error validating token"); 119 return HttpResponse::InternalServerError().finish(); 120 } 121 } ··· 126 query: web::Query<String>, 127 data: web::Data<Arc<Pool<Postgres>>>, 128 ) -> impl Responder { 129 + let _pool = data.get_ref(); 130 let query = query.into_inner(); 131 132 match search_users(&query).await { 133 Ok(users) => HttpResponse::Ok().json(users), 134 Err(e) => { 135 + tracing::error!(error = %e, "Error searching users"); 136 HttpResponse::InternalServerError().finish() 137 } 138 } ··· 144 match get_listens(&user_name).await { 145 Ok(listens) => HttpResponse::Ok().json(listens), 146 Err(e) => { 147 + tracing::error!(error = %e, "Error getting listens for user {}", user_name); 148 HttpResponse::InternalServerError().finish() 149 } 150 } ··· 156 match get_listen_count(&user_name).await { 157 Ok(count) => HttpResponse::Ok().json(count), 158 Err(e) => { 159 + tracing::error!(error = %e, "Error getting listen count for user {}", user_name); 160 HttpResponse::InternalServerError().finish() 161 } 162 } ··· 168 match get_playing_now(&user_name).await { 169 Ok(playing_now) => HttpResponse::Ok().json(playing_now), 170 Err(e) => { 171 + tracing::error!(error = %e, "Error getting playing now for user {}", user_name); 172 HttpResponse::InternalServerError().finish() 173 } 174 } ··· 180 match get_top_artists(&user_name).await { 181 Ok(artists) => HttpResponse::Ok().json(artists), 182 Err(e) => { 183 + tracing::error!(error = %e, "Error getting top artists"); 184 HttpResponse::InternalServerError().finish() 185 } 186 } ··· 192 match get_top_releases(&user_name).await { 193 Ok(releases) => HttpResponse::Ok().json(releases), 194 Err(e) => { 195 + tracing::error!(error = %e, "Error getting top releases"); 196 HttpResponse::InternalServerError().finish() 197 } 198 } ··· 204 match get_top_recordings(&user_name).await { 205 Ok(recordings) => HttpResponse::Ok().json(recordings), 206 Err(e) => { 207 + tracing::error!(error = %e, "Error getting top recordings"); 208 HttpResponse::InternalServerError().finish() 209 } 210 } ··· 216 match get_top_release_groups(&user_name).await { 217 Ok(release_groups) => HttpResponse::Ok().json(release_groups), 218 Err(e) => { 219 + tracing::error!(error = %e, "Error getting top release groups"); 220 HttpResponse::InternalServerError().finish() 221 } 222 } ··· 228 match get_top_recordings(&user_name).await { 229 Ok(recordings) => HttpResponse::Ok().json(recordings), 230 Err(e) => { 231 + tracing::error!(error = %e, "Error getting top recordings"); 232 HttpResponse::InternalServerError().finish() 233 } 234 }
+3 -3
crates/scrobbler/src/main.rs
··· 58 .parse::<u16>() 59 .unwrap_or(7882); 60 61 - println!( 62 - "Starting Scrobble server @ {}", 63 - format!("{}:{}", host, port).green() 64 ); 65 66 let limiter = web::Data::new(
··· 58 .parse::<u16>() 59 .unwrap_or(7882); 60 61 + tracing::info!( 62 + url = %format!("http://{}:{}", host, port).bright_green(), 63 + "Starting Scrobble server @" 64 ); 65 66 let limiter = web::Data::new(
+3 -4
crates/scrobbler/src/rocksky.rs
··· 25 let token = generate_token(did)?; 26 let client = Client::new(); 27 28 - println!("Scrobbling track: \n {:#?}", track); 29 30 let response = client 31 .post(&format!("{}/now-playing", ROCKSKY_API)) ··· 35 .await?; 36 37 let status = response.status(); 38 - println!("Response status: {}", status); 39 if !status.is_success() { 40 let response_text = response.text().await?; 41 - println!("did: {}", did); 42 - println!("Failed to scrobble track: {}", response_text); 43 return Err(Error::msg(format!( 44 "Failed to scrobble track: {}", 45 response_text
··· 25 let token = generate_token(did)?; 26 let client = Client::new(); 27 28 + tracing::info!(did = %did, track = ?track, "Scrobbling track"); 29 30 let response = client 31 .post(&format!("{}/now-playing", ROCKSKY_API)) ··· 35 .await?; 36 37 let status = response.status(); 38 + tracing::info!(did = %did, artist = %track.artist, track = %track.title, status = %status, "Scrobble response"); 39 if !status.is_success() { 40 let response_text = response.text().await?; 41 + tracing::error!(did = %did, response = %response_text, "Failed to scrobble track"); 42 return Err(Error::msg(format!( 43 "Failed to scrobble track: {}", 44 response_text
+21 -51
crates/scrobbler/src/scrobbler.rs
··· 133 ); 134 let cached = cache.get(&key)?; 135 if cached.is_some() { 136 - println!("{}", format!("Cached: {}", key).yellow()); 137 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 138 scrobble.album = Some(track.album.clone()); 139 rocksky::scrobble(cache, &did, track, scrobble.timestamp).await?; ··· 144 if let Some(mbid) = &scrobble.mbid { 145 // let result = repo::track::get_track_by_mbid(pool, mbid).await?; 146 let result = mb_client.get_recording(mbid).await?; 147 - println!("{}", "Musicbrainz (mbid)".yellow()); 148 scrobble.album = Some(Track::from(result.clone()).album); 149 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 150 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 154 let result = repo::track::get_track(pool, &scrobble.track, &scrobble.artist).await?; 155 156 if let Some(track) = result { 157 - println!("{}", "Xata (track)".yellow()); 158 scrobble.album = Some(track.album.clone()); 159 let album = repo::album::get_album_by_track_id(pool, &track.xata_id).await?; 160 let artist = repo::artist::get_artist_by_track_id(pool, &track.xata_id).await?; ··· 204 .await?; 205 206 if let Some(track) = result.tracks.items.first() { 207 - println!("{}", "Spotify (track)".yellow()); 208 scrobble.album = Some(track.album.name.clone()); 209 let mut track = track.clone(); 210 ··· 232 233 if let Some(recording) = result.recordings.first() { 234 let result = mb_client.get_recording(&recording.id).await?; 235 - println!("{}", "Musicbrainz (recording)".yellow()); 236 scrobble.album = Some(Track::from(result.clone()).album); 237 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 238 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 239 continue; 240 } 241 242 - println!( 243 - "{} {} - {}, skipping", 244 - "Track not found: ".yellow(), 245 - scrobble.artist, 246 - scrobble.track 247 - ); 248 scrobble.ignored = Some(true); 249 } 250 ··· 313 ); 314 let cached = cache.get(&key)?; 315 if cached.is_some() { 316 - println!("{}", format!("Cached: {}", key).yellow()); 317 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 318 scrobble.album = Some(track.album.clone()); 319 rocksky::scrobble(cache, &did, track, scrobble.timestamp).await?; ··· 324 if let Some(mbid) = &scrobble.mbid { 325 // let result = repo::track::get_track_by_mbid(pool, mbid).await?; 326 let result = mb_client.get_recording(mbid).await?; 327 - println!("{}", "Musicbrainz (mbid)".yellow()); 328 scrobble.album = Some(Track::from(result.clone()).album); 329 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 330 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 334 let result = repo::track::get_track(pool, &scrobble.track, &scrobble.artist).await?; 335 336 if let Some(track) = result { 337 - println!("{}", "Xata (track)".yellow()); 338 scrobble.album = Some(track.album.clone()); 339 let album = repo::album::get_album_by_track_id(pool, &track.xata_id).await?; 340 let artist = repo::artist::get_artist_by_track_id(pool, &track.xata_id).await?; ··· 384 .await?; 385 386 if let Some(track) = result.tracks.items.first() { 387 - println!("{}", "Spotify (track)".yellow()); 388 scrobble.album = Some(track.album.name.clone()); 389 let mut track = track.clone(); 390 ··· 412 413 if let Some(recording) = result.recordings.first() { 414 let result = mb_client.get_recording(&recording.id).await?; 415 - println!("{}", "Musicbrainz (recording)".yellow()); 416 scrobble.album = Some(Track::from(result.clone()).album); 417 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 418 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 419 return Ok(()); 420 } 421 422 - println!( 423 - "{} {} - {}, skipping", 424 - "Track not found: ".yellow(), 425 - artist, 426 - track 427 - ); 428 429 Ok(()) 430 } ··· 435 req: &SubmitListensRequest, 436 token: &str, 437 ) -> Result<(), Error> { 438 - println!("Listenbrainz\n{:#?}", req); 439 440 if req.payload.is_empty() { 441 return Err(Error::msg("No payload found")); ··· 481 .get(&format!("listenbrainz:cache:{}:{}:{}", artist, track, did))? 482 .is_some() 483 { 484 - println!( 485 - "{} {} - {}, recently scrobbled", 486 - "Already scrobbled: ".yellow(), 487 - artist, 488 - track 489 - ); 490 return Ok(()); 491 } 492 ··· 496 .get(&format!("{}:current", spotify_user.email))? 497 .is_some() 498 { 499 - println!( 500 - "{} {} - {}, currently scrobbling, skipping", 501 - "Currently scrobbling: ".yellow(), 502 - artist, 503 - track 504 - ); 505 return Ok(()); 506 } 507 } 508 509 if cache.get(&format!("nowplaying:{}", did))?.is_some() { 510 - println!( 511 - "{} {} - {}, currently scrobbling, skipping", 512 - "Currently scrobbling: ".yellow(), 513 - artist, 514 - track 515 - ); 516 return Ok(()); 517 } 518 ··· 565 ); 566 let cached = cache.get(&key)?; 567 if cached.is_some() { 568 - println!("{}", format!("Cached: {}", key).yellow()); 569 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 570 scrobble.album = Some(track.album.clone()); 571 rocksky::scrobble(cache, &did, track, scrobble.timestamp).await?; ··· 576 if let Some(mbid) = &scrobble.mbid { 577 // let result = repo::track::get_track_by_mbid(pool, mbid).await?; 578 let result = mb_client.get_recording(mbid).await?; 579 - println!("{}", "Musicbrainz (mbid)".yellow()); 580 scrobble.album = Some(Track::from(result.clone()).album); 581 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 582 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 586 let result = repo::track::get_track(pool, &scrobble.track, &scrobble.artist).await?; 587 588 if let Some(track) = result { 589 - println!("{}", "Xata (track)".yellow()); 590 scrobble.album = Some(track.album.clone()); 591 let album = repo::album::get_album_by_track_id(pool, &track.xata_id).await?; 592 let artist = repo::artist::get_artist_by_track_id(pool, &track.xata_id).await?; ··· 636 .await?; 637 638 if let Some(track) = result.tracks.items.first() { 639 - println!("{}", "Spotify (track)".yellow()); 640 scrobble.album = Some(track.album.name.clone()); 641 let mut track = track.clone(); 642 ··· 676 } 677 */ 678 679 - println!( 680 - "{} {} - {}, skipping", 681 - "Track not found: ".yellow(), 682 - artist, 683 - track 684 - ); 685 686 Ok(()) 687 }
··· 133 ); 134 let cached = cache.get(&key)?; 135 if cached.is_some() { 136 + tracing::info!(key = %key, "Cached:"); 137 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 138 scrobble.album = Some(track.album.clone()); 139 rocksky::scrobble(cache, &did, track, scrobble.timestamp).await?; ··· 144 if let Some(mbid) = &scrobble.mbid { 145 // let result = repo::track::get_track_by_mbid(pool, mbid).await?; 146 let result = mb_client.get_recording(mbid).await?; 147 + tracing::info!(%scrobble.artist, %scrobble.track, "Musicbrainz (mbid)"); 148 scrobble.album = Some(Track::from(result.clone()).album); 149 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 150 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 154 let result = repo::track::get_track(pool, &scrobble.track, &scrobble.artist).await?; 155 156 if let Some(track) = result { 157 + tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Xata (track)"); 158 scrobble.album = Some(track.album.clone()); 159 let album = repo::album::get_album_by_track_id(pool, &track.xata_id).await?; 160 let artist = repo::artist::get_artist_by_track_id(pool, &track.xata_id).await?; ··· 204 .await?; 205 206 if let Some(track) = result.tracks.items.first() { 207 + tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Spotify (track)"); 208 scrobble.album = Some(track.album.name.clone()); 209 let mut track = track.clone(); 210 ··· 232 233 if let Some(recording) = result.recordings.first() { 234 let result = mb_client.get_recording(&recording.id).await?; 235 + tracing::info!(%scrobble.artist, %scrobble.track, "Musicbrainz (recording)"); 236 scrobble.album = Some(Track::from(result.clone()).album); 237 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 238 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 239 continue; 240 } 241 242 + tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Track not found, skipping"); 243 scrobble.ignored = Some(true); 244 } 245 ··· 308 ); 309 let cached = cache.get(&key)?; 310 if cached.is_some() { 311 + tracing::info!(key = %key, "Cached:"); 312 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 313 scrobble.album = Some(track.album.clone()); 314 rocksky::scrobble(cache, &did, track, scrobble.timestamp).await?; ··· 319 if let Some(mbid) = &scrobble.mbid { 320 // let result = repo::track::get_track_by_mbid(pool, mbid).await?; 321 let result = mb_client.get_recording(mbid).await?; 322 + tracing::info!(%scrobble.artist, %scrobble.track, "Musicbrainz (mbid)"); 323 scrobble.album = Some(Track::from(result.clone()).album); 324 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 325 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 329 let result = repo::track::get_track(pool, &scrobble.track, &scrobble.artist).await?; 330 331 if let Some(track) = result { 332 + tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Xata (track)"); 333 scrobble.album = Some(track.album.clone()); 334 let album = repo::album::get_album_by_track_id(pool, &track.xata_id).await?; 335 let artist = repo::artist::get_artist_by_track_id(pool, &track.xata_id).await?; ··· 379 .await?; 380 381 if let Some(track) = result.tracks.items.first() { 382 + tracing::info!(artist = %scrobble.artist, track = %scrobble.track, "Spotify (track)"); 383 scrobble.album = Some(track.album.name.clone()); 384 let mut track = track.clone(); 385 ··· 407 408 if let Some(recording) = result.recordings.first() { 409 let result = mb_client.get_recording(&recording.id).await?; 410 + tracing::info!(%scrobble.artist, %scrobble.track, "Musicbrainz (recording)"); 411 scrobble.album = Some(Track::from(result.clone()).album); 412 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 413 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 414 return Ok(()); 415 } 416 417 + tracing::info!(artist = %artist, track = %track, "Track not found, skipping"); 418 419 Ok(()) 420 } ··· 425 req: &SubmitListensRequest, 426 token: &str, 427 ) -> Result<(), Error> { 428 + tracing::info!(req = ?req, "Listenbrainz submission"); 429 430 if req.payload.is_empty() { 431 return Err(Error::msg("No payload found")); ··· 471 .get(&format!("listenbrainz:cache:{}:{}:{}", artist, track, did))? 472 .is_some() 473 { 474 + tracing::info!(artist= %artist, track = %track, "Recently scrobbled, skipping"); 475 return Ok(()); 476 } 477 ··· 481 .get(&format!("{}:current", spotify_user.email))? 482 .is_some() 483 { 484 + tracing::info!(artist= %artist, track = %track, "Currently scrobbling, skipping"); 485 return Ok(()); 486 } 487 } 488 489 if cache.get(&format!("nowplaying:{}", did))?.is_some() { 490 + tracing::info!(artist= %artist, track = %track, "Currently scrobbling, skipping"); 491 return Ok(()); 492 } 493 ··· 540 ); 541 let cached = cache.get(&key)?; 542 if cached.is_some() { 543 + tracing::info!(key = %key, "Cached"); 544 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 545 scrobble.album = Some(track.album.clone()); 546 rocksky::scrobble(cache, &did, track, scrobble.timestamp).await?; ··· 551 if let Some(mbid) = &scrobble.mbid { 552 // let result = repo::track::get_track_by_mbid(pool, mbid).await?; 553 let result = mb_client.get_recording(mbid).await?; 554 + tracing::info!("Musicbrainz (mbid)"); 555 scrobble.album = Some(Track::from(result.clone()).album); 556 rocksky::scrobble(cache, &did, result.into(), scrobble.timestamp).await?; 557 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 561 let result = repo::track::get_track(pool, &scrobble.track, &scrobble.artist).await?; 562 563 if let Some(track) = result { 564 + tracing::info!("Xata (track)"); 565 scrobble.album = Some(track.album.clone()); 566 let album = repo::album::get_album_by_track_id(pool, &track.xata_id).await?; 567 let artist = repo::artist::get_artist_by_track_id(pool, &track.xata_id).await?; ··· 611 .await?; 612 613 if let Some(track) = result.tracks.items.first() { 614 + tracing::info!("Spotify (track)"); 615 scrobble.album = Some(track.album.name.clone()); 616 let mut track = track.clone(); 617 ··· 651 } 652 */ 653 654 + tracing::warn!(artist = %artist, track = %track, "Track not found, skipping"); 655 656 Ok(()) 657 }
+2 -10
crates/scrobbler/src/spotify/client.rs
··· 36 let data = response.text().await?; 37 38 if data == "Too many requests" { 39 - println!( 40 - "> retry-after {}", 41 - headers.get("retry-after").unwrap().to_str().unwrap() 42 - ); 43 - println!("> {} [get_album]", data); 44 return Ok(None); 45 } 46 ··· 56 let data = response.text().await?; 57 58 if data == "Too many requests" { 59 - println!( 60 - "> retry-after {}", 61 - headers.get("retry-after").unwrap().to_str().unwrap() 62 - ); 63 - println!("> {} [get_artist]", data); 64 return Ok(None); 65 } 66
··· 36 let data = response.text().await?; 37 38 if data == "Too many requests" { 39 + tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_album"); 40 return Ok(None); 41 } 42 ··· 52 let data = response.text().await?; 53 54 if data == "Too many requests" { 55 + tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_artist"); 56 return Ok(None); 57 } 58
+7 -14
crates/webscrobbler/src/handlers.rs
··· 32 req: HttpRequest, 33 ) -> Result<impl Responder, actix_web::Error> { 34 let id = req.match_info().get("id").unwrap(); 35 - println!("Received scrobble for ID: {}", id.cyan()); 36 37 let pool = data.get_ref().clone(); 38 ··· 50 let body = read_payload!(payload); 51 let params = serde_json::from_slice::<ScrobbleRequest>(&body).map_err(|err| { 52 let body = String::from_utf8_lossy(&body); 53 - println!("Failed to parse JSON: {}", body); 54 - println!("Failed to parse JSON: {}", err); 55 actix_web::error::ErrorBadRequest(format!("Failed to parse JSON: {}", err)) 56 })?; 57 58 - println!("Parsed scrobble request: {:#?}", params); 59 60 if params.event_name != "scrobble" { 61 - println!("Skipping non-scrobble event: {}", params.event_name.green()); 62 return Ok(HttpResponse::Ok().body("Skipping non-scrobble event")); 63 } 64 ··· 75 })?; 76 77 if spotify_token.is_some() { 78 - println!("User has a Spotify token, skipping scrobble"); 79 return Ok(HttpResponse::Ok().body("User has a Spotify token, skipping scrobble")); 80 } 81 } ··· 91 )); 92 93 if cached.is_err() { 94 - println!( 95 - "Failed to check cache for Emby scrobble: {}", 96 - cached.unwrap_err() 97 - ); 98 return Ok(HttpResponse::Ok().body("Failed to check cache for Emby scrobble")); 99 } 100 101 if cached.unwrap().is_some() { 102 - println!( 103 - "Skipping duplicate scrobble for Emby: {} - {}", 104 - artist, track 105 - ); 106 return Ok(HttpResponse::Ok().body("Skipping duplicate scrobble for Emby")); 107 } 108 }
··· 32 req: HttpRequest, 33 ) -> Result<impl Responder, actix_web::Error> { 34 let id = req.match_info().get("id").unwrap(); 35 + tracing::info!(id = %id.bright_green(), "Received scrobble"); 36 37 let pool = data.get_ref().clone(); 38 ··· 50 let body = read_payload!(payload); 51 let params = serde_json::from_slice::<ScrobbleRequest>(&body).map_err(|err| { 52 let body = String::from_utf8_lossy(&body); 53 + tracing::error!(body = %body, error = %err, "Failed to parse JSON"); 54 actix_web::error::ErrorBadRequest(format!("Failed to parse JSON: {}", err)) 55 })?; 56 57 + tracing::info!(params = ?params, "Parsed scrobble request"); 58 59 if params.event_name != "scrobble" { 60 + tracing::info!(event_name = %params.event_name.cyan(), "Skipping non-scrobble event"); 61 return Ok(HttpResponse::Ok().body("Skipping non-scrobble event")); 62 } 63 ··· 74 })?; 75 76 if spotify_token.is_some() { 77 + tracing::info!("User has a Spotify token, skipping scrobble"); 78 return Ok(HttpResponse::Ok().body("User has a Spotify token, skipping scrobble")); 79 } 80 } ··· 90 )); 91 92 if cached.is_err() { 93 + tracing::error!(artist = %artist, track = %track, error = %cached.unwrap_err(), "Failed to check cache for Emby scrobble"); 94 return Ok(HttpResponse::Ok().body("Failed to check cache for Emby scrobble")); 95 } 96 97 if cached.unwrap().is_some() { 98 + tracing::warn!(artist = %artist, track = %track, "Skipping duplicate scrobble for Emby"); 99 return Ok(HttpResponse::Ok().body("Skipping duplicate scrobble for Emby")); 100 } 101 }
+1 -4
crates/webscrobbler/src/lib.rs
··· 44 .parse::<u16>() 45 .unwrap_or(7883); 46 47 - println!( 48 - "Starting WebScrobbler Webhook @ {}", 49 - format!("{}:{}", host, port).green() 50 - ); 51 52 let limiter = web::Data::new( 53 Limiter::builder("redis://127.0.0.1")
··· 44 .parse::<u16>() 45 .unwrap_or(7883); 46 47 + tracing::info!(url = %format!("http://{}:{}", host, port).bright_green(), "Starting WebScrobbler server @"); 48 49 let limiter = web::Data::new( 50 Limiter::builder("redis://127.0.0.1")
+4 -8
crates/webscrobbler/src/rocksky.rs
··· 1 use anyhow::Error; 2 - use owo_colors::OwoColorize; 3 use reqwest::Client; 4 5 use crate::{auth::generate_token, cache::Cache, types::Track}; ··· 26 let token = generate_token(did)?; 27 let client = Client::new(); 28 29 - println!("Scrobbling track: \n {:#?}", track); 30 31 let response = client 32 .post(&format!("{}/now-playing", ROCKSKY_API)) ··· 36 .await?; 37 38 if !response.status().is_success() { 39 - println!( 40 - "Failed to scrobble track: {}", 41 - response.status().to_string() 42 - ); 43 let text = response.text().await?; 44 - println!("Response: {}", text); 45 return Err(Error::msg(format!("Failed to scrobble track: {}", text))); 46 } 47 48 - println!("Scrobbled track: {}", track.title.green()); 49 50 Ok(()) 51 }
··· 1 use anyhow::Error; 2 use reqwest::Client; 3 4 use crate::{auth::generate_token, cache::Cache, types::Track}; ··· 25 let token = generate_token(did)?; 26 let client = Client::new(); 27 28 + tracing::info!(did = %did, track = ?track, "Scrobbling track"); 29 30 let response = client 31 .post(&format!("{}/now-playing", ROCKSKY_API)) ··· 35 .await?; 36 37 if !response.status().is_success() { 38 + tracing::error!(did = %did, artist = %track.artist, track = %track.title, status = %response.status(), "Failed to scrobble track"); 39 let text = response.text().await?; 40 + tracing::error!(did = %did, response = %text, "Response"); 41 return Err(Error::msg(format!("Failed to scrobble track: {}", text))); 42 } 43 44 + tracing::info!(did = %did, artist = %track.artist, track = %track.title, "Scrobbled track"); 45 46 Ok(()) 47 }
+4 -9
crates/webscrobbler/src/scrobbler.rs
··· 34 35 let cached = cache.get(&key)?; 36 if cached.is_some() { 37 - println!("{}", format!("Cached: {}", key).yellow()); 38 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 39 rocksky::scrobble(cache, &did, track, scrobble.time).await?; 40 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 127 let result = spotify_client.search(&query).await?; 128 129 if let Some(track) = result.tracks.items.first() { 130 - println!("{}", "Spotify (track)".yellow()); 131 let mut track = track.clone(); 132 133 if let Some(album) = spotify_client.get_album(&track.album.id).await? { ··· 154 155 if let Some(recording) = result.recordings.first() { 156 let result = mb_client.get_recording(&recording.id).await?; 157 - println!("{}", "Musicbrainz (recording)".yellow()); 158 rocksky::scrobble(cache, &did, result.into(), scrobble.time).await?; 159 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 160 return Ok(()); 161 } 162 163 - println!( 164 - "{} {} - {}, skipping", 165 - "Track not found: ".yellow(), 166 - scrobble.data.song.parsed.artist, 167 - scrobble.data.song.parsed.track 168 - ); 169 170 Ok(()) 171 }
··· 34 35 let cached = cache.get(&key)?; 36 if cached.is_some() { 37 + tracing::info!(artist = %scrobble.data.song.parsed.artist, track = %scrobble.data.song.parsed.track, "Using cached track"); 38 let track = serde_json::from_str::<Track>(&cached.unwrap())?; 39 rocksky::scrobble(cache, &did, track, scrobble.time).await?; 40 tokio::time::sleep(std::time::Duration::from_secs(1)).await; ··· 127 let result = spotify_client.search(&query).await?; 128 129 if let Some(track) = result.tracks.items.first() { 130 + tracing::info!("Spotify (track)"); 131 let mut track = track.clone(); 132 133 if let Some(album) = spotify_client.get_album(&track.album.id).await? { ··· 154 155 if let Some(recording) = result.recordings.first() { 156 let result = mb_client.get_recording(&recording.id).await?; 157 + tracing::info!("Musicbrainz (recording)"); 158 rocksky::scrobble(cache, &did, result.into(), scrobble.time).await?; 159 tokio::time::sleep(std::time::Duration::from_secs(1)).await; 160 return Ok(()); 161 } 162 163 + tracing::warn!(artist = %scrobble.data.song.parsed.artist, track = %scrobble.data.song.parsed.track, "Track not found, skipping"); 164 165 Ok(()) 166 }
+2 -10
crates/webscrobbler/src/spotify/client.rs
··· 36 let data = response.text().await?; 37 38 if data == "Too many requests" { 39 - println!( 40 - "> retry-after {}", 41 - headers.get("retry-after").unwrap().to_str().unwrap() 42 - ); 43 - println!("> {} [get_album]", data); 44 return Ok(None); 45 } 46 ··· 56 let data = response.text().await?; 57 58 if data == "Too many requests" { 59 - println!( 60 - "> retry-after {}", 61 - headers.get("retry-after").unwrap().to_str().unwrap() 62 - ); 63 - println!("> {} [get_artist]", data); 64 return Ok(None); 65 } 66
··· 36 let data = response.text().await?; 37 38 if data == "Too many requests" { 39 + tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_album"); 40 return Ok(None); 41 } 42 ··· 52 let data = response.text().await?; 53 54 if data == "Too many requests" { 55 + tracing::info!(retry_after = %headers.get("retry-after").unwrap().to_str().unwrap(), data = %data, "Rate limited on get_artist"); 56 return Ok(None); 57 } 58