forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
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}