Skip to main content

pandora_api/json/
bookmark.rs

1/*!
2Bookmark support messages.
3
4Users can bookmark artists or songs.
5*/
6// SPDX-License-Identifier: MIT AND WTFPL
7use pandora_api_derive::PandoraJsonRequest;
8use serde::{Deserialize, Serialize};
9
10use crate::errors::Error;
11use crate::json::{PandoraJsonApiRequest, PandoraSession, Timestamp};
12
13/// | Name | Type | Description |
14/// | trackToken | string | |
15/// ``` json
16/// {
17///     "trackToken": "f17ff6c86c11743fc890808e1a1dd6ff5b1dca1a2e260f7d998ba6e7786dd9941c5dd4b345a1008e86862353da1e6cdc78172b4199240c76",
18///     "userAuthToken": "XXX",
19///     "syncTime": 1338210690
20/// }
21/// ```
22#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
23#[pandora_request(encrypted = true)]
24#[serde(rename_all = "camelCase")]
25pub struct AddArtistBookmark {
26    /// The unique id (token) for this track.
27    pub track_token: String,
28}
29
30impl<TS: ToString> From<&TS> for AddArtistBookmark {
31    fn from(track_token: &TS) -> Self {
32        Self {
33            track_token: track_token.to_string(),
34        }
35    }
36}
37
38///
39/// ``` json
40/// {
41///     "stat": "ok",
42///     "result": {
43///         "artistName": "Wallis Bird",
44///         "dateCreated": {
45///             "date": 2,
46///             "day": 3,
47///             "hours": 7,
48///             "minutes": 6,
49///             "month": 6,
50///             "seconds": 13,
51///             "time": 1404309973468,
52///             "timezoneOffset": 420,
53///             "year": 114
54///         },
55///         "bookmarkToken": "49854851068341741",
56///         "artUrl": "http://cont-dc6-2.pandora.com/images/public/amg/portrait/pic200/drP900/P998/P99805K1QKS.jpg",
57///         "musicToken": "R278544"
58///     }
59/// }
60/// ```
61#[derive(Debug, Clone, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub struct AddArtistBookmarkResponse {
64    /// The name of the artist being bookmarked.
65    pub artist_name: String,
66    /// Timestamp for when the bookmark was created.
67    pub date_created: Timestamp,
68    /// The unique id (token) for the newly-created bookmark.
69    pub bookmark_token: String,
70    /// A link to an image of the artist.
71    pub art_url: String,
72    /// The unique id (token) for the artist. Artist tokens start with 'R',
73    /// composers with 'C', songs with 'S', and genres with 'G'.
74    pub music_token: String,
75}
76
77/// Convenience function to do a basic addArtistBookmark call.
78pub async fn add_artist_bookmark(
79    session: &mut PandoraSession,
80    track_token: &str,
81) -> Result<AddArtistBookmarkResponse, Error> {
82    AddArtistBookmark::from(&track_token)
83        .response(session)
84        .await
85}
86
87/// | Name | Type | Description |
88/// | trackToken | string | |
89/// ``` json
90/// {
91///     "trackToken": "f17ff6c86c11743fc890808e1a1dd6ff5b1dca1a2e260f7d998ba6e7786dd9941c5dd4b345a1008e86862353da1e6cdc78172b4199240c76",
92///     "userAuthToken": "XXX",
93///     "syncTime": 1338210690
94/// }
95/// ```
96#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
97#[pandora_request(encrypted = true)]
98#[serde(rename_all = "camelCase")]
99pub struct AddSongBookmark {
100    /// The unique id (token) for this track.
101    pub track_token: String,
102}
103
104impl<TS: ToString> From<&TS> for AddSongBookmark {
105    fn from(track_token: &TS) -> Self {
106        Self {
107            track_token: track_token.to_string(),
108        }
109    }
110}
111
112///
113/// ``` json
114/// {
115///     "stat": "ok",
116///     "result": {
117///         "sampleGain": "1.96",
118///         "musicToken": "S1143982",
119///         "bookmarkToken": "200207779061968365",
120///         "sampleUrl": "http://www.pandora.com/favorites/getSample.jsp?token=a74b4f7551e3e174425ba2910f7abf8b&allowExplicit=true",
121///         "albumName": "The 5th Exotic",
122///         "songName": "The 5th Exotic",
123///         "artUrl": "http://cont-sjl-1.pandora.com/images/public/amz/9/4/5/2/800002549_500W_500H.jpg",
124///         "dateCreated": {
125///             "date": 28,
126///             "day": 1,
127///             "hours": 6,
128///             "minutes": 11,
129///             "month": 4,
130///             "seconds": 31,
131///             "time": 1338210691404,
132///             "timezoneOffset": 420,
133///             "year": 112
134///         },
135///         "artistName": "Quantic"
136///     }
137/// }
138/// ```
139#[derive(Debug, Clone, Deserialize)]
140#[serde(rename_all = "camelCase")]
141pub struct AddSongBookmarkResponse {
142    /// The audio gain for the bookmarked track. (?)
143    pub sample_gain: String,
144    /// The unique id (token) for the song. Artist tokens start with 'R',
145    /// composers with 'C', songs with 'S', and genres with 'G'.
146    pub music_token: String,
147    /// The unique id (token) for the newly-created bookmark.
148    pub bookmark_token: String,
149    /// Url for a sample of the bookmarked song.
150    pub sample_url: String,
151    /// The name of the album for the song being bookmarked.
152    pub album_name: String,
153    /// The name of the song being bookmarked.
154    pub song_name: String,
155    /// A link to an image of the artist.
156    pub art_url: String,
157    /// Timestamp for when the bookmark was created.
158    pub date_created: Timestamp,
159    /// The name of the artist being bookmarked.
160    pub artist_name: String,
161}
162
163/// Convenience function to do a basic addSongBookmark call.
164pub async fn add_song_bookmark(
165    session: &mut PandoraSession,
166    track_token: &str,
167) -> Result<AddSongBookmarkResponse, Error> {
168    AddSongBookmark::from(&track_token).response(session).await
169}
170
171/// Bookmarks can be deleted
172///
173/// | Name |   Type |   Description |
174/// | bookmarkToken | string |  |
175/// ``` json
176/// {
177///     "bookmarkToken": "3738252050522320365",
178///     "userAuthToken": "XXX",
179///     "syncTime": 1404910760
180/// }
181/// ```
182#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
183#[pandora_request(encrypted = true)]
184#[serde(rename_all = "camelCase")]
185pub struct DeleteArtistBookmark {
186    /// The unique id (token) for the bookmark submission that should be deleted.
187    pub bookmark_token: String,
188}
189
190impl<TS: ToString> From<&TS> for DeleteArtistBookmark {
191    fn from(bookmark_token: &TS) -> Self {
192        Self {
193            bookmark_token: bookmark_token.to_string(),
194        }
195    }
196}
197
198/// This method does not return data.
199#[derive(Debug, Clone, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub struct DeleteArtistBookmarkResponse {}
202
203/// Convenience function to do a basic deleteArtistBookmark call.
204pub async fn delete_artist_bookmark(
205    session: &mut PandoraSession,
206    bookmark_token: &str,
207) -> Result<DeleteArtistBookmarkResponse, Error> {
208    DeleteArtistBookmark::from(&bookmark_token)
209        .response(session)
210        .await
211}
212
213/// Bookmarks can be deleted
214///
215/// | Name |   Type |   Description |
216/// | bookmarkToken | string |  |
217/// ``` json
218/// {
219///     "bookmarkToken": "3738252050522320365",
220///     "userAuthToken": "XXX",
221///     "syncTime": 1404910760
222/// }
223/// ```
224#[derive(Debug, Clone, Serialize, PandoraJsonRequest)]
225#[pandora_request(encrypted = true)]
226#[serde(rename_all = "camelCase")]
227pub struct DeleteSongBookmark {
228    /// The unique id (token) for the bookmark submission that should be deleted.
229    pub bookmark_token: String,
230}
231
232impl<TS: ToString> From<&TS> for DeleteSongBookmark {
233    fn from(bookmark_token: &TS) -> Self {
234        Self {
235            bookmark_token: bookmark_token.to_string(),
236        }
237    }
238}
239
240/// This method does not return data.
241#[derive(Debug, Clone, Deserialize)]
242#[serde(rename_all = "camelCase")]
243pub struct DeleteSongBookmarkResponse {}
244
245/// Convenience function to do a basic deleteSongBookmark call.
246pub async fn delete_song_bookmark(
247    session: &mut PandoraSession,
248    bookmark_token: &str,
249) -> Result<DeleteSongBookmarkResponse, Error> {
250    DeleteSongBookmark::from(&bookmark_token)
251        .response(session)
252        .await
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use crate::json::{
259        station::get_playlist, tests::session_login, user::get_bookmarks, user::get_station_list,
260        Partner,
261    };
262
263    /* Artist bookmark creation test is failing, so virtually none of the tests here work anymore
264    #[tokio::test]
265    async fn bookmark_test() {
266        flexi_logger::Logger::try_with_str("info, pandora_api=debug")
267            .expect("Failed to set logging configuration")
268            .start()
269            .expect("Failed to start logger");
270
271        let partner = Partner::default();
272        let mut session = session_login(&partner)
273            .await
274            .expect("Failed initializing login session");
275
276        if let Some(station) = get_station_list(&mut session)
277            .await
278            .expect("Failed getting station list to look up a track to bookmark")
279            .stations
280            .first()
281        {
282            if let Some(track) = get_playlist(&mut session, &station.station_token)
283                .await
284                .expect("Failed completing request for playlist")
285                .items
286                .iter()
287                .flat_map(|p| p.get_track())
288                .next()
289            {
290                let artist_bookmark = add_artist_bookmark(&mut session, &track.track_token)
291                    .await
292                    .expect("Failed submitting artist bookmark creation request");
293                log::debug!("Bookmark creation result: {:?}", artist_bookmark);
294
295            // TODO: song bookmark deletion doesn't seem to work yet, so lets
296            // not go creating more with each run.
297            //let song_bookmark = add_song_bookmark(&mut session, &track.track_token)
298            //    .expect("Failed submitting song bookmark creation request");
299            //log::debug!("Bookmark creation result: {:?}", song_bookmark);
300            } else {
301                panic!("Playlist request returned no bookmarkable results.");
302            }
303        } else {
304            panic!("Station list request returned no results, so no bookmarkable content.");
305        }
306
307        let user_bookmarks = get_bookmarks(&mut session)
308            .await
309            .expect("Failed submitting request for user bookmarks");
310
311        for artist_bookmark in user_bookmarks.artists {
312            let _del_bookmark =
313                delete_artist_bookmark(&mut session, &artist_bookmark.bookmark_token)
314                    .await
315                    .expect("Failed submitting artist bookmark deletion request");
316        }
317
318        // TODO: song bookmark deletion is borken, can't figure out why
319        for song_bookmark in user_bookmarks.songs {
320            let _del_bookmark = delete_artist_bookmark(&mut session, &song_bookmark.bookmark_token)
321                .expect("Failed submitting song bookmark deletion request");
322        }
323    }
324    */
325}