A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at main 218 lines 7.4 kB view raw
1use std::sync::{Arc, Mutex}; 2 3use crate::types::scrobble::{GetScrobblesParams, ScrobbleTrack}; 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_scrobbles( 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::<GetScrobblesParams>(&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 scrobbles"); 23 24 let conn = conn.lock().unwrap(); 25 let mut stmt = match did { 26 Some(_) => conn.prepare(r#" 27 SELECT 28 s.id, 29 t.id as track_id, 30 t.title, 31 t.artist, 32 t.album_artist, 33 t.album, 34 t.album_art, 35 u.handle, 36 u.did, 37 s.uri, 38 t.uri as track_uri, 39 a.uri as artist_uri, 40 al.uri as album_uri, 41 s.created_at 42 FROM scrobbles s 43 LEFT JOIN artists a ON s.artist_id = a.id 44 LEFT JOIN albums al ON s.album_id = al.id 45 LEFT JOIN tracks t ON s.track_id = t.id 46 LEFT JOIN users u ON s.user_id = u.id 47 WHERE u.did = ? OR u.handle = ? 48 GROUP BY s.id, s.created_at, t.id, t.title, t.artist, t.album_artist, t.album, t.album_art, s.uri, t.uri, u.handle, u.did, a.uri, al.uri, s.created_at 49 ORDER BY s.created_at DESC 50 OFFSET ? 51 LIMIT ?; 52 "#)?, 53 None => conn.prepare(r#" 54 SELECT 55 s.id, 56 t.id as track_id, 57 t.title, 58 t.artist, 59 t.album_artist, 60 t.album, 61 t.album_art, 62 u.handle, 63 u.did, 64 s.uri, 65 t.uri as track_uri, 66 a.uri as artist_uri, 67 al.uri as album_uri, 68 s.created_at 69 FROM scrobbles s 70 LEFT JOIN artists a ON s.artist_id = a.id 71 LEFT JOIN albums al ON s.album_id = al.id 72 LEFT JOIN tracks t ON s.track_id = t.id 73 LEFT JOIN users u ON s.user_id = u.id 74 GROUP BY s.id, s.created_at, t.id, t.title, t.artist, t.album_artist, t.album, t.album_art, s.uri, t.uri, u.handle, u.did, a.uri, al.uri, s.created_at 75 ORDER BY s.created_at DESC 76 OFFSET ? 77 LIMIT ?; 78 "#)?, 79 }; 80 match did { 81 Some(did) => { 82 let scrobbles = stmt.query_map( 83 [&did, &did, &limit.to_string(), &offset.to_string()], 84 |row| { 85 Ok(ScrobbleTrack { 86 id: row.get(0)?, 87 track_id: row.get(1)?, 88 title: row.get(2)?, 89 artist: row.get(3)?, 90 album_artist: row.get(4)?, 91 album: row.get(5)?, 92 album_art: row.get(6)?, 93 handle: row.get(7)?, 94 did: row.get(8)?, 95 avatar: None, 96 uri: row.get(9)?, 97 track_uri: row.get(10)?, 98 artist_uri: row.get(11)?, 99 album_uri: row.get(12)?, 100 created_at: row.get(13)?, 101 }) 102 }, 103 )?; 104 let scrobbles: Result<Vec<_>, _> = scrobbles.collect(); 105 Ok(HttpResponse::Ok().json(scrobbles?)) 106 } 107 None => { 108 let scrobbles = stmt.query_map([limit, offset], |row| { 109 Ok(ScrobbleTrack { 110 id: row.get(0)?, 111 track_id: row.get(1)?, 112 title: row.get(2)?, 113 artist: row.get(3)?, 114 album_artist: row.get(4)?, 115 album: row.get(5)?, 116 album_art: row.get(6)?, 117 handle: row.get(7)?, 118 did: row.get(8)?, 119 avatar: None, 120 uri: row.get(9)?, 121 track_uri: row.get(10)?, 122 artist_uri: row.get(11)?, 123 album_uri: row.get(12)?, 124 created_at: row.get(13)?, 125 }) 126 })?; 127 let scrobbles: Result<Vec<_>, _> = scrobbles.collect(); 128 Ok(HttpResponse::Ok().json(scrobbles?)) 129 } 130 } 131} 132 133pub async fn get_distinct_scrobbles( 134 payload: &mut web::Payload, 135 _req: &HttpRequest, 136 conn: Arc<Mutex<Connection>>, 137) -> Result<HttpResponse, Error> { 138 let body = read_payload!(payload); 139 let params = serde_json::from_slice::<GetScrobblesParams>(&body)?; 140 let pagination = params.pagination.unwrap_or_default(); 141 let offset = pagination.skip.unwrap_or(0); 142 let limit = pagination.take.unwrap_or(10); 143 tracing::info!(limit, offset, user_did = ?params.user_did, "Get distinct scrobbles"); 144 145 let conn = conn.lock().unwrap(); 146 let mut stmt = conn.prepare( 147 r#" 148 WITH ranked_scrobbles AS ( 149 SELECT 150 s.id, 151 t.id AS track_id, 152 t.title, 153 t.artist, 154 t.album_artist, 155 t.album, 156 t.album_art, 157 u.handle, 158 u.did, 159 u.avatar, 160 s.uri, 161 t.uri AS track_uri, 162 a.uri AS artist_uri, 163 al.uri AS album_uri, 164 s.created_at, 165 ROW_NUMBER() OVER (PARTITION BY u.id ORDER BY s.created_at DESC 166 ) AS rn 167 FROM scrobbles s 168 LEFT JOIN artists a ON s.artist_id = a.id 169 LEFT JOIN albums al ON s.album_id = al.id 170 LEFT JOIN tracks t ON s.track_id = t.id 171 LEFT JOIN users u ON s.user_id = u.id 172 ) 173 SELECT 174 id, 175 track_id, 176 title, 177 artist, 178 album_artist, 179 album, 180 album_art, 181 handle, 182 did, 183 avatar, 184 uri, 185 track_uri, 186 artist_uri, 187 album_uri, 188 created_at 189 FROM ranked_scrobbles 190 WHERE rn = 1 191 ORDER BY created_at DESC 192 OFFSET ? 193 LIMIT ?; 194 "#, 195 )?; 196 197 let scrobbles = stmt.query_map([limit, offset], |row| { 198 Ok(ScrobbleTrack { 199 id: row.get(0)?, 200 track_id: row.get(1)?, 201 title: row.get(2)?, 202 artist: row.get(3)?, 203 album_artist: row.get(4)?, 204 album: row.get(5)?, 205 album_art: row.get(6)?, 206 handle: row.get(7)?, 207 did: row.get(8)?, 208 avatar: row.get(9)?, 209 uri: row.get(10)?, 210 track_uri: row.get(11)?, 211 artist_uri: row.get(12)?, 212 album_uri: row.get(13)?, 213 created_at: row.get(14)?, 214 }) 215 })?; 216 let scrobbles: Result<Vec<_>, _> = scrobbles.collect(); 217 Ok(HttpResponse::Ok().json(scrobbles?)) 218}