mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Add more HTTP endpoints and migrate playlist
This commit is contained in:
parent
922e927231
commit
70eb3f9d72
3 changed files with 85 additions and 56 deletions
|
@ -386,7 +386,7 @@ impl SpClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_lyrics(&self, track_id: SpotifyId) -> SpClientResult {
|
pub async fn get_lyrics(&self, track_id: SpotifyId) -> SpClientResult {
|
||||||
let endpoint = format!("/color-lyrics/v1/track/{}", track_id.to_base62()?);
|
let endpoint = format!("/color-lyrics/v2/track/{}", track_id.to_base62()?);
|
||||||
|
|
||||||
self.request_as_json(&Method::GET, &endpoint, None, None)
|
self.request_as_json(&Method::GET, &endpoint, None, None)
|
||||||
.await
|
.await
|
||||||
|
@ -407,6 +407,87 @@ impl SpClient {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_playlist(&self, playlist_id: SpotifyId) -> SpClientResult {
|
||||||
|
let endpoint = format!("/playlist/v2/playlist/{}", playlist_id);
|
||||||
|
|
||||||
|
self.request(&Method::GET, &endpoint, None, None).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_profile(
|
||||||
|
&self,
|
||||||
|
username: String,
|
||||||
|
playlist_limit: Option<u32>,
|
||||||
|
artist_limit: Option<u32>,
|
||||||
|
) -> SpClientResult {
|
||||||
|
let mut endpoint = format!("/user-profile-view/v3/profile/{}", username);
|
||||||
|
|
||||||
|
if playlist_limit.is_some() || artist_limit.is_some() {
|
||||||
|
let _ = write!(endpoint, "?");
|
||||||
|
|
||||||
|
if let Some(limit) = playlist_limit {
|
||||||
|
let _ = write!(endpoint, "playlist_limit={}", limit);
|
||||||
|
if artist_limit.is_some() {
|
||||||
|
let _ = write!(endpoint, "&");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(limit) = artist_limit {
|
||||||
|
let _ = write!(endpoint, "artist_limit={}", limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.request_as_json(&Method::GET, &endpoint, None, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_followers(&self, username: String) -> SpClientResult {
|
||||||
|
let endpoint = format!("/user-profile-view/v3/profile/{}/followers", username);
|
||||||
|
|
||||||
|
self.request_as_json(&Method::GET, &endpoint, None, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_following(&self, username: String) -> SpClientResult {
|
||||||
|
let endpoint = format!("/user-profile-view/v3/profile/{}/following", username);
|
||||||
|
|
||||||
|
self.request_as_json(&Method::GET, &endpoint, None, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_radio_for_track(&self, track_id: SpotifyId) -> SpClientResult {
|
||||||
|
let endpoint = format!(
|
||||||
|
"/inspiredby-mix/v2/seed_to_playlist/{}?response-format=json",
|
||||||
|
track_id.to_uri()?
|
||||||
|
);
|
||||||
|
|
||||||
|
self.request_as_json(&Method::GET, &endpoint, None, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_apollo_station(
|
||||||
|
&self,
|
||||||
|
context: SpotifyId,
|
||||||
|
count: u32,
|
||||||
|
previous_tracks: Vec<SpotifyId>,
|
||||||
|
autoplay: bool,
|
||||||
|
) -> SpClientResult {
|
||||||
|
let previous_track_str = previous_tracks
|
||||||
|
.iter()
|
||||||
|
.map(|track| track.to_uri())
|
||||||
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
|
.join(",");
|
||||||
|
let endpoint = format!(
|
||||||
|
"/radio-apollo/v3/stations/{}?count={}&prev_tracks={}&autoplay={}",
|
||||||
|
context.to_uri()?,
|
||||||
|
count,
|
||||||
|
previous_track_str,
|
||||||
|
autoplay,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.request_as_json(&Method::GET, &endpoint, None, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
// 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(&self, request: EntityCanvazRequest) -> SpClientResult {
|
pub async fn get_canvases(&self, request: EntityCanvazRequest) -> SpClientResult {
|
||||||
let endpoint = "/canvaz-cache/v0/canvases";
|
let endpoint = "/canvaz-cache/v0/canvases";
|
||||||
|
|
|
@ -163,7 +163,7 @@ impl SpotifyId {
|
||||||
/// can be arbitrary while `{id}` is a 22-character long, base62 encoded Spotify ID.
|
/// can be arbitrary while `{id}` is a 22-character long, base62 encoded Spotify ID.
|
||||||
///
|
///
|
||||||
/// Note that this should not be used for playlists, which have the form of
|
/// Note that this should not be used for playlists, which have the form of
|
||||||
/// `spotify:user:{owner_username}:playlist:{id}`.
|
/// `spotify:playlist:{id}`.
|
||||||
///
|
///
|
||||||
/// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
|
/// [Spotify URI]: https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
|
||||||
pub fn from_uri(src: &str) -> SpotifyIdResult {
|
pub fn from_uri(src: &str) -> SpotifyIdResult {
|
||||||
|
|
|
@ -4,10 +4,8 @@ use std::{
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
|
|
||||||
use protobuf::Message;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
request::{MercuryRequest, RequestResult},
|
request::RequestResult,
|
||||||
util::{impl_deref_wrapped, impl_from_repeated_copy, impl_try_from_repeated},
|
util::{impl_deref_wrapped, impl_from_repeated_copy, impl_try_from_repeated},
|
||||||
Metadata,
|
Metadata,
|
||||||
};
|
};
|
||||||
|
@ -55,11 +53,6 @@ pub struct Playlists(pub Vec<SpotifyId>);
|
||||||
|
|
||||||
impl_deref_wrapped!(Playlists, Vec<SpotifyId>);
|
impl_deref_wrapped!(Playlists, Vec<SpotifyId>);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RootPlaylist(pub SelectedListContent);
|
|
||||||
|
|
||||||
impl_deref_wrapped!(RootPlaylist, SelectedListContent);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SelectedListContent {
|
pub struct SelectedListContent {
|
||||||
pub revision: Vec<u8>,
|
pub revision: Vec<u8>,
|
||||||
|
@ -80,31 +73,6 @@ pub struct SelectedListContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Playlist {
|
impl Playlist {
|
||||||
#[allow(dead_code)]
|
|
||||||
async fn request_for_user(
|
|
||||||
session: &Session,
|
|
||||||
username: &str,
|
|
||||||
playlist_id: SpotifyId,
|
|
||||||
) -> RequestResult {
|
|
||||||
let uri = format!(
|
|
||||||
"hm://playlist/user/{}/playlist/{}",
|
|
||||||
username,
|
|
||||||
playlist_id.to_base62()?
|
|
||||||
);
|
|
||||||
<Self as MercuryRequest>::request(session, &uri).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn get_for_user(
|
|
||||||
session: &Session,
|
|
||||||
username: &str,
|
|
||||||
playlist_id: SpotifyId,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let response = Self::request_for_user(session, username, playlist_id).await?;
|
|
||||||
let msg = <Self as Metadata>::Message::parse_from_bytes(&response)?;
|
|
||||||
Self::parse(&msg, playlist_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tracks(&self) -> impl ExactSizeIterator<Item = &SpotifyId> {
|
pub fn tracks(&self) -> impl ExactSizeIterator<Item = &SpotifyId> {
|
||||||
let tracks = self.contents.items.iter().map(|item| &item.id);
|
let tracks = self.contents.items.iter().map(|item| &item.id);
|
||||||
|
|
||||||
|
@ -125,15 +93,12 @@ impl Playlist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MercuryRequest for Playlist {}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Metadata for Playlist {
|
impl Metadata for Playlist {
|
||||||
type Message = protocol::playlist4_external::SelectedListContent;
|
type Message = protocol::playlist4_external::SelectedListContent;
|
||||||
|
|
||||||
async fn request(session: &Session, playlist_id: SpotifyId) -> RequestResult {
|
async fn request(session: &Session, playlist_id: SpotifyId) -> RequestResult {
|
||||||
let uri = format!("hm://playlist/v2/playlist/{}", playlist_id.to_base62()?);
|
session.spclient().get_playlist(playlist_id).await
|
||||||
<Self as MercuryRequest>::request(session, &uri).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(msg: &Self::Message, id: SpotifyId) -> Result<Self, Error> {
|
fn parse(msg: &Self::Message, id: SpotifyId) -> Result<Self, Error> {
|
||||||
|
@ -161,23 +126,6 @@ impl Metadata for Playlist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MercuryRequest for RootPlaylist {}
|
|
||||||
|
|
||||||
impl RootPlaylist {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
async fn request_for_user(session: &Session, username: &str) -> RequestResult {
|
|
||||||
let uri = format!("hm://playlist/user/{}/rootlist", username,);
|
|
||||||
<Self as MercuryRequest>::request(session, &uri).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn get_root_for_user(session: &Session, username: &str) -> Result<Self, Error> {
|
|
||||||
let response = Self::request_for_user(session, username).await?;
|
|
||||||
let msg = protocol::playlist4_external::SelectedListContent::parse_from_bytes(&response)?;
|
|
||||||
Ok(Self(SelectedListContent::try_from(&msg)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&<Playlist as Metadata>::Message> for SelectedListContent {
|
impl TryFrom<&<Playlist as Metadata>::Message> for SelectedListContent {
|
||||||
type Error = librespot_core::Error;
|
type Error = librespot_core::Error;
|
||||||
fn try_from(playlist: &<Playlist as Metadata>::Message) -> Result<Self, Self::Error> {
|
fn try_from(playlist: &<Playlist as Metadata>::Message) -> Result<Self, Self::Error> {
|
||||||
|
|
Loading…
Reference in a new issue