Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [metadata] Changed arguments for `Metadata` trait from `&SpotifyId` to `&SpotifyUri` (breaking)
- [player] `load` function changed from accepting a `SpotifyId` to accepting a `SpotifyUri` (breaking)
- [player] `preload` function changed from accepting a `SpotifyId` to accepting a `SpotifyUri` (breaking)
- [spclient] `get_radio_for_track` function changed from accepting a `SpotifyId` to accepting a `SpotifyUri` (breaking)
- [core] `get_radio_for_track` function changed from accepting a `SpotifyId` to accepting a `SpotifyUri` (breaking)
- [core] Changed return type of `get_extended_metadata` to return `BatchedExtensionResponse` (breaking)
- [core] Changed parameter of `get_<item>_metadata` from `SpotifyId` to `SpotifyUri` (breaking)

### Fixed

- [core] Fixed a problem where the metadata didn't include the audio file by switching to `get_extended_metadata`

### Removed

Expand Down
92 changes: 56 additions & 36 deletions core/src/spclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use crate::{
connect::PutStateRequest,
context::Context,
extended_metadata::BatchedEntityRequest,
extended_metadata::{BatchedExtensionResponse, EntityRequest, ExtensionQuery},
extension_kind::ExtensionKind,
},
token::Token,
util,
Expand All @@ -32,7 +34,7 @@ use hyper::{
header::{ACCEPT, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, HeaderName, RANGE},
};
use hyper_util::client::legacy::ResponseFuture;
use protobuf::{Enum, Message, MessageFull};
use protobuf::{Enum, EnumOrUnknown, Message, MessageFull};
use rand::RngCore;
use sysinfo::System;
use thiserror::Error;
Expand All @@ -58,18 +60,14 @@ const NO_METRICS_AND_SALT: RequestOptions = RequestOptions {
base_url: None,
};

const SPCLIENT_FALLBACK_ENDPOINT: RequestOptions = RequestOptions {
metrics: true,
salt: true,
base_url: Some("https://spclient.wg.spotify.com"),
};

