Skip to main content

bpi_rs/fav/
action.rs

1use super::info::FavFolderInfo;
2use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
3
4impl BpiClient {
5    /// 新建收藏夹
6    ///
7    /// # 文档
8    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
9    ///
10    /// # 参数
11    ///
12    /// | 名称 | 类型 | 说明 |
13    /// | ---- | ---- | ---- |
14    /// | `title` | &str | 收藏夹标题 |
15    /// | `intro` | `Option<&str>` | 介绍 |
16    /// | `privacy` | `Option<u8>` | 0 公开,1 私密 |
17    /// | `cover` | `Option<&str>` | 封面 URL |
18    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    /// 修改收藏夹
45    ///
46    /// # 文档
47    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
48    ///
49    /// # 参数
50    ///
51    /// | 名称 | 类型 | 说明 |
52    /// | ---- | ---- | ---- |
53    /// | `media_id` | u64 | 收藏夹 media_id |
54    /// | `title` | &str | 标题 |
55    /// | `intro` | `Option<&str>` | 介绍 |
56    /// | `privacy` | `Option<u8>` | 0 公开,1 私密 |
57    /// | `cover` | `Option<&str>` | 封面 URL |
58    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    /// 删除收藏夹
90    ///
91    /// # 文档
92    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
93    ///
94    /// # 参数
95    ///
96    /// | 名称 | 类型 | 说明 |
97    /// | ---- | ---- | ---- |
98    /// | `media_ids` | &`[u64]` | 多个收藏夹 media_id |
99    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    /// 批量复制内容
119    /// `resources`: "{内容id}:{内容类型},..."
120    ///
121    /// # 文档
122    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
123    ///
124    /// # 参数
125    ///
126    /// | 名称 | 类型 | 说明 |
127    /// | ---- | ---- | ---- |
128    /// | `src_media_id` | u64 | 源收藏夹 media_id |
129    /// | `tar_media_id` | u64 | 目标收藏夹 media_id |
130    /// | `mid` | u64 | 用户 mid |
131    /// | `resources` | &str | 形如 "{内容id}:{内容类型},..." |
132    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    /// 批量移动内容
157    /// `resources`: "{内容id}:{内容类型},..."
158    ///
159    /// # 文档
160    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
161    ///
162    /// # 参数
163    ///
164    /// | 名称 | 类型 | 说明 |
165    /// | ---- | ---- | ---- |
166    /// | `src_media_id` | u64 | 源收藏夹 media_id |
167    /// | `tar_media_id` | u64 | 目标收藏夹 media_id |
168    /// | `mid` | u64 | 用户 mid |
169    /// | `resources` | &str | 形如 "{内容id}:{内容类型},..." |
170    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    /// 批量删除内容
195    /// `resources`: "{内容id}:{内容类型},..."
196    ///
197    /// # 文档
198    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
199    ///
200    /// # 参数
201    ///
202    /// | 名称 | 类型 | 说明 |
203    /// | ---- | ---- | ---- |
204    /// | `media_id` | u64 | 收藏夹 media_id |
205    /// | `resources` | &str | 形如 "{内容id}:{内容类型},..." |
206    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    /// 清空所有失效内容
227    ///
228    /// # 文档
229    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav)
230    ///
231    /// # 参数
232    ///
233    /// | 名称 | 类型 | 说明 |
234    /// | ---- | ---- | ---- |
235    /// | `media_id` | u64 | 收藏夹 media_id |
236    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        // 1. 新建一个私密收藏夹
263        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        // 2. 删除新建的收藏夹
273        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        // 替换为你的测试收藏夹ID
282        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        // 替换为你的源/目标收藏夹ID和资源ID
295        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        // 1. 批量复制
303        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        // 2. 批量移动
313        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        // 3. 批量删除
323        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        // 替换为你的测试收藏夹ID
332        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}