1use super::info::FavFolderInfo;
2use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
3
4impl BpiClient {
5 pub async fn fav_folder_add(
19 &self,
20 title: &str,
21 intro: Option<&str>,
22 privacy: Option<u8>,
23 cover: Option<&str>
24 ) -> Result<BpiResponse<FavFolderInfo>, BpiError> {
25 let csrf = self.csrf()?;
26
27 let mut form = vec![("title", title.to_string()), ("csrf", csrf)];
28 if let Some(intro) = intro {
29 form.push(("intro", intro.to_string()));
30 }
31 if let Some(privacy) = privacy {
32 form.push(("privacy", privacy.to_string()));
33 }
34 if let Some(cover) = cover {
35 form.push(("cover", cover.to_string()));
36 }
37
38 self
39 .post("https://api.bilibili.com/x/v3/fav/folder/add")
40 .form(&form)
41 .send_bpi("新建收藏夹").await
42 }
43
44 pub async fn fav_folder_edit(
59 &self,
60 media_id: u64,
61 title: &str,
62 intro: Option<&str>,
63 privacy: Option<u8>,
64 cover: Option<&str>
65 ) -> Result<BpiResponse<FavFolderInfo>, BpiError> {
66 let csrf = self.csrf()?;
67
68 let mut form = vec![
69 ("media_id", media_id.to_string()),
70 ("title", title.to_string()),
71 ("csrf", csrf)
72 ];
73 if let Some(intro) = intro {
74 form.push(("intro", intro.to_string()));
75 }
76 if let Some(privacy) = privacy {
77 form.push(("privacy", privacy.to_string()));
78 }
79 if let Some(cover) = cover {
80 form.push(("cover", cover.to_string()));
81 }
82
83 self
84 .post("https://api.bilibili.com/x/v3/fav/folder/edit")
85 .form(&form)
86 .send_bpi("修改收藏夹").await
87 }
88
89 pub async fn fav_folder_del(&self, media_ids: &[u64]) -> Result<BpiResponse<i32>, BpiError> {
100 let csrf = self.csrf()?;
101 let ids_str = media_ids
102 .iter()
103 .map(|&id| id.to_string())
104 .collect::<Vec<_>>()
105 .join(",");
106
107 let form = [
108 ("media_ids", ids_str),
109 ("csrf", csrf),
110 ];
111
112 self
113 .post("https://api.bilibili.com/x/v3/fav/folder/del")
114 .form(&form)
115 .send_bpi("删除收藏夹").await
116 }
117
118 pub async fn fav_resource_copy(
133 &self,
134 src_media_id: u64,
135 tar_media_id: u64,
136 mid: u64,
137 resources: &str
138 ) -> Result<BpiResponse<i32>, BpiError> {
139 let csrf = self.csrf()?;
140
141 let form = [
142 ("src_media_id", src_media_id.to_string()),
143 ("tar_media_id", tar_media_id.to_string()),
144 ("mid", mid.to_string()),
145 ("resources", resources.to_string()),
146 ("platform", "web".to_string()),
147 ("csrf", csrf),
148 ];
149
150 self
151 .post("https://api.bilibili.com/x/v3/fav/resource/copy")
152 .form(&form)
153 .send_bpi("批量复制内容").await
154 }
155
156 pub async fn fav_resource_move(
171 &self,
172 src_media_id: u64,
173 tar_media_id: u64,
174 mid: u64,
175 resources: &str
176 ) -> Result<BpiResponse<i32>, BpiError> {
177 let csrf = self.csrf()?;
178
179 let form = [
180 ("src_media_id", src_media_id.to_string()),
181 ("tar_media_id", tar_media_id.to_string()),
182 ("mid", mid.to_string()),
183 ("resources", resources.to_string()),
184 ("platform", "web".to_string()),
185 ("csrf", csrf),
186 ];
187
188 self
189 .post("https://api.bilibili.com/x/v3/fav/resource/move")
190 .form(&form)
191 .send_bpi("批量移动内容").await
192 }
193
194 pub async fn fav_resource_batch_del(
207 &self,
208 media_id: u64,
209 resources: &str
210 ) -> Result<BpiResponse<i32>, BpiError> {
211 let csrf = self.csrf()?;
212
213 let form = [
214 ("media_id", media_id.to_string()),
215 ("resources", resources.to_string()),
216 ("platform", "web".to_string()),
217 ("csrf", csrf),
218 ];
219
220 self
221 .post("https://api.bilibili.com/x/v3/fav/resource/batch-del")
222 .form(&form)
223 .send_bpi("批量删除内容").await
224 }
225
226 pub async fn fav_resource_clean(&self, media_id: u64) -> Result<BpiResponse<i32>, BpiError> {
237 let csrf = self.csrf()?;
238
239 let form = [
240 ("media_id", media_id.to_string()),
241 ("csrf", csrf),
242 ];
243
244 self
245 .post("https://api.bilibili.com/x/v3/fav/resource/clean")
246 .form(&form)
247 .send_bpi("清空所有失效内容").await
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use tracing::info;
255
256 #[tokio::test]
257 async fn test_fav_folder_add_and_del() {
258 let bpi = BpiClient::new();
259 let title = "Test_Fav_Folder_Add2";
260 let intro = "This is a test folder2.";
261
262 let add_resp = bpi.fav_folder_add(title, Some(intro), Some(1), None).await;
264 info!("Add folder result: {:?}", add_resp);
265 assert!(add_resp.is_ok());
266
267 let new_folder_id = add_resp.unwrap().data.unwrap().id;
268 info!("New folder ID: {}", new_folder_id);
269
270 tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
271
272 let del_resp = bpi.fav_folder_del(&[new_folder_id]).await;
274 info!("Delete folder result: {:?}", del_resp);
275 assert!(del_resp.is_ok());
276 }
277
278 #[tokio::test]
279 async fn test_fav_folder_edit() {
280 let bpi = BpiClient::new();
281 let media_id = 3717139570;
283 let title = "Edited Title";
284 let intro = "Edited Intro";
285
286 let resp = bpi.fav_folder_edit(media_id, title, Some(intro), Some(0), None).await;
287 info!("Edit folder result: {:?}", resp);
288 assert!(resp.is_ok());
289 }
290
291 #[tokio::test]
292 async fn test_fav_resource_operations() {
293 let bpi = BpiClient::new();
294 let src_media_id = 3717139570;
296 let tar_media_id = 3641682570;
297 let mid = 4279370;
298 let resources_copy = "115087859779103:2";
299 let resources_del = "442608504:2";
300 let resources_move = "739661210:2";
301
302 let copy_resp = bpi.fav_resource_copy(
304 src_media_id,
305 tar_media_id,
306 mid,
307 resources_copy
308 ).await;
309 info!("Copy resources result: {:?}", copy_resp);
310 assert!(copy_resp.is_ok());
311
312 let move_resp = bpi.fav_resource_move(
314 src_media_id,
315 tar_media_id,
316 mid,
317 resources_move
318 ).await;
319 info!("Move resources result: {:?}", move_resp);
320 assert!(move_resp.is_ok());
321
322 let del_resp = bpi.fav_resource_batch_del(tar_media_id, resources_del).await;
324 info!("Batch delete resources result: {:?}", del_resp);
325 assert!(del_resp.is_ok());
326 }
327
328 #[tokio::test]
329 async fn test_fav_resource_clean() {
330 let bpi = BpiClient::new();
331 let media_id = 3717139570;
333 let resp = bpi.fav_resource_clean(media_id).await;
334 info!("Clean invalid resources result: {:?}", resp);
335 assert!(resp.is_ok());
336 }
337}