diff --git a/Cargo.lock b/Cargo.lock index 00274f0..5d50637 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3022,7 +3022,7 @@ dependencies = [ [[package]] name = "soaricarus_api" -version = "0.5.4" +version = "0.5.5" dependencies = [ "axum", "axum-extra", diff --git a/Cargo.toml b/Cargo.toml index e503226..7038430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "soaricarus_api" -version = "0.5.4" +version = "0.5.5" edition = "2024" rust-version = "1.95" license = "MIT" diff --git a/migrations/20250420185217_init_migration.sql b/migrations/20250420185217_init_migration.sql index a50464d..4cee36b 100644 --- a/migrations/20250420185217_init_migration.sql +++ b/migrations/20250420185217_init_migration.sql @@ -28,11 +28,18 @@ CREATE TABLE IF NOT EXISTS "metadataQueue" ( -- Table to store queued coverart CREATE TABLE IF NOT EXISTS "coverartQueue" ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - data BYTEA NULL, file_type TEXT NOT NULL, song_queue_id UUID NULL ); +CREATE TABLE IF NOT EXISTS "coverartQueueData" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + file_key TEXT NOT NULL, + bucket TEXT NOT NULL, + region TEXT NOT NULL, + coverart_queue_id UUID NOT NULL +); + -- Create an index for better query performance CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata); diff --git a/src/callers/coverart.rs b/src/callers/coverart.rs index 2b66f08..a4e033b 100644 --- a/src/callers/coverart.rs +++ b/src/callers/coverart.rs @@ -63,14 +63,16 @@ pub mod endpoint { let mut response = super::response::create_coverart::Response::default(); let id = payload.coverart_queue_id; - match repo_queue::coverart::get_coverart_queue_data_with_id(&pool, &id).await { - Ok(data) => { - let song_id = payload.song_id; - match crate::repo::song::get_song(&pool, &song_id).await { - Ok(song) => { - let directory = sienvy::environment::get_root_directory().value; + match repo_queue::data::queue::coverart::get_with_coverart_queue_id(&pool, &id).await { + Ok((_id, file_key, _bucket, _region, _)) => { + let lab_config = crate::util::maze::get_config(); + let lr = labyrinth::Labyrinth { config: lab_config }; + + match lr.download(&file_key).await { + Ok(data) => { let file_type = simeta::detection::coverart::file_type_from_data(&data).unwrap(); + let coverart_type = if file_type.file_type == simeta::detection::coverart::constants::JPEG_TYPE { @@ -93,34 +95,78 @@ pub mod endpoint { let filename = simodels::coverart::generate_filename(coverart_type, true).unwrap(); - let mut coverart = simodels::coverart::init::init_coverart_dir_and_filename( - &directory, &filename, - ); - coverart.title = song.album.clone(); - coverart.file_type = file_type.file_type; - coverart.data = data; + let data = labyrinth::Data { + raw_data: data, + ..Default::default() + }; + let new_file_key = format!("processed/song/{filename}"); - match coverart.save_to_filesystem() { - Ok(_) => { - match repo::coverart::create(&pool, &coverart, &song.id).await { - Ok(id) => { - coverart.song_id = song_id; - coverart.id = id; - println!("Cover Art created"); + match lr.upload(&new_file_key, &data).await { + Ok(_resp) => { + // TODO: Change it to the s3 bucket + let directory = sienvy::environment::get_root_directory().value; + let mut coverart = + simodels::coverart::init::init_coverart_dir_and_filename( + &directory, &filename, + ); + let song = match crate::repo::song::get_song( + &pool, + &payload.song_id, + ) + .await + { + Ok(song) => song, + Err(err) => { + eprintln!("Error: {err:?}"); + return ( + axum::http::StatusCode::BAD_REQUEST, + axum::Json(response), + ); + } + }; + coverart.title = song.album.clone(); + coverart.file_type = file_type.file_type; + coverart.data = data.raw_data; - response.message = String::from("Successful"); - response.data.push(coverart); + match coverart.save_to_filesystem() { + Ok(_) => { + match repo::coverart::create( + &pool, + &coverart, + &payload.song_id, + ) + .await + { + Ok(id) => { + coverart.song_id = payload.song_id; + coverart.id = id; + println!("Cover Art created"); - (axum::http::StatusCode::OK, axum::Json(response)) + response.message = String::from("Successful"); + response.data.push(coverart); + + (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)) + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) } } } Err(err) => { - response.message = err.to_string(); + eprintln!("Error: {err:?}"); ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, axum::Json(response), @@ -129,14 +175,20 @@ pub mod endpoint { } } Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + eprintln!("Error: {err:?}"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) } } } Err(err) => { - response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + eprintln!("Error: {err:?}"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) } } } diff --git a/src/callers/queue/coverart.rs b/src/callers/queue/coverart.rs index 46612b2..0025b14 100644 --- a/src/callers/queue/coverart.rs +++ b/src/callers/queue/coverart.rs @@ -178,14 +178,60 @@ pub mod endpoint { file_type ); - match repo::coverart::insert(&pool, &raw_data, &file_type.file_type).await { - Ok(id) => { - response.message = String::from("Successful"); - response.data.push(id); - (axum::http::StatusCode::OK, axum::Json(response)) + let lab_config = crate::util::maze::get_config(); + + let lr = labyrinth::Labyrinth { config: lab_config }; + let data = labyrinth::Data { + raw_data: raw_data, + ..Default::default() + }; + + let filename = simodels::coverart::generate_filename( + simodels::types::CoverArtType::JpegExtension, + true, + ) + .unwrap(); + let file_path = format!("queued/coverart/{filename}"); + + println!("Key: {file_path:?}"); + + match lr.upload(&file_path, &data).await { + Ok(_res) => { + match repo::coverart::insert(&pool, &file_type.file_type).await { + Ok(id) => { + println!("Inserting queued data"); + match repo::data::queue::coverart::insert( + &pool, + &file_path, + &lr.config.bucket, + &lr.config.region, + &id, + ) + .await + { + Ok(_id) => { + println!("Successfully did it"); + 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), + ) + } + } + } + Err(err) => { + response.message = err.to_string(); + (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + } + } } Err(err) => { - response.message = err.to_string(); + eprintln!("Error: {err:?}"); (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) } } @@ -313,47 +359,73 @@ pub mod endpoint { )] pub async fn fetch_coverart_with_data( axum::Extension(pool): axum::Extension, - axum::extract::Path(id): axum::extract::Path, + axum::extract::Path(coverart_queue_id): axum::extract::Path, ) -> (axum::http::StatusCode, axum::response::Response) { - match repo::coverart::get_coverart_queue_data_with_id(&pool, &id).await { - Ok(data) => { - let file_type = simeta::detection::coverart::file_type_from_data(&data).unwrap(); - let bytes = axum::body::Bytes::from(data); - let mut response = bytes.into_response(); - let headers = response.headers_mut(); - headers.insert( - axum::http::header::CONTENT_TYPE, - file_type.mime.parse().unwrap(), - ); + match repo::data::queue::coverart::get_with_coverart_queue_id(&pool, &coverart_queue_id) + .await + { + Ok((_id, file_key, _bucket, _region, _)) => { + let lab_config = crate::util::maze::get_config(); + let lr = labyrinth::Labyrinth { config: lab_config }; - let coverart_type = if file_type.file_type - == simeta::detection::coverart::constants::JPEG_TYPE - { - simodels::types::CoverArtType::JpegExtension - } else if file_type.file_type == simeta::detection::coverart::constants::JPG_TYPE { - simodels::types::CoverArtType::JpgExtension - } else if file_type.file_type == simeta::detection::coverart::constants::PNG_TYPE { - simodels::types::CoverArtType::PngExtension - } else { - return ( - axum::http::StatusCode::INTERNAL_SERVER_ERROR, - axum::response::Response::default(), - ); - }; - let filename = simodels::coverart::generate_filename(coverart_type, true).unwrap(); - headers.insert( - axum::http::header::CONTENT_DISPOSITION, - format!("attachment; filename=\"{filename}\"") - .parse() - .unwrap(), - ); + match lr.download(&file_key).await { + Ok(data) => { + let file_type = + simeta::detection::coverart::file_type_from_data(&data).unwrap(); + let bytes = axum::body::Bytes::from(data); + let mut response = bytes.into_response(); + let headers = response.headers_mut(); + headers.insert( + axum::http::header::CONTENT_TYPE, + file_type.mime.parse().unwrap(), + ); - (axum::http::StatusCode::OK, response) + let coverart_type = if file_type.file_type + == simeta::detection::coverart::constants::JPEG_TYPE + { + simodels::types::CoverArtType::JpegExtension + } else if file_type.file_type + == simeta::detection::coverart::constants::JPG_TYPE + { + simodels::types::CoverArtType::JpgExtension + } else if file_type.file_type + == simeta::detection::coverart::constants::PNG_TYPE + { + simodels::types::CoverArtType::PngExtension + } else { + return ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::response::Response::default(), + ); + }; + let filename = + simodels::coverart::generate_filename(coverart_type, true).unwrap(); + headers.insert( + axum::http::header::CONTENT_DISPOSITION, + format!("attachment; filename=\"{filename}\"") + .parse() + .unwrap(), + ); + + (axum::http::StatusCode::OK, response) + } + Err(err) => { + eprintln!("Error: {err:?}"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::response::Response::default(), + ) + } + } + } + Err(err) => { + eprintln!("Record not found"); + eprintln!("Error: {err:?}"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::response::Response::default(), + ) } - Err(_err) => ( - axum::http::StatusCode::BAD_REQUEST, - axum::response::Response::default(), - ), } } @@ -383,16 +455,39 @@ pub mod endpoint { let coverart_queue_id = payload.coverart_queue_id; match repo::coverart::get_coverart_queue_with_id(&pool, &coverart_queue_id).await { - Ok(coverart_queue) => { - match repo::coverart::wipe_data(&pool, &coverart_queue.id).await { - Ok(id) => { - response.message = String::from("Success"); - response.data.push(id); - (axum::http::StatusCode::OK, axum::Json(response)) + Ok(_coverart_queue) => { + match repo::data::queue::coverart::get_with_coverart_queue_id( + &pool, + &coverart_queue_id, + ) + .await + { + Ok((_id, file_key, _bucket, _region, _)) => { + let lab_config = crate::util::maze::get_config(); + let lr = labyrinth::Labyrinth { config: lab_config }; + + match lr.delete(&file_key).await { + Ok(_) => { + response.message = String::from("Success"); + response.data.push(coverart_queue_id); + + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + eprintln!("Error: {err:?}"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) + } + } } Err(err) => { response.message = err.to_string(); - (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)) + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) } } } diff --git a/src/repo/queue/coverart.rs b/src/repo/queue/coverart.rs index d224562..6da1ab4 100644 --- a/src/repo/queue/coverart.rs +++ b/src/repo/queue/coverart.rs @@ -1,16 +1,11 @@ use sqlx::Row; -pub async fn insert( - pool: &sqlx::PgPool, - data: &Vec, - file_type: &str, -) -> Result { +pub async fn insert(pool: &sqlx::PgPool, file_type: &str) -> Result { let result = sqlx::query( r#" - INSERT INTO "coverartQueue" (data, file_type) VALUES($1, $2) RETURNING id; + INSERT INTO "coverartQueue" (file_type) VALUES($1) RETURNING id; "#, ) - .bind(data) .bind(file_type) .fetch_one(pool) .await @@ -120,78 +115,3 @@ pub async fn get_coverart_queue_with_song_queue_id( Err(_) => Err(sqlx::Error::RowNotFound), } } - -pub async fn get_coverart_queue_data_with_id( - pool: &sqlx::PgPool, - id: &uuid::Uuid, -) -> Result, sqlx::Error> { - let result = sqlx::query( - r#" - SELECT data FROM "coverartQueue" WHERE id = $1; - "#, - ) - .bind(id) - .fetch_one(pool) - .await - .map_err(|e| { - eprintln!("Error querying data: {e:?}"); - }); - - match result { - Ok(row) => Ok(row - .try_get("data") - .map_err(|_e| sqlx::Error::RowNotFound) - .unwrap()), - Err(_) => Err(sqlx::Error::RowNotFound), - } -} - -pub async fn get_coverart_queue_data_with_song_queue_id( - pool: &sqlx::PgPool, - song_queue_id: &uuid::Uuid, -) -> Result, sqlx::Error> { - let result = sqlx::query( - r#" - SELECT data FROM "coverartQueue" WHERE song_queue_id = $1; - "#, - ) - .bind(song_queue_id) - .fetch_one(pool) - .await - .map_err(|e| { - eprintln!("Error querying data: {e}"); - }); - - match result { - Ok(row) => Ok(row - .try_get("data") - .map_err(|_e| sqlx::Error::RowNotFound) - .unwrap()), - Err(_) => Err(sqlx::Error::RowNotFound), - } -} - -pub async fn wipe_data( - pool: &sqlx::PgPool, - coverart_queue_id: &uuid::Uuid, -) -> Result { - let result = sqlx::query( - r#" - UPDATE "coverartQueue" SET data = NULL WHERE id = $1 RETURNING id; - "#, - ) - .bind(coverart_queue_id) - .fetch_one(pool) - .await - .map_err(|e| { - eprintln!("Error updating query: {e}"); - }); - - match result { - Ok(row) => Ok(row - .try_get("id") - .map_err(|_e| sqlx::Error::RowNotFound) - .unwrap()), - Err(_) => Err(sqlx::Error::RowNotFound), - } -} diff --git a/src/repo/queue/data.rs b/src/repo/queue/data.rs index ed0afb6..bd6e818 100644 --- a/src/repo/queue/data.rs +++ b/src/repo/queue/data.rs @@ -96,3 +96,109 @@ pub async fn get_with_song_queue_id( Err(err) => Err(err), } } + +pub mod queue { + pub mod coverart { + use sqlx::Row; + + pub async fn insert( + pool: &sqlx::PgPool, + file_key: &str, + bucket: &str, + region: &str, + coverart_queue_id: &uuid::Uuid, + ) -> Result { + match sqlx::query( + r#" + INSERT INTO "coverartQueueData" (file_key, bucket, region, coverart_queue_id) + VALUES($1, $2, $3, $4) RETURNING id; + "#, + ) + .bind(file_key) + .bind(bucket) + .bind(region) + .bind(coverart_queue_id) + .fetch_one(pool) + .await + { + Ok(row) => { + let id: uuid::Uuid = row.try_get("id")?; + Ok(id) + } + Err(_err) => Err(sqlx::Error::RowNotFound), + } + } + + pub async fn update_file_key( + pool: &sqlx::PgPool, + id: &uuid::Uuid, + file_key: &str, + ) -> Result<(), sqlx::Error> { + match sqlx::query( + r#" + UPDATE "coverartQueueData" SET file_key = $1 WHERE id = $2; + "#, + ) + .bind(file_key) + .bind(id) + .execute(pool) + .await + { + Ok(_row) => Ok(()), + Err(_) => Err(sqlx::Error::RowNotFound), + } + } + + pub async fn get( + pool: &sqlx::PgPool, + id: &uuid::Uuid, + ) -> Result<(uuid::Uuid, String, String, String, uuid::Uuid), sqlx::Error> { + match sqlx::query( + r#" + SELECT id, file_key, bucket, region, coverart_queue_id FROM "coverartQueueData" + WHERE id = $1 + "#, + ) + .bind(id) + .fetch_one(pool) + .await + { + Ok(row) => { + let file_key: String = row.try_get("file_key")?; + let bucket: String = row.try_get("bucket")?; + let region: String = row.try_get("region")?; + let coverart_queue_id: uuid::Uuid = row.try_get("coverart_queue_id")?; + + Ok((*id, file_key, bucket, region, coverart_queue_id)) + } + Err(err) => Err(err), + } + } + + pub async fn get_with_coverart_queue_id( + pool: &sqlx::PgPool, + coverart_queue_id: &uuid::Uuid, + ) -> Result<(uuid::Uuid, String, String, String, uuid::Uuid), sqlx::Error> { + match sqlx::query( + r#" + SELECT id, file_key, bucket, region, coverart_queue_id FROM "coverartQueueData" + WHERE coverart_queue_id = $1 + "#, + ) + .bind(coverart_queue_id) + .fetch_one(pool) + .await + { + Ok(row) => { + let id: uuid::Uuid = row.try_get("id")?; + let file_key: String = row.try_get("file_key")?; + let bucket: String = row.try_get("bucket")?; + let region: String = row.try_get("region")?; + + Ok((id, file_key, bucket, region, *coverart_queue_id)) + } + Err(err) => Err(err), + } + } + } +} diff --git a/test_migrations/20250725213944_init.sql b/test_migrations/20250725213944_init.sql index 0f66046..a54885f 100644 --- a/test_migrations/20250725213944_init.sql +++ b/test_migrations/20250725213944_init.sql @@ -28,11 +28,18 @@ CREATE TABLE IF NOT EXISTS "metadataQueue" ( -- Table to store queued coverart CREATE TABLE IF NOT EXISTS "coverartQueue" ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - data BYTEA NULL, file_type TEXT NOT NULL, song_queue_id UUID NULL ); +CREATE TABLE IF NOT EXISTS "coverartQueueData" ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + file_key TEXT NOT NULL, + bucket TEXT NOT NULL, + region TEXT NOT NULL, + coverart_queue_id UUID NOT NULL +); + -- Create an index for better query performance CREATE INDEX metadata_queue_data_metadata ON "metadataQueue" USING gin (metadata);