bpi_rs/audio/
action.rs

1//! 音频投币&收藏
2//!
3//! https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/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("收藏音频到收藏夹")
68            .await?;
69        Ok(result)
70    }
71
72    /// 查询音频收藏状态
73    ///
74    /// # 参数
75    /// | 名称   | 类型 | 说明       |
76    /// | ------ | ---- | ---------- |
77    /// | `sid`  | u64  | 音频 auid  |
78    ///
79    /// # 返回
80    /// | 值       | 说明     |
81    /// | -------- | -------- |
82    /// | `true`   | 操作成功?   |
83    pub async fn audio_collection_to(
84        &self,
85        sid: u64,
86        cids: u64,
87    ) -> Result<BpiResponse<bool>, BpiError> {
88        let csrf = self.csrf()?;
89
90        let result = self
91            .get("https://www.bilibili.com/audio/music-service-c/web/collections/songs-coll")
92            .form(&[
93                ("sid", sid.to_string()),
94                ("cids", cids.to_string()),
95                ("csrf", csrf),
96            ])
97            .send_bpi("收藏音频到歌单")
98            .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/SocialSisterYi/bilibili-API-collect/blob/master/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("查询音频收藏状态")
123            .await?;
124        Ok(result)
125    }
126
127    /// 查询音频投币数
128    ///
129    /// # 参数
130    /// | 名称   | 类型 | 说明       |
131    /// | ------ | ---- | ---------- |
132    /// | `sid`  | u64  | 音频 auid  |
133    ///
134    /// # 返回
135    /// 投币数量,`0` 为未投币,上限为 `2`
136    ///
137    /// # 文档
138    /// [查询音频投币数](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/action.md#查询音频投币数)
139
140    pub async fn audio_coin_count(&self, sid: u64) -> Result<BpiResponse<i32>, BpiError> {
141        let result = self
142            .get("https://www.bilibili.com/audio/music-service-c/web/coin/audio")
143            .query(&[("sid", sid.to_string())])
144            .send_bpi("查询音频投币数")
145            .await?;
146        Ok(result)
147    }
148
149    /// 投币音频
150    ///
151    /// # 参数
152    /// | 名称       | 类型 | 说明                  |
153    /// | ---------- | ---- | --------------------- |
154    /// | `sid`      | u64  | 音频 auid             |
155    /// | `multiply` | i32  | 投币数量(最大为 `2`)|
156    ///
157    /// # 返回
158    /// 当前投币数量
159    ///
160    /// # 文档
161    /// [投币音频](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/audio/action.md#投币音频)
162    pub async fn audio_coin(
163        &self,
164        sid: u64,
165        multiply: u32,
166    ) -> Result<BpiResponse<String>, BpiError> {
167        let multiply = multiply.min(1).max(2);
168        let csrf = self.csrf()?;
169        self.post("https://www.bilibili.com/audio/music-service-c/web/coin/add")
170            .form(&[
171                ("sid", sid.to_string()),
172                ("multiply", multiply.to_string()),
173                ("csrf", csrf),
174            ])
175            .send_bpi("投币音频")
176            .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.map(|_| ()).or_else(|e| {
214            // 34005 代表投币已经投过2个了  API错误 [34005]: 未知错误
215            if e.code() == Some(34005) {
216                Ok(())
217            } else {
218                Err(Box::new(e))
219            }
220        })
221    }
222}