From 04dd4f89e41be288d5a04d3f7a749f9f5ad4f642 Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Sun, 26 Oct 2025 17:19:33 -0400 Subject: [PATCH] tsk-218: Created song module in queue module --- src/callers/mod.rs | 10 - src/callers/queue/coverart.rs | 10 +- src/callers/queue/mod.rs | 14 ++ src/callers/queue/song.rs | 449 ++++++++++++++++++++++++++++++++++ src/callers/song.rs | 434 +------------------------------- src/main.rs | 51 ++-- 6 files changed, 497 insertions(+), 471 deletions(-) diff --git a/src/callers/mod.rs b/src/callers/mod.rs index df3666c..217840b 100644 --- a/src/callers/mod.rs +++ b/src/callers/mod.rs @@ -4,17 +4,7 @@ pub mod queue; pub mod song; pub mod endpoints { - pub const QUEUESONG: &str = "/api/v2/song/queue"; - pub const QUEUESONGLINKUSERID: &str = "/api/v2/song/queue/link"; - pub const QUEUESONGDATA: &str = "/api/v2/song/queue/{id}"; - pub const QUEUESONGUPDATE: &str = "/api/v2/song/queue/{id}"; - pub const NEXTQUEUESONG: &str = "/api/v2/song/queue/next"; pub const QUEUEMETADATA: &str = "/api/v2/song/metadata/queue"; - pub const QUEUECOVERART: &str = "/api/v2/coverart/queue"; - pub const QUEUECOVERARTDATA: &str = "/api/v2/coverart/queue/data/{id}"; - pub const QUEUECOVERARTLINK: &str = "/api/v2/coverart/queue/link"; - pub const QUEUESONGDATAWIPE: &str = "/api/v2/song/queue/data/wipe"; - pub const QUEUECOVERARTDATAWIPE: &str = "/api/v2/coverart/queue/data/wipe"; pub const CREATESONG: &str = "/api/v2/song"; pub const GETSONGS: &str = "/api/v2/song"; diff --git a/src/callers/queue/coverart.rs b/src/callers/queue/coverart.rs index 16ed258..22bf99b 100644 --- a/src/callers/queue/coverart.rs +++ b/src/callers/queue/coverart.rs @@ -125,7 +125,7 @@ pub mod endpoint { /// Endpoint to queue cover art #[utoipa::path( post, - path = super::super::super::endpoints::QUEUECOVERART, + path = super::super::endpoints::QUEUECOVERART, request_body( content = super::request::queue::Request, ), @@ -203,7 +203,7 @@ pub mod endpoint { /// Endpoint to link queued cover art #[utoipa::path( post, - path = super::super::super::endpoints::QUEUECOVERARTLINK, + path = super::super::endpoints::QUEUECOVERARTLINK, request_body( content = super::request::link::Request, description = "Linking queued cover art to queued song", @@ -244,7 +244,7 @@ pub mod endpoint { /// Endpoint to fetch cover art details #[utoipa::path( get, - path = super::super::super::endpoints::QUEUECOVERART, + path = super::super::endpoints::QUEUECOVERART, params( ("id" = uuid::Uuid, Path, description = "Queued cover art Id"), ("song_queue_id" = uuid::Uuid, Path, description = "Queued song Id") @@ -305,7 +305,7 @@ pub mod endpoint { /// Endpoint to fetch the queued cover art data #[utoipa::path( get, - path = super::super::super::endpoints::QUEUECOVERARTDATA, + path = super::super::endpoints::QUEUECOVERARTDATA, params(("id" = uuid::Uuid, Path, description = "Queued cover art Id")), responses( (status = 200, description = "Queued cover art data", body = Vec), @@ -343,7 +343,7 @@ pub mod endpoint { /// Endpoint to wipe data from the cover art queue #[utoipa::path( patch, - path = super::super::super::endpoints::QUEUECOVERARTDATAWIPE, + path = super::super::endpoints::QUEUECOVERARTDATAWIPE, request_body( content = super::request::wipe_data_from_coverart_queue::Request, description = "Data required to wipe the data from the cover art queue", diff --git a/src/callers/queue/mod.rs b/src/callers/queue/mod.rs index 165fc86..b47a5ca 100644 --- a/src/callers/queue/mod.rs +++ b/src/callers/queue/mod.rs @@ -1 +1,15 @@ pub mod coverart; +pub mod song; + +pub mod endpoints { + pub const QUEUESONG: &str = "/api/v2/song/queue"; + pub const QUEUESONGLINKUSERID: &str = "/api/v2/song/queue/link"; + pub const QUEUESONGDATA: &str = "/api/v2/song/queue/{id}"; + pub const QUEUESONGUPDATE: &str = "/api/v2/song/queue/{id}"; + pub const NEXTQUEUESONG: &str = "/api/v2/song/queue/next"; + pub const QUEUECOVERART: &str = "/api/v2/coverart/queue"; + pub const QUEUECOVERARTDATA: &str = "/api/v2/coverart/queue/data/{id}"; + pub const QUEUECOVERARTLINK: &str = "/api/v2/coverart/queue/link"; + pub const QUEUESONGDATAWIPE: &str = "/api/v2/song/queue/data/wipe"; + pub const QUEUECOVERARTDATAWIPE: &str = "/api/v2/coverart/queue/data/wipe"; +} diff --git a/src/callers/queue/song.rs b/src/callers/queue/song.rs index e69de29..d02df3c 100644 --- a/src/callers/queue/song.rs +++ b/src/callers/queue/song.rs @@ -0,0 +1,449 @@ +pub mod request { + + pub mod song_queue { + #[derive(utoipa::ToSchema)] + pub struct SongQueueRequest { + /// Filename + pub file: String, + #[schema(rename = "type")] + /// File type. Should be a file and not a value + pub file_type: String, + /// Raw data of the flac file + pub value: Vec, + } + } + + pub mod update_status { + #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Request { + pub id: uuid::Uuid, + pub status: String, + } + } + + pub mod wipe_data_from_song_queue { + #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Request { + pub song_queue_id: uuid::Uuid, + } + } + + pub mod link_user_id { + #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Request { + pub song_queue_id: uuid::Uuid, + pub user_id: uuid::Uuid, + } + } +} + + +pub mod response { + + pub mod song_queue { + /// Song queue response + #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Response { + pub message: String, + /// Id of the queued song + pub data: Vec, + } + } + + pub mod fetch_queue_song { + use serde::{Deserialize, Serialize}; + + #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } + + pub mod update_status { + #[derive(serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct ChangedStatus { + pub old_status: String, + pub new_status: String, + } + + #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } + + pub mod update_song_queue { + #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } + + pub mod wipe_data_from_song_queue { + #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } + + pub mod link_user_id { + #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } +} + + +pub mod endpoint { + use axum::response::IntoResponse; + + use crate::repo::queue as repo; + + /// Endpoint to queue a song. Starts the process and places the song in a queue + #[utoipa::path( + post, + path = super::super::endpoints::QUEUESONG, + request_body( + content = super::request::song_queue::SongQueueRequest, + description = "Multipart form data for uploading song", + content_type = "multipart/form-data" + ), + responses( + (status = 200, description = "Song queued", body = super::response::song_queue::Response) + ) + )] + pub async fn queue_song( + axum::Extension(pool): axum::Extension, + mut multipart: axum::extract::Multipart, + ) -> (axum::http::StatusCode, axum::Json) { + let mut results: Vec = Vec::new(); + let mut response = super::response::song_queue::Response::default(); + + while let Some(field) = multipart.next_field().await.unwrap() { + let name = field.name().unwrap().to_string(); + let file_name = field.file_name().unwrap().to_string(); + let content_type = field.content_type().unwrap().to_string(); + let data = field.bytes().await.unwrap(); + + println!( + "Received file '{}' (name = '{}', content-type = '{}', size = {})", + file_name, + name, + content_type, + data.len() + ); + + let raw_data: Vec = data.to_vec(); + let queue_repo = repo::song::insert( + &pool, + &raw_data, + &file_name, + &crate::repo::queue::song::status::PENDING.to_string(), + ) + .await + .unwrap(); + results.push(queue_repo); + } + + response.data = results; + response.message = if response.data.is_empty() { + String::from("Error") + } else { + String::from("Success") + }; + + (axum::http::StatusCode::OK, axum::Json(response)) + } + + /// Endpoint to link a user id to a queued song + #[utoipa::path( + patch, + path = super::super::endpoints::QUEUESONGLINKUSERID, + request_body( + content = super::request::link_user_id::Request, + description = "User Id and queued song id", + content_type = "application/json" + ), + responses( + (status = 200, description = "Queued song linked", body = super::response::link_user_id::Response), + (status = 400, description = "Linkage failed", body = super::response::link_user_id::Response) + ) + )] + pub async fn link_user_id( + axum::Extension(pool): axum::Extension, + axum::Json(payload): axum::Json, + ) -> ( + axum::http::StatusCode, + axum::Json, + ) { + let mut response = super::response::link_user_id::Response::default(); + + match repo::song::get_song_queue(&pool, &payload.song_queue_id).await { + Ok(song_queue) => { + match repo::song::link_user_id(&pool, &song_queue.id, &payload.user_id).await + { + Ok(user_id) => { + response.message = String::from(crate::callers::response::SUCCESSFUL); + response.data.push(user_id); + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } + + /// Endpoint to fetch the next queued song as long as it is available + #[utoipa::path( + get, + path = super::super::endpoints::NEXTQUEUESONG, + responses( + (status = 200, description = "Queued song is present and available", body = super::response::fetch_queue_song::Response), + (status = 400, description = "Linkage failed", body = super::response::fetch_queue_song::Response) + ) + )] + pub async fn fetch_queue_song( + axum::Extension(pool): axum::Extension, + ) -> ( + axum::http::StatusCode, + axum::Json, + ) { + let mut response = super::response::fetch_queue_song::Response::default(); + + match repo::song::get_most_recent_and_update(&pool).await { + Ok(item) => { + response.message = String::from("Successful"); + response.data.push(item); + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } + + // TODO: Rename + /// Endpoint to download the queued song + #[utoipa::path( + get, + path = super::super::endpoints::QUEUESONGDATA, + params(("id" = uuid::Uuid, Path, description = "Queued song Id")), + responses( + (status = 200, description = "Queued song linked", body = Vec), + (status = 400, description = "Linkage failed", body = Vec) + ) + )] + pub async fn download_queued_song( + axum::Extension(pool): axum::Extension, + axum::extract::Path(id): axum::extract::Path, + ) -> (axum::http::StatusCode, axum::response::Response) { + println!("Id: {id}"); + + match repo::song::get_data(&pool, &id).await { + Ok(data) => { + let by = axum::body::Bytes::from(data); + let mut response = by.into_response(); + let headers = response.headers_mut(); + headers.insert( + axum::http::header::CONTENT_TYPE, + "audio/flac".parse().unwrap(), + ); + headers.insert( + axum::http::header::CONTENT_DISPOSITION, + format!("attachment; filename=\"{id}.flac\"") + .parse() + .unwrap(), + ); + + (axum::http::StatusCode::OK, response) + } + Err(_err) => (axum::http::StatusCode::BAD_REQUEST, axum::response::Response::default()), + } + } + + /// Endpoint to update the status of a queued song + #[utoipa::path( + patch, + path = super::super::endpoints::QUEUESONG, + request_body( + content = super::request::update_status::Request, + description = "Update the status of a queued song", + content_type = "application/json" + ), + responses( + (status = 200, description = "Status has been updated", body = super::response::update_status::Response), + (status = 400, description = "Error updating status of queued song", body = super::response::update_status::Response) + ) + )] + pub async fn update_song_queue_status( + axum::Extension(pool): axum::Extension, + axum::Json(payload): axum::Json, + ) -> ( + axum::http::StatusCode, + axum::Json, + ) { + let mut response = super::response::update_status::Response::default(); + + if repo::song::status::is_valid(&payload.status).await { + let id = payload.id; + if !id.is_nil() { + match repo::song::get_status_of_song_queue(&pool, &id).await { + Ok(old) => { + match repo::song::update_song_queue_status( + &pool, + &payload.status, + &id, + ) + .await + { + Ok(new) => { + response.message = String::from("Successful"); + response + .data + .push(super::response::update_status::ChangedStatus { + old_status: old, + new_status: new, + }); + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } else { + response.message = String::from("Id is nil"); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } else { + response.message = String::from("Status not valid"); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + + /// Endpoint to update the queued song data + #[utoipa::path( + patch, + path = super::super::endpoints::QUEUESONGUPDATE, + request_body( + content = super::request::song_queue::SongQueueRequest, + description = "Multipart form data for uploading song", + content_type = "multipart/form-data" + ), + params(("id" = uuid::Uuid, Path, description = "Queued song Id")), + responses( + (status = 200, description = "Queued song updated", body = super::response::update_song_queue::Response), + (status = 400, description = "Error updating queued song", body = super::response::update_song_queue::Response), + (status = 404, description = "Queued song not found", body = super::response::update_song_queue::Response) + ) + )] + pub async fn update_song_queue( + axum::extract::Path(id): axum::extract::Path, + axum::Extension(pool): axum::Extension, + mut multipart: axum::extract::Multipart, + ) -> ( + axum::http::StatusCode, + axum::Json, + ) { + let mut response = super::response::update_song_queue::Response::default(); + + if let Some(field) = multipart.next_field().await.unwrap() { + let name = field.name().unwrap().to_string(); + let file_name = field.file_name().unwrap().to_string(); + let content_type = match field.content_type() { + Some(ct) => ct.to_string(), + None => String::new(), + }; + + let data = field.bytes().await.unwrap(); + + println!( + "Received file '{}' (name = '{}', content-type = '{}', size = {})", + file_name, + name, + content_type, + data.len() + ); + + let raw_data: Vec = data.to_vec(); + match repo::song::update(&pool, &raw_data, &id).await { + Ok(_) => { + response.message = String::from("Successful"); + response.data.push(id); + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } + } else { + response.message = String::from("No data provided"); + (axum::http::StatusCode::NOT_FOUND, axum::Json(response)) + } + } + + /// Endpoint to wipe the data from a queued song + #[utoipa::path( + patch, + path = super::super::endpoints::QUEUESONGDATAWIPE, + request_body( + content = super::request::wipe_data_from_song_queue::Request, + description = "Pass the queued song Id to wipe the data", + content_type = "application/json" + ), + responses( + (status = 200, description = "Queued song data wiped", body = super::response::wipe_data_from_song_queue::Response), + (status = 404, description = "Queued song cannot be found", body = super::response::wipe_data_from_song_queue::Response) + ) + )] + pub async fn wipe_data_from_song_queue( + axum::Extension(pool): axum::Extension, + axum::Json(payload): axum::Json, + ) -> ( + axum::http::StatusCode, + axum::Json, + ) { + let mut response = super::response::wipe_data_from_song_queue::Response::default(); + let id = payload.song_queue_id; + + match repo::song::get_song_queue(&pool, &id).await { + Ok(song_queue) => match repo::song::wipe_data(&pool, &song_queue.id).await { + Ok(wiped_id) => { + response.message = String::from("Success"); + response.data.push(wiped_id); + + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::NOT_FOUND, axum::Json(response)) + } + }, + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::NOT_FOUND, axum::Json(response)) + } + } + } +} diff --git a/src/callers/song.rs b/src/callers/song.rs index 47c6389..ba740cc 100644 --- a/src/callers/song.rs +++ b/src/callers/song.rs @@ -1,32 +1,13 @@ -// TODO: Separate queue and song endpoints pub mod request { use serde::{Deserialize, Serialize}; + // TODO: Might not be used #[derive(Default, Deserialize, Serialize)] pub struct Request { pub message: String, } - pub mod song_queue { - #[derive(utoipa::ToSchema)] - pub struct SongQueueRequest { - /// Filename - pub file: String, - #[schema(rename = "type")] - /// File type. Should be a file and not a value - pub file_type: String, - /// Raw data of the flac file - pub value: Vec, - } - } - pub mod update_status { - #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Request { - pub id: uuid::Uuid, - pub status: String, - } - } pub mod create_metadata { #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] @@ -87,20 +68,6 @@ pub mod request { } } - pub mod wipe_data_from_song_queue { - #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Request { - pub song_queue_id: uuid::Uuid, - } - } - - pub mod link_user_id { - #[derive(Debug, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Request { - pub song_queue_id: uuid::Uuid, - pub user_id: uuid::Uuid, - } - } pub mod get_songs { #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] @@ -111,48 +78,6 @@ pub mod request { } pub mod response { - use serde::{Deserialize, Serialize}; - - /// Song queue response - #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] - pub struct Response { - pub message: String, - /// Id of the queued song - pub data: Vec, - } - - pub mod fetch_queue_song { - use serde::{Deserialize, Serialize}; - - #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } - - pub mod update_status { - #[derive(serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct ChangedStatus { - pub old_status: String, - pub new_status: String, - } - - #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } - - pub mod update_song_queue { - #[derive(Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } - pub mod create_metadata { #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] pub struct Response { @@ -161,21 +86,6 @@ pub mod response { } } - pub mod wipe_data_from_song_queue { - #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } - - pub mod link_user_id { - #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] - pub struct Response { - pub message: String, - pub data: Vec, - } - } pub mod get_songs { #[derive(Debug, Default, serde::Deserialize, serde::Serialize, utoipa::ToSchema)] @@ -204,309 +114,12 @@ pub mod response { /// Module for song related endpoints pub mod endpoint { - use axum::{Json, http::StatusCode, response::IntoResponse}; + use axum::{Json, response::IntoResponse}; // use crate::callers::song::song_queue; use crate::repo; use crate::repo::queue as repo_queue; - /// Endpoint to queue a song. Starts the process and places the song in a queue - #[utoipa::path( - post, - path = super::super::endpoints::QUEUESONG, - request_body( - content = super::request::song_queue::SongQueueRequest, - description = "Multipart form data for uploading song", - content_type = "multipart/form-data" - ), - responses( - (status = 200, description = "Song queued", body = super::response::Response) - ) - )] - pub async fn queue_song( - axum::Extension(pool): axum::Extension, - mut multipart: axum::extract::Multipart, - ) -> (StatusCode, Json) { - let mut results: Vec = Vec::new(); - let mut response = super::response::Response::default(); - - while let Some(field) = multipart.next_field().await.unwrap() { - let name = field.name().unwrap().to_string(); - let file_name = field.file_name().unwrap().to_string(); - let content_type = field.content_type().unwrap().to_string(); - let data = field.bytes().await.unwrap(); - - println!( - "Received file '{}' (name = '{}', content-type = '{}', size = {})", - file_name, - name, - content_type, - data.len() - ); - - let raw_data: Vec = data.to_vec(); - let queue_repo = repo_queue::song::insert( - &pool, - &raw_data, - &file_name, - &repo::queue::song::status::PENDING.to_string(), - ) - .await - .unwrap(); - results.push(queue_repo); - } - - response.data = results; - response.message = if response.data.is_empty() { - String::from("Error") - } else { - String::from("Success") - }; - - (StatusCode::OK, Json(response)) - } - - /// Endpoint to link a user id to a queued song - #[utoipa::path( - patch, - path = super::super::endpoints::QUEUESONGLINKUSERID, - request_body( - content = super::request::link_user_id::Request, - description = "User Id and queued song id", - content_type = "application/json" - ), - responses( - (status = 200, description = "Queued song linked", body = super::response::link_user_id::Response), - (status = 400, description = "Linkage failed", body = super::response::link_user_id::Response) - ) - )] - pub async fn link_user_id( - axum::Extension(pool): axum::Extension, - axum::Json(payload): axum::Json, - ) -> ( - axum::http::StatusCode, - axum::Json, - ) { - let mut response = super::response::link_user_id::Response::default(); - - match repo_queue::song::get_song_queue(&pool, &payload.song_queue_id).await { - Ok(song_queue) => { - match repo_queue::song::link_user_id(&pool, &song_queue.id, &payload.user_id).await - { - Ok(user_id) => { - response.message = String::from(crate::callers::response::SUCCESSFUL); - response.data.push(user_id); - (axum::http::StatusCode::OK, axum::Json(response)) - } - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } - } - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } - } - - /// Endpoint to fetch the next queued song as long as it is available - #[utoipa::path( - get, - path = super::super::endpoints::NEXTQUEUESONG, - responses( - (status = 200, description = "Queued song is present and available", body = super::response::fetch_queue_song::Response), - (status = 400, description = "Linkage failed", body = super::response::fetch_queue_song::Response) - ) - )] - pub async fn fetch_queue_song( - axum::Extension(pool): axum::Extension, - ) -> ( - StatusCode, - Json, - ) { - let mut response = super::response::fetch_queue_song::Response::default(); - - match repo_queue::song::get_most_recent_and_update(&pool).await { - Ok(item) => { - response.message = String::from("Successful"); - response.data.push(item); - (StatusCode::OK, Json(response)) - } - Err(err) => { - response.message = err.to_string(); - (StatusCode::BAD_REQUEST, Json(response)) - } - } - } - - // TODO: Rename - /// Endpoint to download the queued song - #[utoipa::path( - get, - path = super::super::endpoints::QUEUESONGDATA, - params(("id" = uuid::Uuid, Path, description = "Queued song Id")), - responses( - (status = 200, description = "Queued song linked", body = Vec), - (status = 400, description = "Linkage failed", body = Vec) - ) - )] - pub async fn download_queued_song( - axum::Extension(pool): axum::Extension, - axum::extract::Path(id): axum::extract::Path, - ) -> (StatusCode, axum::response::Response) { - println!("Id: {id}"); - - match repo_queue::song::get_data(&pool, &id).await { - Ok(data) => { - let by = axum::body::Bytes::from(data); - let mut response = by.into_response(); - let headers = response.headers_mut(); - headers.insert( - axum::http::header::CONTENT_TYPE, - "audio/flac".parse().unwrap(), - ); - headers.insert( - axum::http::header::CONTENT_DISPOSITION, - format!("attachment; filename=\"{id}.flac\"") - .parse() - .unwrap(), - ); - - (StatusCode::OK, response) - } - Err(_err) => (StatusCode::BAD_REQUEST, axum::response::Response::default()), - } - } - - /// Endpoint to update the status of a queued song - #[utoipa::path( - patch, - path = super::super::endpoints::QUEUESONG, - request_body( - content = super::request::update_status::Request, - description = "Update the status of a queued song", - content_type = "application/json" - ), - responses( - (status = 200, description = "Status has been updated", body = super::response::update_status::Response), - (status = 400, description = "Error updating status of queued song", body = super::response::update_status::Response) - ) - )] - pub async fn update_song_queue_status( - axum::Extension(pool): axum::Extension, - axum::Json(payload): axum::Json, - ) -> ( - axum::http::StatusCode, - axum::Json, - ) { - let mut response = super::response::update_status::Response::default(); - - if repo::queue::song::status::is_valid(&payload.status).await { - let id = payload.id; - if !id.is_nil() { - match repo::queue::song::get_status_of_song_queue(&pool, &id).await { - Ok(old) => { - match repo::queue::song::update_song_queue_status( - &pool, - &payload.status, - &id, - ) - .await - { - Ok(new) => { - response.message = String::from("Successful"); - response - .data - .push(super::response::update_status::ChangedStatus { - old_status: old, - new_status: new, - }); - (axum::http::StatusCode::OK, axum::Json(response)) - } - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } - } - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } - } else { - response.message = String::from("Id is nil"); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } else { - response.message = String::from("Status not valid"); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } - - /// Endpoint to update the queued song data - #[utoipa::path( - patch, - path = super::super::endpoints::QUEUESONGUPDATE, - request_body( - content = super::request::song_queue::SongQueueRequest, - description = "Multipart form data for uploading song", - content_type = "multipart/form-data" - ), - params(("id" = uuid::Uuid, Path, description = "Queued song Id")), - responses( - (status = 200, description = "Queued song updated", body = super::response::update_song_queue::Response), - (status = 400, description = "Error updating queued song", body = super::response::update_song_queue::Response), - (status = 404, description = "Queued song not found", body = super::response::update_song_queue::Response) - ) - )] - pub async fn update_song_queue( - axum::extract::Path(id): axum::extract::Path, - axum::Extension(pool): axum::Extension, - mut multipart: axum::extract::Multipart, - ) -> ( - axum::http::StatusCode, - axum::Json, - ) { - let mut response = super::response::update_song_queue::Response::default(); - - if let Some(field) = multipart.next_field().await.unwrap() { - let name = field.name().unwrap().to_string(); - let file_name = field.file_name().unwrap().to_string(); - let content_type = match field.content_type() { - Some(ct) => ct.to_string(), - None => String::new(), - }; - - let data = field.bytes().await.unwrap(); - - println!( - "Received file '{}' (name = '{}', content-type = '{}', size = {})", - file_name, - name, - content_type, - data.len() - ); - - let raw_data: Vec = data.to_vec(); - match repo_queue::song::update(&pool, &raw_data, &id).await { - Ok(_) => { - response.message = String::from("Successful"); - response.data.push(id); - (axum::http::StatusCode::OK, axum::Json(response)) - } - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) - } - } - } else { - response.message = String::from("No data provided"); - (axum::http::StatusCode::NOT_FOUND, axum::Json(response)) - } - } /// Endpoint to create song #[utoipa::path( @@ -591,49 +204,6 @@ pub mod endpoint { } } - /// Endpoint to wipe the data from a queued song - #[utoipa::path( - patch, - path = super::super::endpoints::QUEUESONGDATAWIPE, - request_body( - content = super::request::wipe_data_from_song_queue::Request, - description = "Pass the queued song Id to wipe the data", - content_type = "application/json" - ), - responses( - (status = 200, description = "Queued song data wiped", body = super::response::wipe_data_from_song_queue::Response), - (status = 404, description = "Queued song cannot be found", body = super::response::wipe_data_from_song_queue::Response) - ) - )] - pub async fn wipe_data_from_song_queue( - axum::Extension(pool): axum::Extension, - axum::Json(payload): axum::Json, - ) -> ( - axum::http::StatusCode, - axum::Json, - ) { - let mut response = super::response::wipe_data_from_song_queue::Response::default(); - let id = payload.song_queue_id; - - match repo_queue::song::get_song_queue(&pool, &id).await { - Ok(song_queue) => match repo_queue::song::wipe_data(&pool, &song_queue.id).await { - Ok(wiped_id) => { - response.message = String::from("Success"); - response.data.push(wiped_id); - - (axum::http::StatusCode::OK, axum::Json(response)) - } - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::NOT_FOUND, axum::Json(response)) - } - }, - Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::NOT_FOUND, axum::Json(response)) - } - } - } // Endpoint to get songs #[utoipa::path( diff --git a/src/main.rs b/src/main.rs index 603b214..2dadcb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,6 +55,7 @@ pub mod init { use crate::callers::coverart as coverart_caller; use crate::callers::metadata as metadata_caller; use crate::callers::queue::coverart as coverart_queue_callers; + use crate::callers::queue::song as song_queue_callers; use crate::callers::song as song_caller; use coverart_caller::endpoint as coverart_endpoints; use coverart_caller::response as coverart_responses; @@ -64,6 +65,8 @@ pub mod init { use metadata_caller::response as metadata_responses; use song_caller::endpoint as song_endpoints; use song_caller::response as song_responses; + use song_queue_callers::endpoint as song_queue_endpoints; + use song_queue_callers::response as song_queue_responses; mod cors { pub async fn configure_cors() -> tower_http::cors::CorsLayer { @@ -115,15 +118,15 @@ pub mod init { #[derive(utoipa::OpenApi)] #[openapi( - paths(song_endpoints::queue_song, song_endpoints::link_user_id, song_endpoints::fetch_queue_song, song_endpoints::download_queued_song, - song_endpoints::update_song_queue_status, song_endpoints::update_song_queue, song_endpoints::create_metadata, song_endpoints::wipe_data_from_song_queue, song_endpoints::get_songs, song_endpoints::get_all_songs, song_endpoints::stream_song, song_endpoints::download_song, + paths(song_queue_endpoints::queue_song, song_queue_endpoints::link_user_id, song_queue_endpoints::fetch_queue_song, song_queue_endpoints::download_queued_song, + song_queue_endpoints::update_song_queue_status, song_queue_endpoints::update_song_queue, song_endpoints::create_metadata, song_queue_endpoints::wipe_data_from_song_queue, song_endpoints::get_songs, song_endpoints::get_all_songs, song_endpoints::stream_song, song_endpoints::download_song, song_endpoints::delete_song, coverart_queue_endpoints::queue, coverart_queue_endpoints::link, coverart_queue_endpoints::fetch_coverart_no_data, coverart_queue_endpoints::fetch_coverart_with_data, coverart_endpoints::create_coverart, coverart_queue_endpoints::wipe_data_from_coverart_queue, coverart_endpoints::get_coverart, coverart_endpoints::download_coverart, metadata_endpoints::queue_metadata, metadata_endpoints::fetch_metadata), - components(schemas(song_responses::Response, song_responses::link_user_id::Response, song_responses::fetch_queue_song::Response, - song_responses::update_status::Response, song_responses::update_song_queue::Response, song_responses::create_metadata::Response, - song_responses::wipe_data_from_song_queue::Response, song_responses::get_songs::Response, song_responses::delete_song::Response, + components(schemas(song_queue_callers::response::song_queue::Response, song_queue_callers::response::link_user_id::Response, song_queue_callers::response::fetch_queue_song::Response, + song_queue_responses::update_status::Response, song_queue_callers::response::update_song_queue::Response, song_responses::create_metadata::Response, + song_queue_callers::response::wipe_data_from_song_queue::Response, song_responses::get_songs::Response, song_responses::delete_song::Response, coverart_queue_responses::queue::Response, coverart_queue_responses::link::Response, coverart_queue_responses::fetch_coverart_no_data::Response, coverart_queue_responses::fetch_coverart_with_data::Response, coverart_responses::create_coverart::Response, coverart_queue_responses::wipe_data_from_coverart_queue::Response, coverart_responses::get_coverart::Response, @@ -138,44 +141,44 @@ pub mod init { axum::Router::new() .route(crate::ROOT, get(crate::root)) .route( - crate::callers::endpoints::QUEUESONG, - post(crate::callers::song::endpoint::queue_song).route_layer( + crate::callers::queue::endpoints::QUEUESONG, + post(crate::callers::queue::song::endpoint::queue_song).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUESONG, - patch(crate::callers::song::endpoint::update_song_queue_status).route_layer( + crate::callers::queue::endpoints::QUEUESONG, + patch(crate::callers::queue::song::endpoint::update_song_queue_status).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUESONGLINKUSERID, - patch(crate::callers::song::endpoint::link_user_id).route_layer( + crate::callers::queue::endpoints::QUEUESONGLINKUSERID, + patch(crate::callers::queue::song::endpoint::link_user_id).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUESONGDATA, - get(crate::callers::song::endpoint::download_queued_song).route_layer( + crate::callers::queue::endpoints::QUEUESONGDATA, + get(crate::callers::queue::song::endpoint::download_queued_song).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::NEXTQUEUESONG, - get(crate::callers::song::endpoint::fetch_queue_song).route_layer( + crate::callers::queue::endpoints::NEXTQUEUESONG, + get(crate::callers::queue::song::endpoint::fetch_queue_song).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUESONGUPDATE, - patch(crate::callers::song::endpoint::update_song_queue).route_layer( + crate::callers::queue::endpoints::QUEUESONGUPDATE, + patch(crate::callers::queue::song::endpoint::update_song_queue).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUESONGDATAWIPE, - patch(crate::callers::song::endpoint::wipe_data_from_song_queue).route_layer( + crate::callers::queue::endpoints::QUEUESONGDATAWIPE, + patch(crate::callers::queue::song::endpoint::wipe_data_from_song_queue).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) @@ -192,32 +195,32 @@ pub mod init { ), ) .route( - crate::callers::endpoints::QUEUECOVERART, + crate::callers::queue::endpoints::QUEUECOVERART, post(crate::callers::queue::coverart::endpoint::queue).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUECOVERARTDATA, + crate::callers::queue::endpoints::QUEUECOVERARTDATA, get(crate::callers::queue::coverart::endpoint::fetch_coverart_with_data) .route_layer(axum::middleware::from_fn( crate::auth::auth::, )), ) .route( - crate::callers::endpoints::QUEUECOVERART, + crate::callers::queue::endpoints::QUEUECOVERART, get(crate::callers::queue::coverart::endpoint::fetch_coverart_no_data).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUECOVERARTLINK, + crate::callers::queue::endpoints::QUEUECOVERARTLINK, patch(crate::callers::queue::coverart::endpoint::link).route_layer( axum::middleware::from_fn(crate::auth::auth::), ), ) .route( - crate::callers::endpoints::QUEUECOVERARTDATAWIPE, + crate::callers::queue::endpoints::QUEUECOVERARTDATAWIPE, patch(crate::callers::queue::coverart::endpoint::wipe_data_from_coverart_queue) .route_layer(axum::middleware::from_fn( crate::auth::auth::,