diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc6f9d2..24b4c613 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,14 @@ ### Server - API version is now 8.0 -- Clients are now expected to send their preferred API major version in a `Accept-Version` header. Omitting this defaults to `7`, but will become an error in future Polaris releases. +- Clients are now expected to send their preferred API major version in a `Accept-Version` header. Omitting this currently defaults to `7`, but will become an error in future Polaris releases. Support for API version 7 will be removed in a future release. - Most API responses now support gzip compression. -- Added support for multi-value metadata for the following fields: `artist`, `album artist`, `genre`, `label`, `lyricist` and `composer`. +- Added support for multi-value metadata for the following fields: `artist`, `album artist`, `composer`, `genre`, `label` and `lyricist`. - The response format of the `/browse`, `/flatten` and `/get_playlist` endpoints has been modified. -- Added new endpoints to query albums and artists +- Added new endpoints to query albums and artists. - The `/random` and `/recent` albums are deprecated in favor of `/albums/random` and `/albums/recent`. These endpoints now have optional parameters for RNG seeding and pagination. +- The `/search/` endpoint now requires a non-empty query (`/search/` now returns HTTP status code 404, regardless of API version). +- The response format of the `/search/` endpoint has changed. - Added a new `/get_songs` endpoint which returns song metadata in bulk. - Added a new `/peaks` endpoint which returns audio signal peaks that can be used to draw waveform visualizations. - The `/thumbnail` endpoint supports a new size labeled `tiny` which returns 40x40px images. diff --git a/src/server/axum/api.rs b/src/server/axum/api.rs index d5d627d4..2b050e3f 100644 --- a/src/server/axum/api.rs +++ b/src/server/axum/api.rs @@ -510,14 +510,24 @@ async fn get_search( _auth: Auth, api_version: APIMajorVersion, State(index_manager): State, - Path(query): Path, // TODO return dto::SongList + Path(query): Path, ) -> Response { let paths = match index_manager.search(query).await { Ok(f) => f, Err(e) => return APIError::from(e).into_response(), }; let song_list = make_song_list(paths, &index_manager).await; - song_list_to_response(song_list, api_version) + match api_version { + APIMajorVersion::V7 => Json( + song_list + .paths + .iter() + .map(|p| dto::v7::CollectionFile::Song(p.into())) + .collect::>(), + ) + .into_response(), + APIMajorVersion::V8 => Json(song_list).into_response(), + } } async fn get_playlists( diff --git a/src/server/dto/v7.rs b/src/server/dto/v7.rs index 81068a88..b5252fb2 100644 --- a/src/server/dto/v7.rs +++ b/src/server/dto/v7.rs @@ -278,7 +278,7 @@ impl VecExt for Vec { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Song { pub path: PathBuf, pub track_number: Option, @@ -300,19 +300,7 @@ impl From<&PathBuf> for Song { fn from(path: &PathBuf) -> Self { Self { path: path.clone(), - track_number: None, - disc_number: None, - title: None, - artist: None, - album_artist: None, - year: None, - album: None, - artwork: None, - duration: None, - lyricist: None, - composer: None, - genre: None, - label: None, + ..Default::default() } } } diff --git a/src/server/test/collection.rs b/src/server/test/collection.rs index 1b4d6c7f..19a3553b 100644 --- a/src/server/test/collection.rs +++ b/src/server/test/collection.rs @@ -278,38 +278,47 @@ async fn recent_golden_path_api_v7() { #[tokio::test] async fn search_requires_auth() { let mut service = ServiceType::new(&test_name!()).await; - let request = protocol::search::(""); + let request = protocol::search::("rhapsody"); let response = service.fetch(&request).await; assert_eq!(response.status(), StatusCode::UNAUTHORIZED); } #[tokio::test] -async fn search_without_query() { +async fn search_with_query() { let mut service = ServiceType::new(&test_name!()).await; service.complete_initial_setup().await; + service.login_admin().await; + service.index().await; service.login().await; - let request = protocol::search::(""); - let response = service - .fetch_json::<_, Vec>(&request) - .await; - assert_eq!(response.status(), StatusCode::OK); + let request = protocol::search::("door"); + let response = service.fetch_json::<_, dto::SongList>(&request).await; + let songs = response.body(); + + let path: PathBuf = [ + TEST_MOUNT_NAME, + "Khemmis", + "Hunted", + "04 - Beyond The Door.mp3", + ] + .iter() + .collect(); + assert_eq!(songs.paths, vec![path]); } #[tokio::test] -async fn search_with_query() { +async fn search_with_query_v7() { let mut service = ServiceType::new(&test_name!()).await; service.complete_initial_setup().await; service.login_admin().await; service.index().await; service.login().await; - let request = protocol::search::("door"); + let request = protocol::search::("door"); let response = service - .fetch_json::<_, Vec>(&request) + .fetch_json::<_, Vec>(&request) .await; - let results = response.body(); - assert_eq!(results.len(), 1); + let songs = response.body(); let path: PathBuf = [ TEST_MOUNT_NAME, @@ -319,5 +328,12 @@ async fn search_with_query() { ] .iter() .collect(); - assert_eq!(results[0].path, path); + + assert_eq!( + *songs, + vec![dto::v7::CollectionFile::Song(dto::v7::Song { + path, + ..Default::default() + })] + ); }