Create metadata (#133)
* Added migration for metadata table used to store a song's metadata * Added comment for new table * Added endpoint to create metadata * Filling out code * Changed type of duration in request * Reorganizing code * Changing metadata table to song * Added TODO for data type discrepancy for the future * Fix migration syntax error * Added types to request * Added environment variable for root directory: * Moving keys to its own module * Moved environement related code to its own module * Added function to get root_directory * Added endpoint to create song * Minor fixes * Added test and refactored tests * Ironing out some kinks * Resolved test issue Turns out I was using the wrong id value * Added env variable in workflow * Added TODO * Updated env variable * clippy warning fixes * Making sure date_created is populated when returneD * Code cleanup * Added TODOs * Uncommenting tests * Code formatting
This commit was merged in pull request #133.
This commit is contained in:
@@ -11,4 +11,6 @@ pub mod endpoints {
|
||||
pub const QUEUECOVERART: &str = "/api/v2/coverart/queue";
|
||||
pub const QUEUECOVERARTDATA: &str = "/api/v2/coverart/queue/data";
|
||||
pub const QUEUECOVERARTLINK: &str = "/api/v2/coverart/queue/link";
|
||||
|
||||
pub const CREATESONG: &str = "/api/v2/song";
|
||||
}
|
||||
|
||||
@@ -13,6 +13,69 @@ pub mod request {
|
||||
pub status: String,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod create_metadata {
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Request {
|
||||
pub title: String,
|
||||
pub artist: String,
|
||||
pub album_artist: String,
|
||||
pub album: String,
|
||||
pub genre: String,
|
||||
pub date: String,
|
||||
pub track: i32,
|
||||
pub disc: i32,
|
||||
pub track_count: i32,
|
||||
pub disc_count: i32,
|
||||
pub duration: i32,
|
||||
pub audio_type: String,
|
||||
pub user_id: uuid::Uuid,
|
||||
pub song_queue_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
!self.title.is_empty()
|
||||
|| !self.artist.is_empty()
|
||||
|| !self.album_artist.is_empty()
|
||||
|| !self.album.is_empty()
|
||||
|| !self.genre.is_empty()
|
||||
|| !self.date.is_empty()
|
||||
|| self.track > 0
|
||||
|| self.disc > 0
|
||||
|| self.track_count > 0
|
||||
|| self.disc_count > 0
|
||||
|| self.duration > 0
|
||||
|| !self.audio_type.is_empty()
|
||||
|| !self.user_id.is_nil()
|
||||
|| !self.song_queue_id.is_nil()
|
||||
}
|
||||
|
||||
pub fn to_song(&self) -> icarus_models::song::Song {
|
||||
icarus_models::song::Song {
|
||||
id: uuid::Uuid::nil(),
|
||||
title: self.title.clone(),
|
||||
artist: self.artist.clone(),
|
||||
album_artist: self.album_artist.clone(),
|
||||
album: self.album.clone(),
|
||||
genre: self.genre.clone(),
|
||||
year: self.date[..3].parse().unwrap(),
|
||||
track: self.track,
|
||||
disc: self.disc,
|
||||
track_count: self.track_count,
|
||||
disc_count: self.disc_count,
|
||||
duration: self.duration,
|
||||
audio_type: self.audio_type.clone(),
|
||||
user_id: self.user_id,
|
||||
// TODO: Change the type of this in icarus_models lib
|
||||
date_created: String::new(),
|
||||
filename: String::new(),
|
||||
data: Vec::new(),
|
||||
directory: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod response {
|
||||
@@ -55,8 +118,18 @@ pub mod response {
|
||||
pub data: Vec<Vec<u8>>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod create_metadata {
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
pub struct Response {
|
||||
pub message: String,
|
||||
pub data: Vec<icarus_models::song::Song>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Might make a distinction between year and date in a song's tag at some point
|
||||
|
||||
pub mod status {
|
||||
pub const PENDING: &str = "pending";
|
||||
pub const PROCESSING: &str = "processing";
|
||||
@@ -67,6 +140,60 @@ pub mod status {
|
||||
}
|
||||
}
|
||||
|
||||
mod song_db {
|
||||
use sqlx::Row;
|
||||
|
||||
// TODO: Change first parameter of return value from string to a time type
|
||||
pub async fn insert(
|
||||
pool: &sqlx::PgPool,
|
||||
song: &icarus_models::song::Song,
|
||||
) -> Result<(String, uuid::Uuid), sqlx::Error> {
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO "song" (title, artist, album_artist, album, genre, year, track, disc, track_count, disc_count, duration, audio_type, filename, directory, user_id)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING date_created, id;
|
||||
"#
|
||||
)
|
||||
.bind(&song.title)
|
||||
.bind(&song.artist)
|
||||
.bind(&song.album_artist)
|
||||
.bind(&song.album)
|
||||
.bind(&song.genre)
|
||||
.bind(song.year)
|
||||
.bind(song.track)
|
||||
.bind(song.disc)
|
||||
.bind(song.track_count)
|
||||
.bind(song.disc_count)
|
||||
.bind(song.duration)
|
||||
.bind(&song.audio_type)
|
||||
.bind(&song.filename)
|
||||
.bind(&song.directory)
|
||||
.bind(song.user_id)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Error inserting query: {:?}", e);
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(row) => {
|
||||
let id: uuid::Uuid = row
|
||||
.try_get("id")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap();
|
||||
let date_created_time: time::OffsetDateTime = row
|
||||
.try_get("date_created")
|
||||
.map_err(|_e| sqlx::Error::RowNotFound)
|
||||
.unwrap();
|
||||
let date_created = date_created_time.to_string();
|
||||
|
||||
Ok((date_created, id))
|
||||
}
|
||||
Err(_) => Err(sqlx::Error::RowNotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod song_queue {
|
||||
use sqlx::Row;
|
||||
|
||||
@@ -289,6 +416,7 @@ pub mod endpoint {
|
||||
data.len()
|
||||
);
|
||||
|
||||
// TODO: Remove this
|
||||
// Save the file to disk
|
||||
let mut file = std::fs::File::create(&file_name).unwrap();
|
||||
file.write_all(&data).unwrap();
|
||||
@@ -456,4 +584,59 @@ pub mod endpoint {
|
||||
(axum::http::StatusCode::NOT_FOUND, axum::Json(response))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_metadata(
|
||||
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
|
||||
axum::Json(payload): axum::Json<super::request::create_metadata::Request>,
|
||||
) -> (
|
||||
axum::http::StatusCode,
|
||||
axum::Json<super::response::create_metadata::Response>,
|
||||
) {
|
||||
let mut response = super::response::create_metadata::Response::default();
|
||||
|
||||
if payload.is_valid() {
|
||||
let mut song = payload.to_song();
|
||||
song.filename =
|
||||
song.generate_filename(icarus_models::types::MusicTypes::FlacExtension, true);
|
||||
song.directory = crate::environment::get_root_directory().await.unwrap();
|
||||
|
||||
match song_queue::get_data(&pool, &payload.song_queue_id).await {
|
||||
Ok(data) => {
|
||||
song.data = data;
|
||||
let dir = std::path::Path::new(&song.directory);
|
||||
let save_path = dir.join(&song.filename);
|
||||
|
||||
let mut file = std::fs::File::create(&save_path).unwrap();
|
||||
file.write_all(&song.data).unwrap();
|
||||
|
||||
match song.song_path() {
|
||||
Ok(_) => match super::song_db::insert(&pool, &song).await {
|
||||
Ok((date_created, id)) => {
|
||||
song.id = id;
|
||||
song.date_created = date_created;
|
||||
response.data.push(song);
|
||||
|
||||
(axum::http::StatusCode::OK, axum::Json(response))
|
||||
}
|
||||
Err(err) => {
|
||||
response.message = format!("{:?} song {:?}", err.to_string(), song);
|
||||
(axum::http::StatusCode::BAD_REQUEST, 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("Request body is not valid");
|
||||
(axum::http::StatusCode::BAD_REQUEST, axum::Json(response))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user