1use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PromptData {
12 prompt: bool,
14}
15
16impl BpiClient {
17 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(¶ms)
67 .send_bpi("收藏音频到收藏夹")
68 .await?;
69 Ok(result)
70 }
71
72 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 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 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 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 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 if e.code() == Some(34005) {
216 Ok(())
217 } else {
218 Err(Box::new(e))
219 }
220 })
221 }
222}