A decentralized music tracking and discovery platform built on AT Protocol 馃幍
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at setup-tracing 493 lines 16 kB view raw
1use std::sync::{Arc, Mutex}; 2 3use crate::types::{ 4 album::Album, 5 artist::{ 6 Artist, ArtistListener, GetArtistAlbumsParams, GetArtistListenersParams, 7 GetArtistTracksParams, GetArtistsParams, GetTopArtistsParams, 8 }, 9 track::Track, 10}; 11use actix_web::{web, HttpRequest, HttpResponse}; 12use anyhow::Error; 13use duckdb::Connection; 14use tokio_stream::StreamExt; 15 16use crate::read_payload; 17 18pub async fn get_artists( 19 payload: &mut web::Payload, 20 _req: &HttpRequest, 21 conn: Arc<Mutex<Connection>>, 22) -> Result<HttpResponse, Error> { 23 let body = read_payload!(payload); 24 let params = serde_json::from_slice::<GetArtistsParams>(&body)?; 25 let pagination = params.pagination.unwrap_or_default(); 26 let offset = pagination.skip.unwrap_or(0); 27 let limit = pagination.take.unwrap_or(20); 28 let did = params.user_did; 29 30 let conn = conn.lock().unwrap(); 31 let mut stmt = match did { 32 Some(_) => conn.prepare( 33 r#" 34 SELECT a.*, 35 COUNT(*) AS play_count, 36 COUNT(DISTINCT s.user_id) AS unique_listeners 37 FROM user_artists ua 38 LEFT JOIN artists a ON ua.artist_id = a.id 39 LEFT JOIN users u ON ua.user_id = u.id 40 LEFT JOIN scrobbles s ON s.artist_id = a.id 41 WHERE u.did = ? OR u.handle = ? 42 GROUP BY a.* 43 ORDER BY play_count DESC OFFSET ? LIMIT ?; 44 "#, 45 )?, 46 None => conn.prepare( 47 "SELECT a.*, 48 COUNT(*) AS play_count, 49 COUNT(DISTINCT s.user_id) AS unique_listeners 50 FROM artists a 51 LEFT JOIN scrobbles s ON s.artist_id = a.id 52 GROUP BY a.* 53 ORDER BY play_count DESC OFFSET ? LIMIT ?", 54 )?, 55 }; 56 57 match did { 58 Some(did) => { 59 let artists = stmt.query_map( 60 [&did, &did, &limit.to_string(), &offset.to_string()], 61 |row| { 62 Ok(Artist { 63 id: row.get(0)?, 64 name: row.get(1)?, 65 biography: row.get(2)?, 66 born: row.get(3)?, 67 born_in: row.get(4)?, 68 died: row.get(5)?, 69 picture: row.get(6)?, 70 sha256: row.get(7)?, 71 spotify_link: row.get(8)?, 72 tidal_link: row.get(9)?, 73 youtube_link: row.get(10)?, 74 apple_music_link: row.get(11)?, 75 uri: row.get(12)?, 76 play_count: row.get(13)?, 77 unique_listeners: row.get(14)?, 78 }) 79 }, 80 )?; 81 82 let artists: Result<Vec<_>, _> = artists.collect(); 83 Ok(HttpResponse::Ok().json(artists?)) 84 } 85 None => { 86 let artists = stmt.query_map([limit, offset], |row| { 87 Ok(Artist { 88 id: row.get(0)?, 89 name: row.get(1)?, 90 biography: row.get(2)?, 91 born: row.get(3)?, 92 born_in: row.get(4)?, 93 died: row.get(5)?, 94 picture: row.get(6)?, 95 sha256: row.get(7)?, 96 spotify_link: row.get(8)?, 97 tidal_link: row.get(9)?, 98 youtube_link: row.get(10)?, 99 apple_music_link: row.get(11)?, 100 uri: row.get(12)?, 101 play_count: row.get(13)?, 102 unique_listeners: row.get(14)?, 103 }) 104 })?; 105 106 let artists: Result<Vec<_>, _> = artists.collect(); 107 Ok(HttpResponse::Ok().json(artists?)) 108 } 109 } 110} 111 112pub async fn get_top_artists( 113 payload: &mut web::Payload, 114 _req: &HttpRequest, 115 conn: Arc<Mutex<Connection>>, 116) -> Result<HttpResponse, Error> { 117 let body = read_payload!(payload); 118 let params = serde_json::from_slice::<GetTopArtistsParams>(&body)?; 119 let pagination = params.pagination.unwrap_or_default(); 120 let offset = pagination.skip.unwrap_or(0); 121 let limit = pagination.take.unwrap_or(20); 122 let did = params.user_did; 123 124 let conn = conn.lock().unwrap(); 125 let mut stmt = match did { 126 Some(_) => conn.prepare( 127 r#" 128 SELECT 129 s.artist_id AS id, 130 ar.name AS artist_name, 131 ar.picture AS picture, 132 ar.sha256 AS sha256, 133 ar.uri AS uri, 134 COUNT(*) AS play_count, 135 COUNT(DISTINCT s.user_id) AS unique_listeners 136 FROM 137 scrobbles s 138 LEFT JOIN 139 artists ar ON s.artist_id = ar.id 140 LEFT JOIN 141 users u ON s.user_id = u.id 142 WHERE 143 s.artist_id IS NOT NULL AND (u.did = ? OR u.handle = ?) 144 GROUP BY 145 s.artist_id, ar.name, ar.uri, ar.picture, ar.sha256 146 ORDER BY 147 play_count DESC 148 OFFSET ? 149 LIMIT ?; 150 "#, 151 )?, 152 None => conn.prepare( 153 r#" 154 SELECT 155 s.artist_id AS id, 156 ar.name AS artist_name, 157 ar.picture AS picture, 158 ar.sha256 AS sha256, 159 ar.uri AS uri, 160 COUNT(*) AS play_count, 161 COUNT(DISTINCT s.user_id) AS unique_listeners 162 FROM 163 scrobbles s 164 LEFT JOIN 165 artists ar ON s.artist_id = ar.id 166 WHERE 167 s.artist_id IS NOT NULL 168 GROUP BY 169 s.artist_id, ar.name, ar.uri, ar.picture, ar.sha256 170 ORDER BY 171 play_count DESC 172 OFFSET ? 173 LIMIT ?; 174 "#, 175 )?, 176 }; 177 178 match did { 179 Some(did) => { 180 let artists = stmt.query_map( 181 [&did, &did, &limit.to_string(), &offset.to_string()], 182 |row| { 183 Ok(Artist { 184 id: row.get(0)?, 185 name: row.get(1)?, 186 biography: None, 187 born: None, 188 born_in: None, 189 died: None, 190 picture: row.get(2)?, 191 sha256: row.get(3)?, 192 spotify_link: None, 193 tidal_link: None, 194 youtube_link: None, 195 apple_music_link: None, 196 uri: row.get(4)?, 197 play_count: Some(row.get(5)?), 198 unique_listeners: Some(row.get(6)?), 199 }) 200 }, 201 )?; 202 203 let artists: Result<Vec<_>, _> = artists.collect(); 204 Ok(HttpResponse::Ok().json(artists?)) 205 } 206 None => { 207 let artists = stmt.query_map([limit, offset], |row| { 208 Ok(Artist { 209 id: row.get(0)?, 210 name: row.get(1)?, 211 biography: None, 212 born: None, 213 born_in: None, 214 died: None, 215 picture: row.get(2)?, 216 sha256: row.get(3)?, 217 spotify_link: None, 218 tidal_link: None, 219 youtube_link: None, 220 apple_music_link: None, 221 uri: row.get(4)?, 222 play_count: Some(row.get(5)?), 223 unique_listeners: Some(row.get(6)?), 224 }) 225 })?; 226 227 let artists: Result<Vec<_>, _> = artists.collect(); 228 Ok(HttpResponse::Ok().json(artists?)) 229 } 230 } 231} 232 233pub async fn get_artist_tracks( 234 payload: &mut web::Payload, 235 _req: &HttpRequest, 236 conn: Arc<Mutex<Connection>>, 237) -> Result<HttpResponse, Error> { 238 let body = read_payload!(payload); 239 let params = serde_json::from_slice::<GetArtistTracksParams>(&body)?; 240 let pagination = params.pagination.unwrap_or_default(); 241 let offset = pagination.skip.unwrap_or(0); 242 let limit = pagination.take.unwrap_or(20); 243 let conn = conn.lock().unwrap(); 244 245 let mut stmt = conn.prepare(r#" 246 SELECT 247 t.id, 248 t.title, 249 t.artist, 250 t.album_artist, 251 t.album, 252 t.uri, 253 t.album_art, 254 t.duration, 255 t.disc_number, 256 t.track_number, 257 t.artist_uri, 258 t.album_uri, 259 t.sha256, 260 t.copyright_message, 261 t.label, 262 t.created_at, 263 COUNT(*) AS play_count, 264 COUNT(DISTINCT s.user_id) AS unique_listeners 265 FROM artist_tracks at 266 LEFT JOIN tracks t ON at.track_id = t.id 267 LEFT JOIN artists a ON at.artist_id = a.id 268 LEFT JOIN scrobbles s ON s.track_id = t.id 269 WHERE at.artist_id = ? OR a.uri = ? 270 GROUP BY 271 t.id, t.title, t.artist, t.album_artist, t.album, t.uri, t.album_art, t.duration, t.disc_number, t.track_number, t.artist_uri, t.album_uri, t.sha256, t.copyright_message, t.label, t.created_at 272 ORDER BY play_count DESC 273 OFFSET ? 274 LIMIT ?; 275 "#)?; 276 277 let tracks = stmt.query_map( 278 [ 279 &params.artist_id, 280 &params.artist_id, 281 &limit.to_string(), 282 &offset.to_string(), 283 ], 284 |row| { 285 Ok(Track { 286 id: row.get(0)?, 287 title: row.get(1)?, 288 artist: row.get(2)?, 289 album_artist: row.get(3)?, 290 album: row.get(4)?, 291 uri: row.get(5)?, 292 album_art: row.get(6)?, 293 duration: row.get(7)?, 294 disc_number: row.get(8)?, 295 track_number: row.get(9)?, 296 artist_uri: row.get(10)?, 297 album_uri: row.get(11)?, 298 sha256: row.get(12)?, 299 copyright_message: row.get(13)?, 300 label: row.get(14)?, 301 created_at: row.get(15)?, 302 play_count: Some(row.get(16)?), 303 unique_listeners: Some(row.get(17)?), 304 ..Default::default() 305 }) 306 }, 307 )?; 308 309 let tracks: Result<Vec<_>, _> = tracks.collect(); 310 Ok(HttpResponse::Ok().json(tracks?)) 311} 312 313pub async fn get_artist_albums( 314 payload: &mut web::Payload, 315 _req: &HttpRequest, 316 conn: Arc<Mutex<Connection>>, 317) -> Result<HttpResponse, Error> { 318 let body = read_payload!(payload); 319 let params = serde_json::from_slice::<GetArtistAlbumsParams>(&body)?; 320 let conn = conn.lock().unwrap(); 321 tracing::info!(artist_id = %params.artist_id, "Get artist albums"); 322 323 let mut stmt = conn.prepare(r#" 324 SELECT 325 al.id, 326 al.title, 327 al.artist, 328 al.album_art, 329 al.release_date, 330 al.year, 331 al.uri, 332 al.sha256, 333 al.artist_uri, 334 COUNT(*) AS play_count, 335 COUNT(DISTINCT s.user_id) AS unique_listeners 336 FROM 337 artist_albums aa 338 LEFT JOIN artists ar ON aa.artist_id = ar.id 339 LEFT JOIN albums al ON aa.album_id = al.id 340 LEFT JOIN scrobbles s ON aa.album_id = s.album_id 341 WHERE ar.id = ? OR ar.uri = ? 342 GROUP BY al.id, al.title, al.artist, al.album_art, al.release_date, al.year, al.uri, al.sha256, al.artist_uri 343 ORDER BY play_count DESC; 344 "#)?; 345 346 let albums = stmt.query_map([&params.artist_id, &params.artist_id], |row| { 347 Ok(Album { 348 id: row.get(0)?, 349 title: row.get(1)?, 350 artist: row.get(2)?, 351 album_art: row.get(3)?, 352 release_date: row.get(4)?, 353 year: row.get(5)?, 354 spotify_link: None, 355 tidal_link: None, 356 youtube_link: None, 357 apple_music_link: None, 358 sha256: row.get(7)?, 359 uri: row.get(6)?, 360 artist_uri: row.get(8)?, 361 play_count: Some(row.get(9)?), 362 unique_listeners: Some(row.get(10)?), 363 }) 364 })?; 365 366 let albums: Result<Vec<_>, _> = albums.collect(); 367 Ok(HttpResponse::Ok().json(albums?)) 368} 369 370pub async fn get_artist_listeners( 371 payload: &mut web::Payload, 372 _req: &HttpRequest, 373 conn: Arc<Mutex<Connection>>, 374) -> Result<HttpResponse, Error> { 375 let body = read_payload!(payload); 376 let params = serde_json::from_slice::<GetArtistListenersParams>(&body)?; 377 let pagination = params.pagination.unwrap_or_default(); 378 let offset = pagination.skip.unwrap_or(0); 379 let limit = pagination.take.unwrap_or(10); 380 tracing::info!(artist_id = %params.artist_id, limit, offset, "Get artist listeners"); 381 382 let conn = conn.lock().unwrap(); 383 let mut stmt = 384 conn.prepare("SELECT id, name, uri FROM artists WHERE id = ? OR uri = ? OR name = ?")?; 385 let artist = stmt.query_row( 386 [&params.artist_id, &params.artist_id, &params.artist_id], 387 |row| { 388 Ok(crate::types::artist::ArtistBasic { 389 id: row.get(0)?, 390 name: row.get(1)?, 391 uri: row.get(2)?, 392 }) 393 }, 394 )?; 395 396 if artist.id.is_empty() { 397 return Ok(HttpResponse::Ok().json(Vec::<ArtistListener>::new())); 398 } 399 400 let mut stmt = conn.prepare( 401 r#" 402 WITH user_track_counts AS ( 403 SELECT 404 s.user_id, 405 s.track_id, 406 t.album_artist AS artist, 407 t.title as track_title, 408 t.uri as track_uri, 409 COUNT(*) as play_count 410 FROM scrobbles s 411 JOIN tracks t ON s.track_id = t.id 412 WHERE t.album_artist = ? 413 GROUP BY s.user_id, s.track_id, t.title, t.uri, t.album_artist 414 ), 415 user_top_tracks AS ( 416 SELECT 417 user_id, 418 artist, 419 track_id, 420 track_title, 421 track_uri, 422 play_count, 423 ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY play_count DESC, track_title) as rn 424 FROM user_track_counts 425 ), 426 artist_listener_counts AS ( 427 SELECT 428 user_id, 429 artist, 430 SUM(play_count) as total_artist_plays 431 FROM user_track_counts 432 GROUP BY user_id, artist 433 ), 434 top_artist_listeners AS ( 435 SELECT 436 user_id, 437 artist, 438 total_artist_plays, 439 ROW_NUMBER() OVER (ORDER BY total_artist_plays DESC) as listener_rank 440 FROM artist_listener_counts 441 ), 442 paginated_listeners AS ( 443 SELECT 444 user_id, 445 artist, 446 total_artist_plays, 447 listener_rank 448 FROM top_artist_listeners 449 ORDER BY listener_rank 450 LIMIT ? OFFSET ? 451 ) 452 SELECT 453 pl.artist, 454 pl.listener_rank, 455 u.id as user_id, 456 u.display_name, 457 u.did, 458 u.handle, 459 u.avatar, 460 pl.total_artist_plays, 461 utt.track_title as most_played_track, 462 utt.track_uri as most_played_track_uri, 463 utt.play_count as track_play_count 464 FROM paginated_listeners pl 465 JOIN users u ON pl.user_id = u.id 466 JOIN user_top_tracks utt ON pl.user_id = utt.user_id 467 AND utt.rn = 1 468 ORDER BY pl.listener_rank; 469 "#, 470 )?; 471 472 let listeners = stmt.query_map( 473 [&artist.name, &limit.to_string(), &offset.to_string()], 474 |row| { 475 Ok(ArtistListener { 476 artist: row.get(0)?, 477 listener_rank: row.get(1)?, 478 user_id: row.get(2)?, 479 display_name: row.get(3)?, 480 did: row.get(4)?, 481 handle: row.get(5)?, 482 avatar: row.get(6)?, 483 total_artist_plays: row.get(7)?, 484 most_played_track: row.get(8)?, 485 most_played_track_uri: row.get(9)?, 486 track_play_count: row.get(10)?, 487 }) 488 }, 489 )?; 490 491 let listeners: Result<Vec<_>, _> = listeners.collect(); 492 Ok(HttpResponse::Ok().json(listeners?)) 493}