Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: master
hooks:
- id: fmt
- id: clippy
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