diff --git a/core/src/session.rs b/core/src/session.rs index f1136e53..6b5a06df 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -373,6 +373,16 @@ impl Session { self.0.data.write().user_data.attributes.extend(attributes) } + pub fn get_user_attribute(&self, key: &str) -> Option { + self.0 + .data + .read() + .user_data + .attributes + .get(key) + .map(Clone::clone) + } + fn weak(&self) -> SessionWeak { SessionWeak(Arc::downgrade(&self.0)) } diff --git a/core/src/spclient.rs b/core/src/spclient.rs index 1adfa3f8..addb547d 100644 --- a/core/src/spclient.rs +++ b/core/src/spclient.rs @@ -10,6 +10,7 @@ use hyper::{ }; use protobuf::Message; use rand::Rng; +use thiserror::Error; use crate::{ apresolve::SocketAddress, @@ -31,6 +32,18 @@ component! { pub type SpClientResult = Result; +#[derive(Debug, Error)] +pub enum SpClientError { + #[error("missing attribute {0}")] + Attribute(String), +} + +impl From for Error { + fn from(err: SpClientError) -> Self { + Self::failed_precondition(err) + } +} + #[derive(Copy, Clone, Debug)] pub enum RequestStrategy { TryTimes(usize), @@ -290,4 +303,50 @@ impl SpClient { Ok(stream) } + + pub async fn request_url(&self, url: String) -> SpClientResult { + let request = Request::builder() + .method(&Method::GET) + .uri(url) + .body(Body::empty())?; + + self.session().http_client().request_body(request).await + } + + // Audio preview in 96 kbps MP3, unencrypted + pub async fn get_audio_preview(&self, preview_id: &FileId) -> SpClientResult { + let attribute = "audio-preview-url-template"; + let template = self + .session() + .get_user_attribute(attribute) + .ok_or_else(|| SpClientError::Attribute(attribute.to_string()))?; + + let url = template.replace("{id}", &preview_id.to_base16()); + + self.request_url(url).await + } + + // The first 128 kB of a track, unencrypted + pub async fn get_head_file(&self, file_id: FileId) -> SpClientResult { + let attribute = "head-files-url"; + let template = self + .session() + .get_user_attribute(attribute) + .ok_or_else(|| SpClientError::Attribute(attribute.to_string()))?; + + let url = template.replace("{file_id}", &file_id.to_base16()); + + self.request_url(url).await + } + + pub async fn get_image(&self, image_id: FileId) -> SpClientResult { + let attribute = "image-url"; + let template = self + .session() + .get_user_attribute(attribute) + .ok_or_else(|| SpClientError::Attribute(attribute.to_string()))?; + let url = template.replace("{file_id}", &image_id.to_base16()); + + self.request_url(url).await + } }