From 7e6b65392e4828d41c0408f9cb9ae3910abb7f14 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Sat, 17 Aug 2024 01:25:41 +0200 Subject: [PATCH] playlists --- src/library/event.rs | 62 +++++++++++++++++++++++++++++++++++++++++++ src/library/mod.rs | 1 + src/library/track.rs | 22 +++++++++++++-- src/main.rs | 3 ++- src/route/event.rs | 34 ++++++++++++++++++++++++ src/route/mod.rs | 1 + src/route/playlist.rs | 3 ++- 7 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/library/event.rs create mode 100644 src/route/event.rs diff --git a/src/library/event.rs b/src/library/event.rs new file mode 100644 index 0000000..f06e038 --- /dev/null +++ b/src/library/event.rs @@ -0,0 +1,62 @@ +use mongod::{ + assert_reference_of, + derive::{Model, Referencable}, + Model, Referencable, Reference, Validate, +}; +use mongodb::bson::doc; +use serde::{Deserialize, Serialize}; + +use crate::library::user::User; + +use super::track::Track; + +#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +pub struct Event { + pub _id: String, + pub kind: EventKind, + pub user: Reference, + pub track: Reference, + pub timestamp: i64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum EventKind { + Play, + Played, + Stop, +} + +impl Event { + pub async fn create(kind: EventKind, user: &User, track: Reference) -> Self { + let event = Self { + _id: uuid::Uuid::new_v4().to_string(), + kind: kind, + user: user.reference(), + track, + timestamp: chrono::Utc::now().timestamp(), + }; + + event.insert().await.unwrap(); + + event + } + + pub async fn get_latest_events_of(u: &User) -> Vec { + Self::find( + doc! { "user": u.reference() }, + Some(300), + Some(doc! { "timestamp": -1 }), + ) + .await + .unwrap() + } +} + +impl Validate for Event { + async fn validate(&self) -> Result<(), String> { + assert_reference_of!(self.user, User); + assert_reference_of!(self.track, Track); + + Ok(()) + } +} diff --git a/src/library/mod.rs b/src/library/mod.rs index d2c4131..5de78ed 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -12,6 +12,7 @@ use crate::cache::RouteCache; pub mod album; pub mod artist; +pub mod event; pub mod metadata; pub mod playlist; pub mod track; diff --git a/src/library/track.rs b/src/library/track.rs index 1b30d5d..1ece95d 100644 --- a/src/library/track.rs +++ b/src/library/track.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use mongod::{ assert_reference_of, derive::{Model, Referencable}, @@ -12,9 +14,9 @@ use crate::{ route::ToAPI, }; -use super::metadata::AudioMetadata; +use super::{event::Event, metadata::AudioMetadata, user::User}; -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable, PartialEq, Eq)] pub struct Track { pub _id: String, pub path: String, @@ -42,6 +44,22 @@ impl Track { .unwrap(); } + pub async fn get_latest_of_user(u: &User) -> Vec { + let latest_events = Event::get_latest_events_of(u).await; + let mut ids = HashSet::new(); + let mut tracks = vec![]; + + for event in latest_events { + let track: Track = event.track.get().await; + if !ids.contains(&track._id) { + ids.insert(track._id.clone()); + tracks.push(track); + } + } + + tracks + } + /// Transcode audio to OPUS with `bitrate` pub fn get_opus(&self, bitrate: u32) -> Option { self.transcode("libopus", bitrate, "opus") diff --git a/src/main.rs b/src/main.rs index ddd6eac..2c6c16c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,7 +69,8 @@ async fn rocket() -> _ { route::playlist::playlist_tracks_route, route::admin::clean_library, route::admin::get_orphans_route, - route::admin::get_singles_route + route::admin::get_singles_route, + route::event::event_report_route ], ) .manage(lib) diff --git a/src/route/event.rs b/src/route/event.rs new file mode 100644 index 0000000..1031a12 --- /dev/null +++ b/src/route/event.rs @@ -0,0 +1,34 @@ +use super::api_error; +use super::FallibleApiResponse; +use mongod::reference_of; +use mongod::Model; +use mongodb::bson::doc; +use rocket::post; +use rocket::serde::json::Json; +use serde::Deserialize; +use serde_json::json; + +use crate::library::event::Event; +use crate::library::event::EventKind; +use crate::library::track::Track; +use crate::library::user::User; +use mongod::Referencable; + +#[derive(Debug, Clone, Deserialize)] +pub struct EventJson { + pub kind: EventKind, + pub track: String, +} + +#[post("/report", data = "")] +pub async fn event_report_route(report: Json, u: User) -> FallibleApiResponse { + let track = &report.track; + Event::create( + report.kind.clone(), + &u, + reference_of!(Track, track).ok_or_else(|| api_error("Invalid track"))?, + ) + .await; + + Ok(json!({"ok": 1})) +} diff --git a/src/route/mod.rs b/src/route/mod.rs index 91edffd..74de508 100644 --- a/src/route/mod.rs +++ b/src/route/mod.rs @@ -8,6 +8,7 @@ use serde_json::json; pub mod admin; pub mod album; pub mod artist; +pub mod event; pub mod playlist; pub mod track; pub mod user; diff --git a/src/route/playlist.rs b/src/route/playlist.rs index dd20e19..c78eadd 100644 --- a/src/route/playlist.rs +++ b/src/route/playlist.rs @@ -87,7 +87,8 @@ pub async fn playlist_route(id: &str, u: User) -> FallibleApiResponse { #[get("/playlist//tracks")] pub async fn playlist_tracks_route(id: &str, u: User) -> FallibleApiResponse { if id == "recents" { - // todo : recently played + let tracks = Track::get_latest_of_user(&u).await; + return Ok(json!(to_api(&tracks).await)); } if id == "recentlyAdded" {