A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at setup-tracing 381 lines 15 kB view raw
1use std::sync::{Arc, Mutex}; 2 3use crate::types::track::{GetLovedTracksParams, GetTopTracksParams, GetTracksParams, Track}; 4use actix_web::{web, HttpRequest, HttpResponse}; 5use anyhow::Error; 6use duckdb::Connection; 7use tokio_stream::StreamExt; 8 9use crate::read_payload; 10 11pub async fn get_tracks( 12 payload: &mut web::Payload, 13 _req: &HttpRequest, 14 conn: Arc<Mutex<Connection>>, 15) -> Result<HttpResponse, Error> { 16 let body = read_payload!(payload); 17 let params = serde_json::from_slice::<GetTracksParams>(&body)?; 18 let pagination = params.pagination.unwrap_or_default(); 19 let offset = pagination.skip.unwrap_or(0); 20 let limit = pagination.take.unwrap_or(20); 21 let did = params.user_did; 22 tracing::info!(limit, offset, user_did = ?did, "Get tracks"); 23 24 let conn = conn.lock().unwrap(); 25 match did { 26 Some(did) => { 27 let mut stmt = conn.prepare(r#" 28 SELECT 29 t.id, 30 t.title, 31 t.artist, 32 t.album_artist, 33 t.album_art, 34 t.album, 35 t.track_number, 36 t.duration, 37 t.mb_id, 38 t.youtube_link, 39 t.spotify_link, 40 t.tidal_link, 41 t.apple_music_link, 42 t.sha256, 43 t.composer, 44 t.genre, 45 t.disc_number, 46 t.label, 47 t.uri, 48 t.copyright_message, 49 t.artist_uri, 50 t.album_uri, 51 t.created_at, 52 COUNT(*) AS play_count, 53 COUNT(DISTINCT s.user_id) AS unique_listeners 54 FROM tracks t 55 LEFT JOIN user_tracks ut ON t.id = ut.track_id 56 LEFT JOIN users u ON ut.user_id = u.id 57 LEFT JOIN scrobbles s ON s.track_id = t.id 58 WHERE u.did = ? OR u.handle = ? 59 GROUP BY t.id, t.title, t.artist, t.album_artist, t.album_art, t.album, t.track_number, t.duration, t.mb_id, t.youtube_link, t.spotify_link, t.tidal_link, t.apple_music_link, t.sha256, t.composer, t.genre, t.disc_number, t.label, t.copyright_message, t.uri, t.created_at, t.artist_uri, t.album_uri 60 ORDER BY play_count DESC 61 OFFSET ? 62 LIMIT ?; 63 "#)?; 64 let tracks = stmt.query_map( 65 [&did, &did, &limit.to_string(), &offset.to_string()], 66 |row| { 67 Ok(Track { 68 id: row.get(0)?, 69 title: row.get(1)?, 70 artist: row.get(2)?, 71 album_artist: row.get(3)?, 72 album_art: row.get(4)?, 73 album: row.get(5)?, 74 track_number: row.get(6)?, 75 duration: row.get(7)?, 76 mb_id: row.get(8)?, 77 youtube_link: row.get(9)?, 78 spotify_link: row.get(10)?, 79 tidal_link: row.get(11)?, 80 apple_music_link: row.get(12)?, 81 sha256: row.get(13)?, 82 composer: row.get(14)?, 83 genre: row.get(15)?, 84 disc_number: row.get(16)?, 85 label: row.get(17)?, 86 uri: row.get(18)?, 87 copyright_message: row.get(19)?, 88 artist_uri: row.get(20)?, 89 album_uri: row.get(21)?, 90 created_at: row.get(22)?, 91 play_count: row.get(23)?, 92 unique_listeners: row.get(24)?, 93 ..Default::default() 94 }) 95 }, 96 )?; 97 let tracks: Result<Vec<_>, _> = tracks.collect(); 98 Ok(HttpResponse::Ok().json(tracks?)) 99 } 100 None => { 101 let mut stmt = conn.prepare(r#" 102 SELECT 103 t.id, 104 t.title, 105 t.artist, 106 t.album_artist, 107 t.album_art, 108 t.album, 109 t.track_number, 110 t.duration, 111 t.mb_id, 112 t.youtube_link, 113 t.spotify_link, 114 t.tidal_link, 115 t.apple_music_link, 116 t.sha256, 117 t.composer, 118 t.genre, 119 t.disc_number, 120 t.label, 121 t.uri, 122 t.copyright_message, 123 t.artist_uri, 124 t.album_uri, 125 t.created_at, 126 COUNT(*) AS play_count, 127 COUNT(DISTINCT s.user_id) AS unique_listeners 128 FROM tracks t 129 LEFT JOIN scrobbles s ON s.track_id = t.id 130 GROUP BY t.id, t.title, t.artist, t.album_artist, t.album_art, t.album, t.track_number, t.duration, t.mb_id, t.youtube_link, t.spotify_link, t.tidal_link, t.apple_music_link, t.sha256, t.composer, t.genre, t.disc_number, t.label, t.copyright_message, t.uri, t.created_at, t.artist_uri, t.album_uri 131 ORDER BY play_count DESC 132 OFFSET ? 133 LIMIT ?; 134 "#)?; 135 let tracks = stmt.query_map([limit, offset], |row| { 136 Ok(Track { 137 id: row.get(0)?, 138 title: row.get(1)?, 139 artist: row.get(2)?, 140 album_artist: row.get(3)?, 141 album_art: row.get(4)?, 142 album: row.get(5)?, 143 track_number: row.get(6)?, 144 duration: row.get(7)?, 145 mb_id: row.get(8)?, 146 youtube_link: row.get(9)?, 147 spotify_link: row.get(10)?, 148 tidal_link: row.get(11)?, 149 apple_music_link: row.get(12)?, 150 sha256: row.get(13)?, 151 composer: row.get(14)?, 152 genre: row.get(15)?, 153 disc_number: row.get(16)?, 154 label: row.get(17)?, 155 uri: row.get(18)?, 156 copyright_message: row.get(19)?, 157 artist_uri: row.get(20)?, 158 album_uri: row.get(21)?, 159 created_at: row.get(22)?, 160 play_count: row.get(23)?, 161 unique_listeners: row.get(24)?, 162 ..Default::default() 163 }) 164 })?; 165 let tracks: Result<Vec<_>, _> = tracks.collect(); 166 Ok(HttpResponse::Ok().json(tracks?)) 167 } 168 } 169} 170 171pub async fn get_loved_tracks( 172 payload: &mut web::Payload, 173 _req: &HttpRequest, 174 conn: Arc<Mutex<Connection>>, 175) -> Result<HttpResponse, Error> { 176 let body = read_payload!(payload); 177 let params = serde_json::from_slice::<GetLovedTracksParams>(&body)?; 178 let pagination = params.pagination.unwrap_or_default(); 179 let offset = pagination.skip.unwrap_or(0); 180 let limit = pagination.take.unwrap_or(20); 181 let did = params.user_did; 182 tracing::info!(limit, offset, user_did = ?did, "Get loved tracks"); 183 184 let conn = conn.lock().unwrap(); 185 let mut stmt = conn.prepare( 186 r#" 187 SELECT 188 t.id, 189 t.title, 190 t.artist, 191 t.album, 192 t.album_artist, 193 t.album_art, 194 t.album_uri, 195 t.artist_uri, 196 t.composer, 197 t.copyright_message, 198 t.disc_number, 199 t.duration, 200 t.track_number, 201 t.label, 202 t.spotify_link, 203 t.tidal_link, 204 t.youtube_link, 205 t.apple_music_link, 206 t.sha256, 207 t.uri, 208 u.handle, 209 u.did, 210 l.created_at 211 FROM loved_tracks l 212 LEFT JOIN users u ON l.user_id = u.id 213 LEFT JOIN tracks t ON l.track_id = t.id 214 WHERE u.did = ? OR u.handle = ? 215 ORDER BY l.created_at DESC 216 OFFSET ? 217 LIMIT ?; 218 "#, 219 )?; 220 let loved_tracks = stmt.query_map( 221 [&did, &did, &limit.to_string(), &offset.to_string()], 222 |row| { 223 Ok(Track { 224 id: row.get(0)?, 225 title: row.get(1)?, 226 artist: row.get(2)?, 227 album: row.get(3)?, 228 album_artist: row.get(4)?, 229 album_art: row.get(5)?, 230 album_uri: row.get(6)?, 231 artist_uri: row.get(7)?, 232 composer: row.get(8)?, 233 copyright_message: row.get(9)?, 234 disc_number: row.get(10)?, 235 duration: row.get(11)?, 236 track_number: row.get(12)?, 237 label: row.get(13)?, 238 spotify_link: row.get(14)?, 239 tidal_link: row.get(15)?, 240 youtube_link: row.get(16)?, 241 apple_music_link: row.get(17)?, 242 sha256: row.get(18)?, 243 uri: row.get(19)?, 244 handle: row.get(20)?, 245 did: row.get(21)?, 246 created_at: row.get(22)?, 247 ..Default::default() 248 }) 249 }, 250 )?; 251 let loved_tracks: Result<Vec<_>, _> = loved_tracks.collect(); 252 Ok(HttpResponse::Ok().json(loved_tracks?)) 253} 254 255pub async fn get_top_tracks( 256 payload: &mut web::Payload, 257 _req: &HttpRequest, 258 conn: Arc<Mutex<Connection>>, 259) -> Result<HttpResponse, Error> { 260 let body = read_payload!(payload); 261 let params = serde_json::from_slice::<GetTopTracksParams>(&body)?; 262 let pagination = params.pagination.unwrap_or_default(); 263 let offset = pagination.skip.unwrap_or(0); 264 let limit = pagination.take.unwrap_or(20); 265 let did = params.user_did; 266 tracing::info!(limit, offset, user_did = ?did, "Get top tracks"); 267 268 let conn = conn.lock().unwrap(); 269 match did { 270 Some(did) => { 271 let mut stmt = conn.prepare(r#" 272 SELECT 273 t.id, 274 t.title, 275 t.artist, 276 t.album_artist, 277 t.album, 278 t.uri, 279 t.album_art, 280 t.duration, 281 t.disc_number, 282 t.track_number, 283 t.artist_uri, 284 t.album_uri, 285 t.sha256, 286 t.created_at, 287 COUNT(*) AS play_count, 288 COUNT(DISTINCT s.user_id) AS unique_listeners 289 FROM scrobbles s 290 LEFT JOIN tracks t ON s.track_id = t.id 291 LEFT JOIN artists ar ON s.artist_id = ar.id 292 LEFT JOIN albums a ON s.album_id = a.id 293 LEFT JOIN users u ON s.user_id = u.id 294 WHERE u.did = ? OR u.handle = ? 295 GROUP BY t.id, s.track_id, t.title, ar.name, a.title, t.artist, t.uri, t.album_art, t.duration, t.disc_number, t.track_number, t.artist_uri, t.album_uri, t.created_at, t.sha256, t.album_artist, t.album 296 ORDER BY play_count DESC 297 OFFSET ? 298 LIMIT ?; 299 "#)?; 300 let top_tracks = stmt.query_map( 301 [&did, &did, &limit.to_string(), &offset.to_string()], 302 |row| { 303 Ok(Track { 304 id: row.get(0)?, 305 title: row.get(1)?, 306 artist: row.get(2)?, 307 album_artist: row.get(3)?, 308 album: row.get(4)?, 309 uri: row.get(5)?, 310 album_art: row.get(6)?, 311 duration: row.get(7)?, 312 disc_number: row.get(8)?, 313 track_number: row.get(9)?, 314 artist_uri: row.get(10)?, 315 album_uri: row.get(11)?, 316 sha256: row.get(12)?, 317 created_at: row.get(13)?, 318 play_count: row.get(14)?, 319 unique_listeners: row.get(15)?, 320 ..Default::default() 321 }) 322 }, 323 )?; 324 let top_tracks: Result<Vec<_>, _> = top_tracks.collect(); 325 Ok(HttpResponse::Ok().json(top_tracks?)) 326 } 327 None => { 328 let mut stmt = conn.prepare(r#" 329 SELECT 330 t.id, 331 t.title, 332 t.artist, 333 t.album_artist, 334 t.album, 335 t.uri, 336 t.album_art, 337 t.duration, 338 t.disc_number, 339 t.track_number, 340 t.artist_uri, 341 t.album_uri, 342 t.sha256, 343 t.created_at, 344 COUNT(*) AS play_count, 345 COUNT(DISTINCT s.user_id) AS unique_listeners 346 FROM scrobbles s 347 LEFT JOIN tracks t ON s.track_id = t.id 348 LEFT JOIN artists ar ON s.artist_id = ar.id 349 LEFT JOIN albums a ON s.album_id = a.id 350 WHERE s.track_id IS NOT NULL AND s.artist_id IS NOT NULL AND s.album_id IS NOT NULL 351 GROUP BY t.id, s.track_id, t.title, ar.name, a.title, t.artist, t.uri, t.album_art, t.duration, t.disc_number, t.track_number, t.artist_uri, t.album_uri, t.created_at, t.sha256, t.album_artist, t.album 352 ORDER BY play_count DESC 353 OFFSET ? 354 LIMIT ?; 355 "#)?; 356 let top_tracks = stmt.query_map([limit, offset], |row| { 357 Ok(Track { 358 id: row.get(0)?, 359 title: row.get(1)?, 360 artist: row.get(2)?, 361 album_artist: row.get(3)?, 362 album: row.get(4)?, 363 uri: row.get(5)?, 364 album_art: row.get(6)?, 365 duration: row.get(7)?, 366 disc_number: row.get(8)?, 367 track_number: row.get(9)?, 368 artist_uri: row.get(10)?, 369 album_uri: row.get(11)?, 370 sha256: row.get(12)?, 371 created_at: row.get(13)?, 372 play_count: row.get(14)?, 373 unique_listeners: row.get(15)?, 374 ..Default::default() 375 }) 376 })?; 377 let top_tracks: Result<Vec<_>, _> = top_tracks.collect(); 378 Ok(HttpResponse::Ok().json(top_tracks?)) 379 } 380 } 381}