1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use std::collections::HashMap;
use crate::client::endpoints::TargetAPI;
use crate::client::{
EndpointPlaylist, Error, PlaylistItemMod, PlaylistItemOperations, PlaylistItemsMod,
PlaylistItemsOperations, Wrapped,
};
use crate::client::{Paginated, SignedIn};
use crate::mc::playlist::{Playlist, PlaylistID};
use crate::mc::release::AnyRelease;
use uuid::uuid;
const TOP_30: PlaylistID = PlaylistID(uuid!("991334fb-ca5e-48c6-bc73-cb83c364357d"));
impl<ClientAuthState> EndpointPlaylist<'_, ClientAuthState> {
/// Get the public playlist of top 30 tracks.
pub fn get_top_30_playlist_id(&self) -> PlaylistID {
TOP_30
}
/// Get a playlist by id.
pub fn by_id(&self, id: PlaylistID) -> Result<Playlist, Error> {
self.client
.get::<Wrapped<Playlist>>(
TargetAPI::Player,
&format!("/playlist/{id}"),
None::<HashMap<String, String>>,
)?
.remove("Playlist")
.ok_or(Error::NotFound("latest artists"))
}
/// Get the tracks of a playlist.
pub fn get_tracks_by_playlist_id(
&self,
id: PlaylistID,
) -> Result<Paginated<AnyRelease>, Error> {
self.client.get::<Paginated<AnyRelease>>(
TargetAPI::Player,
&format!("/playlist/{id}/catalog"),
None::<HashMap<String, String>>,
)
}
/// Get playlist tile image.
///
/// Example
/// ```rust
/// use bombay::client::Client;
/// use bombay::mc::playlist::PlaylistID;
/// use uuid::uuid;
///
/// let mc = Client::default(); // Without authentication.
/// let mut reader = mc.playlist().get_tile_image(
/// PlaylistID(uuid!("991334fb-ca5e-48c6-bc73-cb83c364357d"))
/// ).expect("Could not get tile.");
///
/// let _dir = std::fs::create_dir_all("downloads").unwrap();
/// let mut file_out = std::fs::File::create("downloads/top_30_tile.png").expect("Could not create file.");
/// std::io::copy(&mut reader, &mut file_out).expect("Could not save tile.");
/// ```
pub fn get_tile_image(
&self,
playlist_id: PlaylistID,
) -> Result<Box<dyn std::io::Read + Send + Sync>, Error> {
self.client.get_reader(
TargetAPI::Player,
&format!("/playlist/{playlist_id}/tile"),
None::<HashMap<String, String>>,
)
}
/// Get playlist background image.
///
/// Example
/// ```rust
/// use bombay::client::Client;
/// use bombay::mc::playlist::PlaylistID;
/// use uuid::uuid;
///
/// let mc = Client::default(); // Without authentication.
/// let mut reader = mc.playlist().get_background_image(
/// PlaylistID(uuid!("991334fb-ca5e-48c6-bc73-cb83c364357d"))
/// ).expect("Could not get background.");
///
/// let _dir = std::fs::create_dir_all("downloads").unwrap();
/// let mut file_out = std::fs::File::create("downloads/top_30_background.png").expect("Could not create file.");
/// std::io::copy(&mut reader, &mut file_out).expect("Could not save background.");
/// ```
pub fn get_background_image(
&self,
playlist_id: PlaylistID,
) -> Result<Box<dyn std::io::Read + Send + Sync>, Error> {
self.client.get_reader(
TargetAPI::Player,
&format!("/playlist/{playlist_id}/background"),
None::<HashMap<String, String>>,
)
}
}
impl EndpointPlaylist<'_, SignedIn> {
/// Get all of the user's playlist.
pub fn get_all(&self) -> Result<Paginated<Playlist>, Error> {
self.client
.get::<Wrapped<Paginated<Playlist>>>(
TargetAPI::Player,
"/playlists",
None::<HashMap<String, String>>,
)?
.remove("Playlists")
.ok_or(Error::NotFound("Playlists not found."))
}
/// Create a playlist.
pub fn create(&self, playlist: Playlist) -> Result<PlaylistID, Error> {
let wrapped_id = self
.client
.post::<Wrapped<PlaylistID>>(
TargetAPI::Player,
&format!("/playlist"),
None::<HashMap<String, String>>,
Some(playlist),
)?
.remove("Id")
.ok_or(Error::NotFound("Playlist not found."));
wrapped_id
}
/// Edit a playlist.
pub fn edit(&self, playlist: Playlist) -> Result<Playlist, Error> {
self.client.post::<Playlist>(
TargetAPI::Player,
&format!("/playlist/{}", &playlist.id),
None::<HashMap<String, String>>,
Some(playlist),
)
}
/// Modify a single playlist item.
pub fn modify_item(
&self,
playlist_id: PlaylistID,
operation: PlaylistItemOperations,
item_mod: PlaylistItemMod,
) -> Result<(), Error> {
if operation == PlaylistItemOperations::To && item_mod.move_to.is_none() {
Err(Error::Message(
"Playlist item move operation requires a move_to index.",
))
} else {
self.client.post_empty_response(
TargetAPI::Player,
&format!("/playlist/{playlist_id}/modify-item"),
Some(operation),
Some(item_mod),
)
}
}
/// Modify multiple playlist items.
pub fn modify_items(
&self,
playlist_id: PlaylistID,
operation: PlaylistItemsOperations,
items_mod: PlaylistItemsMod,
) -> Result<(), Error> {
self.client.post_empty_response(
TargetAPI::Player,
&format!("/playlist/{playlist_id}/modify-items"),
Some(operation),
Some(items_mod),
)
}
/// Delete playlist.
pub fn delete(&self, playlist_id: PlaylistID) -> Result<(), Error> {
self.client.post_empty_response(
TargetAPI::Player,
&format!("/playlist/{playlist_id}/delete"),
None::<HashMap<String, String>>,
None::<()>,
)
}
}