mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Fix lyrics retrieval
This commit is contained in:
parent
d19fd24074
commit
e1b273b8a1
2 changed files with 45 additions and 21 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http::header::HeaderValue;
|
||||||
|
use http::uri::InvalidUri;
|
||||||
|
use hyper::header::InvalidHeaderValue;
|
||||||
use hyper::{Body, Client, Request, Response, StatusCode};
|
use hyper::{Body, Client, Request, Response, StatusCode};
|
||||||
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
|
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
|
||||||
use hyper_rustls::HttpsConnector;
|
use hyper_rustls::HttpsConnector;
|
||||||
|
@ -11,7 +15,7 @@ pub struct HttpClient {
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum HttpClientError {
|
pub enum HttpClientError {
|
||||||
#[error("could not parse request: {0}")]
|
#[error("could not parse request: {0}")]
|
||||||
Parsing(#[from] http::uri::InvalidUri),
|
Parsing(#[from] http::Error),
|
||||||
#[error("could not send request: {0}")]
|
#[error("could not send request: {0}")]
|
||||||
Request(hyper::Error),
|
Request(hyper::Error),
|
||||||
#[error("could not read response: {0}")]
|
#[error("could not read response: {0}")]
|
||||||
|
@ -20,6 +24,18 @@ pub enum HttpClientError {
|
||||||
ProxyBuilder(#[from] std::io::Error),
|
ProxyBuilder(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<InvalidHeaderValue> for HttpClientError {
|
||||||
|
fn from(err: InvalidHeaderValue) -> Self {
|
||||||
|
Self::Parsing(err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InvalidUri> for HttpClientError {
|
||||||
|
fn from(err: InvalidUri) -> Self {
|
||||||
|
Self::Parsing(err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HttpClient {
|
impl HttpClient {
|
||||||
pub fn new(proxy: Option<&Url>) -> Self {
|
pub fn new(proxy: Option<&Url>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -27,10 +43,17 @@ impl HttpClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request(&self, req: Request<Body>) -> Result<Response<Body>, HttpClientError> {
|
pub async fn request(&self, mut req: Request<Body>) -> Result<Response<Body>, HttpClientError> {
|
||||||
let connector = HttpsConnector::with_native_roots();
|
let connector = HttpsConnector::with_native_roots();
|
||||||
let uri = req.uri().clone();
|
let uri = req.uri().clone();
|
||||||
|
|
||||||
|
let headers_mut = req.headers_mut();
|
||||||
|
headers_mut.insert(
|
||||||
|
"User-Agent",
|
||||||
|
// Some features like lyrics are version-gated and require a "real" version string.
|
||||||
|
HeaderValue::from_str("Spotify/8.6.80 iOS/13.5 (iPhone11,2)")?,
|
||||||
|
);
|
||||||
|
|
||||||
let response = if let Some(url) = &self.proxy {
|
let response = if let Some(url) = &self.proxy {
|
||||||
let uri = url.to_string().parse()?;
|
let uri = url.to_string().parse()?;
|
||||||
let proxy = Proxy::new(Intercept::All, uri);
|
let proxy = Proxy::new(Intercept::All, uri);
|
||||||
|
@ -58,7 +81,7 @@ impl HttpClient {
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request_body(&self, req: Request<Body>) -> Result<bytes::Bytes, HttpClientError> {
|
pub async fn request_body(&self, req: Request<Body>) -> Result<Bytes, HttpClientError> {
|
||||||
let response = self.request(req).await?;
|
let response = self.request(req).await?;
|
||||||
hyper::body::to_bytes(response.into_body())
|
hyper::body::to_bytes(response.into_body())
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use crate::apresolve::SocketAddress;
|
use crate::apresolve::SocketAddress;
|
||||||
use crate::http_client::HttpClientError;
|
use crate::http_client::HttpClientError;
|
||||||
use crate::mercury::MercuryError;
|
use crate::mercury::MercuryError;
|
||||||
use crate::protocol;
|
use crate::protocol::canvaz::EntityCanvazRequest;
|
||||||
use crate::spotify_id::SpotifyId;
|
use crate::protocol::connect::PutStateRequest;
|
||||||
|
use crate::protocol::extended_metadata::BatchedEntityRequest;
|
||||||
|
use crate::spotify_id::{FileId, SpotifyId};
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http::header::HeaderValue;
|
||||||
use hyper::header::InvalidHeaderValue;
|
use hyper::header::InvalidHeaderValue;
|
||||||
use hyper::{Body, HeaderMap, Request};
|
use hyper::{Body, HeaderMap, Request};
|
||||||
|
use protobuf::Message;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -17,7 +22,7 @@ component! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SpClientResult = Result<bytes::Bytes, SpClientError>;
|
pub type SpClientResult = Result<Bytes, SpClientError>;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SpClientError {
|
pub enum SpClientError {
|
||||||
|
@ -83,7 +88,7 @@ impl SpClient {
|
||||||
method: &str,
|
method: &str,
|
||||||
endpoint: &str,
|
endpoint: &str,
|
||||||
headers: Option<HeaderMap>,
|
headers: Option<HeaderMap>,
|
||||||
message: &dyn protobuf::Message,
|
message: &dyn Message,
|
||||||
) -> SpClientResult {
|
) -> SpClientResult {
|
||||||
let body = protobuf::text_format::print_to_string(message);
|
let body = protobuf::text_format::print_to_string(message);
|
||||||
|
|
||||||
|
@ -126,7 +131,7 @@ impl SpClient {
|
||||||
}
|
}
|
||||||
headers_mut.insert(
|
headers_mut.insert(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
http::header::HeaderValue::from_str(&format!(
|
HeaderValue::from_str(&format!(
|
||||||
"Bearer {}",
|
"Bearer {}",
|
||||||
self.session()
|
self.session()
|
||||||
.token_provider()
|
.token_provider()
|
||||||
|
@ -186,7 +191,7 @@ impl SpClient {
|
||||||
pub async fn put_connect_state(
|
pub async fn put_connect_state(
|
||||||
&self,
|
&self,
|
||||||
connection_id: String,
|
connection_id: String,
|
||||||
state: protocol::connect::PutStateRequest,
|
state: PutStateRequest,
|
||||||
) -> SpClientResult {
|
) -> SpClientResult {
|
||||||
let endpoint = format!("/connect-state/v1/devices/{}", self.session().device_id());
|
let endpoint = format!("/connect-state/v1/devices/{}", self.session().device_id());
|
||||||
|
|
||||||
|
@ -223,10 +228,12 @@ impl SpClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Not working at the moment, always returns 400.
|
// TODO: Not working at the moment, always returns 400.
|
||||||
pub async fn get_lyrics(&self, track_id: SpotifyId) -> SpClientResult {
|
pub async fn get_lyrics(&self, track_id: SpotifyId, image_id: FileId) -> SpClientResult {
|
||||||
// /color-lyrics/v2/track/22L7bfCiAkJo5xGSQgmiIO/image/spotify:image:ab67616d0000b273d9194aa18fa4c9362b47464f?clientLanguage=en
|
let endpoint = format!(
|
||||||
// https://spclient.wg.spotify.com/color-lyrics/v2/track/{track_id}/image/spotify:image:{image_id}?clientLanguage=en
|
"/color-lyrics/v2/track/{}/image/spotify:image:{}",
|
||||||
let endpoint = format!("/color-lyrics/v2/track/{}", track_id.to_base16());
|
track_id.to_base16(),
|
||||||
|
image_id
|
||||||
|
);
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert("Content-Type", "application/json".parse()?);
|
headers.insert("Content-Type", "application/json".parse()?);
|
||||||
|
@ -235,19 +242,13 @@ impl SpClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Find endpoint for newer canvas.proto and upgrade to that.
|
// TODO: Find endpoint for newer canvas.proto and upgrade to that.
|
||||||
pub async fn get_canvases(
|
pub async fn get_canvases(&self, request: EntityCanvazRequest) -> SpClientResult {
|
||||||
&self,
|
|
||||||
request: protocol::canvaz::EntityCanvazRequest,
|
|
||||||
) -> SpClientResult {
|
|
||||||
let endpoint = "/canvaz-cache/v0/canvases";
|
let endpoint = "/canvaz-cache/v0/canvases";
|
||||||
self.protobuf_request("POST", endpoint, None, &request)
|
self.protobuf_request("POST", endpoint, None, &request)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_extended_metadata(
|
pub async fn get_extended_metadata(&self, request: BatchedEntityRequest) -> SpClientResult {
|
||||||
&self,
|
|
||||||
request: protocol::extended_metadata::BatchedEntityRequest,
|
|
||||||
) -> SpClientResult {
|
|
||||||
let endpoint = "/extended-metadata/v0/extended-metadata";
|
let endpoint = "/extended-metadata/v0/extended-metadata";
|
||||||
self.protobuf_request("POST", endpoint, None, &request)
|
self.protobuf_request("POST", endpoint, None, &request)
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Reference in a new issue