#[derive(Debug, Error)]
pub enum SpClientError {
#[error("missing attribute {0}")]
Attribute(String),
#[error("expected data but received none")]
NoData,
#[error("expected an entry to exist in {0}")]
ExpectedEntry(&'static str),
}

impl From<SpClientError> for Error {
Expand Down Expand Up @@ -572,39 +570,67 @@ impl SpClient {
.await
}

pub async fn get_metadata(&self, scope: &str, id: &SpotifyId) -> SpClientResult {
let endpoint = format!("/metadata/4/{}/{}", scope, id.to_base16()?);
// For unknown reasons, metadata requests must now be sent through spclient.wg.spotify.com.
// Otherwise, the API will respond with 500 Internal Server Error responses.
// Context: https://github.com/librespot-org/librespot/issues/1527
self.request_with_options(
&Method::GET,
&endpoint,
None,
None,
&SPCLIENT_FALLBACK_ENDPOINT,
)
.await
pub async fn get_extended_metadata(
&self,
request: BatchedEntityRequest,
) -> Result<BatchedExtensionResponse, Error> {
let endpoint = "/extended-metadata/v0/extended-metadata";
let res = self
.request_with_protobuf(&Method::POST, endpoint, None, &request)
.await?;
Ok(BatchedExtensionResponse::parse_from_bytes(&res)?)
}

pub async fn get_metadata(&self, kind: ExtensionKind, id: &SpotifyUri) -> SpClientResult {
let req = BatchedEntityRequest {
entity_request: vec![EntityRequest {
entity_uri: id.to_uri()?,
query: vec![ExtensionQuery {
extension_kind: EnumOrUnknown::new(kind),
..Default::default()
}],
..Default::default()
}],
..Default::default()
};

let mut res = self.get_extended_metadata(req).await?;
let mut extended_metadata = res
.extended_metadata
.pop()
.ok_or(SpClientError::ExpectedEntry("extended_metadata"))?;

let mut data = extended_metadata
.extension_data
.pop()
.ok_or(SpClientError::ExpectedEntry("extension_data"))?;

match data.extension_data.take() {
None => Err(SpClientError::ExpectedEntry("data").into()),
Some(data) => Ok(Bytes::from(data.value)),
}
}

pub async fn get_track_metadata(&self, track_id: &SpotifyId) -> SpClientResult {
self.get_metadata("track", track_id).await
pub async fn get_track_metadata(&self, track_uri: &SpotifyUri) -> SpClientResult {
self.get_metadata(ExtensionKind::TRACK_V4, track_uri).await
}

pub async fn get_episode_metadata(&self, episode_id: &SpotifyId) -> SpClientResult {
self.get_metadata("episode", episode_id).await
pub async fn get_episode_metadata(&self, episode_uri: &SpotifyUri) -> SpClientResult {
self.get_metadata(ExtensionKind::EPISODE_V4, episode_uri)
.await
}

pub async fn get_album_metadata(&self, album_id: &SpotifyId) -> SpClientResult {
self.get_metadata("album", album_id).await
pub async fn get_album_metadata(&self, album_uri: &SpotifyUri) -> SpClientResult {
self.get_metadata(ExtensionKind::ALBUM_V4, album_uri).await
}

pub async fn get_artist_metadata(&self, artist_id: &SpotifyId) -> SpClientResult {
self.get_metadata("artist", artist_id).await
pub async fn get_artist_metadata(&self, artist_uri: &SpotifyUri) -> SpClientResult {
self.get_metadata(ExtensionKind::ARTIST_V4, artist_uri)
.await
}

pub async fn get_show_metadata(&self, show_id: &SpotifyId) -> SpClientResult {
self.get_metadata("show", show_id).await
pub async fn get_show_metadata(&self, show_uri: &SpotifyUri) -> SpClientResult {
self.get_metadata(ExtensionKind::SHOW_V4, show_uri).await
}

pub async fn get_lyrics(&self, track_id: &SpotifyId) -> SpClientResult {
Expand Down Expand Up @@ -733,12 +759,6 @@ impl SpClient {
// TODO: Seen-in-the-wild but unimplemented endpoints
// - /presence-view/v1/buddylist

pub async fn get_extended_metadata(&self, request: BatchedEntityRequest) -> SpClientResult {
let endpoint = "/extended-metadata/v0/extended-metadata";
self.request_with_protobuf(&Method::POST, endpoint, None, &request)
.await
}

pub async fn get_audio_storage(&self, file_id: &FileId) -> SpClientResult {
let endpoint = format!(
"/storage-resolve/files/audio/interactive/{}",
Expand Down
2 changes: 1 addition & 1 deletion discovery/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ fn launch_libmdns(
}
.map_err(|e| DiscoveryError::DnsSdError(Box::new(e)))?;

let svc = responder.register(&DNS_SD_SERVICE_NAME, &name, port, &TXT_RECORD);
let svc = responder.register(DNS_SD_SERVICE_NAME, &name, port, &TXT_RECORD);

let _ = shutdown_rx.blocking_recv();

Expand Down
4 changes: 2 additions & 2 deletions metadata/src/album.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ impl Metadata for Album {
type Message = protocol::metadata::Album;

async fn request(session: &Session, album_uri: &SpotifyUri) -> RequestResult {
let SpotifyUri::Album { id: album_id } = album_uri else {
let SpotifyUri::Album { .. } = album_uri else {
return Err(Error::invalid_argument("album_uri"));
};

session.spclient().get_album_metadata(album_id).await
session.spclient().get_album_metadata(album_uri).await
}

fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
Expand Down
4 changes: 2 additions & 2 deletions metadata/src/artist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,11 @@ impl Metadata for Artist {
type Message = protocol::metadata::Artist;

async fn request(session: &Session, artist_uri: &SpotifyUri) -> RequestResult {
let SpotifyUri::Artist { id: artist_id } = artist_uri else {
let SpotifyUri::Artist { .. } = artist_uri else {
return Err(Error::invalid_argument("artist_uri"));
};

session.spclient().get_artist_metadata(artist_id).await
session.spclient().get_artist_metadata(artist_uri).await
}

fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
Expand Down
4 changes: 2 additions & 2 deletions metadata/src/episode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ impl Metadata for Episode {
type Message = protocol::metadata::Episode;

async fn request(session: &Session, episode_uri: &SpotifyUri) -> RequestResult {
let SpotifyUri::Episode { id: episode_id } = episode_uri else {
let SpotifyUri::Episode { .. } = episode_uri else {
return Err(Error::invalid_argument("episode_uri"));
};

session.spclient().get_episode_metadata(episode_id).await
session.spclient().get_episode_metadata(episode_uri).await
}

fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
Expand Down
4 changes: 2 additions & 2 deletions metadata/src/show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ impl Metadata for Show {
type Message = protocol::metadata::Show;

async fn request(session: &Session, show_uri: &SpotifyUri) -> RequestResult {
let SpotifyUri::Show { id: show_id } = show_uri else {
let SpotifyUri::Show { .. } = show_uri else {
return Err(Error::invalid_argument("show_uri"));
};

session.spclient().get_show_metadata(show_id).await
session.spclient().get_show_metadata(show_uri).await
}

fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
Expand Down
4 changes: 2 additions & 2 deletions metadata/src/track.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ impl Metadata for Track {
type Message = protocol::metadata::Track;

async fn request(session: &Session, track_uri: &SpotifyUri) -> RequestResult {
let SpotifyUri::Track { id: track_id } = track_uri else {
let SpotifyUri::Track { .. } = track_uri else {
return Err(Error::invalid_argument("track_uri"));
};

session.spclient().get_track_metadata(track_id).await
session.spclient().get_track_metadata(track_uri).await
}

fn parse(msg: &Self::Message, _: &SpotifyUri) -> Result<Self, Error> {
Expand Down
36 changes: 8 additions & 28 deletions playback/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use std::{mem, str::FromStr, time::Duration};
pub use crate::dither::{DithererBuilder, TriangularDitherer, mk_ditherer};
use crate::{convert::i24, player::duration_to_coefficient};

#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum Bitrate {
Bitrate96,
#[default]
Bitrate160,
Bitrate320,
}
Expand All @@ -22,19 +23,14 @@ impl FromStr for Bitrate {
}
}

impl Default for Bitrate {
fn default() -> Self {
Self::Bitrate160
}
}

#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum AudioFormat {
F64,
F32,
S32,
S24,
S24_3,
#[default]
S16,
}

Expand All @@ -53,12 +49,6 @@ impl FromStr for AudioFormat {
}
}

impl Default for AudioFormat {
fn default() -> Self {
Self::S16
}
}

impl AudioFormat {
// not used by all backends
#[allow(dead_code)]
Expand All @@ -73,10 +63,11 @@ impl AudioFormat {
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum NormalisationType {
Album,
Track,
#[default]
Auto,
}

Expand All @@ -92,15 +83,10 @@ impl FromStr for NormalisationType {
}
}

impl Default for NormalisationType {
fn default() -> Self {
Self::Auto
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum NormalisationMethod {
Basic,
#[default]
Dynamic,
}

Expand All @@ -115,12 +101,6 @@ impl FromStr for NormalisationMethod {
}
}

impl Default for NormalisationMethod {
fn default() -> Self {
Self::Dynamic
}
}

#[derive(Clone)]
pub struct PlayerConfig {
pub bitrate: Bitrate,
Expand Down
Loading