use std::collections::HashMap;
use pandora_api_derive::PandoraJsonRequest;
use serde::{Deserialize, Serialize};
use crate::errors::Error;
use crate::json::{PandoraJsonApiRequest, PandoraSession};
pub struct GetSearchRecommendationsUnsupported {}
#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
#[pandora_request(encrypted = true)]
#[serde(rename_all = "camelCase")]
pub struct GetTrack {
pub music_id: String,
}
impl<TS: ToString> From<&TS> for GetTrack {
fn from(music_id: &TS) -> Self {
Self {
music_id: music_id.to_string(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetTrackResponse {
pub song_name: String,
pub artist_name: String,
pub album_name: String,
pub track_token: String,
pub music_id: String,
pub music_token: String,
#[serde(flatten)]
pub optional: HashMap<String, serde_json::value::Value>,
}
pub async fn get_track(
session: &mut PandoraSession,
track_token: &str,
) -> Result<GetTrackResponse, Error> {
GetTrack::from(&track_token).response(session).await
}
pub struct PublishSongShareUnsupported {}
#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
#[pandora_request(encrypted = true)]
#[serde(rename_all = "camelCase")]
pub struct Search {
pub search_text: String,
#[serde(flatten)]
pub optional: HashMap<String, serde_json::value::Value>,
}
impl Search {
pub fn and_boolean_option(mut self, option: &str, value: bool) -> Self {
self.optional
.insert(option.to_string(), serde_json::value::Value::from(value));
self
}
pub fn include_near_matches(self, value: bool) -> Self {
self.and_boolean_option("includeNearMatches", value)
}
pub fn include_genre_stations(self, value: bool) -> Self {
self.and_boolean_option("includeGenreStations", value)
}
}
impl<TS: ToString> From<&TS> for Search {
fn from(search_text: &TS) -> Self {
Self {
search_text: search_text.to_string(),
optional: HashMap::new(),
}
}
}
pub async fn search(
session: &mut PandoraSession,
search_text: &str,
) -> Result<SearchResponse, Error> {
Search::from(&search_text)
.include_near_matches(false)
.include_genre_stations(false)
.response(session)
.await
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchResponse {
#[serde(default)]
pub songs: Vec<SongMatch>,
#[serde(default)]
pub artists: Vec<ArtistMatch>,
#[serde(default)]
pub genre_stations: Vec<GenreMatch>,
#[serde(flatten)]
pub optional: HashMap<String, serde_json::value::Value>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SongMatch {
pub song_name: String,
pub artist_name: String,
pub music_token: String,
pub score: u8,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ArtistMatch {
pub artist_name: String,
pub music_token: String,
pub likely_match: bool,
pub score: u8,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenreMatch {
pub music_token: String,
pub score: u8,
pub station_name: String,
}
pub struct ShareMusicUnsupported {}
#[cfg(test)]
mod tests {
use super::*;
use crate::json::{
station::get_playlist, tests::session_login, user::get_station_list, Partner,
};
#[tokio::test]
async fn search_test() {
let partner = Partner::default();
let mut session = session_login(&partner)
.await
.expect("Failed initializing login session");
let _search_response = search(&mut session, "INXS")
.await
.expect("Failed completing search request");
let _search_response: SearchResponse = Search::from(&"Alternative")
.include_genre_stations(true)
.response(&mut session)
.await
.expect("Failed completing search request");
}
#[tokio::test]
async fn get_track_test() {
let partner = Partner::default();
let mut session = session_login(&partner)
.await
.expect("Failed initializing login session");
for station in get_station_list(&mut session)
.await
.expect("Failed getting station list to look up a track to bookmark")
.stations
{
for track in get_playlist(&mut session, &station.station_token)
.await
.expect("Failed completing request for playlist")
.items
.iter()
.flat_map(|p| p.get_track())
{
if let Some(serde_json::value::Value::String(music_id)) =
track.optional.get("musicId")
{
let _response = get_track(&mut session, &music_id)
.await
.expect("Failed getting track information");
}
}
}
}
}