diff --git a/src/subsonic.rs b/src/subsonic.rs index 676d103..16d3478 100644 --- a/src/subsonic.rs +++ b/src/subsonic.rs @@ -80,6 +80,11 @@ pub struct Client { base_url: reqwest::Url, } +struct ByteResponse { + bytes: Bytes, + content_type: String, +} + fn random_salt(length: usize) -> String { let mut rng = rand::thread_rng(); std::iter::repeat(()) @@ -155,7 +160,10 @@ impl Client { Ok(Client { client, base_url }) } - async fn send(&self, request: reqwest_middleware::RequestBuilder) -> Result { + async fn send( + &self, + request: reqwest_middleware::RequestBuilder, + ) -> Result { // FIXME: is an entire channel per request overkill? maybe pool them? let (sender, receiver) = async_channel::bounded(1); @@ -168,12 +176,15 @@ impl Client { // wrap this logic in a fn so we can use ? async fn perform( response: Result, - ) -> Result { + ) -> Result { let response = response?.error_for_status()?; let xcache = response.headers()[http_cache::XCACHE].to_str().ok(); - let content_type = response.headers()[reqwest::header::CONTENT_TYPE] - .to_str() - .ok(); + let content_type = String::from_utf8( + response.headers()[reqwest::header::CONTENT_TYPE] + .as_bytes() + .to_vec(), + ) + .expect("invalid content type (required)"); event!( name: "response", target: "audrey::http", @@ -181,12 +192,16 @@ impl Client { url=response.url().as_str(), status=response.status().as_str(), cache_status=xcache, - content_type=content_type + content_type=&content_type ); - response + let bytes = response .bytes() .await - .map_err(|e| Error::ReqwestError(reqwest_middleware::Error::Reqwest(e))) + .map_err(|e| Error::ReqwestError(reqwest_middleware::Error::Reqwest(e)))?; + Ok(ByteResponse { + bytes, + content_type, + }) } sender @@ -211,7 +226,7 @@ impl Client { url } - async fn get_bytes(&self, url: url::Url) -> Result { + async fn get_bytes(&self, url: url::Url) -> Result { self.send(self.client.get(url)).await } @@ -219,8 +234,8 @@ impl Client { &self, url: url::Url, ) -> Result { - let bytes = self.get_bytes(url).await?; - serde_json::from_slice::(&bytes).map_err(Error::JsonDecodeError) + let byteresponse = self.get_bytes(url).await?; + serde_json::from_slice::(&byteresponse.bytes).map_err(Error::JsonDecodeError) } async fn get_subsonic( @@ -266,9 +281,19 @@ impl Client { size: Option, ) -> Result { let url = self.cover_art_url(id, size); - let cover_art_bytes = self.get_bytes(url).await?; + let byteresponse = self.get_bytes(url).await?; + if byteresponse.content_type == "application/json" { + let json = + serde_json::from_slice::>(&byteresponse.bytes) + .map_err(Error::JsonDecodeError)?; + let error = match json.subsonic_response { + schema::SubsonicResponse::Failed { error } => Err(Error::SubsonicError(error)), + _ => panic!("error response didn't get parsed as error"), + }; + return error; + } let image = ImageReader::new(std::io::BufReader::new(std::io::Cursor::new( - cover_art_bytes, + byteresponse.bytes, ))) .with_guessed_format() .map_err(|e| Error::ImageDecodeError(image::ImageError::IoError(e)))?