From 82c330cc1398b70052aaa2664b0dfefaf2c16fdf Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sun, 11 Aug 2024 21:56:55 +0200 Subject: [PATCH] playlists --- src/library/playlist.rs | 32 ++++++++++++-- src/main.rs | 4 +- src/route/mod.rs | 2 +- src/route/playlist.rs | 92 +++++++++++++++++++++++++++++++++++------ 4 files changed, 112 insertions(+), 18 deletions(-) diff --git a/src/library/playlist.rs b/src/library/playlist.rs index 05c18a5..0c7147b 100644 --- a/src/library/playlist.rs +++ b/src/library/playlist.rs @@ -1,11 +1,14 @@ use mongod::{ assert_reference_of, derive::{Model, Referencable}, - Referencable, Reference, Validate, + reference_of, Model, Referencable, Reference, Validate, }; use serde::{Deserialize, Serialize}; -use crate::{library::{track::Track, user::User}, route::ToAPI}; +use crate::{ + library::{track::Track, user::User}, + route::ToAPI, +}; #[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] pub struct Playlist { @@ -16,6 +19,29 @@ pub struct Playlist { pub tracks: Vec, } +impl Playlist { + pub async fn create( + owner: User, + title: &str, + visibility: Visibility, + tracks: &[String], + ) -> Option { + let mut tracks_ref = vec![]; + + for track in tracks { + tracks_ref.push(reference_of!(Track, track)?); + } + + Some(Self { + _id: uuid::Uuid::new_v4().to_string(), + owner: owner.reference(), + title: title.to_string(), + visibility, + tracks: tracks_ref, + }) + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum Visibility { Private, @@ -43,4 +69,4 @@ impl ToAPI for Playlist { "tracks": self.tracks.iter().map(|x| x.id()).collect::>() }) } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 76a97e5..3792cc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,9 @@ async fn rocket() -> _ { route::user::users_route, route::track::track_audio_opus128_route, route::playlist::playlists_route, - route::playlist::playlist_route + route::playlist::playlist_route, + route::playlist::playlist_add_route, + route::playlist::playlist_edit_route ], ) .manage(lib) diff --git a/src/route/mod.rs b/src/route/mod.rs index 4a2e01f..4289343 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -7,9 +7,9 @@ use serde_json::json; pub mod album; pub mod artist; +pub mod playlist; pub mod track; pub mod user; -pub mod playlist; // todo : rework api diff --git a/src/route/playlist.rs b/src/route/playlist.rs index cd29fac..68a147c 100644 --- a/src/route/playlist.rs +++ b/src/route/playlist.rs @@ -1,27 +1,34 @@ -use super::api_error; -use super::FallibleApiResponse; -use super::ToAPI; +use crate::library::track::Track; +use crate::library::user::User; +use mongod::reference_of; use mongod::Model; use mongod::Referencable; use mongodb::bson::doc; -use rocket::*; +use rocket::get; +use rocket::post; +use rocket::serde::json::Json; use serde_json::json; use crate::library::playlist::Playlist; use crate::library::playlist::Visibility; -use crate::library::user::User; +use crate::route::FallibleApiResponse; +use super::api_error; +use super::ToAPI; #[get("/playlists")] pub async fn playlists_route(u: User) -> FallibleApiResponse { - let mut playlists = vec![ - "recent".to_string() - ]; + let mut playlists = vec![json!({"id": "recent", "name": "Recently Played"})]; - let own_playlists = Playlist::find(doc! { "owner": u.reference()}, None).await.unwrap(); + let own_playlists = Playlist::find(doc! { "owner": u.reference()}, None) + .await + .unwrap(); for playlist in own_playlists { - playlists.push(playlist._id); + playlists.push(json!({ + "id": playlist._id, + "name": playlist.title + })); } Ok(json!(playlists)) @@ -33,11 +40,70 @@ pub async fn playlist_route(id: &str, u: User) -> FallibleApiResponse { // todo : recently played } - let playlist = Playlist::get(id).await.ok_or_else(|| api_error("No playlist with that ID found"))?; + let playlist = Playlist::get(id) + .await + .ok_or_else(|| api_error("No playlist with that ID found"))?; - if matches!(playlist.visibility, Visibility::Private) && u.username != playlist.owner.get::().await.username { + if matches!(playlist.visibility, Visibility::Private) + && u.username != playlist.owner.get::().await.username + { return Err(api_error("Forbidden")); } Ok(playlist.api().await) -} \ No newline at end of file +} + +#[derive(rocket::serde::Deserialize)] +pub struct PlaylistData { + pub title: String, + pub visibility: Visibility, + pub tracks: Vec, +} + +#[post("/playlist", data = "")] +pub async fn playlist_add_route(playlist: Json, u: User) -> FallibleApiResponse { + let playlist = Playlist::create( + u, + &playlist.title, + playlist.visibility.clone(), + &playlist.tracks, + ) + .await + .ok_or_else(|| api_error("Failed to create playlist"))?; + playlist.insert().await.unwrap(); + + Ok(json!({"created": playlist._id})) +} + +#[post("/playlist/", data = "")] +pub async fn playlist_edit_route( + id: &str, + edit: Json, + u: User, +) -> FallibleApiResponse { + let mut playlist = Playlist::get(id) + .await + .ok_or_else(|| api_error("No playlist with that ID found"))?; + + if playlist.owner.id() != u._id { + return Err(api_error("Forbidden")); + } + + let mut tracks_ref = vec![]; + + for track in &edit.tracks { + tracks_ref + .push(reference_of!(Track, track).ok_or_else(|| api_error("Invalid tracks found"))?); + } + + playlist + .update(&json!({ + "title": edit.title, + "visibility": edit.visibility, + "tracks": tracks_ref + })) + .await + .unwrap(); + + Ok(json!({"edited": playlist._id})) +}