diff --git a/rave/src/rest/mod.rs b/rave/src/rest/mod.rs index 39cc111..86cbd16 100644 --- a/rave/src/rest/mod.rs +++ b/rave/src/rest/mod.rs @@ -16,6 +16,8 @@ mod stream; mod start_scan; // rest/getScanStatus mod get_scan_status; +// rest/search3 +mod search3; pub fn build() -> Box> { Route::new() diff --git a/rave/src/rest/search3.rs b/rave/src/rest/search3.rs new file mode 100644 index 0000000..a670045 --- /dev/null +++ b/rave/src/rest/search3.rs @@ -0,0 +1,75 @@ +use poem::web::{Data, Query}; +use poem_ext::db::DbTxn; +use serde::Deserialize; +use tracing::{error, instrument}; + +use crate::{ + authentication::Authentication, + scan, + subsonic::{Error, SubsonicResponse}, + utils::{self}, +}; + +#[poem::handler] +#[instrument(skip(txn, auth))] +pub async fn search3( + Data(txn): Data<&DbTxn>, + auth: Authentication, + Query(params): Query, +) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; + + match u { + Ok(_) => {} + Err(e) => return e, + }; + + todo!("actually implement"); + + let artists = vec![]; + let albums = vec![]; + let songs = vec![]; + + SubsonicResponse::new_search_result3(artists, albums, songs) +} + +#[derive(Debug, Clone, Deserialize)] +pub struct Search3Params { + pub query: String, + #[serde(rename = "artistCount", default = "default_artist_count")] + pub artist_count: i32, + #[serde(rename = "artistOffset", default = "default_artist_offset")] + pub artist_offset: i32, + #[serde(rename = "albumCount", default = "default_album_count")] + pub album_count: i32, + #[serde(rename = "albumOffset", default = "default_album_offset")] + pub album_offset: i32, + #[serde(rename = "songCount", default = "default_song_count")] + pub song_count: i32, + #[serde(rename = "songOffset", default = "default_song_offset")] + pub song_offset: i32, +} + +const fn default_artist_count() -> i32 { + 20 +} + +const fn default_artist_offset() -> i32 { + 0 +} + +const fn default_album_count() -> i32 { + 20 +} + +const fn default_album_offset() -> i32 { + 0 +} + +const fn default_song_count() -> i32 { + 20 +} + +const fn default_song_offset() -> i32 { + 0 +} diff --git a/rave/src/rest/start_scan.rs b/rave/src/rest/start_scan.rs index 5fcdd4c..7d58468 100644 --- a/rave/src/rest/start_scan.rs +++ b/rave/src/rest/start_scan.rs @@ -34,7 +34,7 @@ pub async fn start_scan(Data(txn): Data<&DbTxn>, auth: Authentication) -> Subson } else { error!("Failed to start scan:"); for e in status.errors { - error!(error = e.root_cause(), "{e}"); + error!(error = e.report.root_cause(), "{e:?}"); } SubsonicResponse::new_error(Error::Generic(None)) } diff --git a/rave/src/scan.rs b/rave/src/scan.rs index 4df0a86..6240c53 100644 --- a/rave/src/scan.rs +++ b/rave/src/scan.rs @@ -43,7 +43,7 @@ pub async fn get_scan_status() -> Result { pub struct ScanStatus { pub scanning: bool, pub count: u64, - pub errors: Vec>, + pub errors: Vec, } async fn scan() { @@ -109,7 +109,10 @@ async fn do_entry(de: walk::DirEntry, txn: DatabaseTransaction, state: Arc, + pub additional: String, +} + #[instrument(skip(dbc, state))] async fn create_root_music_folder( dbc: &sea_orm::DatabaseConnection, @@ -250,7 +255,10 @@ async fn create_root_music_folder( { let mut stat = STATUS.write().await; stat.scanning = false; - stat.errors.push(Arc::new(Report::new(err))); + stat.errors.push(ScanError { + report: Arc::new(Report::new(err)), + additional: root_dir.to_string_lossy().to_string(), + }); } return ControlFlow::Break(()); }; @@ -290,7 +298,10 @@ async fn create_root_music_folder( { let mut stat = STATUS.write().await; stat.scanning = false; - stat.errors.push(Arc::new(Report::new(err))); + stat.errors.push(ScanError { + report: Arc::new(Report::new(err)), + additional: root_dir.to_string_lossy().to_string(), + }); } let _ = txn.rollback().await; @@ -314,7 +325,10 @@ async fn create_txn(dbc: &sea_orm::DatabaseConnection) -> Option Option { { let mut stat = STATUS.write().await; stat.scanning = false; - stat.errors - .push(Arc::new(Report::msg("Failed to connect to database"))); + stat.errors.push(ScanError { + report: Arc::new(Report::new(e)), + additional: String::new(), + }); } return None; diff --git a/rave/src/subsonic/mod.rs b/rave/src/subsonic/mod.rs index 6ba6155..9089f80 100644 --- a/rave/src/subsonic/mod.rs +++ b/rave/src/subsonic/mod.rs @@ -11,6 +11,8 @@ pub use types::album::Album; pub use types::child::Child; pub use types::music_folder::MusicFolder; +use self::types::artist::Artist; + impl IntoResponse for SubsonicResponse { fn into_response(self) -> poem::Response { let body = quick_xml::se::to_string(&self).expect("Failed to serialize response"); @@ -60,6 +62,14 @@ impl SubsonicResponse { }) } + pub fn new_search_result3(artists: Vec, albums: Vec, songs: Vec) -> Self { + Self::new(SubResponseType::SearchResult3 { + artists, + albums, + songs, + }) + } + pub fn new_empty() -> Self { Self::new(SubResponseType::Empty) } @@ -121,6 +131,15 @@ pub enum SubResponseType { #[serde(rename = "count")] count: u64, }, + #[serde(rename = "searchResult3")] + SearchResult3 { + #[serde(rename = "artist")] + artists: Vec, + #[serde(rename = "album")] + albums: Vec, + #[serde(rename = "song")] + songs: Vec, + }, Empty, } diff --git a/rave/src/subsonic/types/artist.rs b/rave/src/subsonic/types/artist.rs new file mode 100644 index 0000000..e329a02 --- /dev/null +++ b/rave/src/subsonic/types/artist.rs @@ -0,0 +1,4 @@ +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct Artist {} diff --git a/rave/src/subsonic/types.rs b/rave/src/subsonic/types/mod.rs similarity index 76% rename from rave/src/subsonic/types.rs rename to rave/src/subsonic/types/mod.rs index 9c32382..55a8340 100644 --- a/rave/src/subsonic/types.rs +++ b/rave/src/subsonic/types/mod.rs @@ -1,3 +1,4 @@ pub mod album; +pub mod artist; pub mod child; pub mod music_folder; diff --git a/rave/src/ui/errors.rs b/rave/src/ui/errors.rs new file mode 100644 index 0000000..99cfe28 --- /dev/null +++ b/rave/src/ui/errors.rs @@ -0,0 +1,43 @@ +use poem::{http::StatusCode, Response}; + +use crate::scan; + +#[poem::handler] +pub async fn errors() -> Response { + let scan_status = scan::get_scan_status().await; + + let mut body = String::new(); + + body.push_str("Rave"); + + body.push_str("

Errors

"); + + match scan_status { + Ok(status) => { + body.push_str(&format!( + "

Scan status: {} ({})

", + status.scanning, status.count + )); + + body.push_str("
    "); + for error in status.errors { + body.push_str(&format!( + "
  • {}: {}
  • ", + error.additional, error.report + )); + } + body.push_str("
"); + } + Err(e) => { + body.push_str("

Error

"); + body.push_str(&format!("

{e}

")); + } + } + + body.push_str(""); + + Response::builder() + .status(StatusCode::OK) + .header("Content-Type", "text/html") + .body(body) +} diff --git a/rave/src/ui/mod.rs b/rave/src/ui/mod.rs index 70337ac..3f5d09a 100644 --- a/rave/src/ui/mod.rs +++ b/rave/src/ui/mod.rs @@ -1,5 +1,7 @@ use poem::{Endpoint, EndpointExt, Route}; +mod errors; + pub fn build() -> Box> { - Route::new().boxed() + Route::new().at("/errors", errors::errors).boxed() }