bpi_rs/video/collection/
action.rs

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