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}