bpi_rs/fav/
action.rs

1use super::info::FavFolderInfo;
2use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
3
4impl BpiClient {
5    /// 新建收藏夹
6    ///
7    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
8    ///
9    /// 参数
10    ///
11    /// | 名称 | 类型 | 说明 |
12    /// | ---- | ---- | ---- |
13    /// | `title` | &str | 收藏夹标题 |
14    /// | `intro` | Option<&str> | 介绍 |
15    /// | `privacy` | Option<u8> | 0 公开,1 私密 |
16    /// | `cover` | Option<&str> | 封面 URL |
17    pub async fn fav_folder_add(
18        &self,
19        title: &str,
20        intro: Option<&str>,
21        privacy: Option<u8>,
22        cover: Option<&str>,
23    ) -> Result<BpiResponse<FavFolderInfo>, BpiError> {
24        let csrf = self.csrf()?;
25
26        let mut form = vec![("title", title.to_string()), ("csrf", csrf)];
27        if let Some(intro) = intro {
28            form.push(("intro", intro.to_string()));
29        }
30        if let Some(privacy) = privacy {
31            form.push(("privacy", privacy.to_string()));
32        }
33        if let Some(cover) = cover {
34            form.push(("cover", cover.to_string()));
35        }
36
37        self.post("https://api.bilibili.com/x/v3/fav/folder/add")
38            .form(&form)
39            .send_bpi("新建收藏夹")
40            .await
41    }
42
43    /// 修改收藏夹
44    ///
45    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
46    ///
47    /// 参数
48    ///
49    /// | 名称 | 类型 | 说明 |
50    /// | ---- | ---- | ---- |
51    /// | `media_id` | u64 | 收藏夹 media_id |
52    /// | `title` | &str | 标题 |
53    /// | `intro` | Option<&str> | 介绍 |
54    /// | `privacy` | Option<u8> | 0 公开,1 私密 |
55    /// | `cover` | Option<&str> | 封面 URL |
56    pub async fn fav_folder_edit(
57        &self,
58        media_id: u64,
59        title: &str,
60        intro: Option<&str>,
61        privacy: Option<u8>,
62        cover: Option<&str>,
63    ) -> Result<BpiResponse<FavFolderInfo>, BpiError> {
64        let csrf = self.csrf()?;
65
66        let mut form = vec![
67            ("media_id", media_id.to_string()),
68            ("title", title.to_string()),
69            ("csrf", csrf),
70        ];
71        if let Some(intro) = intro {
72            form.push(("intro", intro.to_string()));
73        }
74        if let Some(privacy) = privacy {
75            form.push(("privacy", privacy.to_string()));
76        }
77        if let Some(cover) = cover {
78            form.push(("cover", cover.to_string()));
79        }
80
81        self.post("https://api.bilibili.com/x/v3/fav/folder/edit")
82            .form(&form)
83            .send_bpi("修改收藏夹")
84            .await
85    }
86
87    /// 删除收藏夹
88    ///
89    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
90    ///
91    /// 参数
92    ///
93    /// | 名称 | 类型 | 说明 |
94    /// | ---- | ---- | ---- |
95    /// | `media_ids` | &[u64] | 多个收藏夹 media_id |
96    pub async fn fav_folder_del(&self, media_ids: &[u64]) -> Result<BpiResponse<i32>, BpiError> {
97        let csrf = self.csrf()?;
98        let ids_str = media_ids
99            .iter()
100            .map(|&id| id.to_string())
101            .collect::<Vec<_>>()
102            .join(",");
103
104        let form = [("media_ids", ids_str), ("csrf", csrf)];
105
106        self.post("https://api.bilibili.com/x/v3/fav/folder/del")
107            .form(&form)
108            .send_bpi("删除收藏夹")
109            .await
110    }
111
112    /// 批量复制内容
113    /// `resources`: "{内容id}:{内容类型},..."
114    ///
115    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
116    ///
117    /// 参数
118    ///
119    /// | 名称 | 类型 | 说明 |
120    /// | ---- | ---- | ---- |
121    /// | `src_media_id` | u64 | 源收藏夹 media_id |
122    /// | `tar_media_id` | u64 | 目标收藏夹 media_id |
123    /// | `mid` | u64 | 用户 mid |
124    /// | `resources` | &str | 形如 "{内容id}:{内容类型},..." |
125    pub async fn fav_resource_copy(
126        &self,
127        src_media_id: u64,
128        tar_media_id: u64,
129        mid: u64,
130        resources: &str,
131    ) -> Result<BpiResponse<i32>, BpiError> {
132        let csrf = self.csrf()?;
133
134        let form = [
135            ("src_media_id", src_media_id.to_string()),
136            ("tar_media_id", tar_media_id.to_string()),
137            ("mid", mid.to_string()),
138            ("resources", resources.to_string()),
139            ("platform", "web".to_string()),
140            ("csrf", csrf),
141        ];
142
143        self.post("https://api.bilibili.com/x/v3/fav/resource/copy")
144            .form(&form)
145            .send_bpi("批量复制内容")
146            .await
147    }
148
149    /// 批量移动内容
150    /// `resources`: "{内容id}:{内容类型},..."
151    ///
152    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
153    ///
154    /// 参数
155    ///
156    /// | 名称 | 类型 | 说明 |
157    /// | ---- | ---- | ---- |
158    /// | `src_media_id` | u64 | 源收藏夹 media_id |
159    /// | `tar_media_id` | u64 | 目标收藏夹 media_id |
160    /// | `mid` | u64 | 用户 mid |
161    /// | `resources` | &str | 形如 "{内容id}:{内容类型},..." |
162    pub async fn fav_resource_move(
163        &self,
164        src_media_id: u64,
165        tar_media_id: u64,
166        mid: u64,
167        resources: &str,
168    ) -> Result<BpiResponse<i32>, BpiError> {
169        let csrf = self.csrf()?;
170
171        let form = [
172            ("src_media_id", src_media_id.to_string()),
173            ("tar_media_id", tar_media_id.to_string()),
174            ("mid", mid.to_string()),
175            ("resources", resources.to_string()),
176            ("platform", "web".to_string()),
177            ("csrf", csrf),
178        ];
179
180        self.post("https://api.bilibili.com/x/v3/fav/resource/move")
181            .form(&form)
182            .send_bpi("批量移动内容")
183            .await
184    }
185
186    /// 批量删除内容
187    /// `resources`: "{内容id}:{内容类型},..."
188    ///
189    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
190    ///
191    /// 参数
192    ///
193    /// | 名称 | 类型 | 说明 |
194    /// | ---- | ---- | ---- |
195    /// | `media_id` | u64 | 收藏夹 media_id |
196    /// | `resources` | &str | 形如 "{内容id}:{内容类型},..." |
197    pub async fn fav_resource_batch_del(
198        &self,
199        media_id: u64,
200        resources: &str,
201    ) -> Result<BpiResponse<i32>, BpiError> {
202        let csrf = self.csrf()?;
203
204        let form = [
205            ("media_id", media_id.to_string()),
206            ("resources", resources.to_string()),
207            ("platform", "web".to_string()),
208            ("csrf", csrf),
209        ];
210
211        self.post("https://api.bilibili.com/x/v3/fav/resource/batch-del")
212            .form(&form)
213            .send_bpi("批量删除内容")
214            .await
215    }
216
217    /// 清空所有失效内容
218    ///
219    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
220    ///
221    /// 参数
222    ///
223    /// | 名称 | 类型 | 说明 |
224    /// | ---- | ---- | ---- |
225    /// | `media_id` | u64 | 收藏夹 media_id |
226    pub async fn fav_resource_clean(&self, media_id: u64) -> Result<BpiResponse<i32>, BpiError> {
227        let csrf = self.csrf()?;
228
229        let form = [("media_id", media_id.to_string()), ("csrf", csrf)];
230
231        self.post("https://api.bilibili.com/x/v3/fav/resource/clean")
232            .form(&form)
233            .send_bpi("清空所有失效内容")
234            .await
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241    use tracing::info;
242
243    #[tokio::test]
244    async fn test_fav_folder_add_and_del() {
245        let bpi = BpiClient::new();
246        let title = "Test_Fav_Folder_Add2";
247        let intro = "This is a test folder2.";
248
249        // 1. 新建一个私密收藏夹
250        let add_resp = bpi.fav_folder_add(title, Some(intro), Some(1), None).await;
251        info!("Add folder result: {:?}", add_resp);
252        assert!(add_resp.is_ok());
253
254        let new_folder_id = add_resp.unwrap().data.unwrap().id;
255        info!("New folder ID: {}", new_folder_id);
256
257        tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
258
259        // 2. 删除新建的收藏夹
260        let del_resp = bpi.fav_folder_del(&[new_folder_id]).await;
261        info!("Delete folder result: {:?}", del_resp);
262        assert!(del_resp.is_ok());
263    }
264
265    #[tokio::test]
266    async fn test_fav_folder_edit() {
267        let bpi = BpiClient::new();
268        // 替换为你的测试收藏夹ID
269        let media_id = 3717139570;
270        let title = "Edited Title";
271        let intro = "Edited Intro";
272
273        let resp = bpi
274            .fav_folder_edit(media_id, title, Some(intro), Some(0), None)
275            .await;
276        info!("Edit folder result: {:?}", resp);
277        assert!(resp.is_ok());
278    }
279
280    #[tokio::test]
281    async fn test_fav_resource_operations() {
282        let bpi = BpiClient::new();
283        // 替换为你的源/目标收藏夹ID和资源ID
284        let src_media_id = 3717139570;
285        let tar_media_id = 3641682570;
286        let mid = 4279370;
287        let resources_copy = "115087859779103:2";
288        let resources_del = "442608504:2";
289        let resources_move = "739661210:2";
290
291        // 1. 批量复制
292        let copy_resp = bpi
293            .fav_resource_copy(src_media_id, tar_media_id, mid, resources_copy)
294            .await;
295        info!("Copy resources result: {:?}", copy_resp);
296        assert!(copy_resp.is_ok());
297
298        // 2. 批量移动
299        let move_resp = bpi
300            .fav_resource_move(src_media_id, tar_media_id, mid, resources_move)
301            .await;
302        info!("Move resources result: {:?}", move_resp);
303        assert!(move_resp.is_ok());
304
305        // 3. 批量删除
306        let del_resp = bpi
307            .fav_resource_batch_del(tar_media_id, resources_del)
308            .await;
309        info!("Batch delete resources result: {:?}", del_resp);
310        assert!(del_resp.is_ok());
311    }
312
313    #[tokio::test]
314    async fn test_fav_resource_clean() {
315        let bpi = BpiClient::new();
316        // 替换为你的测试收藏夹ID
317        let media_id = 3717139570;
318        let resp = bpi.fav_resource_clean(media_id).await;
319        info!("Clean invalid resources result: {:?}", resp);
320        assert!(resp.is_ok());
321    }
322}