Skip to main content

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}