This commit is contained in:
JMARyA 2024-07-24 11:07:24 +02:00
parent 0d3df6bb64
commit dcf546fa9c
Signed by: jmarya
GPG key ID: 901B2ADDF27C2263
18 changed files with 3463 additions and 1 deletions

83
src/route/album.rs Normal file
View file

@ -0,0 +1,83 @@
use std::cmp::Ordering;
use mongodb::bson::doc;
use rocket::*;
use serde_json::json;
use rocket::fs::NamedFile;
use mongod::Referencable;
use super::FallibleApiResponse;
use super::api_error;
use crate::library::Libary;
#[get("/artist/<artist_id>/albums")]
pub async fn albums_route(artist_id: &str, lib: &State<Libary>) -> FallibleApiResponse {
let albums = lib.get_albums_by_artist(artist_id).await;
Ok(json!({
"artist": artist_id,
"albums": &albums
}))
}
fn sort_by_tracknumber(a: &serde_json::Value, b: &serde_json::Value) -> Ordering {
a.get("tracknumber")
.unwrap()
.as_i64()
.unwrap()
.cmp(&b.get("tracknumber").unwrap().as_i64().unwrap())
}
#[get("/album/<album_id>/cover")]
pub async fn album_cover_route(album_id: &str, lib: &State<Libary>) -> Option<NamedFile> {
let album = lib.get_album_by_id(album_id).await?;
let track_path = lib
.get_tracks_of_album(&format!("album::{}", album._id))
.await
.first()?
.path
.clone();
let track_path = std::path::Path::new(&track_path);
for ext in ["png", "jpg", "jpeg", "avif"] {
let cover_file = track_path.parent()?.join(format!("cover.{ext}"));
if cover_file.exists() {
return NamedFile::open(cover_file).await.ok();
}
}
None
}
#[get("/album/<album_id>")]
pub async fn album_route(album_id: &str, lib: &State<Libary>) -> FallibleApiResponse {
let album = lib
.get_album_by_id(album_id)
.await
.ok_or_else(|| api_error("No album with that ID found"))?;
let mut tracks = lib
.get_tracks_of_album(&format!("album::{}", album._id))
.await
.into_iter()
.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);
let mut album = serde_json::to_value(album).unwrap();
album
.as_object_mut()
.unwrap()
.insert("tracks".into(), tracks.into());
Ok(album)
}

23
src/route/artist.rs Normal file
View file

@ -0,0 +1,23 @@
use mongodb::bson::doc;
use rocket::*;
use super::FallibleApiResponse;
use super::api_error;
use crate::library::Libary;
/// Get all artists
#[get("/artists")]
pub async fn artists_route(lib: &State<Libary>) -> FallibleApiResponse {
let artists = lib.get_artists().await;
Ok(serde_json::to_value(&artists).unwrap())
}
#[get("/artist/<id>")]
pub async fn artist_route(id: &str, lib: &State<Libary>) -> FallibleApiResponse {
Ok(serde_json::to_value(
&lib.get_artist_by_id(id)
.await
.ok_or_else(|| api_error("No artist with that ID found"))?,
)
.unwrap())
}

17
src/route/mod.rs Normal file
View file

@ -0,0 +1,17 @@
use rocket::response::status::BadRequest;
use serde_json::json;
pub mod artist;
pub mod album;
pub mod track;
pub mod user;
type ApiError = BadRequest<serde_json::Value>;
type FallibleApiResponse = Result<serde_json::Value, ApiError>;
pub fn api_error(msg: &str) -> ApiError {
BadRequest(json!({
"error": msg
}))
}

25
src/route/track.rs Normal file
View file

@ -0,0 +1,25 @@
use rocket::*;
use fs::NamedFile;
use mongodb::bson::doc;
use super::FallibleApiResponse;
use super::api_error;
use crate::library::Libary;
#[get("/track/<track_id>")]
pub async fn track_route(track_id: &str, lib: &State<Libary>) -> FallibleApiResponse {
Ok(serde_json::to_value(
&lib.get_track_by_id(track_id)
.await
.ok_or_else(|| api_error("No track with that ID found"))?,
)
.unwrap())
}
#[get("/track/<track_id>/audio")]
pub async fn track_audio_route(track_id: &str, lib: &State<Libary>) -> Option<NamedFile> {
let track = lib.get_track_by_id(track_id).await?;
NamedFile::open(std::path::Path::new(&track.path))
.await
.ok()
}

51
src/route/user.rs Normal file
View file

@ -0,0 +1,51 @@
use rocket::http::Status;
use mongod::Model;
use mongodb::bson::doc;
use rocket::outcome::Outcome;
use rocket::post;
use rocket::request::FromRequest;
use rocket::Request;
use serde_json::json;
use serde::Deserialize;
use rocket::serde::json::Json;
use crate::library::user::Session;
use crate::library::user::User;
use super::FallibleApiResponse;
use super::api_error;
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
match request.headers().get_one("token") {
Some(key) => {
if let Some(session) = Session::find_one(doc! { "token": key} ).await {
let user = session.user.get().await;
Outcome::Success(user)
} else {
Outcome::Error((Status::Unauthorized, ()))
}
},
None => Outcome::Error((Status::Unauthorized, ())),
}
}
}
#[derive(Deserialize)]
pub struct LoginData {
pub username: String,
pub password: String
}
#[post("/login", data = "<login>")]
pub async fn login_route(login: Json<LoginData>) -> FallibleApiResponse {
let ses = User::login(&login.username, &login.password).await.ok_or_else(|| api_error("Login failed"))?;
Ok(json!({
"token": ses.token
}))
}