Skip to main content

bpi_rs/video/collection/
action.rs

1//! B站视频合集相关接口实现
2//!
3//! [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/video)
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7// --- 响应数据结构体 ---
8
9/// 创建视频列表响应数据
10#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct CreateSeriesResponseData {
12    /// 视频列表 ID
13    pub series_id: u64,
14}
15
16impl BpiClient {
17    /// 创建视频列表并添加视频
18    ///
19    /// # 文档
20    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/collection.md)
21    ///
22    /// # 参数
23    /// | 名称         | 类型           | 说明                 |
24    /// | ------------ | --------------| -------------------- |
25    /// | `mid`        | u64           | 用户 mid             |
26    /// | `name`       | &str          | 标题                 |
27    /// | `keywords`   | `Option<&str>`  | 关键词,可选         |
28    /// | `description`| `Option<&str>`  | 简介,可选           |
29    /// | `aids`       | `Option<&str>`  | 视频 aid 列表,以`,`分隔,可选 |
30    pub async fn collection_create_and_add_archives(
31        &self,
32        mid: u64,
33        name: &str,
34        keywords: Option<&str>,
35        description: Option<&str>,
36        aids: Option<&str>
37    ) -> Result<BpiResponse<CreateSeriesResponseData>, BpiError> {
38        let csrf = self.csrf()?;
39        let mut form = reqwest::multipart::Form
40            ::new()
41            .text("mid", mid.to_string())
42            .text("name", name.to_string());
43
44        if let Some(k) = keywords {
45            form = form.text("keywords", k.to_string());
46        }
47        if let Some(d) = description {
48            form = form.text("description", d.to_string());
49        }
50        if let Some(a) = aids {
51            form = form.text("aids", a.to_string());
52        }
53
54        self
55            .post("https://api.bilibili.com/x/series/series/createAndAddArchives")
56            .query(&[("csrf", csrf)])
57            .multipart(form)
58            .send_bpi("创建视频列表并添加视频").await
59    }
60
61    /// 删除视频列表
62    ///
63    /// # 文档
64    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/collection.md)
65    ///
66    /// # 参数
67    /// | 名称         | 类型           | 说明                 |
68    /// | ------------ | --------------| -------------------- |
69    /// | `mid`        | u64           | 用户 mid             |
70    /// | `series_id`  | u64           | 视频列表 ID          |
71    pub async fn collection_delete_series(
72        &self,
73        mid: u64,
74        series_id: u64
75    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
76        let csrf = self.csrf()?;
77
78        self
79            .post("https://api.bilibili.com/x/series/series/delete")
80            .query(
81                &[
82                    ("csrf", csrf),
83                    ("mid", mid.to_string()),
84                    ("series_id", series_id.to_string()),
85                    ("aids", "".to_string()),
86                ]
87            )
88            .send_bpi("删除视频列表").await
89    }
90
91    /// 从视频列表中删除稿件
92    ///
93    /// # 文档
94    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/collection.md)
95    ///
96    /// # 参数
97    /// | 名称         | 类型           | 说明                 |
98    /// | ------------ | --------------| -------------------- |
99    /// | `mid`        | u64           | 用户 mid             |
100    /// | `series_id`  | u64           | 视频列表 ID          |
101    /// | `aids`       | &str          | 视频 aid 列表,以`,`分隔 |
102    pub async fn collection_delete_archives_from_series(
103        &self,
104        mid: u64,
105        series_id: u64,
106        aids: &str
107    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
108        let csrf = self.csrf()?;
109
110        let params = [
111            ("mid", mid.to_string()),
112            ("series_id", series_id.to_string()),
113            ("aids", aids.to_string()),
114        ];
115
116        self
117            .post("https://api.bilibili.com/x/series/series/delArchives")
118            .query(&[("csrf", csrf)])
119            .form(&params)
120            .send_bpi("从视频列表中删除稿件").await
121    }
122
123    /// 添加稿件至视频列表
124    ///
125    /// # 文档
126    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/collection.md)
127    ///
128    /// # 参数
129    /// | 名称         | 类型           | 说明                 |
130    /// | ------------ | --------------| -------------------- |
131    /// | `mid`        | u64           | 用户 mid             |
132    /// | `series_id`  | u64           | 视频列表 ID          |
133    /// | `aids`       | &str          | 视频 aid 列表,以`,`分隔 |
134    pub async fn collection_add_archives_to_series(
135        &self,
136        mid: u64,
137        series_id: u64,
138        aids: &str
139    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
140        let csrf = self.csrf()?;
141
142        let params = [
143            ("mid", mid.to_string()),
144            ("series_id", series_id.to_string()),
145            ("aids", aids.to_string()),
146        ];
147
148        self
149            .post("https://api.bilibili.com/x/series/series/addArchives")
150            .query(&[("csrf", csrf)])
151            .form(&params)
152            .send_bpi("添加稿件至视频列表").await
153    }
154
155    /// 编辑视频列表信息
156    ///
157    /// # 文档
158    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/collection.md)
159    ///
160    /// # 参数
161    /// | 名称         | 类型           | 说明                 |
162    /// | ------------ | --------------| -------------------- |
163    /// | `mid`        | u64           | 用户 mid             |
164    /// | `series_id`  | u64           | 视频列表 ID          |
165    /// | `name`       | &str          | 标题                 |
166    /// | `keywords`   | `Option<&str>`  | 关键词,可选         |
167    /// | `description`| `Option<&str>`  | 简介,可选           |
168    /// | `add_aids`   | `Option<&str>`  | 要添加的视频 aid 列表,以`,`分隔,可选 |
169    /// | `del_aids`   | `Option<&str>`  | 要删除的视频 aid 列表,以`,`分隔,可选 |
170    pub async fn collection_update_series(
171        &self,
172        mid: u64,
173        series_id: u64,
174        name: &str,
175        keywords: Option<&str>,
176        description: Option<&str>,
177        add_aids: Option<&str>,
178        del_aids: Option<&str>
179    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
180        let csrf = self.csrf()?;
181
182        let mut form = reqwest::multipart::Form
183            ::new()
184            .text("mid", mid.to_string())
185            .text("series_id", series_id.to_string())
186            .text("name", name.to_string());
187
188        if let Some(k) = keywords {
189            form = form.text("keywords", k.to_string());
190        }
191        if let Some(d) = description {
192            form = form.text("description", d.to_string());
193        }
194        if let Some(a) = add_aids {
195            form = form.text("add_aids", a.to_string());
196        }
197        if let Some(d) = del_aids {
198            form = form.text("del_aids", d.to_string());
199        }
200
201        self
202            .post("https://api.bilibili.com/x/series/series/update")
203            .query(&[("csrf", csrf)])
204            .multipart(form)
205            .send_bpi("编辑视频列表信息").await
206    }
207}
208
209// --- 测试模块 ---
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use tracing::info;
215
216    // 请在运行测试前设置环境变量 `BPI_COOKIE`,以包含 SESSDATA 等登录信息
217    // mid 和 series_id 根据实际情况修改
218
219    // 测试用的 mid
220    const TEST_MID: u64 = 4279370;
221    // 测试用的合集 ID
222    const TEST_SERIES_ID: u64 = 4954206;
223
224    const TEST_AID: &str = "772876546";
225
226    #[tokio::test]
227    async fn test_create_and_add_archives() -> Result<(), BpiError> {
228        let bpi = BpiClient::new();
229        let resp = bpi.collection_create_and_add_archives(
230            TEST_MID,
231            "Rust Bilibili API Test",
232            Some("rust,api"),
233            Some("这是一个用于 Rust Bilibili API 测试的视频列表"),
234            Some(TEST_AID)
235        ).await?;
236        let data = resp.into_data()?;
237
238        info!("创建的视频列表 ID: {:?}", data.series_id);
239        assert!(data.series_id > 0);
240
241        Ok(())
242    }
243
244    #[tokio::test]
245    async fn test_add_archives_to_series() -> Result<(), BpiError> {
246        let bpi = BpiClient::new();
247        let resp = bpi.collection_add_archives_to_series(TEST_MID, TEST_SERIES_ID, TEST_AID).await?;
248
249        info!("添加稿件至视频列表成功: {:?}", resp);
250
251        Ok(())
252    }
253
254    #[tokio::test]
255    async fn test_update_series() -> Result<(), BpiError> {
256        let bpi = BpiClient::new();
257        let resp = bpi.collection_update_series(
258            TEST_MID,
259            TEST_SERIES_ID,
260            "Rust Bilibili API Test Updated",
261            Some("rust,api,update"),
262            Some("更新后的简介"),
263            Some(TEST_AID),
264            None
265        ).await?;
266
267        info!("编辑视频列表成功: {:?}", resp);
268
269        Ok(())
270    }
271
272    #[tokio::test]
273    async fn test_delete_archives_from_series() -> Result<(), BpiError> {
274        let bpi = BpiClient::new();
275        let resp = bpi.collection_delete_archives_from_series(
276            TEST_MID,
277            TEST_SERIES_ID,
278            TEST_AID
279        ).await?;
280
281        info!("从视频列表中删除稿件成功: {:?}", resp);
282
283        Ok(())
284    }
285
286    #[tokio::test]
287    async fn test_delete_series() -> Result<(), BpiError> {
288        let bpi = BpiClient::new();
289        // 假设 TEST_SERIES_ID 是一个需要被删除的测试用列表
290        let resp = bpi.collection_delete_series(TEST_MID, TEST_SERIES_ID).await?;
291
292        info!("删除视频列表成功: {:?}", resp);
293
294        Ok(())
295    }
296}