pandora_api/json/track.rs
1/*!
2Track support methods.
3*/
4// SPDX-License-Identifier: MIT AND WTFPL
5
6use pandora_api_derive::PandoraJsonRequest;
7use serde::{Deserialize, Serialize};
8
9use crate::errors::Error;
10use crate::json::{PandoraJsonApiRequest, PandoraSession};
11
12/// Get (incomplete) list of attributes assigned to song by Music Genome Project.
13///
14/// | Name | Type | Description |
15/// | trackToken | string | See Retrieve playlist |
16/// ``` json
17/// {
18/// "trackToken": "94f36e09e341780c2ee7ebbb3581a55c4f2066dbaa60f2ee253ede5bc407fbd3c4f6db7ed00f92312437e020e0bf0e05d2924742c2ccece2",
19/// "userAuthToken": "XXX",
20/// "syncTime": 1336675993
21/// }
22/// ```
23#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
24#[pandora_request(encrypted = true)]
25#[serde(rename_all = "camelCase")]
26pub struct ExplainTrack {
27 /// The token associated with the track for which an explanation is being requested.
28 pub track_token: String,
29}
30
31impl<TS: ToString> From<&TS> for ExplainTrack {
32 fn from(track_token: &TS) -> Self {
33 Self {
34 track_token: track_token.to_string(),
35 }
36 }
37}
38
39/// The request returns a list of attributes. Note that the last item is not an actual attribute.
40///
41/// | Name | Type | Description |
42/// | explanations | array | |
43/// ``` json
44/// {
45/// "stat": "ok",
46/// "result": {
47/// "explanations": [{
48/// "focusTraitName": "trance roots",
49/// "focusTraitId": "F7524"
50/// },
51/// {
52/// "focusTraitName": "many other similarities identified in the Music Genome Project",
53/// "focusTraitId": "F4797"
54/// }]
55/// }
56/// }
57/// ```
58#[derive(Debug, Clone, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct ExplainTrackResponse {
61 /// A list of explanations for why the track was chosen.
62 _explanations: Vec<Explanation>,
63}
64
65/// Describes traits of a track that would explain why it's recommended.
66#[derive(Debug, Clone, Deserialize)]
67#[serde(rename_all = "camelCase")]
68pub struct Explanation {
69 /// Text description of the audio trait for which the track was chosen.
70 pub focus_trait_name: String,
71 /// A token or identifier associated with the audio trait.
72 pub focus_trait_id: String,
73}
74
75/// Convenience function to do a basic explainTrack call.
76pub async fn explain_track(
77 session: &mut PandoraSession,
78 track_token: &str,
79) -> Result<ExplainTrackResponse, Error> {
80 ExplainTrack::from(&track_token).response(session).await
81}
82
83/// **Unsupported!**
84/// Undocumented method
85/// [track.trackStarted()](https://6xq.net/pandora-apidoc/json/methods/)
86pub struct TrackStartedUnsupported {}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::json::{
92 station::get_playlist, tests::session_login, user::get_station_list, Partner,
93 };
94
95 #[tokio::test]
96 async fn explain_track_test() {
97 /*
98 flexi_logger::Logger::try_with_str("info, pandora_api=debug")
99 .expect("Failed to set logging configuration")
100 .start()
101 .expect("Failed to start logger");
102 */
103
104 let partner = Partner::default();
105 let mut session = session_login(&partner)
106 .await
107 .expect("Failed initializing login session");
108
109 if let Some(station) = get_station_list(&mut session)
110 .await
111 .expect("Failed getting station list to look up a track to bookmark")
112 .stations
113 .first()
114 {
115 if let Some(track) = get_playlist(&mut session, &station.station_token)
116 .await
117 .expect("Failed completing request for playlist")
118 .items
119 .iter()
120 .flat_map(|p| p.get_track())
121 .next()
122 {
123 let explain_track = explain_track(&mut session, &track.track_token)
124 .await
125 .expect("Failed submitting track explanation request");
126 log::debug!("Track explanation: {:?}", explain_track);
127 } else {
128 panic!("Playlist request returned no explainable results.");
129 }
130 } else {
131 panic!("Station list request returned no results, so no explanable content.");
132 }
133 }
134}