This commit is contained in:
JMARyA 2024-08-12 18:48:33 +02:00
parent 87ad77907b
commit db325e7700
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
12 changed files with 36 additions and 41 deletions

View file

@ -65,7 +65,7 @@ impl ToAPI for Album {
json!({ json!({
"id": &self._id, "id": &self._id,
"title": &self.title, "title": &self.title,
"artist": self.artist_id.as_ref().map(|x| x.id()), "artist": self.artist_id.as_ref().map(mongod::Reference::id),
"cover_url": if self.get_cover().await.is_some() { "cover_url": if self.get_cover().await.is_some() {
Some(format!("/album/{}/cover", self._id)) Some(format!("/album/{}/cover", self._id))
} else { } else {

View file

@ -5,15 +5,15 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AudioMetadata(pub serde_json::Value); pub struct AudioMetadata(pub serde_json::Value);
impl Into<mongodb::bson::Bson> for AudioMetadata { impl From<AudioMetadata> for mongodb::bson::Bson {
fn into(self) -> mongodb::bson::Bson { fn from(val: AudioMetadata) -> Self {
mongodb::bson::to_bson(&self.0).unwrap() mongodb::bson::to_bson(&val.0).unwrap()
} }
} }
impl AudioMetadata { impl AudioMetadata {
fn get_key(&self, key: &str) -> Option<&str> { fn get_key(&self, key: &str) -> Option<&str> {
Some(self.0.as_object()?.get(key)?.as_str()?) self.0.as_object()?.get(key)?.as_str()
} }
pub fn title(&self) -> Option<&str> { pub fn title(&self) -> Option<&str> {

View file

@ -31,7 +31,7 @@ pub struct Libary {
} }
impl Libary { impl Libary {
pub async fn new(root_dir: PathBuf) -> Self { pub const fn new(root_dir: PathBuf) -> Self {
Self { root_dir } Self { root_dir }
} }
@ -53,9 +53,13 @@ impl Libary {
pub async fn add_path_to_library(&self, path: &str) { pub async fn add_path_to_library(&self, path: &str) {
// search for path already present // search for path already present
if let Some(_) = Track::find_one_partial(doc! { "path": path }, json!({})).await { if Track::find_one_partial(doc! { "path": path }, json!({}))
.await
.is_some()
{
return; return;
} }
log::info!("Adding {path} to library"); log::info!("Adding {path} to library");
// add track to library // add track to library
@ -124,7 +128,7 @@ impl Libary {
); );
} }
Track::create(&entry.as_object().unwrap()).await; Track::create(entry.as_object().unwrap()).await;
} }
pub async fn get_artists(&self) -> Vec<Artist> { pub async fn get_artists(&self) -> Vec<Artist> {
@ -164,8 +168,8 @@ impl Libary {
log::info!("Rescanning library"); log::info!("Rescanning library");
for entry in WalkDir::new(self.root_dir.clone()) for entry in WalkDir::new(self.root_dir.clone())
.follow_links(true) .follow_links(true)
.into_iter() // todo : remove .into_iter()
.filter_map(|e| e.ok()) .filter_map(std::result::Result::ok)
{ {
let path = entry.path(); let path = entry.path();
if path.is_file() && is_music_file(path) { if path.is_file() && is_music_file(path) {

View file

@ -42,7 +42,7 @@ impl Playlist {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum Visibility { pub enum Visibility {
Private, Private,
Public, Public,
@ -67,7 +67,7 @@ impl ToAPI for Playlist {
"owner": self.owner.id(), "owner": self.owner.id(),
"visibility": serde_json::to_value(&self.visibility).unwrap(), "visibility": serde_json::to_value(&self.visibility).unwrap(),
"title": self.title, "title": self.title,
"tracks": self.tracks.iter().map(|x| x.id()).collect::<Vec<_>>() "tracks": self.tracks.iter().map(mongod::Reference::id).collect::<Vec<_>>()
}) })
} }
} }

View file

@ -37,7 +37,7 @@ impl Track {
meta: data.get("meta").map(|x| AudioMetadata(x.clone())), meta: data.get("meta").map(|x| AudioMetadata(x.clone())),
}; };
t.insert().await.unwrap(); t.insert().await.unwrap();
t.update(&serde_json::to_value(&data).unwrap()) t.update(&serde_json::to_value(data).unwrap())
.await .await
.unwrap(); .unwrap();
} }
@ -88,12 +88,12 @@ impl Track {
impl ToAPI for Track { impl ToAPI for Track {
async fn api(&self) -> serde_json::Value { async fn api(&self) -> serde_json::Value {
let (cover, album_title) = if let Some(album_ref) = &self.album_id { let (cover, album_title, album_id) = if let Some(album_ref) = &self.album_id {
let album = album_ref.get::<Album>().await; let album = album_ref.get::<Album>().await;
(album.get_cover().await.is_some(), album.title) (album.get_cover().await.is_some(), album.title, album._id)
} else { } else {
(false, String::new()) (false, String::new(), String::new())
}; };
let artist_title = if let Some(artist_ref) = &self.artist_id { let artist_title = if let Some(artist_ref) = &self.artist_id {
@ -108,12 +108,12 @@ impl ToAPI for Track {
json!({ json!({
"id": self._id, "id": self._id,
"title": self.title, "title": self.title,
"track_number": self.meta.as_ref().map(|x| x.track_number()), "track_number": self.meta.as_ref().map(super::metadata::AudioMetadata::track_number),
"meta": serde_json::to_value(&self.meta).unwrap(), "meta": serde_json::to_value(&self.meta).unwrap(),
"album_id": self.album_id.as_ref().map(|x| x.id().to_string()), "album_id": self.album_id.as_ref().map(|x| x.id().to_string()),
"album": album_title, "album": album_title,
"cover": if cover { "cover": if cover {
Some(format!("/album/{}/cover", self._id)) Some(format!("/album/{album_id}/cover"))
} else { } else {
None None
}, },

View file

@ -41,14 +41,14 @@ impl Validate for User {
impl User { impl User {
pub async fn create(username: &str, password: &str, role: UserRole) -> Option<Self> { pub async fn create(username: &str, password: &str, role: UserRole) -> Option<Self> {
if User::find_one_partial(doc! { "username": username }, json!({})) if Self::find_one_partial(doc! { "username": username }, json!({}))
.await .await
.is_some() .is_some()
{ {
return None; return None;
} }
let u = User { let u = Self {
_id: uuid::Uuid::new_v4().to_string(), _id: uuid::Uuid::new_v4().to_string(),
username: username.to_string(), username: username.to_string(),
password: bcrypt::hash(password, bcrypt::DEFAULT_COST).unwrap(), password: bcrypt::hash(password, bcrypt::DEFAULT_COST).unwrap(),
@ -61,7 +61,7 @@ impl User {
} }
pub async fn login(username: &str, password: &str) -> Option<(Session, UserRole)> { pub async fn login(username: &str, password: &str) -> Option<(Session, UserRole)> {
let u = User::find_one(doc! { "username": username }).await?; let u = Self::find_one(doc! { "username": username }).await?;
if !u.verify_pw(password) { if !u.verify_pw(password) {
return None; return None;
@ -99,7 +99,7 @@ impl User {
s s
} }
pub fn is_admin(&self) -> bool { pub const fn is_admin(&self) -> bool {
matches!(self.role, UserRole::Admin) matches!(self.role, UserRole::Admin)
} }

View file

@ -27,7 +27,7 @@ async fn rocket() -> _ {
.to_cors() .to_cors()
.expect("error creating CORS options"); .expect("error creating CORS options");
let lib = Libary::new("./media".into()).await; let lib = Libary::new("./media".into());
lib.rescan().await; lib.rescan().await;

View file

@ -3,10 +3,9 @@ use std::cmp::Ordering;
use super::api_error; use super::api_error;
use super::FallibleApiResponse; use super::FallibleApiResponse;
use super::ToAPI; use super::ToAPI;
use mongod::Referencable;
use mongodb::bson::doc; use mongodb::bson::doc;
use rocket::fs::NamedFile; use rocket::fs::NamedFile;
use rocket::*; use rocket::{get, State};
use serde_json::json; use serde_json::json;
use crate::cache::RouteCache; use crate::cache::RouteCache;
@ -69,17 +68,9 @@ pub async fn album_route(
.await .await
.ok_or_else(|| api_error("No album with that ID found"))?; .ok_or_else(|| api_error("No album with that ID found"))?;
let mut tracks = Album::get_tracks_of_album(&format!("album::{}", album._id)) let tracks = Album::get_tracks_of_album(&format!("album::{}", album._id)).await;
.await
.into_iter() let mut tracks = to_api(&tracks).await;
.map(|x| {
json!({
"id": x.id(),
"title": x.title,
"tracknumber": x.meta.map(|x| x.track_number())
})
})
.collect::<Vec<_>>();
tracks.sort_by(sort_by_tracknumber); tracks.sort_by(sort_by_tracknumber);

View file

@ -5,7 +5,7 @@ use super::ToAPI;
use fs::NamedFile; use fs::NamedFile;
use mongod::Model; use mongod::Model;
use mongodb::bson::doc; use mongodb::bson::doc;
use rocket::*; use rocket::{fs, get, State};
use crate::cache::RouteCache; use crate::cache::RouteCache;
use crate::library::artist::Artist; use crate::library::artist::Artist;

View file

@ -38,11 +38,11 @@ pub async fn to_api(albums: &[impl ToAPI]) -> Vec<serde_json::Value> {
} }
#[get("/")] #[get("/")]
pub async fn index_redir() -> Redirect { pub fn index_redir() -> Redirect {
Redirect::to(uri!("/web")) Redirect::to(uri!("/web"))
} }
#[get("/manifest.json")] #[get("/manifest.json")]
pub async fn manifest_redir() -> Redirect { pub fn manifest_redir() -> Redirect {
Redirect::to(uri!("/web/manifest.json")) Redirect::to(uri!("/web/manifest.json"))
} }

View file

@ -3,7 +3,7 @@ use super::FallibleApiResponse;
use super::ToAPI; use super::ToAPI;
use fs::NamedFile; use fs::NamedFile;
use mongodb::bson::doc; use mongodb::bson::doc;
use rocket::*; use rocket::{fs, get, State};
use crate::library::Libary; use crate::library::Libary;

View file

@ -71,7 +71,7 @@ pub struct PasswdData {
pub async fn passwd_route(passwd: Json<PasswdData>, mut u: User) -> FallibleApiResponse { pub async fn passwd_route(passwd: Json<PasswdData>, mut u: User) -> FallibleApiResponse {
u.passwd(&passwd.old, &passwd.new) u.passwd(&passwd.old, &passwd.new)
.await .await
.map_err(|_| api_error("Password change failed"))?; .map_err(|()| api_error("Password change failed"))?;
Ok(json!({ Ok(json!({
"ok": 1 "ok": 1