Skip to main content

bpi_rs/audio/
action.rs

1//! 音频投币&收藏
2//!
3//! [查看 API 文档](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/action.md)
4//!
5
6use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
7use serde::{ Deserialize, Serialize };
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PromptData {
12    /// 是否为未关注用户收藏
13    prompt: bool,
14}
15
16impl BpiClient {
17    /// 收藏音频到收藏夹(同视频收藏夹)
18    ///
19    /// # 参数
20    /// | 名称   | 类型 | 说明       |
21    /// | ------ | ---- | ---------- |
22    /// | `sid`  | u64  | 音频 auid  |
23    /// | `add_media_ids` | `Vec<&str>`|添加的合集ids|
24    /// | `del_media_ids` | `Vec<&str>`|从中删除的合集ids|
25    ///
26    /// 与视频收藏几乎一样
27    pub async fn audio_collection_to_fav(
28        &self,
29        rid: u64,
30        add_media_ids: Option<Vec<&str>>,
31        del_media_ids: Option<Vec<&str>>
32    ) -> Result<BpiResponse<PromptData>, BpiError> {
33        if add_media_ids.is_none() && del_media_ids.is_none() {
34            return Err(BpiError::InvalidParameter {
35                field: "media_ids",
36                message: "请至少指定一个操作",
37            });
38        }
39        let csrf = self.csrf()?;
40        let mut params = HashMap::new();
41
42        params.extend([
43            ("rid", rid.to_string()),
44            ("type", "12".to_string()),
45            ("csrf", csrf),
46        ]);
47
48        if let Some(ids) = add_media_ids {
49            let s = ids
50                .into_iter()
51                .map(|id| id.to_string())
52                .collect::<Vec<_>>()
53                .join(",");
54            params.insert("add_media_ids", s);
55        }
56        if let Some(ids) = del_media_ids {
57            let s = ids
58                .into_iter()
59                .map(|id| id.to_string())
60                .collect::<Vec<_>>()
61                .join(",");
62            params.insert("del_media_ids", s);
63        }
64        let result = self
65            .get("https://api.bilibili.com/medialist/gateway/coll/resource/deal")
66            .form(&params)
67            .send_bpi("收藏音频到收藏夹").await?;
68        Ok(result)
69    }
70
71    /// 查询音频收藏状态
72    ///
73    /// # 参数
74    /// | 名称   | 类型 | 说明       |
75    /// | ------ | ---- | ---------- |
76    /// | `sid`  | u64  | 音频 auid  |
77    ///
78    /// # 返回
79    /// | 值       | 说明     |
80    /// | -------- | -------- |
81    /// | `true`   | 操作成功?   |
82    pub async fn audio_collection_to(
83        &self,
84        sid: u64,
85        cids: u64
86    ) -> Result<BpiResponse<bool>, BpiError> {
87        let csrf = self.csrf()?;
88
89        let result = self
90            .get("https://www.bilibili.com/audio/music-service-c/web/collections/songs-coll")
91            .form(
92                &[
93                    ("sid", sid.to_string()),
94                    ("cids", cids.to_string()),
95                    ("csrf", csrf),
96                ]
97            )
98            .send_bpi("收藏音频到歌单").await?;
99        Ok(result)
100    }
101
102    /// 查询音频收藏状态
103    ///
104    /// # 参数
105    /// | 名称   | 类型 | 说明       |
106    /// | ------ | ---- | ---------- |
107    /// | `sid`  | u64  | 音频 auid  |
108    ///
109    /// # 返回
110    /// | 值       | 说明     |
111    /// | -------- | -------- |
112    /// | `true`   | 已收藏   |
113    /// | `false`  | 未收藏   |
114    ///
115    /// # 文档
116    /// [查询音频收藏状态](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/action.md#查询音频收藏状态)
117
118    pub async fn audio_collection_status(&self, sid: u64) -> Result<BpiResponse<bool>, BpiError> {
119        let result = self
120            .get("https://www.bilibili.com/audio/music-service-c/web/collections/songs-coll")
121            .query(&[("sid", sid.to_string())])
122            .send_bpi("查询音频收藏状态").await?;
123        Ok(result)
124    }
125
126    /// 查询音频投币数
127    ///
128    /// # 参数
129    /// | 名称   | 类型 | 说明       |
130    /// | ------ | ---- | ---------- |
131    /// | `sid`  | u64  | 音频 auid  |
132    ///
133    /// # 返回
134    /// 投币数量,`0` 为未投币,上限为 `2`
135    ///
136    /// # 文档
137    /// [查询音频投币数](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/action.md#查询音频投币数)
138
139    pub async fn audio_coin_count(&self, sid: u64) -> Result<BpiResponse<i32>, BpiError> {
140        let result = self
141            .get("https://www.bilibili.com/audio/music-service-c/web/coin/audio")
142            .query(&[("sid", sid.to_string())])
143            .send_bpi("查询音频投币数").await?;
144        Ok(result)
145    }
146
147    /// 投币音频
148    ///
149    /// # 参数
150    /// | 名称       | 类型 | 说明                  |
151    /// | ---------- | ---- | --------------------- |
152    /// | `sid`      | u64  | 音频 auid             |
153    /// | `multiply` | i32  | 投币数量(最大为 `2`)|
154    ///
155    /// # 返回
156    /// 当前投币数量
157    ///
158    /// # 文档
159    /// [投币音频](https://github.com/Yuelioi/bilibili-API-collect/tree/cfc5fddcc8a94b74d91970bb5b4eaeb349addc47/docs/audio/action.md#投币音频)
160    pub async fn audio_coin(
161        &self,
162        sid: u64,
163        multiply: u32
164    ) -> Result<BpiResponse<String>, BpiError> {
165        let multiply = multiply.min(1).max(2);
166        let csrf = self.csrf()?;
167        self
168            .post("https://www.bilibili.com/audio/music-service-c/web/coin/add")
169            .form(
170                &[
171                    ("sid", sid.to_string()),
172                    ("multiply", multiply.to_string()),
173                    ("csrf", csrf),
174                ]
175            )
176            .send_bpi("投币音频").await
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    // https://www.bilibili.com/audio/au13598
185
186    const TEST_SID: u64 = 13603;
187
188    #[tokio::test]
189    async fn test_audio_collection_status() -> Result<(), Box<BpiError>> {
190        let bpi = BpiClient::new();
191        let result = bpi.audio_collection_status(TEST_SID).await?;
192
193        let data = result.into_data()?;
194        tracing::info!("{:#?}", data);
195
196        Ok(())
197    }
198
199    #[tokio::test]
200    async fn test_audio_coin_count() -> Result<(), Box<BpiError>> {
201        let bpi = BpiClient::new();
202        let result = bpi.audio_coin_count(TEST_SID).await?;
203
204        let data = result.data.unwrap();
205        assert!(data >= 0 && data <= 2);
206
207        Ok(())
208    }
209
210    #[tokio::test]
211    async fn test_coin_audio() -> Result<(), Box<BpiError>> {
212        let bpi = BpiClient::new();
213        bpi.audio_coin(TEST_SID, 1).await
214            .map(|_| ())
215            .or_else(|e| {
216                // 34005 代表投币已经投过2个了  API错误 [34005]: 未知错误
217                if e.code() == Some(34005) {
218                    Ok(())
219                } else {
220                    Err(Box::new(e))
221                }
222            })
223    }
224}