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("收藏音频到收藏夹").await?;
68 Ok(result)
69 }
70
71 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 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 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 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 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 if e.code() == Some(34005) {
218 Ok(())
219 } else {
220 Err(Box::new(e))
221 }
222 })
223 }
224}